2 * FreeRTOS+FAT FS V1.0.0 (C) 2013 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 pure GPL open source license (as opposed to the modified GPL licence
\r
9 * under which FreeRTOS is distributed) or a commercial license. Details of
\r
10 * both license options follow:
\r
12 * - Open source licensing -
\r
13 * FreeRTOS+FAT SL is a free download and may be used, modified, evaluated and
\r
14 * distributed without charge provided the user adheres to version two of the
\r
15 * GNU General Public License (GPL) and does not remove the copyright notice or
\r
16 * this text. The GPL V2 text is available on the gnu.org web site, and on the
\r
17 * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
\r
19 * - Commercial licensing -
\r
20 * Businesses and individuals who for commercial or other reasons cannot comply
\r
21 * with the terms of the GPL V2 license must obtain a commercial license before
\r
22 * incorporating FreeRTOS+FAT SL into proprietary software for distribution in
\r
23 * any form. Commercial licenses can be purchased from
\r
24 * http://shop.freertos.org/fat_sl and do not require any source files to be
\r
27 * FreeRTOS+FAT SL is distributed in the hope that it will be useful. You
\r
28 * cannot use FreeRTOS+FAT SL unless you agree that you use the software 'as
\r
29 * is'. FreeRTOS+FAT SL is provided WITHOUT ANY WARRANTY; without even the
\r
30 * implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A
\r
31 * PARTICULAR PURPOSE. Real Time Engineers Ltd. and HCC Embedded disclaims all
\r
32 * conditions and terms, be they implied, expressed, or statutory.
\r
34 * http://www.FreeRTOS.org
\r
35 * http://www.FreeRTOS.org/FreeRTOS-Plus
\r
39 #include "../../api/fat_sl.h"
\r
40 #include "../../psp/include/psp_string.h"
\r
49 #include "../../version/ver_fat_sl.h"
\r
50 #if VER_FAT_SL_MAJOR != 3 || VER_FAT_SL_MINOR != 2
\r
51 #error Incompatible FAT_SL version number!
\r
55 /****************************************************************************
\r
59 * internal function to finding file in directory entry with or without
\r
65 * ext - fileextension
\r
66 * pos - where to start searching, and contains current position
\r
67 * pde - store back the directory entry pointer
\r
68 * wc - wildcard checking?
\r
72 * 0 - if file was not found
\r
73 * 1 - if file was found
\r
75 ***************************************************************************/
\r
76 unsigned char _f_findfilewc ( char * name, char * ext, F_POS * pos, F_DIRENTRY * * pde, unsigned char wc )
\r
78 while ( pos->cluster < F_CLUSTER_RESERVED )
\r
80 for ( ; pos->sector < pos->sectorend ; pos->sector++ )
\r
82 F_DIRENTRY * de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * pos->pos );
\r
84 if ( _f_readglsector( pos->sector ) )
\r
86 return 0; /*not found*/
\r
89 for ( ; pos->pos < F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ; de++, pos->pos++ )
\r
91 unsigned char b, ok;
\r
98 if ( (unsigned char)( de->name[0] ) == 0xe5 )
\r
100 continue; /*deleted*/
\r
103 if ( de->attr & F_ATTR_VOLUME )
\r
110 for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ )
\r
112 if ( name[b] == '*' )
\r
117 if ( name[b] != '?' )
\r
119 if ( de->name[b] != name[b] )
\r
129 for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ )
\r
131 if ( ext[b] == '*' )
\r
141 if ( ext[b] != '?' )
\r
143 if ( de->ext[b] != ext[b] )
\r
164 for ( b = 0, ok = 1 ; b < sizeof( de->name ) ; b++ )
\r
166 if ( de->name[b] != name[b] )
\r
175 for ( b = 0, ok = 1 ; b < sizeof( de->ext ) ; b++ )
\r
177 if ( de->ext[b] != ext[b] )
\r
200 if ( !pos->cluster )
\r
202 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
204 pos->cluster = gl_volume.bootrecord.rootcluster;
\r
211 unsigned long nextcluster;
\r
212 gl_volume.fatsector = (unsigned long)-1;
\r
213 if ( _f_getclustervalue( pos->cluster, &nextcluster ) )
\r
215 return 0; /*not found*/
\r
218 if ( nextcluster >= F_CLUSTER_RESERVED )
\r
223 _f_clustertopos( nextcluster, pos );
\r
225 } /* _f_findfilewc */
\r
231 /****************************************************************************
\r
235 * create a complete filename from name and extension
\r
239 * dest - where to store filename
\r
240 * name - name of the file
\r
241 * ext - extension of the file
\r
243 ***************************************************************************/
\r
244 static void _f_getfilename ( char * dest, char * name, char * ext )
\r
246 unsigned char a, len;
\r
248 for ( len = a = F_MAXNAME ; a ; a--, len-- )
\r
250 if ( name[a - 1] != ' ' )
\r
256 for ( a = 0 ; a < len ; a++ )
\r
262 for ( len = a = F_MAXEXT ; a ; a--, len-- )
\r
264 if ( ext[a - 1] != ' ' )
\r
275 for ( a = 0 ; a < len ; a++ )
\r
280 *dest = 0; /*terminateit*/
\r
281 } /* _f_getfilename */
\r
286 /****************************************************************************
\r
290 * get a directory entry structure start cluster value
\r
294 * de - directory entry
\r
298 * directory entry cluster value
\r
300 ***************************************************************************/
\r
301 unsigned long _f_getdecluster ( F_DIRENTRY * de )
\r
303 unsigned long cluster;
\r
304 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
306 cluster = _f_getword( &de->clusterhi );
\r
308 cluster |= _f_getword( &de->clusterlo );
\r
312 return _f_getword( &de->clusterlo );
\r
316 /****************************************************************************
\r
320 * set a directory entry structure start cluster value
\r
324 * de - directory entry
\r
325 * cluster - value of the start cluster
\r
327 ***************************************************************************/
\r
328 void _f_setdecluster ( F_DIRENTRY * de, unsigned long cluster )
\r
330 _f_setword( &de->clusterlo, (unsigned short)( cluster & 0xffff ) );
\r
331 if ( gl_volume.mediatype == F_FAT32_MEDIA )
\r
333 _f_setword( &de->clusterhi, (unsigned short)( cluster >> 16 ) );
\r
337 _f_setword( &de->clusterhi, (unsigned short)0 );
\r
342 /****************************************************************************
\r
346 * finding out if path is valid in F_NAME and
\r
347 * correct path info with absolute path (removes relatives)
\r
351 * fsname - filled structure with path,drive
\r
352 * pos - where to start searching, and contains current position
\r
356 * 0 - if path was not found or invalid
\r
357 * 1 - if path was found
\r
359 ***************************************************************************/
\r
360 unsigned char _f_findpath ( F_NAME * fsname, F_POS * pos )
\r
362 char * path = fsname->path;
\r
363 char * mpath = path;
\r
366 _f_clustertopos( 0, pos );
\r
370 char name[F_MAXNAME];
\r
371 char ext[F_MAXEXT];
\r
373 unsigned char len = _f_setnameext( path, name, ext );
\r
375 if ( ( pos->cluster == 0 ) && ( len == 1 ) && ( name[0] == '.' ) )
\r
377 _f_clustertopos( 0, pos );
\r
381 if ( !_f_findfilewc( name, ext, pos, &de, 0 ) )
\r
385 if ( !( de->attr & F_ATTR_DIR ) )
\r
390 _f_clustertopos( _f_getdecluster( de ), pos );
\r
394 if ( name[0] == '.' )
\r
402 if ( mpath != fsname->path )
\r
404 mpath--; /*if we are now at the top*/
\r
414 if ( name[1] != '.' )
\r
416 return 0; /*invalid name*/
\r
421 return 0; /*invalid name !*/
\r
426 if ( mpath == fsname->path )
\r
428 return 0; /*we are in the top*/
\r
431 mpath--; /*no on separator*/
\r
434 if ( mpath == fsname->path )
\r
436 break; /*we are now at the top*/
\r
440 if ( *mpath == '/' )
\r
449 if ( mpath != fsname->path )
\r
451 mpath--; /*if we are now at the top*/
\r
462 if ( path == mpath ) /*if no was dots just step*/
\r
470 for ( a = 0 ; a < len ; a++ )
\r
472 *mpath++ = *path++; /*copy if in different pos*/
\r
483 *mpath++ = '/'; /*add separator*/
\r
486 *mpath = 0; /*terminate it*/
\r
488 } /* _f_findpath */
\r
491 /****************************************************************************
\r
495 * getting a current working directory of current drive
\r
499 * buffer - where to store current working folder
\r
500 * maxlen - buffer length (possible size is F_MAXPATH)
\r
504 * error code or zero if successful
\r
506 ***************************************************************************/
\r
509 unsigned char fn_getcwd ( char * buffer, unsigned char maxlen, char root )
\r
518 maxlen--; /*need for termination*/
\r
519 if ( root && maxlen )
\r
525 for ( a = 0 ; a < maxlen ; a++ )
\r
527 char ch = gl_volume.cwd[a];
\r
535 buffer[a] = 0; /*add terminator at the end*/
\r
542 /****************************************************************************
\r
546 * find a file(s) or directory(s) in directory
\r
550 * filename - filename (with or without wildcards)
\r
551 * find - where to store found file information
\r
555 * error code or zero if successful
\r
557 ***************************************************************************/
\r
560 unsigned char fn_findfirst ( const char * filename, F_FIND * find )
\r
564 if ( _f_setfsname( filename, &find->findfsname ) )
\r
566 return F_ERR_INVALIDNAME; /*invalid name*/
\r
569 if ( _f_checkname( find->findfsname.filename, find->findfsname.fileext ) )
\r
571 return F_ERR_INVALIDNAME; /*invalid name, wildcard is ok*/
\r
575 ret = _f_getvolume();
\r
581 if ( !_f_findpath( &find->findfsname, &find->pos ) )
\r
583 return F_ERR_INVALIDDIR; /*search for path*/
\r
586 return fn_findnext( find );
\r
587 } /* fn_findfirst */
\r
591 /****************************************************************************
\r
595 * find further file(s) or directory(s) in directory
\r
599 * find - where to store found file information (findfirst should call 1st)
\r
603 * error code or zero if successful
\r
605 ***************************************************************************/
\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
828 return F_ERR_NOMOREENTRY;
\r
832 unsigned long cluster;
\r
834 gl_volume.fatsector = (unsigned long)-1;
\r
835 ret = _f_getclustervalue( pos->cluster, &cluster ); /*try to get next cluster*/
\r
841 if ( cluster < F_CLUSTER_RESERVED )
\r
843 _f_clustertopos( cluster, pos );
\r
847 ret = _f_alloccluster( &cluster ); /*get a new one*/
\r
853 if ( cluster < F_CLUSTER_RESERVED )
\r
855 if ( gl_file.mode != F_FILE_CLOSE )
\r
857 return F_ERR_NOMOREENTRY;
\r
860 _f_clustertopos( cluster, &gl_file.pos );
\r
862 ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );
\r
868 ret = _f_setclustervalue( pos->cluster, gl_file.pos.cluster );
\r
874 ret = _f_writefatsector();
\r
880 gl_volume.fatsector = (unsigned long)-1;
\r
881 psp_memset( gl_sector, 0, F_SECTOR_SIZE );
\r
882 while ( gl_file.pos.sector < gl_file.pos.sectorend )
\r
884 ret = _f_writeglsector( gl_file.pos.sector );
\r
890 gl_file.pos.sector++;
\r
893 _f_clustertopos( gl_file.pos.cluster, pos );
\r
897 return F_ERR_NOMOREENTRY;
\r
901 } /* _f_addentry */
\r
903 return F_ERR_NOMOREENTRY;
\r
908 /****************************************************************************
\r
912 * making a new directory
\r
916 * dirname - new directory name
\r
920 * error code or zero if successful
\r
922 ***************************************************************************/
\r
923 unsigned char fn_mkdir ( const char * dirname )
\r
929 unsigned long cluster;
\r
932 #if F_FILE_CHANGED_EVENT
\r
933 ST_FILE_CHANGED fc;
\r
936 if ( _f_setfsname( dirname, &fsname ) )
\r
938 return F_ERR_INVALIDNAME; /*invalid name*/
\r
941 if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
\r
943 return F_ERR_INVALIDNAME; /*invalid name*/
\r
946 ret = _f_getvolume();
\r
952 if ( !_f_findpath( &fsname, &posdir ) )
\r
954 return F_ERR_INVALIDDIR;
\r
959 if ( fsname.filename[0] == '.' )
\r
961 return F_ERR_NOTFOUND;
\r
964 if ( _f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )
\r
966 return F_ERR_DUPLICATED;
\r
971 gl_volume.fatsector = (unsigned long)-1;
\r
972 ret = _f_alloccluster( &cluster );
\r
978 ret = _f_addentry( &fsname, &pos, &de );
\r
984 de->attr |= F_ATTR_DIR; /*set as directory*/
\r
986 #if F_FILE_CHANGED_EVENT
\r
987 if ( f_filechangedevent )
\r
989 fc.action = FACTION_ADDED;
\r
990 fc.flags = FFLAGS_DIR_NAME | FFLAGS_ATTRIBUTES | FFLAGS_SIZE | FFLAGS_LAST_WRITE;
\r
991 fc.attr = de->attr;
\r
992 fc.ctime = _f_getword( de->ctime );
\r
993 fc.cdate = _f_getword( de->cdate );
\r
994 fc.filesize = _f_getlong( de->filesize );
\r
999 if ( gl_file.mode != F_FILE_CLOSE )
\r
1001 return F_ERR_LOCKED;
\r
1004 _f_clustertopos( cluster, &gl_file.pos );
\r
1005 _f_setdecluster( de, cluster ); /*new dir*/
\r
1007 (void)_f_writeglsector( (unsigned long)-1 ); /*write actual directory sector*/
\r
1010 de = (F_DIRENTRY *)gl_sector;
\r
1012 _f_initentry( de, ". ", " " );
\r
1013 de->attr = F_ATTR_DIR; /*set as directory*/
\r
1014 _f_setdecluster( de, cluster ); /*current*/
\r
1017 _f_initentry( de, ".. ", " " );
\r
1018 de->attr = F_ATTR_DIR; /*set as directory*/
\r
1019 _f_setdecluster( de, posdir.cluster ); /*parent*/
\r
1022 psp_memset( de, 0, ( F_SECTOR_SIZE - 2 * sizeof( F_DIRENTRY ) ) );
\r
1025 ret = _f_writeglsector( gl_file.pos.sector );
\r
1031 gl_file.pos.sector++;
\r
1032 psp_memset( gl_sector, 0, ( 2 * sizeof( F_DIRENTRY ) ) );
\r
1033 while ( gl_file.pos.sector < gl_file.pos.sectorend )
\r
1035 ret = _f_writeglsector( gl_file.pos.sector );
\r
1041 gl_file.pos.sector++;
\r
1044 gl_volume.fatsector = (unsigned long)-1;
\r
1045 ret = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );
\r
1051 ret = _f_writefatsector();
\r
1052 #if F_FILE_CHANGED_EVENT
\r
1053 if ( f_filechangedevent && !ret )
\r
1055 fc.action = FACTION_ADDED;
\r
1056 fc.flags = FFLAGS_DIR_NAME;
\r
1058 if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )
\r
1060 f_filechangedevent( &fc );
\r
1071 /****************************************************************************
\r
1075 * Remove directory, only could be removed if empty
\r
1079 * dirname - which directory needed to be removed
\r
1083 * error code or zero if successful
\r
1085 ***************************************************************************/
\r
1086 unsigned char fn_rmdir ( const char * dirname )
\r
1088 unsigned char ret;
\r
1092 unsigned long dirsector;
\r
1095 if ( _f_setfsname( dirname, &fsname ) )
\r
1097 return F_ERR_INVALIDNAME; /*invalid name*/
\r
1100 if ( _f_checknamewc( fsname.filename, fsname.fileext ) )
\r
1102 return F_ERR_INVALIDNAME; /*invalid name*/
\r
1105 if ( fsname.filename[0] == '.' )
\r
1107 return F_ERR_NOTFOUND;
\r
1110 ret = _f_getvolume();
\r
1116 if ( !( _f_findpath( &fsname, &pos ) ) )
\r
1118 return F_ERR_INVALIDDIR;
\r
1121 if ( !_f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )
\r
1123 return F_ERR_NOTFOUND;
\r
1126 if ( !( de->attr & F_ATTR_DIR ) )
\r
1128 return F_ERR_INVALIDDIR; /*not a directory*/
\r
1131 dirsector = gl_volume.actsector;
\r
1133 if ( gl_file.mode != F_FILE_CLOSE )
\r
1135 return F_ERR_LOCKED;
\r
1138 _f_clustertopos( _f_getdecluster( de ), &gl_file.pos );
\r
1145 ret = _f_getcurrsector();
\r
1146 if ( ret == F_ERR_EOF )
\r
1156 de2 = (F_DIRENTRY *)gl_sector;
\r
1157 for ( a = 0 ; a < ( F_SECTOR_SIZE / sizeof( F_DIRENTRY ) ) ; a++, de2++ )
\r
1159 ch = de2->name[0];
\r
1165 if ( (unsigned char)ch == 0xe5 )
\r
1175 return F_ERR_NOTEMPTY; /*something is there*/
\r
1183 gl_file.pos.sector++;
\r
1186 ret = _f_readglsector( dirsector );
\r
1192 de->name[0] = (unsigned char)0xe5;
\r
1194 ret = _f_writeglsector( dirsector );
\r
1200 gl_volume.fatsector = (unsigned long)-1;
\r
1201 ret = _f_removechain( _f_getdecluster( de ) );
\r
1202 #if F_FILE_CHANGED_EVENT
\r
1203 if ( f_filechangedevent && !ret )
\r
1205 ST_FILE_CHANGED fc;
\r
1206 fc.action = FACTION_REMOVED;
\r
1207 fc.flags = FFLAGS_DIR_NAME;
\r
1209 if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )
\r
1211 f_filechangedevent( &fc );
\r