2 * FreeRTOS+FAT SL V1.0.1 (C) 2014 HCC Embedded
\r
4 * The FreeRTOS+FAT SL license terms are different to the FreeRTOS license
\r
7 * FreeRTOS+FAT SL uses a dual license model that allows the software to be used
\r
8 * under a standard GPL open source license, or a commercial license. The
\r
9 * standard GPL license (unlike the modified GPL license under which FreeRTOS
\r
10 * itself is distributed) requires that all software statically linked with
\r
11 * FreeRTOS+FAT SL is also distributed under the same GPL V2 license terms.
\r
12 * Details of both license options follow:
\r
14 * - Open source licensing -
\r
15 * FreeRTOS+FAT SL is a free download and may be used, modified, evaluated and
\r
16 * distributed without charge provided the user adheres to version two of the
\r
17 * GNU General Public License (GPL) and does not remove the copyright notice or
\r
18 * this text. The GPL V2 text is available on the gnu.org web site, and on the
\r
19 * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
\r
21 * - Commercial licensing -
\r
22 * Businesses and individuals who for commercial or other reasons cannot comply
\r
23 * with the terms of the GPL V2 license must obtain a commercial license before
\r
24 * incorporating FreeRTOS+FAT SL into proprietary software for distribution in
\r
25 * any form. Commercial licenses can be purchased from
\r
26 * http://shop.freertos.org/fat_sl and do not require any source files to be
\r
29 * FreeRTOS+FAT SL is distributed in the hope that it will be useful. You
\r
30 * cannot use FreeRTOS+FAT SL unless you agree that you use the software 'as
\r
31 * is'. FreeRTOS+FAT SL is provided WITHOUT ANY WARRANTY; without even the
\r
32 * implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A
\r
33 * PARTICULAR PURPOSE. Real Time Engineers Ltd. and HCC Embedded disclaims all
\r
34 * conditions and terms, be they implied, expressed, or statutory.
\r
36 * http://www.FreeRTOS.org
\r
37 * http://www.FreeRTOS.org/FreeRTOS-Plus
\r
41 #include "../../api/fat_sl.h"
\r
42 #include "../../psp/include/psp_string.h"
\r
51 #include "../../version/ver_fat_sl.h"
\r
52 #if VER_FAT_SL_MAJOR != 5 || VER_FAT_SL_MINOR != 2
\r
53 #error Incompatible FAT_SL version number!
\r
57 /****************************************************************************
\r
61 * internal function to finding file in directory entry with or without
\r
67 * ext - fileextension
\r
68 * pos - where to start searching, and contains current position
\r
69 * pde - store back the directory entry pointer
\r
70 * wc - wildcard checking?
\r
74 * 0 - if file was not found
\r
75 * 1 - if file was found
\r
77 ***************************************************************************/
\r
78 unsigned char _f_findfilewc ( char * name, char * ext, F_POS * pos, F_DIRENTRY * * pde, unsigned char wc )
\r
80 while ( pos->cluster < F_CLUSTER_RESERVED )
\r
82 for ( ; pos->sector < pos->sectorend ; pos->sector++ )
\r
84 F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos );
\r
86 if ( _f_readglsector( pos->sector ) )
\r
88 return 0; /*not found*/
\r
91 for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ )
\r
93 unsigned char b, ok;
\r
100 if ( (unsigned char)( de->name[0] ) == 0xe5 )
\r
102 continue; /*deleted*/
\r
105 if ( de->attr & F_ATTR_VOLUME )
\r
112 for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ )
\r
114 if ( name[b] == '*' )
\r
119 if ( name[b] != '?' )
\r
121 if ( de->name[b] != name[b] )
\r
131 for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ )
\r
133 if ( ext[b] == '*' )
\r
143 if ( ext[b] != '?' )
\r
145 if ( de->ext[b] != ext[b] )
\r
166 for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ )
\r
168 if ( de->name[b] != name[b] )
\r
177 for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ )
\r
179 if ( de->ext[b] != ext[b] )
\r
202 if ( !pos->cluster )
\r
204 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
206 pos->cluster = gl_volume.bootrecord.rootcluster;
\r
215 unsigned long nextcluster;
\r
216 gl_volume.fatsector = (unsigned long)-1;
\r
217 if ( _f_getclustervalue( pos->cluster, &nextcluster ) )
\r
219 return 0; /*not found*/
\r
222 if ( nextcluster >= F_CLUSTER_RESERVED )
\r
227 _f_clustertopos( nextcluster, pos );
\r
229 } /* _f_findfilewc */
\r
235 /****************************************************************************
\r
239 * create a complete filename from name and extension
\r
243 * dest - where to store filename
\r
244 * name - name of the file
\r
245 * ext - extension of the file
\r
247 ***************************************************************************/
\r
248 static void _f_getfilename ( char * dest, char * name, char * ext )
\r
250 unsigned char a, len;
\r
252 for ( len = a = F_MAXNAME ; a ; a--, len-- )
\r
254 if ( name[a - 1] != ' ' )
\r
260 for ( a = 0 ; a < len ; a++ )
\r
266 for ( len = a = F_MAXEXT ; a ; a--, len-- )
\r
268 if ( ext[a - 1] != ' ' )
\r
279 for ( a = 0 ; a < len ; a++ )
\r
284 *dest = 0; /*terminateit*/
\r
285 } /* _f_getfilename */
\r
290 /****************************************************************************
\r
294 * get a directory entry structure start cluster value
\r
298 * de - directory entry
\r
302 * directory entry cluster value
\r
304 ***************************************************************************/
\r
305 unsigned long _f_getdecluster ( F_DIRENTRY * de )
\r
307 unsigned long cluster;
\r
309 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
311 cluster = _f_getword( &de->clusterhi );
\r
313 cluster |= _f_getword( &de->clusterlo );
\r
317 return _f_getword( &de->clusterlo );
\r
321 /****************************************************************************
\r
325 * set a directory entry structure start cluster value
\r
329 * de - directory entry
\r
330 * cluster - value of the start cluster
\r
332 ***************************************************************************/
\r
333 void _f_setdecluster ( F_DIRENTRY * de, unsigned long cluster )
\r
335 _f_setword( &de->clusterlo, (unsigned short)( cluster & 0xffff ) );
\r
336 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
338 _f_setword( &de->clusterhi, (unsigned short)( cluster >> 16 ) );
\r
342 _f_setword( &de->clusterhi, (unsigned short)0 );
\r
347 /****************************************************************************
\r
351 * finding out if path is valid in F_NAME and
\r
352 * correct path info with absolute path (removes relatives)
\r
356 * fsname - filled structure with path,drive
\r
357 * pos - where to start searching, and contains current position
\r
361 * 0 - if path was not found or invalid
\r
362 * 1 - if path was found
\r
364 ***************************************************************************/
\r
365 unsigned char _f_findpath ( F_NAME * fsname, F_POS * pos )
\r
367 char * path = fsname->path;
\r
368 char * mpath = path;
\r
371 _f_clustertopos( 0, pos );
\r
375 char name[F_MAXNAME];
\r
376 char ext[F_MAXEXT];
\r
378 unsigned char len = _f_setnameext( path, name, ext );
\r
380 if ( ( pos->cluster == 0 ) && ( len == 1 ) && ( name[0] == '.' ) )
\r
382 _f_clustertopos( 0, pos );
\r
386 if ( !_f_findfilewc( name, ext, pos, &de, 0 ) )
\r
391 if ( !( de->attr & F_ATTR_DIR ) )
\r
396 _f_clustertopos( _f_getdecluster( de ), pos );
\r
400 if ( name[0] == '.' )
\r
408 if ( mpath != fsname->path )
\r
410 mpath--; /*if we are now at the top*/
\r
420 if ( name[1] != '.' )
\r
422 return 0; /*invalid name*/
\r
427 return 0; /*invalid name !*/
\r
432 if ( mpath == fsname->path )
\r
434 return 0; /*we are in the top*/
\r
437 mpath--; /*no on separator*/
\r
440 if ( mpath == fsname->path )
\r
442 break; /*we are now at the top*/
\r
446 if ( *mpath == '/' )
\r
455 if ( mpath != fsname->path )
\r
457 mpath--; /*if we are now at the top*/
\r
468 if ( path == mpath ) /*if no was dots just step*/
\r
476 for ( a = 0 ; a < len ; a++ )
\r
478 *mpath++ = *path++; /*copy if in different pos*/
\r
489 *mpath++ = '/'; /*add separator*/
\r
492 *mpath = 0; /*terminate it*/
\r
494 } /* _f_findpath */
\r
497 /****************************************************************************
\r
501 * getting a current working directory of current drive
\r
505 * buffer - where to store current working folder
\r
506 * maxlen - buffer length (possible size is F_MAXPATH)
\r
510 * error code or zero if successful
\r
512 ***************************************************************************/
\r
513 unsigned char fn_getcwd ( char * buffer, unsigned char maxlen, char root )
\r
522 maxlen--; /*need for termination*/
\r
523 if ( root && maxlen )
\r
529 for ( a = 0 ; a < maxlen ; a++ )
\r
531 char ch = gl_volume.cwd[a];
\r
539 buffer[a] = 0; /*add terminator at the end*/
\r
546 /****************************************************************************
\r
550 * find a file(s) or directory(s) in directory
\r
554 * filename - filename (with or without wildcards)
\r
555 * find - where to store found file information
\r
559 * error code or zero if successful
\r
561 ***************************************************************************/
\r
562 unsigned char fn_findfirst ( const char * filename, F_FIND * find )
\r
566 if ( _f_setfsname( filename, &find->findfsname ) )
\r
568 return F_ERR_INVALIDNAME; /*invalid name*/
\r
571 if ( _f_checkname( find->findfsname.filename, find->findfsname.fileext ) )
\r
573 return F_ERR_INVALIDNAME; /*invalid name, wildcard is ok*/
\r
577 ret = _f_getvolume();
\r
583 if ( !_f_findpath( &find->findfsname, &find->pos ) )
\r
585 return F_ERR_INVALIDDIR; /*search for path*/
\r
588 return fn_findnext( find );
\r
589 } /* fn_findfirst */
\r
593 /****************************************************************************
\r
597 * find further file(s) or directory(s) in directory
\r
601 * find - where to store found file information (findfirst should call 1st)
\r
605 * error code or zero if successful
\r
607 ***************************************************************************/
\r
608 unsigned char fn_findnext ( F_FIND * find )
\r
614 ret = _f_getvolume();
\r
620 if ( !_f_findfilewc( find->findfsname.filename, find->findfsname.fileext, &find->pos, &de, 1 ) )
\r
622 return F_ERR_NOTFOUND;
\r
625 for ( a = 0 ; a < F_MAXNAME ; a++ )
\r
627 find->name[a] = de->name[a];
\r
630 for ( a = 0 ; a < F_MAXEXT ; a++ )
\r
632 find->ext[a] = de->ext[a];
\r
635 _f_getfilename( find->filename, (char *)de->name, (char *)de->ext );
\r
637 find->attr = de->attr;
\r
638 find->cdate = _f_getword( &de->cdate );
\r
639 find->ctime = _f_getword( &de->ctime );
\r
640 find->filesize = (long)_f_getlong( &de->filesize );
\r
641 find->cluster = _f_getdecluster( de );
\r
642 find->pos.pos++; /*goto next position*/
\r
645 } /* fn_findnext */
\r
649 /****************************************************************************
\r
653 * change current working directory
\r
657 * dirname - new working directory name
\r
661 * 0 - if successfully
\r
662 * other - if any error
\r
664 ***************************************************************************/
\r
665 unsigned char fn_chdir ( const char * dirname )
\r
673 ret = _f_setfsname( dirname, &fsname );
\r
677 return F_ERR_INVALIDNAME; /*invalid name*/
\r
680 if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
\r
682 return F_ERR_INVALIDNAME; /*invalid name*/
\r
685 ret = _f_getvolume();
\r
691 for ( len = 0 ; fsname.path[len] ; )
\r
696 if ( len && ( ( fsname.filename[0] != 32 ) || ( fsname.fileext[0] != 32 ) ) )
\r
698 fsname.path[len++] = '/';
\r
701 _f_getfilename( fsname.path + len, fsname.filename, fsname.fileext );
\r
703 if ( !( _f_findpath( &fsname, &pos ) ) )
\r
705 return F_ERR_NOTFOUND;
\r
708 for ( a = 0 ; a < F_MAXPATH ; a++ )
\r
710 gl_volume.cwd[a] = fsname.path[a];
\r
718 /****************************************************************************
\r
722 * init directory entry, this function is called if a new entry is coming
\r
726 * de - directory entry which needs to be initialized
\r
727 * name - fil ename (8)
\r
728 * ext - file extension (3)
\r
730 ***************************************************************************/
\r
731 static void _f_initentry ( F_DIRENTRY * de, char * name, char * ext )
\r
733 unsigned short date;
\r
734 unsigned short time;
\r
736 psp_memset( de, 0, sizeof( F_DIRENTRY ) ); /*reset all entries*/
\r
738 psp_memcpy( de->name, name, sizeof( de->name ) );
\r
739 psp_memcpy( de->ext, ext, sizeof( de->ext ) );
\r
741 f_igettimedate( &time, &date );
\r
742 _f_setword( &de->cdate, date ); /*if there is realtime clock then creation date could be set from*/
\r
743 _f_setword( &de->ctime, time ); /*if there is realtime clock then creation time could be set from*/
\r
750 /****************************************************************************
\r
754 * Add a new directory entry into driectory list
\r
758 * fs_name - filled structure what to add into directory list
\r
759 * pos - where directory cluster chains starts
\r
760 * pde - F_DIRENTRY pointer where to store the entry where it was added
\r
764 * 0 - if successfully added
\r
765 * other - if any error (see FS_xxx errorcodes)
\r
767 ***************************************************************************/
\r
768 unsigned char _f_addentry ( F_NAME * fsname, F_POS * pos, F_DIRENTRY * * pde )
\r
771 unsigned short date;
\r
772 unsigned short time;
\r
774 if ( !fsname->filename[0] )
\r
776 return F_ERR_INVALIDNAME;
\r
779 if ( fsname->filename[0] == '.' )
\r
781 return F_ERR_INVALIDNAME;
\r
784 while ( pos->cluster < F_CLUSTER_RESERVED )
\r
786 for ( ; pos->sector < pos->sectorend ; pos->sector++ )
\r
788 F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos );
\r
790 ret = _f_readglsector( pos->sector );
\r
796 for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ )
\r
798 if ( ( !de->name[0] ) || ( (unsigned char)( de->name[0] ) == 0xe5 ) )
\r
800 _f_initentry( de, fsname->filename, fsname->fileext );
\r
801 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
803 f_igettimedate( &time, &date );
\r
804 _f_setword( &de->crtdate, date ); /*if there is realtime clock then creation date could be set from*/
\r
805 _f_setword( &de->crttime, time ); /*if there is realtime clock then creation time could be set from*/
\r
806 _f_setword( &de->lastaccessdate, date ); /*if there is realtime clock then creation date could be set from*/
\r
821 if ( !pos->cluster )
\r
823 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
825 pos->cluster = gl_volume.bootrecord.rootcluster;
\r
829 return F_ERR_NOMOREENTRY;
\r
834 unsigned long cluster;
\r
836 gl_volume.fatsector = (unsigned long)-1;
\r
837 ret = _f_getclustervalue( pos->cluster, &cluster ); /*try to get next cluster*/
\r
843 if ( cluster < F_CLUSTER_RESERVED )
\r
845 _f_clustertopos( cluster, pos );
\r
849 ret = _f_alloccluster( &cluster ); /*get a new one*/
\r
855 if ( cluster < F_CLUSTER_RESERVED )
\r
857 if ( gl_file.mode != F_FILE_CLOSE )
\r
859 return F_ERR_NOMOREENTRY;
\r
862 _f_clustertopos( cluster, &gl_file.pos );
\r
864 ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );
\r
870 ret = _f_setclustervalue( pos->cluster, gl_file.pos.cluster );
\r
876 ret = _f_writefatsector();
\r
882 gl_volume.fatsector = (unsigned long)-1;
\r
883 psp_memset( gl_sector, 0, F_SECTOR_SIZE );
\r
884 while ( gl_file.pos.sector < gl_file.pos.sectorend )
\r
886 ret = _f_writeglsector( gl_file.pos.sector );
\r
892 gl_file.pos.sector++;
\r
895 _f_clustertopos( gl_file.pos.cluster, pos );
\r
899 return F_ERR_NOMOREENTRY;
\r
903 } /* _f_addentry */
\r
905 return F_ERR_NOMOREENTRY;
\r
910 /****************************************************************************
\r
914 * making a new directory
\r
918 * dirname - new directory name
\r
922 * error code or zero if successful
\r
924 ***************************************************************************/
\r
925 unsigned char fn_mkdir ( const char * dirname )
\r
931 unsigned long cluster;
\r
934 #if F_FILE_CHANGED_EVENT
\r
935 ST_FILE_CHANGED fc;
\r
938 if ( _f_setfsname( dirname, &fsname ) )
\r
940 return F_ERR_INVALIDNAME; /*invalid name*/
\r
943 if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
\r
945 return F_ERR_INVALIDNAME; /*invalid name*/
\r
948 ret = _f_getvolume();
\r
954 if ( !_f_findpath( &fsname, &posdir ) )
\r
956 return F_ERR_INVALIDDIR;
\r
961 if ( fsname.filename[0] == '.' )
\r
963 return F_ERR_NOTFOUND;
\r
966 if ( _f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )
\r
968 return F_ERR_DUPLICATED;
\r
973 gl_volume.fatsector = (unsigned long)-1;
\r
974 ret = _f_alloccluster( &cluster );
\r
980 ret = _f_addentry( &fsname, &pos, &de );
\r
986 de->attr |= F_ATTR_DIR; /*set as directory*/
\r
988 #if F_FILE_CHANGED_EVENT
\r
989 if ( f_filechangedevent )
\r
991 fc.action = FACTION_ADDED;
\r
992 fc.flags = FFLAGS_DIR_NAME | FFLAGS_ATTRIBUTES | FFLAGS_SIZE | FFLAGS_LAST_WRITE;
\r
993 fc.attr = de->attr;
\r
994 fc.ctime = _f_getword( de->ctime );
\r
995 fc.cdate = _f_getword( de->cdate );
\r
996 fc.filesize = _f_getlong( de->filesize );
\r
1001 if ( gl_file.mode != F_FILE_CLOSE )
\r
1003 return F_ERR_LOCKED;
\r
1006 _f_clustertopos( cluster, &gl_file.pos );
\r
1007 _f_setdecluster( de, cluster ); /*new dir*/
\r
1009 (void)_f_writeglsector( (unsigned long)-1 ); /*write actual directory sector*/
\r
1012 de = (F_DIRENTRY *)gl_sector;
\r
1014 _f_initentry( de, ". ", " " );
\r
1015 de->attr = F_ATTR_DIR; /*set as directory*/
\r
1016 _f_setdecluster( de, cluster ); /*current*/
\r
1019 _f_initentry( de, ".. ", " " );
\r
1020 de->attr = F_ATTR_DIR; /*set as directory*/
\r
1021 _f_setdecluster( de, posdir.cluster ); /*parent*/
\r
1024 psp_memset( de, 0, ( F_SECTOR_SIZE - 2 * sizeof( F_DIRENTRY ) ) );
\r
1027 ret = _f_writeglsector( gl_file.pos.sector );
\r
1033 gl_file.pos.sector++;
\r
1034 psp_memset( gl_sector, 0, ( 2 * sizeof( F_DIRENTRY ) ) );
\r
1035 while ( gl_file.pos.sector < gl_file.pos.sectorend )
\r
1037 ret = _f_writeglsector( gl_file.pos.sector );
\r
1043 gl_file.pos.sector++;
\r
1046 gl_volume.fatsector = (unsigned long)-1;
\r
1047 ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );
\r
1053 ret = _f_writefatsector();
\r
1054 #if F_FILE_CHANGED_EVENT
\r
1055 if ( f_filechangedevent && !ret )
\r
1057 fc.action = FACTION_ADDED;
\r
1058 fc.flags = FFLAGS_DIR_NAME;
\r
1060 if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )
\r
1062 f_filechangedevent( &fc );
\r
1073 /****************************************************************************
\r
1077 * Remove directory, only could be removed if empty
\r
1081 * dirname - which directory needed to be removed
\r
1085 * error code or zero if successful
\r
1087 ***************************************************************************/
\r
1088 unsigned char fn_rmdir ( const char * dirname )
\r
1090 unsigned char ret;
\r
1094 unsigned long dirsector;
\r
1097 if ( _f_setfsname( dirname, &fsname ) )
\r
1099 return F_ERR_INVALIDNAME; /*invalid name*/
\r
1102 if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
\r
1104 return F_ERR_INVALIDNAME; /*invalid name*/
\r
1107 if ( fsname.filename[0] == '.' )
\r
1109 return F_ERR_NOTFOUND;
\r
1112 ret = _f_getvolume();
\r
1118 if ( !( _f_findpath( &fsname, &pos ) ) )
\r
1120 return F_ERR_INVALIDDIR;
\r
1123 if ( !_f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )
\r
1125 return F_ERR_NOTFOUND;
\r
1128 if ( !( de->attr & F_ATTR_DIR ) )
\r
1130 return F_ERR_INVALIDDIR; /*not a directory*/
\r
1133 dirsector = gl_volume.actsector;
\r
1135 if ( gl_file.mode != F_FILE_CLOSE )
\r
1137 return F_ERR_LOCKED;
\r
1140 _f_clustertopos( _f_getdecluster( de ), &gl_file.pos );
\r
1147 ret = _f_getcurrsector();
\r
1148 if ( ret == F_ERR_EOF )
\r
1158 de2 = (F_DIRENTRY *)gl_sector;
\r
1159 for ( a = 0 ; a < ( F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ) ; a++, de2++ )
\r
1161 ch = de2->name[0];
\r
1167 if ( (unsigned char)ch == 0xe5 )
\r
1177 return F_ERR_NOTEMPTY; /*something is there*/
\r
1185 gl_file.pos.sector++;
\r
1188 ret = _f_readglsector( dirsector );
\r
1194 de->name[0] = (unsigned char)0xe5;
\r
1196 ret = _f_writeglsector( dirsector );
\r
1202 gl_volume.fatsector = (unsigned long)-1;
\r
1203 ret = _f_removechain( _f_getdecluster( de ) );
\r
1204 #if F_FILE_CHANGED_EVENT
\r
1205 if ( f_filechangedevent && !ret )
\r
1207 ST_FILE_CHANGED fc;
\r
1208 fc.action = FACTION_REMOVED;
\r
1209 fc.flags = FFLAGS_DIR_NAME;
\r
1211 if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )
\r
1213 f_filechangedevent( &fc );
\r