/* * FreeRTOS+FAT SL V1.0.1 (C) 2014 HCC Embedded * * The FreeRTOS+FAT SL license terms are different to the FreeRTOS license * terms. * * FreeRTOS+FAT SL uses a dual license model that allows the software to be used * under a standard GPL open source license, or a commercial license. The * standard GPL license (unlike the modified GPL license under which FreeRTOS * itself is distributed) requires that all software statically linked with * FreeRTOS+FAT SL is also distributed under the same GPL V2 license terms. * Details of both license options follow: * * - Open source licensing - * FreeRTOS+FAT SL is a free download and may be used, modified, evaluated and * distributed without charge provided the user adheres to version two of the * GNU General Public License (GPL) and does not remove the copyright notice or * this text. The GPL V2 text is available on the gnu.org web site, and on the * following URL: http://www.FreeRTOS.org/gpl-2.0.txt. * * - Commercial licensing - * Businesses and individuals who for commercial or other reasons cannot comply * with the terms of the GPL V2 license must obtain a commercial license before * incorporating FreeRTOS+FAT SL into proprietary software for distribution in * any form. Commercial licenses can be purchased from * http://shop.freertos.org/fat_sl and do not require any source files to be * changed. * * FreeRTOS+FAT SL is distributed in the hope that it will be useful. You * cannot use FreeRTOS+FAT SL unless you agree that you use the software 'as * is'. FreeRTOS+FAT SL is provided WITHOUT ANY WARRANTY; without even the * implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. Real Time Engineers Ltd. and HCC Embedded disclaims all * conditions and terms, be they implied, expressed, or statutory. * * http://www.FreeRTOS.org * http://www.FreeRTOS.org/FreeRTOS-Plus * */ #include "../../api/fat_sl.h" #include "../../psp/include/psp_string.h" #include "dir.h" #include "util.h" #include "volume.h" #include "drv.h" #include "fat.h" #include "file.h" #include "../../version/ver_fat_sl.h" #if VER_FAT_SL_MAJOR != 5 || VER_FAT_SL_MINOR != 2 #error Incompatible FAT_SL version number! #endif /**************************************************************************** * * _f_findfilewc * * internal function to finding file in directory entry with or without * wildcard * * INPUTS * * name - filename * ext - fileextension * pos - where to start searching, and contains current position * pde - store back the directory entry pointer * wc - wildcard checking? * * RETURNS * * 0 - if file was not found * 1 - if file was found * ***************************************************************************/ unsigned char _f_findfilewc ( char * name, char * ext, F_POS * pos, F_DIRENTRY * * pde, unsigned char wc ) { while ( pos->cluster < F_CLUSTER_RESERVED ) { for ( ; pos->sector < pos->sectorend ; pos->sector++ ) { F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos ); if ( _f_readglsector( pos->sector ) ) { return 0; /*not found*/ } for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ ) { unsigned char b, ok; if ( !de->name[0] ) { return 0; /*empty*/ } if ( (unsigned char)( de->name[0] ) == 0xe5 ) { continue; /*deleted*/ } if ( de->attr & F_ATTR_VOLUME ) { continue; } if ( wc ) { for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ ) { if ( name[b] == '*' ) { break; } if ( name[b] != '?' ) { if ( de->name[b] != name[b] ) { ok = 0; break; } } } if ( ok ) { for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ ) { if ( ext[b] == '*' ) { if ( pde ) { *pde = de; } return 1; } if ( ext[b] != '?' ) { if ( de->ext[b] != ext[b] ) { ok = 0; break; } } } if ( ok ) { if ( pde ) { *pde = de; } return 1; } } } else { for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ ) { if ( de->name[b] != name[b] ) { ok = 0; break; } } if ( ok ) { for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ ) { if ( de->ext[b] != ext[b] ) { ok = 0; break; } } if ( ok ) { if ( pde ) { *pde = de; } return 1; } } } } pos->pos = 0; } if ( !pos->cluster ) { if ( gl_volume.mediatype == F_FAT32_MEDIA ) { pos->cluster = gl_volume.bootrecord.rootcluster; } else { return 0; } } { unsigned long nextcluster; gl_volume.fatsector = (unsigned long)-1; if ( _f_getclustervalue( pos->cluster, &nextcluster ) ) { return 0; /*not found*/ } if ( nextcluster >= F_CLUSTER_RESERVED ) { return 0; /*eof*/ } _f_clustertopos( nextcluster, pos ); } } /* _f_findfilewc */ return 0; } /**************************************************************************** * * _f_getfilename * * create a complete filename from name and extension * * INPUTS * * dest - where to store filename * name - name of the file * ext - extension of the file * ***************************************************************************/ static void _f_getfilename ( char * dest, char * name, char * ext ) { unsigned char a, len; for ( len = a = F_MAXNAME ; a ; a--, len-- ) { if ( name[a - 1] != ' ' ) { break; } } for ( a = 0 ; a < len ; a++ ) { *dest++ = *name++; } for ( len = a = F_MAXEXT ; a ; a--, len-- ) { if ( ext[a - 1] != ' ' ) { break; } } if ( len ) { *dest++ = '.'; } for ( a = 0 ; a < len ; a++ ) { *dest++ = *ext++; } *dest = 0; /*terminateit*/ } /* _f_getfilename */ /**************************************************************************** * * _f_getdecluster * * get a directory entry structure start cluster value * * INPUTS * * de - directory entry * * RETURNS * * directory entry cluster value * ***************************************************************************/ unsigned long _f_getdecluster ( F_DIRENTRY * de ) { unsigned long cluster; if ( gl_volume.mediatype == F_FAT32_MEDIA ) { cluster = _f_getword( &de->clusterhi ); cluster <<= 16; cluster |= _f_getword( &de->clusterlo ); return cluster; } return _f_getword( &de->clusterlo ); } /**************************************************************************** * * _f_setdecluster * * set a directory entry structure start cluster value * * INPUTS * * de - directory entry * cluster - value of the start cluster * ***************************************************************************/ void _f_setdecluster ( F_DIRENTRY * de, unsigned long cluster ) { _f_setword( &de->clusterlo, (unsigned short)( cluster & 0xffff ) ); if ( gl_volume.mediatype == F_FAT32_MEDIA ) { _f_setword( &de->clusterhi, (unsigned short)( cluster >> 16 ) ); } else { _f_setword( &de->clusterhi, (unsigned short)0 ); } } /**************************************************************************** * * _f_findpath * * finding out if path is valid in F_NAME and * correct path info with absolute path (removes relatives) * * INPUTS * * fsname - filled structure with path,drive * pos - where to start searching, and contains current position * * RETURNS * * 0 - if path was not found or invalid * 1 - if path was found * ***************************************************************************/ unsigned char _f_findpath ( F_NAME * fsname, F_POS * pos ) { char * path = fsname->path; char * mpath = path; F_DIRENTRY * de; _f_clustertopos( 0, pos ); for ( ; *path ; ) { char name[F_MAXNAME]; char ext[F_MAXEXT]; unsigned char len = _f_setnameext( path, name, ext ); if ( ( pos->cluster == 0 ) && ( len == 1 ) && ( name[0] == '.' ) ) { _f_clustertopos( 0, pos ); } else { if ( !_f_findfilewc( name, ext, pos, &de, 0 ) ) { return 0; } if ( !( de->attr & F_ATTR_DIR ) ) { return 0; } _f_clustertopos( _f_getdecluster( de ), pos ); } if ( name[0] == '.' ) { if ( len == 1 ) { path += len; if ( !( *path ) ) { if ( mpath != fsname->path ) { mpath--; /*if we are now at the top*/ } break; } path++; continue; } if ( name[1] != '.' ) { return 0; /*invalid name*/ } if ( len != 2 ) { return 0; /*invalid name !*/ } path += len; if ( mpath == fsname->path ) { return 0; /*we are in the top*/ } mpath--; /*no on separator*/ for ( ; ; ) { if ( mpath == fsname->path ) { break; /*we are now at the top*/ } mpath--; if ( *mpath == '/' ) { mpath++; break; } } if ( !( *path ) ) { if ( mpath != fsname->path ) { mpath--; /*if we are now at the top*/ } break; } path++; continue; } else { if ( path == mpath ) /*if no was dots just step*/ { path += len; mpath += len; } else { unsigned char a; for ( a = 0 ; a < len ; a++ ) { *mpath++ = *path++; /*copy if in different pos*/ } } } if ( !( *path ) ) { break; } path++; *mpath++ = '/'; /*add separator*/ } *mpath = 0; /*terminate it*/ return 1; } /* _f_findpath */ /**************************************************************************** * * fn_getcwd * * getting a current working directory of current drive * * INPUTS * * buffer - where to store current working folder * maxlen - buffer length (possible size is F_MAXPATH) * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char fn_getcwd ( char * buffer, unsigned char maxlen, char root ) { unsigned char a; if ( !maxlen ) { return F_NO_ERROR; } maxlen--; /*need for termination*/ if ( root && maxlen ) { *buffer++ = '/'; maxlen--; } for ( a = 0 ; a < maxlen ; a++ ) { char ch = gl_volume.cwd[a]; buffer[a] = ch; if ( !ch ) { break; } } buffer[a] = 0; /*add terminator at the end*/ return F_NO_ERROR; } /* fn_getcwd */ /**************************************************************************** * * fn_findfirst * * find a file(s) or directory(s) in directory * * INPUTS * * filename - filename (with or without wildcards) * find - where to store found file information * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char fn_findfirst ( const char * filename, F_FIND * find ) { unsigned char ret; if ( _f_setfsname( filename, &find->findfsname ) ) { return F_ERR_INVALIDNAME; /*invalid name*/ } if ( _f_checkname( find->findfsname.filename, find->findfsname.fileext ) ) { return F_ERR_INVALIDNAME; /*invalid name, wildcard is ok*/ } ret = _f_getvolume(); if ( ret ) { return ret; } if ( !_f_findpath( &find->findfsname, &find->pos ) ) { return F_ERR_INVALIDDIR; /*search for path*/ } return fn_findnext( find ); } /* fn_findfirst */ /**************************************************************************** * * fn_findnext * * find further file(s) or directory(s) in directory * * INPUTS * * find - where to store found file information (findfirst should call 1st) * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char fn_findnext ( F_FIND * find ) { F_DIRENTRY * de; unsigned char a; unsigned char ret; ret = _f_getvolume(); if ( ret ) { return ret; } if ( !_f_findfilewc( find->findfsname.filename, find->findfsname.fileext, &find->pos, &de, 1 ) ) { return F_ERR_NOTFOUND; } for ( a = 0 ; a < F_MAXNAME ; a++ ) { find->name[a] = de->name[a]; } for ( a = 0 ; a < F_MAXEXT ; a++ ) { find->ext[a] = de->ext[a]; } _f_getfilename( find->filename, (char *)de->name, (char *)de->ext ); find->attr = de->attr; find->cdate = _f_getword( &de->cdate ); find->ctime = _f_getword( &de->ctime ); find->filesize = (long)_f_getlong( &de->filesize ); find->cluster = _f_getdecluster( de ); find->pos.pos++; /*goto next position*/ return 0; } /* fn_findnext */ /**************************************************************************** * * fn_chdir * * change current working directory * * INPUTS * * dirname - new working directory name * * RETURNS * * 0 - if successfully * other - if any error * ***************************************************************************/ unsigned char fn_chdir ( const char * dirname ) { F_POS pos; F_NAME fsname; unsigned char len; unsigned char a; unsigned char ret; ret = _f_setfsname( dirname, &fsname ); if ( ret == 1 ) { return F_ERR_INVALIDNAME; /*invalid name*/ } if ( _f_checknamewc( fsname.filename, fsname.fileext ) ) { return F_ERR_INVALIDNAME; /*invalid name*/ } ret = _f_getvolume(); if ( ret ) { return ret; } for ( len = 0 ; fsname.path[len] ; ) { len++; } if ( len && ( ( fsname.filename[0] != 32 ) || ( fsname.fileext[0] != 32 ) ) ) { fsname.path[len++] = '/'; } _f_getfilename( fsname.path + len, fsname.filename, fsname.fileext ); if ( !( _f_findpath( &fsname, &pos ) ) ) { return F_ERR_NOTFOUND; } for ( a = 0 ; a < F_MAXPATH ; a++ ) { gl_volume.cwd[a] = fsname.path[a]; } return F_NO_ERROR; } /* fn_chdir */ /**************************************************************************** * * _f_initentry * * init directory entry, this function is called if a new entry is coming * * INPUTS * * de - directory entry which needs to be initialized * name - fil ename (8) * ext - file extension (3) * ***************************************************************************/ static void _f_initentry ( F_DIRENTRY * de, char * name, char * ext ) { unsigned short date; unsigned short time; psp_memset( de, 0, sizeof( F_DIRENTRY ) ); /*reset all entries*/ psp_memcpy( de->name, name, sizeof( de->name ) ); psp_memcpy( de->ext, ext, sizeof( de->ext ) ); f_igettimedate( &time, &date ); _f_setword( &de->cdate, date ); /*if there is realtime clock then creation date could be set from*/ _f_setword( &de->ctime, time ); /*if there is realtime clock then creation time could be set from*/ } /**************************************************************************** * * _f_addentry * * Add a new directory entry into driectory list * * INPUTS * * fs_name - filled structure what to add into directory list * pos - where directory cluster chains starts * pde - F_DIRENTRY pointer where to store the entry where it was added * * RETURNS * * 0 - if successfully added * other - if any error (see FS_xxx errorcodes) * ***************************************************************************/ unsigned char _f_addentry ( F_NAME * fsname, F_POS * pos, F_DIRENTRY * * pde ) { unsigned char ret; unsigned short date; unsigned short time; if ( !fsname->filename[0] ) { return F_ERR_INVALIDNAME; } if ( fsname->filename[0] == '.' ) { return F_ERR_INVALIDNAME; } while ( pos->cluster < F_CLUSTER_RESERVED ) { for ( ; pos->sector < pos->sectorend ; pos->sector++ ) { F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos ); ret = _f_readglsector( pos->sector ); if ( ret ) { return ret; } for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ ) { if ( ( !de->name[0] ) || ( (unsigned char)( de->name[0] ) == 0xe5 ) ) { _f_initentry( de, fsname->filename, fsname->fileext ); if ( gl_volume.mediatype == F_FAT32_MEDIA ) { f_igettimedate( &time, &date ); _f_setword( &de->crtdate, date ); /*if there is realtime clock then creation date could be set from*/ _f_setword( &de->crttime, time ); /*if there is realtime clock then creation time could be set from*/ _f_setword( &de->lastaccessdate, date ); /*if there is realtime clock then creation date could be set from*/ } if ( pde ) { *pde = de; } return F_NO_ERROR; } } pos->pos = 0; } if ( !pos->cluster ) { if ( gl_volume.mediatype == F_FAT32_MEDIA ) { pos->cluster = gl_volume.bootrecord.rootcluster; } else { return F_ERR_NOMOREENTRY; } } { unsigned long cluster; gl_volume.fatsector = (unsigned long)-1; ret = _f_getclustervalue( pos->cluster, &cluster ); /*try to get next cluster*/ if ( ret ) { return ret; } if ( cluster < F_CLUSTER_RESERVED ) { _f_clustertopos( cluster, pos ); } else { ret = _f_alloccluster( &cluster ); /*get a new one*/ if ( ret ) { return ret; } if ( cluster < F_CLUSTER_RESERVED ) { if ( gl_file.mode != F_FILE_CLOSE ) { return F_ERR_NOMOREENTRY; } _f_clustertopos( cluster, &gl_file.pos ); ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST ); if ( ret ) { return ret; } ret = _f_setclustervalue( pos->cluster, gl_file.pos.cluster ); if ( ret ) { return ret; } ret = _f_writefatsector(); if ( ret ) { return ret; } gl_volume.fatsector = (unsigned long)-1; psp_memset( gl_sector, 0, F_SECTOR_SIZE ); while ( gl_file.pos.sector < gl_file.pos.sectorend ) { ret = _f_writeglsector( gl_file.pos.sector ); if ( ret ) { return ret; } gl_file.pos.sector++; } _f_clustertopos( gl_file.pos.cluster, pos ); } else { return F_ERR_NOMOREENTRY; } } } } /* _f_addentry */ return F_ERR_NOMOREENTRY; } /**************************************************************************** * * fn_mkdir * * making a new directory * * INPUTS * * dirname - new directory name * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char fn_mkdir ( const char * dirname ) { F_POS posdir; F_POS pos; F_DIRENTRY * de; F_NAME fsname; unsigned long cluster; unsigned char ret; #if F_FILE_CHANGED_EVENT ST_FILE_CHANGED fc; #endif if ( _f_setfsname( dirname, &fsname ) ) { return F_ERR_INVALIDNAME; /*invalid name*/ } if ( _f_checknamewc( fsname.filename, fsname.fileext ) ) { return F_ERR_INVALIDNAME; /*invalid name*/ } ret = _f_getvolume(); if ( ret ) { return ret; } if ( !_f_findpath( &fsname, &posdir ) ) { return F_ERR_INVALIDDIR; } pos = posdir; if ( fsname.filename[0] == '.' ) { return F_ERR_NOTFOUND; } if ( _f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) ) { return F_ERR_DUPLICATED; } pos = posdir; gl_volume.fatsector = (unsigned long)-1; ret = _f_alloccluster( &cluster ); if ( ret ) { return ret; } ret = _f_addentry( &fsname, &pos, &de ); if ( ret ) { return ret; } de->attr |= F_ATTR_DIR; /*set as directory*/ #if F_FILE_CHANGED_EVENT if ( f_filechangedevent ) { fc.action = FACTION_ADDED; fc.flags = FFLAGS_DIR_NAME | FFLAGS_ATTRIBUTES | FFLAGS_SIZE | FFLAGS_LAST_WRITE; fc.attr = de->attr; fc.ctime = _f_getword( de->ctime ); fc.cdate = _f_getword( de->cdate ); fc.filesize = _f_getlong( de->filesize ); } #endif if ( gl_file.mode != F_FILE_CLOSE ) { return F_ERR_LOCKED; } _f_clustertopos( cluster, &gl_file.pos ); _f_setdecluster( de, cluster ); /*new dir*/ (void)_f_writeglsector( (unsigned long)-1 ); /*write actual directory sector*/ de = (F_DIRENTRY *)gl_sector; _f_initentry( de, ". ", " " ); de->attr = F_ATTR_DIR; /*set as directory*/ _f_setdecluster( de, cluster ); /*current*/ de++; _f_initentry( de, ".. ", " " ); de->attr = F_ATTR_DIR; /*set as directory*/ _f_setdecluster( de, posdir.cluster ); /*parent*/ de++; psp_memset( de, 0, ( F_SECTOR_SIZE - 2 * sizeof( F_DIRENTRY ) ) ); ret = _f_writeglsector( gl_file.pos.sector ); if ( ret ) { return ret; } gl_file.pos.sector++; psp_memset( gl_sector, 0, ( 2 * sizeof( F_DIRENTRY ) ) ); while ( gl_file.pos.sector < gl_file.pos.sectorend ) { ret = _f_writeglsector( gl_file.pos.sector ); if ( ret ) { return ret; } gl_file.pos.sector++; } gl_volume.fatsector = (unsigned long)-1; ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST ); if ( ret ) { return ret; } ret = _f_writefatsector(); #if F_FILE_CHANGED_EVENT if ( f_filechangedevent && !ret ) { fc.action = FACTION_ADDED; fc.flags = FFLAGS_DIR_NAME; if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) ) { f_filechangedevent( &fc ); } } #endif return ret; } /* fn_mkdir */ /**************************************************************************** * * fn_rmdir * * Remove directory, only could be removed if empty * * INPUTS * * dirname - which directory needed to be removed * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char fn_rmdir ( const char * dirname ) { unsigned char ret; F_POS pos; F_DIRENTRY * de; F_NAME fsname; unsigned long dirsector; unsigned char a; if ( _f_setfsname( dirname, &fsname ) ) { return F_ERR_INVALIDNAME; /*invalid name*/ } if ( _f_checknamewc( fsname.filename, fsname.fileext ) ) { return F_ERR_INVALIDNAME; /*invalid name*/ } if ( fsname.filename[0] == '.' ) { return F_ERR_NOTFOUND; } ret = _f_getvolume(); if ( ret ) { return ret; } if ( !( _f_findpath( &fsname, &pos ) ) ) { return F_ERR_INVALIDDIR; } if ( !_f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) ) { return F_ERR_NOTFOUND; } if ( !( de->attr & F_ATTR_DIR ) ) { return F_ERR_INVALIDDIR; /*not a directory*/ } dirsector = gl_volume.actsector; if ( gl_file.mode != F_FILE_CLOSE ) { return F_ERR_LOCKED; } _f_clustertopos( _f_getdecluster( de ), &gl_file.pos ); for ( ; ; ) { F_DIRENTRY * de2; char ch = 0; ret = _f_getcurrsector(); if ( ret == F_ERR_EOF ) { break; } if ( ret ) { return ret; } de2 = (F_DIRENTRY *)gl_sector; for ( a = 0 ; a < ( F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ) ; a++, de2++ ) { ch = de2->name[0]; if ( !ch ) { break; } if ( (unsigned char)ch == 0xe5 ) { continue; } if ( ch == '.' ) { continue; } return F_ERR_NOTEMPTY; /*something is there*/ } if ( !ch ) { break; } gl_file.pos.sector++; } ret = _f_readglsector( dirsector ); if ( ret ) { return ret; } de->name[0] = (unsigned char)0xe5; ret = _f_writeglsector( dirsector ); if ( ret ) { return ret; } gl_volume.fatsector = (unsigned long)-1; ret = _f_removechain( _f_getdecluster( de ) ); #if F_FILE_CHANGED_EVENT if ( f_filechangedevent && !ret ) { ST_FILE_CHANGED fc; fc.action = FACTION_REMOVED; fc.flags = FFLAGS_DIR_NAME; if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) ) { f_filechangedevent( &fc ); } } #endif return ret; } /* fn_rmdir */