]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/FileSystem/FatFs-0.7e/src/ff.c
Ensure a queue or semaphore that is not empty cannot be added to a queue set.
[freertos] / FreeRTOS / Demo / Common / FileSystem / FatFs-0.7e / src / ff.c
1 /*----------------------------------------------------------------------------/\r
2 /  FatFs - FAT file system module  R0.07e                    (C)ChaN, 2009\r
3 /-----------------------------------------------------------------------------/\r
4 / FatFs module is a generic FAT file system module for small embedded systems.\r
5 / This is a free software that opened for education, research and commercial\r
6 / developments under license policy of following trems.\r
7 /\r
8 /  Copyright (C) 2009, ChaN, all right reserved.\r
9 /\r
10 / * The FatFs module is a free software and there is NO WARRANTY.\r
11 / * No restriction on use. You can use, modify and redistribute it for\r
12 /   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.\r
13 / * Redistributions of source code must retain the above copyright notice.\r
14 /\r
15 /-----------------------------------------------------------------------------/\r
16 / Feb 26,'06 R0.00  Prototype.\r
17 /\r
18 / Apr 29,'06 R0.01  First stable version.\r
19 /\r
20 / Jun 01,'06 R0.02  Added FAT12 support.\r
21 /                   Removed unbuffered mode.\r
22 /                   Fixed a problem on small (<32M) patition.\r
23 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).\r
24 /\r
25 / Sep 22,'06 R0.03  Added f_rename().\r
26 /                   Changed option _FS_MINIMUM to _FS_MINIMIZE.\r
27 / Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.\r
28 /                   Fixed f_mkdir() creates incorrect directory on FAT32.\r
29 /\r
30 / Feb 04,'07 R0.04  Supported multiple drive system.\r
31 /                   Changed some interfaces for multiple drive system.\r
32 /                   Changed f_mountdrv() to f_mount().\r
33 /                   Added f_mkfs().\r
34 / Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.\r
35 /                   Added a capability of extending file size to f_lseek().\r
36 /                   Added minimization level 3.\r
37 /                   Fixed an endian sensitive code in f_mkfs().\r
38 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.\r
39 /                   Added FSInfo support.\r
40 /                   Fixed DBCS name can result FR_INVALID_NAME.\r
41 /                   Fixed short seek (<= csize) collapses the file object.\r
42 /\r
43 / Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().\r
44 /                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.\r
45 /                   Fixed f_mkdir() on FAT32 creates incorrect directory.\r
46 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().\r
47 /                   Fixed off by one error at FAT sub-type determination.\r
48 /                   Fixed btr in f_read() can be mistruncated.\r
49 /                   Fixed cached sector is not flushed when create and close\r
50 /                   without write.\r
51 /\r
52 / Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().\r
53 /                   Improved performance of f_lseek() on moving to the same\r
54 /                   or following cluster.\r
55 /\r
56 / Apr 01,'09 R0.07  Merged Tiny-FatFs as a buffer configuration option.\r
57 /                   Added long file name support.\r
58 /                   Added multiple code page support.\r
59 /                   Added re-entrancy for multitask operation.\r
60 /                   Added auto cluster size selection to f_mkfs().\r
61 /                   Added rewind option to f_readdir().\r
62 /                   Changed result code of critical errors.\r
63 /                   Renamed string functions to avoid name collision.\r
64 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.\r
65 /                   Added multiple sector size support.\r
66 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.\r
67 /                   Fixed wrong cache control in f_lseek().\r
68 /                   Added relative path feature.\r
69 /                   Added f_chdir() and f_chdrive().\r
70 /                   Added proper case conversion to extended char.\r
71 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.\r
72 /                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.\r
73 /                   Fixed name matching error on the 13 char boundary.\r
74 /                   Added a configuration option, _LFN_UNICODE.\r
75 /                   Changed f_readdir() to return the SFN with always upper\r
76 /                   case on non-LFN cfg.\r
77 /---------------------------------------------------------------------------*/\r
78 #include "ff.h"                                         /* FatFs configurations and declarations */\r
79 #include "diskio.h"                                     /* Declarations of low level disk I/O functions */\r
80 \r
81 /*--------------------------------------------------------------------------\r
82 \r
83    Module Private Definitions\r
84 \r
85 ---------------------------------------------------------------------------*/\r
86 #if _FATFS != 0x007E\r
87         #error Wrong include file (ff.h).\r
88 #endif\r
89 #if _FS_REENTRANT\r
90         #if _USE_LFN == 1\r
91                 #error Static LFN work area must not be used in re-entrant configuration.\r
92         #endif\r
93         #define ENTER_FF( fs )     \\r
94         {                                                  \\r
95                 if( !lock_fs(fs) )         \\r
96                 {                                          \\r
97                         return FR_TIMEOUT; \\r
98                 }                                          \\r
99         }\r
100 \r
101         #define LEAVE_FF( fs, res ) \\r
102         {                                                       \\r
103                 unlock_fs( fs, res );   \\r
104                 return res;                             \\r
105         }\r
106 \r
107 #else\r
108         #define ENTER_FF( fs )\r
109         #define LEAVE_FF( fs, res ) return res\r
110 #endif\r
111 #define ABORT( fs, res )           \\r
112         {                                                  \\r
113                 fp->flag |= FA__ERROR; \\r
114                 LEAVE_FF( fs, res );   \\r
115         }\r
116 \r
117 #ifndef NULL\r
118         #define NULL    0\r
119 #endif\r
120 \r
121 /* Name status flags */\r
122 #define NS              11                                      /* Offset of name status byte */\r
123 #define NS_LOSS 0x01                            /* Out of 8.3 format */\r
124 #define NS_LFN  0x02                            /* Force to create LFN entry */\r
125 #define NS_LAST 0x04                            /* Last segment */\r
126 #define NS_BODY 0x08                            /* Lower case flag (body) */\r
127 #define NS_EXT  0x10                            /* Lower case flag (ext) */\r
128 #define NS_DOT  0x20                            /* Dot entry */\r
129 \r
130 /*--------------------------------------------------------------------------\r
131 \r
132    Private Work Area\r
133 \r
134 ---------------------------------------------------------------------------*/\r
135 #if _DRIVES < 1 || _DRIVES > 9\r
136         #error Number of drives must be 1-9.\r
137 #endif\r
138 static FATFS    *FatFs[_DRIVES];        /* Pointer to the file system objects (logical drives) */\r
139 \r
140 static WORD             Fsid;                           /* File system mount ID */\r
141 \r
142 #if _FS_RPATH\r
143 static BYTE             Drive;                          /* Current drive */\r
144 #endif\r
145 #if _USE_LFN == 1                                       /* LFN with static LFN working buffer */\r
146 static WCHAR    LfnBuf[_MAX_LFN + 1];\r
147         #define NAMEBUF( sp, lp ) \\r
148         BYTE  sp[12];                     \\r
149         WCHAR                                           *lp = LfnBuf\r
150         #define INITBUF( dj, sp, lp ) \\r
151         dj.fn = sp;                                       \\r
152         dj.lfn = lp\r
153 \r
154 #elif _USE_LFN > 1                                      /* LFN with dynamic LFN working buffer */\r
155         #define NAMEBUF( sp, lp ) \\r
156         BYTE sp[12];                      \\r
157         WCHAR   lbuf[_MAX_LFN + 1], *lp = lbuf\r
158         #define INITBUF( dj, sp, lp ) \\r
159         dj.fn = sp;                                       \\r
160         dj.lfn = lp\r
161 \r
162 #else /* No LFN */\r
163         #define NAMEBUF( sp, lp )               BYTE sp[12]\r
164         #define INITBUF( dj, sp, lp )   dj.fn = sp\r
165 #endif\r
166 \r
167 /*--------------------------------------------------------------------------\r
168 \r
169    Module Private Functions\r
170 \r
171 ---------------------------------------------------------------------------*/\r
172 \r
173 /*-----------------------------------------------------------------------*/\r
174 \r
175 /* String functions                                                      */\r
176 \r
177 /*-----------------------------------------------------------------------*/\r
178 \r
179 /* Copy memory to memory */\r
180 static void mem_cpy ( void *dst, const void *src, int cnt )\r
181 {\r
182         char *d = ( char * ) dst;\r
183         const char *s = ( const char * ) src;\r
184         while( cnt-- )\r
185         {\r
186                 *d++ = *s++;\r
187         }\r
188 }\r
189 \r
190 /* Fill memory */\r
191 static void mem_set ( void *dst, int val, int cnt )\r
192 {\r
193         char *d = ( char * ) dst;\r
194         while( cnt-- )\r
195         {\r
196                 *d++ = ( char ) val;\r
197         }\r
198 }\r
199 \r
200 /* Compare memory to memory */\r
201 static int mem_cmp ( const void *dst, const void *src, int cnt )\r
202 {\r
203         const char *d = ( const char * ) dst, *s = ( const char * ) src;\r
204         int r = 0;\r
205         while( cnt-- && (r = *d++ -*s++) == 0 );\r
206         return r;\r
207 }\r
208 \r
209 /* Check if chr is contained in the string */\r
210 static int chk_chr ( const char *str, int chr )\r
211 {\r
212         while( *str && *str != chr )\r
213         {\r
214                 str++;\r
215         }\r
216 \r
217         return *str;\r
218 }\r
219 \r
220 /*-----------------------------------------------------------------------*/\r
221 \r
222 /* Request/Release grant to access the volume                            */\r
223 \r
224 /*-----------------------------------------------------------------------*/\r
225 #if _FS_REENTRANT\r
226 static BOOL lock_fs ( FATFS * fs /* File system object */ )\r
227 {\r
228         return ff_req_grant( fs->sobj );\r
229 }\r
230 \r
231 static void unlock_fs ( FATFS * fs, /* File system object */ FRESULT res /* Result code to be returned */ )\r
232 {\r
233         if( res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_INVALID_OBJECT && res != FR_TIMEOUT )\r
234         {\r
235                 ff_rel_grant( fs->sobj );\r
236         }\r
237 }\r
238 \r
239 #endif\r
240 \r
241 /*-----------------------------------------------------------------------*/\r
242 \r
243 /* Change window offset                                                  */\r
244 \r
245 /*-----------------------------------------------------------------------*/\r
246 static FRESULT move_window ( FATFS * fs, /* File system object */ DWORD sector /* Sector number to make apperance in the fs->win[] */ ) /* Move to zero only writes back dirty window */\r
247 {\r
248         DWORD wsect;\r
249 \r
250         wsect = fs->winsect;\r
251         if( wsect != sector )\r
252         {                               /* Changed current window */\r
253                 #if !_FS_READONLY\r
254                 if( fs->wflag )\r
255                 {                       /* Write back dirty window if needed */\r
256                         if( disk_write(fs->drive, fs->win, wsect, 1) != RES_OK )\r
257                         {\r
258                                 return FR_DISK_ERR;\r
259                         }\r
260 \r
261                         fs->wflag = 0;\r
262                         if( wsect < (fs->fatbase + fs->sects_fat) )\r
263                         {               /* In FAT area */\r
264                                 BYTE nf;\r
265                                 for( nf = fs->n_fats; nf > 1; nf-- )\r
266                                 {       /* Refrect the change to all FAT copies */\r
267                                         wsect += fs->sects_fat;\r
268                                         disk_write( fs->drive, fs->win, wsect, 1 );\r
269                                 }\r
270                         }\r
271                 }\r
272 \r
273                 #endif\r
274                 if( sector )\r
275                 {\r
276                         if( disk_read(fs->drive, fs->win, sector, 1) != RES_OK )\r
277                         {\r
278                                 return FR_DISK_ERR;\r
279                         }\r
280 \r
281                         fs->winsect = sector;\r
282                 }\r
283         }\r
284 \r
285         return FR_OK;\r
286 }\r
287 \r
288 /*-----------------------------------------------------------------------*/\r
289 \r
290 /* Clean-up cached data                                                  */\r
291 \r
292 /*-----------------------------------------------------------------------*/\r
293 #if !_FS_READONLY\r
294 static FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ FATFS * fs /* File system object */ )\r
295 {\r
296         FRESULT res;\r
297 \r
298         res = move_window( fs, 0 );\r
299         if( res == FR_OK )\r
300         {\r
301                 /* Update FSInfo sector if needed */\r
302                 if( fs->fs_type == FS_FAT32 && fs->fsi_flag )\r
303                 {\r
304                         fs->winsect = 0;\r
305                         mem_set( fs->win, 0, 512 );\r
306                         ST_WORD( fs->win + BS_55AA, 0xAA55 );\r
307                         ST_DWORD( fs->win + FSI_LeadSig, 0x41615252 );\r
308                         ST_DWORD( fs->win + FSI_StrucSig, 0x61417272 );\r
309                         ST_DWORD( fs->win + FSI_Free_Count, fs->free_clust );\r
310                         ST_DWORD( fs->win + FSI_Nxt_Free, fs->last_clust );\r
311                         disk_write( fs->drive, fs->win, fs->fsi_sector, 1 );\r
312                         fs->fsi_flag = 0;\r
313                 }\r
314 \r
315                 /* Make sure that no pending write process in the physical drive */\r
316                 if( disk_ioctl(fs->drive, CTRL_SYNC, ( void * ) NULL) != RES_OK )\r
317                 {\r
318                         res = FR_DISK_ERR;\r
319                 }\r
320         }\r
321 \r
322         return res;\r
323 }\r
324 \r
325 #endif\r
326 \r
327 /*-----------------------------------------------------------------------*/\r
328 \r
329 /* FAT access - Read value of a FAT entry                                */\r
330 \r
331 /*-----------------------------------------------------------------------*/\r
332 DWORD get_fat\r
333         (                               /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */\r
334                 FATFS * fs, /* File system object */ DWORD clst /* Cluster# to get the link information */\r
335         )\r
336 {\r
337         UINT wc, bc;\r
338         DWORD fsect;\r
339 \r
340         if( clst < 2 || clst >= fs->max_clust )\r
341         {                                       /* Range check */\r
342                 return 1;\r
343         }\r
344 \r
345         fsect = fs->fatbase;\r
346         switch( fs->fs_type )\r
347         {\r
348                 case FS_FAT12:\r
349                         bc = clst;\r
350                         bc += bc / 2;\r
351                         if( move_window(fs, fsect + (bc / SS(fs))) )\r
352                         {\r
353                                 break;\r
354                         }\r
355 \r
356                         wc = fs->win[bc & ( SS(fs) - 1 )];\r
357                         bc++;\r
358                         if( move_window(fs, fsect + (bc / SS(fs))) )\r
359                         {\r
360                                 break;\r
361                         }\r
362 \r
363                         wc |= ( WORD ) fs->win[bc & ( SS(fs) - 1 )] << 8;\r
364                         return( clst & 1 ) ? ( wc >> 4 ) : ( wc & 0xFFF );\r
365 \r
366                 case FS_FAT16:\r
367                         if( move_window(fs, fsect + (clst / (SS(fs) / 2))) )\r
368                         {\r
369                                 break;\r
370                         }\r
371 \r
372                         return LD_WORD( &fs->win[((WORD) clst * 2) & (SS(fs) - 1)] );\r
373 \r
374                 case FS_FAT32:\r
375                         if( move_window(fs, fsect + (clst / (SS(fs) / 4))) )\r
376                         {\r
377                                 break;\r
378                         }\r
379 \r
380                         return\r
381                         LD_DWORD( &fs->win[((WORD) clst * 4) & (SS(fs) - 1)] )\r
382                         & 0x0FFFFFFF;\r
383         }\r
384 \r
385         return 0xFFFFFFFF;      /* An error occured at the disk I/O layer */\r
386 }\r
387 \r
388 /*-----------------------------------------------------------------------*/\r
389 \r
390 /* FAT access - Change value of a FAT entry                              */\r
391 \r
392 /*-----------------------------------------------------------------------*/\r
393 #if !_FS_READONLY\r
394 FRESULT put_fat\r
395         ( FATFS * fs, /* File system object */ DWORD clst, /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */ DWORD val    /* New value to mark the cluster */ )\r
396 {\r
397         UINT bc;\r
398         BYTE * p;\r
399         DWORD fsect;\r
400         FRESULT res;\r
401 \r
402         if( clst < 2 || clst >= fs->max_clust )\r
403         {                                       /* Range check */\r
404                 res = FR_INT_ERR;\r
405         }\r
406         else\r
407         {\r
408                 fsect = fs->fatbase;\r
409                 switch( fs->fs_type )\r
410                 {\r
411                         case FS_FAT12:\r
412                                 bc = clst;\r
413                                 bc += bc / 2;\r
414                                 res = move_window( fs, fsect + (bc / SS(fs)) );\r
415                                 if( res != FR_OK )\r
416                                 {\r
417                                         break;\r
418                                 }\r
419 \r
420                                 p = &fs->win[bc & ( SS(fs) - 1 )];\r
421                                 *p = ( clst & 1 ) ? ( (*p & 0x0F) | ((BYTE) val << 4) ) : ( BYTE ) val;\r
422                                 bc++;\r
423                                 fs->wflag = 1;\r
424                                 res = move_window( fs, fsect + (bc / SS(fs)) );\r
425                                 if( res != FR_OK )\r
426                                 {\r
427                                         break;\r
428                                 }\r
429 \r
430                                 p = &fs->win[bc & ( SS(fs) - 1 )];\r
431                                 *p = ( clst & 1 ) ? ( BYTE ) ( val >> 4 ) : ( (*p & 0xF0) | ((BYTE) (val >> 8) & 0x0F) );\r
432                                 break;\r
433 \r
434                         case FS_FAT16:\r
435                                 res = move_window( fs, fsect + (clst / (SS(fs) / 2)) );\r
436                                 if( res != FR_OK )\r
437                                 {\r
438                                         break;\r
439                                 }\r
440 \r
441                                 ST_WORD( &fs->win[((WORD) clst * 2) & (SS(fs) - 1)], (WORD) val );\r
442                                 break;\r
443 \r
444                         case FS_FAT32:\r
445                                 res = move_window( fs, fsect + (clst / (SS(fs) / 4)) );\r
446                                 if( res != FR_OK )\r
447                                 {\r
448                                         break;\r
449                                 }\r
450 \r
451                                 ST_DWORD( &fs->win[((WORD) clst * 4) & (SS(fs) - 1)], val );\r
452                                 break;\r
453 \r
454                         default:\r
455                                 res = FR_INT_ERR;\r
456                 }\r
457 \r
458                 fs->wflag = 1;\r
459         }\r
460 \r
461         return res;\r
462 }\r
463 \r
464 #endif /* !_FS_READONLY */\r
465 \r
466 /*-----------------------------------------------------------------------*/\r
467 \r
468 /* FAT handling - Remove a cluster chain                                 */\r
469 \r
470 /*-----------------------------------------------------------------------*/\r
471 #if !_FS_READONLY\r
472 static FRESULT remove_chain ( FATFS * fs, /* File system object */ DWORD clst /* Cluster# to remove a chain from */ )\r
473 {\r
474         FRESULT res;\r
475         DWORD nxt;\r
476 \r
477         if( clst < 2 || clst >= fs->max_clust )\r
478         {                                       /* Check the range of cluster# */\r
479                 res = FR_INT_ERR;\r
480         }\r
481         else\r
482         {\r
483                 res = FR_OK;\r
484                 while( clst < fs->max_clust )\r
485                 {                               /* Not a last link? */\r
486                         nxt = get_fat( fs, clst );      /* Get cluster status */\r
487                         if( nxt == 0 )\r
488                         {\r
489                                 break;                                  /* Empty cluster? */\r
490                         }\r
491 \r
492                         if( nxt == 1 )\r
493                         {\r
494                                 res = FR_INT_ERR;\r
495                                 break;\r
496                         }       /* Internal error? */\r
497                         if( nxt == 0xFFFFFFFF )\r
498                         {\r
499                                 res = FR_DISK_ERR;\r
500                                 break;\r
501                         }       /* Disk error? */\r
502                         res = put_fat( fs, clst, 0 );   /* Mark the cluster "empty" */\r
503                         if( res != FR_OK )\r
504                         {\r
505                                 break;\r
506                         }\r
507 \r
508                         if( fs->free_clust != 0xFFFFFFFF )\r
509                         {                       /* Update FSInfo */\r
510                                 fs->free_clust++;\r
511                                 fs->fsi_flag = 1;\r
512                         }\r
513 \r
514                         clst = nxt; /* Next cluster */\r
515                 }\r
516         }\r
517 \r
518         return res;\r
519 }\r
520 \r
521 #endif\r
522 \r
523 /*-----------------------------------------------------------------------*/\r
524 \r
525 /* FAT handling - Stretch or Create a cluster chain                      */\r
526 \r
527 /*-----------------------------------------------------------------------*/\r
528 #if !_FS_READONLY\r
529 static DWORD create_chain\r
530         (                                       /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */\r
531                 FATFS * fs, /* File system object */ DWORD clst /* Cluster# to stretch. 0 means create a new chain. */\r
532         )\r
533 {\r
534         DWORD cs, ncl, scl, mcl;\r
535 \r
536         mcl = fs->max_clust;\r
537         if( clst == 0 )\r
538         {       /* Create new chain */\r
539                 scl = fs->last_clust;   /* Get suggested start point */\r
540                 if( scl == 0 || scl >= mcl )\r
541                 {\r
542                         scl = 1;\r
543                 }\r
544         }\r
545         else\r
546         {       /* Stretch existing chain */\r
547                 cs = get_fat( fs, clst );       /* Check the cluster status */\r
548                 if( cs < 2 )\r
549                 {\r
550                         return 1;                               /* It is an invalid cluster */\r
551                 }\r
552 \r
553                 if( cs < mcl )\r
554                 {\r
555                         return cs;                              /* It is already followed by next cluster */\r
556                 }\r
557 \r
558                 scl = clst;\r
559         }\r
560 \r
561         ncl = scl;                                              /* Start cluster */\r
562         for( ;; )\r
563         {\r
564                 ncl++;                                          /* Next cluster */\r
565                 if( ncl >= mcl )\r
566                 {                                       /* Wrap around */\r
567                         ncl = 2;\r
568                         if( ncl > scl )\r
569                         {\r
570                                 return 0;       /* No free custer */\r
571                         }\r
572                 }\r
573 \r
574                 cs = get_fat( fs, ncl );        /* Get the cluster status */\r
575                 if( cs == 0 )\r
576                 {\r
577                         break;                                  /* Found a free cluster */\r
578                 }\r
579 \r
580                 if( cs == 0xFFFFFFFF || cs == 1 )\r
581                 {                                       /* An error occured */\r
582                         return cs;\r
583                 }\r
584 \r
585                 if( ncl == scl )\r
586                 {\r
587                         return 0;               /* No free custer */\r
588                 }\r
589         }\r
590 \r
591         if( put_fat(fs, ncl, 0x0FFFFFFF) )\r
592         {                                               /* Mark the new cluster "in use" */\r
593                 return 0xFFFFFFFF;\r
594         }\r
595 \r
596         if( clst != 0 )\r
597         {                                               /* Link it to the previous one if needed */\r
598                 if( put_fat(fs, clst, ncl) )\r
599                 {\r
600                         return 0xFFFFFFFF;\r
601                 }\r
602         }\r
603 \r
604         fs->last_clust = ncl;   /* Update FSINFO */\r
605         if( fs->free_clust != 0xFFFFFFFF )\r
606         {\r
607                 fs->free_clust--;\r
608                 fs->fsi_flag = 1;\r
609         }\r
610 \r
611         return ncl;                             /* Return new cluster number */\r
612 }\r
613 \r
614 #endif /* !_FS_READONLY */\r
615 \r
616 /*-----------------------------------------------------------------------*/\r
617 \r
618 /* Get sector# from cluster#                                             */\r
619 \r
620 /*-----------------------------------------------------------------------*/\r
621 DWORD clust2sect\r
622         ( /* !=0: Sector number, 0: Failed - invalid cluster# */ FATFS * fs, /* File system object */ DWORD clst /* Cluster# to be converted */ )\r
623 {\r
624         clst -= 2;\r
625         if( clst >= (fs->max_clust - 2) )\r
626         {\r
627                 return 0;                       /* Invalid cluster# */\r
628         }\r
629 \r
630         return clst * fs->csize + fs->database;\r
631 }\r
632 \r
633 /*-----------------------------------------------------------------------*/\r
634 \r
635 /* Directory handling - Seek directory index                             */\r
636 \r
637 /*-----------------------------------------------------------------------*/\r
638 static FRESULT dir_seek ( DIR * dj, /* Pointer to directory object */ WORD idx /* Directory index number */ )\r
639 {\r
640         DWORD clst;\r
641         WORD ic;\r
642 \r
643         dj->index = idx;\r
644         clst = dj->sclust;\r
645         if( clst == 1 || clst >= dj->fs->max_clust )\r
646         {                                               /* Check start cluster range */\r
647                 return FR_INT_ERR;\r
648         }\r
649 \r
650         if( !clst && dj->fs->fs_type == FS_FAT32 )\r
651         {                                               /* Replace cluster# 0 with root cluster# if in FAT32 */\r
652                 clst = dj->fs->dirbase;\r
653         }\r
654 \r
655         if( clst == 0 )\r
656         {                                               /* Static table */\r
657                 dj->clust = clst;\r
658                 if( idx >= dj->fs->n_rootdir )\r
659                 {                                       /* Index is out of range */\r
660                         return FR_INT_ERR;\r
661                 }\r
662 \r
663                 dj->sect = dj->fs->dirbase + idx / ( SS(dj->fs) / 32 ); /* Sector# */\r
664         }\r
665         else\r
666         {       /* Dynamic table */\r
667                 ic =\r
668                 SS( dj->fs )\r
669                 / 32 * dj->fs->csize;                           /* Entries per cluster */\r
670                 while( idx >= ic )\r
671                 {                                                                       /* Follow cluster chain */\r
672                         clst = get_fat( dj->fs, clst ); /* Get next cluster */\r
673                         if( clst == 0xFFFFFFFF )\r
674                         {\r
675                                 return FR_DISK_ERR;                     /* Disk error */\r
676                         }\r
677 \r
678                         if( clst < 2 || clst >= dj->fs->max_clust )\r
679                         {       /* Reached to end of table or int error */\r
680                                 return FR_INT_ERR;\r
681                         }\r
682 \r
683                         idx -= ic;\r
684                 }\r
685 \r
686                 dj->clust = clst;\r
687                 dj->sect =\r
688                 clust2sect( dj->fs, clst )\r
689                 + idx / ( SS(dj->fs) / 32 );    /* Sector# */\r
690         }\r
691 \r
692         dj->dir = dj->fs->win + ( idx % (SS(dj->fs) / 32) ) * 32;       /* Ptr to the entry in the sector */\r
693 \r
694         return FR_OK;   /* Seek succeeded */\r
695 }\r
696 \r
697 /*-----------------------------------------------------------------------*/\r
698 \r
699 /* Directory handling - Move directory index next                        */\r
700 \r
701 /*-----------------------------------------------------------------------*/\r
702 static FRESULT dir_next\r
703         (                               /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */\r
704                 DIR *\r
705                 dj, /* Pointer to directory object */ BOOL streach      /* FALSE: Do not streach table, TRUE: Streach table if needed */\r
706         )\r
707 {\r
708         DWORD clst;\r
709         WORD i;\r
710 \r
711         i = dj->index + 1;\r
712         if( !i || !dj->sect )\r
713         {                               /* Report EOT when index has reached 65535 */\r
714                 return FR_NO_FILE;\r
715         }\r
716 \r
717         if( !(i % (SS(dj->fs) / 32)) )\r
718         {                               /* Sector changed? */\r
719                 dj->sect++; /* Next sector */\r
720 \r
721                 if( dj->clust == 0 )\r
722                 {                       /* Static table */\r
723                         if( i >= dj->fs->n_rootdir )\r
724                         {               /* Report EOT when end of table */\r
725                                 return FR_NO_FILE;\r
726                         }\r
727                 }\r
728                 else\r
729                 {                       /* Dynamic table */\r
730                         if( ((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0 )\r
731                         {               /* Cluster changed? */\r
732                                 clst = get_fat( dj->fs, dj->clust );    /* Get next cluster */\r
733                                 if( clst <= 1 )\r
734                                 {\r
735                                         return FR_INT_ERR;\r
736                                 }\r
737 \r
738                                 if( clst == 0xFFFFFFFF )\r
739                                 {\r
740                                         return FR_DISK_ERR;\r
741                                 }\r
742 \r
743                                 if( clst >= dj->fs->max_clust )\r
744                                 {       /* When it reached end of dynamic table */\r
745                                         #if !_FS_READONLY\r
746                                         BYTE c;\r
747                                         if( !streach )\r
748                                         {\r
749                                                 return FR_NO_FILE;                                              /* When do not streach, report EOT */\r
750                                         }\r
751 \r
752                                         clst = create_chain( dj->fs, dj->clust );       /* Streach cluster chain */\r
753                                         if( clst == 0 )\r
754                                         {\r
755                                                 return FR_DENIED;                                               /* No free cluster */\r
756                                         }\r
757 \r
758                                         if( clst == 1 )\r
759                                         {\r
760                                                 return FR_INT_ERR;\r
761                                         }\r
762 \r
763                                         if( clst == 0xFFFFFFFF )\r
764                                         {\r
765                                                 return FR_DISK_ERR;\r
766                                         }\r
767 \r
768                                         /* Clean-up streached table */\r
769                                         if( move_window(dj->fs, 0) )\r
770                                         {\r
771                                                 return FR_DISK_ERR;                                             /* Flush active window */\r
772                                         }\r
773 \r
774                                         mem_set( dj->fs->win, 0, SS(dj->fs) );          /* Clear window buffer */\r
775                                         dj->fs->winsect = clust2sect( dj->fs, clst );   /* Cluster start sector */\r
776                                         for( c = 0; c < dj->fs->csize; c++ )\r
777                                         {                                               /* Fill the new cluster with 0 */\r
778                                                 dj->fs->wflag = 1;\r
779                                                 if( move_window(dj->fs, 0) )\r
780                                                 {\r
781                                                         return FR_DISK_ERR;\r
782                                                 }\r
783 \r
784                                                 dj->fs->winsect++;\r
785                                         }\r
786 \r
787                                         dj->fs->winsect -= c;   /* Rewind window address */\r
788                                         #else\r
789                                         return FR_NO_FILE;              /* Report EOT */\r
790                                         #endif\r
791                                 }\r
792 \r
793                                 dj->clust = clst;                       /* Initialize data for new cluster */\r
794                                 dj->sect = clust2sect( dj->fs, clst );\r
795                         }\r
796                 }\r
797         }\r
798 \r
799         dj->index = i;\r
800         dj->dir = dj->fs->win + ( i % (SS(dj->fs) / 32) ) * 32;\r
801 \r
802         return FR_OK;\r
803 }\r
804 \r
805 /*-----------------------------------------------------------------------*/\r
806 \r
807 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */\r
808 \r
809 /*-----------------------------------------------------------------------*/\r
810 #if _USE_LFN\r
811 static const BYTE LfnOfs[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };\r
812 \r
813 /* Offset of LFN chars in the directory entry */\r
814 \r
815 static BOOL cmp_lfn\r
816         (                                       /* TRUE:Matched, FALSE:Not matched */\r
817                 WCHAR *lfnbuf,  /* Pointer to the LFN to be compared */\r
818                 BYTE *dir               /* Pointer to the directory entry containing a part of LFN */\r
819         )\r
820 {\r
821         int             i, s;\r
822         WCHAR   wc, uc;\r
823 \r
824         i = ( (dir[LDIR_Ord] & 0xBF) - 1 ) * 13;        /* Get offset in the LFN buffer */\r
825         s = 0;\r
826         wc = 1;\r
827         do\r
828         {\r
829                 uc = LD_WORD( dir + LfnOfs[s] );                /* Pick an LFN character from the entry */\r
830                 if( wc )\r
831                 {       /* Last char has not been processed */\r
832                         wc = ff_wtoupper( uc ); /* Convert it to upper case */\r
833                         if( i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]) )\r
834                         {                                               /* Compare it */\r
835                                 return FALSE;           /* Not matched */\r
836                         }\r
837                 }\r
838                 else\r
839                 {\r
840                         if( uc != 0xFFFF )\r
841                         {\r
842                                 return FALSE;           /* Check filler */\r
843                         }\r
844                 }\r
845         } while( ++s < 13 );\r
846 \r
847         /* Repeat until all chars in the entry are checked */\r
848 \r
849         if( (dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i] )\r
850         {                               /* Last segment matched but different length */\r
851                 return FALSE;\r
852         }\r
853 \r
854         return TRUE;    /* The part of LFN matched */\r
855 }\r
856 \r
857 static BOOL pick_lfn\r
858         (                                       /* TRUE:Succeeded, FALSE:Buffer overflow */\r
859                 WCHAR *lfnbuf,  /* Pointer to the Unicode-LFN buffer */\r
860                 BYTE *dir               /* Pointer to the directory entry */\r
861         )\r
862 {\r
863         int             i, s;\r
864         WCHAR   wc, uc;\r
865 \r
866         i = ( (dir[LDIR_Ord] & 0x3F) - 1 ) * 13;        /* Offset in the LFN buffer */\r
867 \r
868         s = 0;\r
869         wc = 1;\r
870         do\r
871         {\r
872                 uc = LD_WORD( dir + LfnOfs[s] );                /* Pick an LFN character from the entry */\r
873                 if( wc )\r
874                 {                                               /* Last char has not been processed */\r
875                         if( i >= _MAX_LFN )\r
876                         {\r
877                                 return FALSE;   /* Buffer overflow? */\r
878                         }\r
879 \r
880                         lfnbuf[i++] = wc = uc;  /* Store it */\r
881                 }\r
882                 else\r
883                 {\r
884                         if( uc != 0xFFFF )\r
885                         {\r
886                                 return FALSE;           /* Check filler */\r
887                         }\r
888                 }\r
889         } while( ++s < 13 );\r
890 \r
891         /* Read all character in the entry */\r
892 \r
893         if( dir[LDIR_Ord] & 0x40 )\r
894         {                                               /* Put terminator if it is the last LFN part */\r
895                 if( i >= _MAX_LFN )\r
896                 {\r
897                         return FALSE;   /* Buffer overflow? */\r
898                 }\r
899 \r
900                 lfnbuf[i] = 0;\r
901         }\r
902 \r
903         return TRUE;\r
904 }\r
905 \r
906         #if !_FS_READONLY\r
907 static void fit_lfn\r
908                         (\r
909                                 const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */\r
910                                 BYTE            *dir,           /* Pointer to the directory entry */\r
911                                 BYTE            ord,            /* LFN order (1-20) */\r
912                                 BYTE            sum                     /* SFN sum */\r
913                         )\r
914 {\r
915         int             i, s;\r
916         WCHAR   wc;\r
917 \r
918         dir[LDIR_Chksum] = sum;                         /* Set check sum */\r
919         dir[LDIR_Attr] = AM_LFN;                        /* Set attribute. LFN entry */\r
920         dir[LDIR_Type] = 0;\r
921         ST_WORD( dir + LDIR_FstClusLO, 0 );\r
922 \r
923         i = ( ord - 1 ) * 13;                           /* Get offset in the LFN buffer */\r
924         s = wc = 0;\r
925         do\r
926         {\r
927                 if( wc != 0xFFFF )\r
928                 {\r
929                         wc = lfnbuf[i++];                       /* Get an effective char */\r
930                 }\r
931 \r
932                 ST_WORD( dir + LfnOfs[s], wc ); /* Put it */\r
933                 if( !wc )\r
934                 {\r
935                         wc = 0xFFFF;                            /* Padding chars following last char */\r
936                 }\r
937         } while( ++s < 13 );\r
938         if( wc == 0xFFFF || !lfnbuf[i] )\r
939         {\r
940                 ord |= 0x40;                                    /* Bottom LFN part is the start of LFN sequence */\r
941         }\r
942 \r
943         dir[LDIR_Ord] = ord;                            /* Set the LFN order */\r
944 }\r
945 \r
946         #endif\r
947 #endif\r
948 \r
949 /*-----------------------------------------------------------------------*/\r
950 \r
951 /* Create numbered name                                                  */\r
952 \r
953 /*-----------------------------------------------------------------------*/\r
954 #if _USE_LFN\r
955 void gen_numname\r
956         (\r
957                 BYTE            *dst,   /* Pointer to genartated SFN */\r
958                 const BYTE      *src,   /* Pointer to source SFN to be modified */\r
959                 const WCHAR *lfn,       /* Pointer to LFN */\r
960                 WORD            num             /* Sequense number */\r
961         )\r
962 {\r
963         char    ns[8];\r
964         int             i, j;\r
965 \r
966         mem_cpy( dst, src, 11 );\r
967 \r
968         if( num > 5 )\r
969         {       /* On many collisions, generate a hash number instead of sequencial number */\r
970                 do\r
971                 {\r
972                         num = ( num >> 1 ) + ( num << 15 ) + ( WORD ) * lfn++;\r
973                 } while( *lfn );\r
974         }\r
975 \r
976         /* itoa */\r
977         i = 7;\r
978         do\r
979         {\r
980                 ns[i--] = ( num % 10 ) + '0';\r
981                 num /= 10;\r
982         } while( num );\r
983         ns[i] = '~';\r
984 \r
985         /* Append the number */\r
986         for( j = 0; j < i && dst[j] != ' '; j++ )\r
987         {\r
988                 if( IsDBCS1(dst[j]) )\r
989                 {\r
990                         if( j == i - 1 )\r
991                         {\r
992                                 break;\r
993                         }\r
994 \r
995                         j++;\r
996                 }\r
997         }\r
998 \r
999         do\r
1000         {\r
1001                 dst[j++] = ( i < 8 ) ? ns[i++] : ' ';\r
1002         } while( j < 8 );\r
1003 }\r
1004 \r
1005 #endif\r
1006 \r
1007 /*-----------------------------------------------------------------------*/\r
1008 \r
1009 /* Calculate sum of an SFN                                               */\r
1010 \r
1011 /*-----------------------------------------------------------------------*/\r
1012 #if _USE_LFN\r
1013 static BYTE sum_sfn( const BYTE *dir /* Ptr to directory entry */ )\r
1014 {\r
1015         BYTE    sum = 0;\r
1016         int             n = 11;\r
1017 \r
1018         do\r
1019         {\r
1020                 sum = ( sum >> 1 ) + ( sum << 7 ) +*dir++;\r
1021         } while( --n );\r
1022         return sum;\r
1023 }\r
1024 \r
1025 #endif\r
1026 \r
1027 /*-----------------------------------------------------------------------*/\r
1028 \r
1029 /* Directory handling - Find an object in the directory                  */\r
1030 \r
1031 /*-----------------------------------------------------------------------*/\r
1032 static FRESULT dir_find( DIR *dj /* Pointer to the directory object linked to the file name */ )\r
1033 {\r
1034         FRESULT res;\r
1035         BYTE    c, *dir;\r
1036         #if _USE_LFN\r
1037         BYTE    a, ord, sum;\r
1038         #endif\r
1039         res = dir_seek( dj, 0 );                        /* Rewind directory object */\r
1040         if( res != FR_OK )\r
1041         {\r
1042                 return res;\r
1043         }\r
1044 \r
1045         #if _USE_LFN\r
1046         ord = sum = 0xFF;\r
1047         #endif\r
1048         do\r
1049         {\r
1050                 res = move_window( dj->fs, dj->sect );\r
1051                 if( res != FR_OK )\r
1052                 {\r
1053                         break;\r
1054                 }\r
1055 \r
1056                 dir = dj->dir;                                  /* Ptr to the directory entry of current index */\r
1057                 c = dir[DIR_Name];\r
1058                 if( c == 0 )\r
1059                 {\r
1060                         res = FR_NO_FILE;\r
1061                         break;\r
1062                 }                                                               /* Reached to end of table */\r
1063 \r
1064                 #if _USE_LFN                                    /* LFN configuration */\r
1065                 a = dir[DIR_Attr] & AM_MASK;\r
1066                 if( c == 0xE5 || ((a & AM_VOL) && a != AM_LFN) )\r
1067                 {                                                               /* An entry without valid data */\r
1068                         ord = 0xFF;\r
1069                 }\r
1070                 else\r
1071                 {\r
1072                         if( a == AM_LFN )\r
1073                         {                                                       /* An LFN entry is found */\r
1074                                 if( dj->lfn )\r
1075                                 {\r
1076                                         if( c & 0x40 )\r
1077                                         {                                       /* Is it start of LFN sequence? */\r
1078                                                 sum = dir[LDIR_Chksum];\r
1079                                                 c &= 0xBF;\r
1080                                                 ord = c;                /* LFN start order */\r
1081                                                 dj->lfn_idx = dj->index;\r
1082                                         }\r
1083 \r
1084                                         /* Check validity of the LFN entry and compare it with given name */\r
1085                                         ord = ( c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir) ) ? ord - 1 : 0xFF;\r
1086                                 }\r
1087                         }\r
1088                         else\r
1089                         {                                                       /* An SFN entry is found */\r
1090                                 if( !ord && sum == sum_sfn(dir) )\r
1091                                 {\r
1092                                         break;                          /* LFN matched? */\r
1093                                 }\r
1094 \r
1095                                 ord = 0xFF;\r
1096                                 dj->lfn_idx = 0xFFFF;   /* Reset LFN sequence */\r
1097                                 if( !(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11) )\r
1098                                 {\r
1099                                         break;                          /* SFN matched? */\r
1100                                 }\r
1101                         }\r
1102                 }\r
1103 \r
1104                 #else /* Non LFN configuration */\r
1105                 if( !(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11) )\r
1106                 {       /* Is it a valid entry? */\r
1107                         break;\r
1108                 }\r
1109 \r
1110                 #endif\r
1111                 res = dir_next( dj, FALSE );    /* Next entry */\r
1112         } while( res == FR_OK );\r
1113 \r
1114         return res;\r
1115 }\r
1116 \r
1117 /*-----------------------------------------------------------------------*/\r
1118 \r
1119 /* Read an object from the directory                                     */\r
1120 \r
1121 /*-----------------------------------------------------------------------*/\r
1122 #if _FS_MINIMIZE <= 1\r
1123 static FRESULT dir_read( DIR *dj /* Pointer to the directory object that pointing the entry to be read */ )\r
1124 {\r
1125         FRESULT res;\r
1126         BYTE    c, *dir;\r
1127                 #if _USE_LFN\r
1128         BYTE    a, ord = 0xFF, sum = 0xFF;\r
1129                 #endif\r
1130         res = FR_NO_FILE;\r
1131         while( dj->sect )\r
1132         {\r
1133                 res = move_window( dj->fs, dj->sect );\r
1134                 if( res != FR_OK )\r
1135                 {\r
1136                         break;\r
1137                 }\r
1138 \r
1139                 dir = dj->dir;                                  /* Ptr to the directory entry of current index */\r
1140                 c = dir[DIR_Name];\r
1141                 if( c == 0 )\r
1142                 {\r
1143                         res = FR_NO_FILE;\r
1144                         break;\r
1145                 }                                       /* Reached to end of table */\r
1146 \r
1147                         #if _USE_LFN    /* LFN configuration */\r
1148                 a = dir[DIR_Attr] & AM_MASK;\r
1149                 if( c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN) )\r
1150                 {                                       /* An entry without valid data */\r
1151                         ord = 0xFF;\r
1152                 }\r
1153                 else\r
1154                 {\r
1155                         if( a == AM_LFN )\r
1156                         {                               /* An LFN entry is found */\r
1157                                 if( c & 0x40 )\r
1158                                 {                       /* Is it start of LFN sequence? */\r
1159                                         sum = dir[LDIR_Chksum];\r
1160                                         c &= 0xBF;\r
1161                                         ord = c;\r
1162                                         dj->lfn_idx = dj->index;\r
1163                                 }\r
1164 \r
1165                                 /* Check LFN validity and capture it */\r
1166                                 ord = ( c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir) ) ? ord - 1 : 0xFF;\r
1167                         }\r
1168                         else\r
1169                         {                               /* An SFN entry is found */\r
1170                                 if( ord || sum != sum_sfn(dir) )\r
1171                                 {                       /* Is there a valid LFN? */\r
1172                                         dj->lfn_idx = 0xFFFF;   /* It has no LFN. */\r
1173                                 }\r
1174 \r
1175                                 break;\r
1176                         }\r
1177                 }\r
1178 \r
1179                         #else /* Non LFN configuration */\r
1180                 if( c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL) )\r
1181                 {       /* Is it a valid entry? */\r
1182                         break;\r
1183                 }\r
1184 \r
1185                         #endif\r
1186                 res = dir_next( dj, FALSE );    /* Next entry */\r
1187                 if( res != FR_OK )\r
1188                 {\r
1189                         break;\r
1190                 }\r
1191         }\r
1192 \r
1193         if( res != FR_OK )\r
1194         {\r
1195                 dj->sect = 0;\r
1196         }\r
1197 \r
1198         return res;\r
1199 }\r
1200 \r
1201 #endif\r
1202 \r
1203 /*-----------------------------------------------------------------------*/\r
1204 \r
1205 /* Register an object to the directory                                   */\r
1206 \r
1207 /*-----------------------------------------------------------------------*/\r
1208 #if !_FS_READONLY\r
1209 static FRESULT dir_register\r
1210         (                       /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */\r
1211                 DIR *dj /* Target directory with object name to be created */\r
1212         )\r
1213 {\r
1214         FRESULT res;\r
1215         BYTE    c, *dir;\r
1216                 #if _USE_LFN                    /* LFN configuration */\r
1217         WORD    n, ne, is;\r
1218         BYTE    sn[12], *fn, sum;\r
1219         WCHAR   *lfn;\r
1220 \r
1221         fn = dj->fn;\r
1222         lfn = dj->lfn;\r
1223         mem_cpy( sn, fn, 12 );\r
1224 \r
1225         if( _FS_RPATH && (sn[NS] & NS_DOT) )\r
1226         {\r
1227                 return FR_INVALID_NAME; /* Cannot create dot entry */\r
1228         }\r
1229 \r
1230         if( sn[NS] & NS_LOSS )\r
1231         {                                       /* When LFN is out of 8.3 format, generate a numbered name */\r
1232                 fn[NS] = 0;\r
1233                 dj->lfn = NULL; /* Find only SFN */\r
1234                 for( n = 1; n < 100; n++ )\r
1235                 {\r
1236                         gen_numname( fn, sn, lfn, n );  /* Generate a numbered name */\r
1237                         res = dir_find( dj );                   /* Check if the name collides with existing SFN */\r
1238                         if( res != FR_OK )\r
1239                         {\r
1240                                 break;\r
1241                         }\r
1242                 }\r
1243 \r
1244                 if( n == 100 )\r
1245                 {\r
1246                         return FR_DENIED;                               /* Abort if too many collisions */\r
1247                 }\r
1248 \r
1249                 if( res != FR_NO_FILE )\r
1250                 {\r
1251                         return res;                                             /* Abort if the result is other than 'not collided' */\r
1252                 }\r
1253 \r
1254                 fn[NS] = sn[NS];\r
1255                 dj->lfn = lfn;\r
1256         }\r
1257 \r
1258         if( sn[NS] & NS_LFN )\r
1259         {                                       /* When LFN is to be created, reserve reserve an SFN + LFN entries. */\r
1260                 for( ne = 0; lfn[ne]; ne++ );\r
1261                 ne = ( ne + 25 ) / 13;\r
1262         }\r
1263         else\r
1264         {                                       /* Otherwise reserve only an SFN entry. */\r
1265                 ne = 1;\r
1266         }\r
1267 \r
1268         /* Reserve contiguous entries */\r
1269         res = dir_seek( dj, 0 );\r
1270         if( res != FR_OK )\r
1271         {\r
1272                 return res;\r
1273         }\r
1274 \r
1275         n = is = 0;\r
1276         do\r
1277         {\r
1278                 res = move_window( dj->fs, dj->sect );\r
1279                 if( res != FR_OK )\r
1280                 {\r
1281                         break;\r
1282                 }\r
1283 \r
1284                 c = *dj->dir;   /* Check the entry status */\r
1285                 if( c == 0xE5 || c == 0 )\r
1286                 {                               /* Is it a blank entry? */\r
1287                         if( n == 0 )\r
1288                         {\r
1289                                 is = dj->index;         /* First index of the contigulus entry */\r
1290                         }\r
1291 \r
1292                         if( ++n == ne )\r
1293                         {\r
1294                                 break;                          /* A contiguous entry that requiered count is found */\r
1295                         }\r
1296                 }\r
1297                 else\r
1298                 {\r
1299                         n = 0;                                  /* Not a blank entry. Restart to search */\r
1300                 }\r
1301 \r
1302                 res = dir_next( dj, TRUE ); /* Next entry with table streach */\r
1303         } while( res == FR_OK );\r
1304 \r
1305         if( res == FR_OK && ne > 1 )\r
1306         {       /* Initialize LFN entry if needed */\r
1307                 res = dir_seek( dj, is );\r
1308                 if( res == FR_OK )\r
1309                 {\r
1310                         sum = sum_sfn( dj->fn );        /* Sum of the SFN tied to the LFN */\r
1311                         ne--;\r
1312                         do\r
1313                         {       /* Store LFN entries in bottom first */\r
1314                                 res = move_window( dj->fs, dj->sect );\r
1315                                 if( res != FR_OK )\r
1316                                 {\r
1317                                         break;\r
1318                                 }\r
1319 \r
1320                                 fit_lfn( dj->lfn, dj->dir, (BYTE) ne, sum );\r
1321                                 dj->fs->wflag = 1;\r
1322                                 res = dir_next( dj, FALSE );    /* Next entry */\r
1323                         } while( res == FR_OK && --ne );\r
1324                 }\r
1325         }\r
1326 \r
1327                 #else /* Non LFN configuration */\r
1328         res = dir_seek( dj, 0 );\r
1329         if( res == FR_OK )\r
1330         {\r
1331                 do\r
1332                 {                               /* Find a blank entry for the SFN */\r
1333                         res = move_window( dj->fs, dj->sect );\r
1334                         if( res != FR_OK )\r
1335                         {\r
1336                                 break;\r
1337                         }\r
1338 \r
1339                         c = *dj->dir;\r
1340                         if( c == 0xE5 || c == 0 )\r
1341                         {\r
1342                                 break;  /* Is it a blank entry? */\r
1343                         }\r
1344 \r
1345                         res = dir_next( dj, TRUE ); /* Next entry with table streach */\r
1346                 } while( res == FR_OK );\r
1347         }\r
1348 \r
1349                 #endif\r
1350         if( res == FR_OK )\r
1351         {       /* Initialize the SFN entry */\r
1352                 res = move_window( dj->fs, dj->sect );\r
1353                 if( res == FR_OK )\r
1354                 {\r
1355                         dir = dj->dir;\r
1356                         mem_set( dir, 0, 32 );          /* Clean the entry */\r
1357                         mem_cpy( dir, dj->fn, 11 ); /* Put SFN */\r
1358                         dir[DIR_NTres] = *( dj->fn + NS ) & ( NS_BODY | NS_EXT );       /* Put NT flag */\r
1359                         dj->fs->wflag = 1;\r
1360                 }\r
1361         }\r
1362 \r
1363         return res;\r
1364 }\r
1365 \r
1366 #endif /* !_FS_READONLY */\r
1367 \r
1368 /*-----------------------------------------------------------------------*/\r
1369 \r
1370 /* Remove an object from the directory                                   */\r
1371 \r
1372 /*-----------------------------------------------------------------------*/\r
1373 #if !_FS_READONLY && !_FS_MINIMIZE\r
1374 static FRESULT dir_remove\r
1375         (                       /* FR_OK: Successful, FR_DISK_ERR: A disk error */\r
1376                 DIR *dj /* Directory object pointing the entry to be removed */\r
1377         )\r
1378 {\r
1379         FRESULT res;\r
1380                 #if _USE_LFN    /* LFN configuration */\r
1381         WORD    i;\r
1382 \r
1383         i = dj->index;          /* SFN index */\r
1384         res = dir_seek( dj, (WORD) ((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx) );       /* Goto the SFN or top of the LFN entries */\r
1385         if( res == FR_OK )\r
1386         {\r
1387                 do\r
1388                 {\r
1389                         res = move_window( dj->fs, dj->sect );\r
1390                         if( res != FR_OK )\r
1391                         {\r
1392                                 break;\r
1393                         }\r
1394 \r
1395                         *dj->dir = 0xE5;                                /* Mark the entry "deleted" */\r
1396                         dj->fs->wflag = 1;\r
1397                         if( dj->index >= i )\r
1398                         {\r
1399                                 break;                                          /* When reached SFN, all entries of the object has been deleted. */\r
1400                         }\r
1401 \r
1402                         res = dir_next( dj, FALSE );    /* Next entry */\r
1403                 } while( res == FR_OK );\r
1404                 if( res == FR_NO_FILE )\r
1405                 {\r
1406                         res = FR_INT_ERR;\r
1407                 }\r
1408         }\r
1409 \r
1410                 #else /* Non LFN configuration */\r
1411         res = dir_seek( dj, dj->index );\r
1412         if( res == FR_OK )\r
1413         {\r
1414                 res = move_window( dj->fs, dj->sect );\r
1415                 if( res == FR_OK )\r
1416                 {\r
1417                         *dj->dir = 0xE5;                                /* Mark the entry "deleted" */\r
1418                         dj->fs->wflag = 1;\r
1419                 }\r
1420         }\r
1421 \r
1422                 #endif\r
1423         return res;\r
1424 }\r
1425 \r
1426 #endif /* !_FS_READONLY */\r
1427 \r
1428 /*-----------------------------------------------------------------------*/\r
1429 \r
1430 /* Pick a segment and create the object name in directory form           */\r
1431 \r
1432 /*-----------------------------------------------------------------------*/\r
1433 static FRESULT create_name\r
1434                         (\r
1435                                 DIR                     *dj,    /* Pointer to the directory object */\r
1436                                 const XCHAR **path      /* Pointer to pointer to the segment in the path string */\r
1437                         )\r
1438 {\r
1439         #ifdef _EXCVT\r
1440 \r
1441         static const BYTE       cvt[] = _EXCVT;\r
1442         #endif\r
1443         #if _USE_LFN                                    /* LFN configuration */\r
1444         BYTE                            b, cf;\r
1445         WCHAR                           w, *lfn;\r
1446         int                                     i, ni, si, di;\r
1447         const XCHAR                     *p;\r
1448 \r
1449         /* Create LFN in Unicode */\r
1450         si = di = 0;\r
1451         p = *path;\r
1452         lfn = dj->lfn;\r
1453         for( ;; )\r
1454         {\r
1455                 w = p[si++];                            /* Get a character */\r
1456                 if( w < ' ' || w == '/' || w == '\\' )\r
1457                 {\r
1458                         break;                                  /* Break on end of segment */\r
1459                 }\r
1460 \r
1461                 if( di >= _MAX_LFN )\r
1462                 {                                                       /* Reject too long name */\r
1463                         return FR_INVALID_NAME;\r
1464                 }\r
1465 \r
1466                         #if !_LFN_UNICODE\r
1467                 w &= 0xFF;\r
1468                 if( IsDBCS1(w) )\r
1469                 {                                                       /* If it is a DBC 1st byte */\r
1470                         b = p[si++];                    /* Get 2nd byte */\r
1471                         if( !IsDBCS2(b) )\r
1472                         {                                               /* Reject invalid code for DBC */\r
1473                                 return FR_INVALID_NAME;\r
1474                         }\r
1475 \r
1476                         w = ( w << 8 ) + b;\r
1477                 }\r
1478 \r
1479                 w = ff_convert( w, 1 );         /* Convert OEM to Unicode */\r
1480                 if( !w )\r
1481                 {\r
1482                         return FR_INVALID_NAME; /* Reject invalid code */\r
1483                 }\r
1484 \r
1485                         #endif\r
1486                 if( w < 0x80 && chk_chr("\"*:<>\?|\x7F", w) )\r
1487                 {                               /* Reject illegal chars for LFN */\r
1488                         return FR_INVALID_NAME;\r
1489                 }\r
1490 \r
1491                 lfn[di++] = w;  /* Store the Unicode char */\r
1492         }\r
1493 \r
1494         *path = &p[si];         /* Rerurn pointer to the next segment */\r
1495         cf = ( w < ' ' ) ? NS_LAST : 0; /* Set last segment flag if end of path */\r
1496                 #if _FS_RPATH\r
1497         if( (di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.') )\r
1498         {\r
1499                 lfn[di] = 0;\r
1500                 for( i = 0; i < 11; i++ )\r
1501                 {\r
1502                         dj->fn[i] = ( i < di ) ? '.' : ' ';\r
1503                 }\r
1504 \r
1505                 dj->fn[i] = cf | NS_DOT;        /* This is a dot entry */\r
1506                 return FR_OK;\r
1507         }\r
1508 \r
1509                 #endif\r
1510         while( di )\r
1511         {       /* Strip trailing spaces and dots */\r
1512                 w = lfn[di - 1];\r
1513                 if( w != ' ' && w != '.' )\r
1514                 {\r
1515                         break;\r
1516                 }\r
1517 \r
1518                 di--;\r
1519         }\r
1520 \r
1521         if( !di )\r
1522         {\r
1523                 return FR_INVALID_NAME;                 /* Reject null string */\r
1524         }\r
1525 \r
1526         lfn[di] = 0;                                            /* LFN is created */\r
1527 \r
1528         /* Create SFN in directory form */\r
1529         mem_set( dj->fn, ' ', 11 );\r
1530         for( si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++ );\r
1531 \r
1532         /* Strip leading spaces and dots */\r
1533         if( si )\r
1534         {\r
1535                 cf |= NS_LOSS | NS_LFN;\r
1536         }\r
1537 \r
1538         while( di && lfn[di - 1] != '.' )\r
1539         {\r
1540                 di--;                                                   /* Find extension (di<=si: no extension) */\r
1541         }\r
1542 \r
1543         b = i = 0;\r
1544         ni = 8;\r
1545         for( ;; )\r
1546         {\r
1547                 w = lfn[si++];                                  /* Get an LFN char */\r
1548                 if( !w )\r
1549                 {\r
1550                         break;                                          /* Break on enf of the LFN */\r
1551                 }\r
1552 \r
1553                 if( w == ' ' || (w == '.' && si != di) )\r
1554                 {                                                               /* Remove spaces and dots */\r
1555                         cf |= NS_LOSS | NS_LFN;\r
1556                         continue;\r
1557                 }\r
1558 \r
1559                 if( i >= ni || si == di )\r
1560                 {                                                               /* Extension or end of SFN */\r
1561                         if( ni == 11 )\r
1562                         {                                                       /* Long extension */\r
1563                                 cf |= NS_LOSS | NS_LFN;\r
1564                                 break;\r
1565                         }\r
1566 \r
1567                         if( si != di )\r
1568                         {\r
1569                                 cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */\r
1570                         }\r
1571 \r
1572                         if( si > di )\r
1573                         {\r
1574                                 break;                                  /* No extension */\r
1575                         }\r
1576 \r
1577                         si = di;\r
1578                         i = 8;\r
1579                         ni = 11;                                        /* Enter extension section */\r
1580                         b <<= 2;\r
1581                         continue;\r
1582                 }\r
1583 \r
1584                 if( w >= 0x80 )\r
1585                 {       /* Non ASCII char */\r
1586                                 #ifdef _EXCVT\r
1587                         w = ff_convert( w, 0 );                                 /* Unicode -> OEM code */\r
1588                         if( w )\r
1589                         {\r
1590                                 w = cvt[w - 0x80];                                      /* Convert extended char to upper (SBCS) */\r
1591                         }\r
1592 \r
1593                                 #else\r
1594                         w = ff_convert( ff_wtoupper(w), 0 );    /* Upper converted Unicode -> OEM code */\r
1595                                 #endif\r
1596                         cf |= NS_LFN;                           /* Force create LFN entry */\r
1597                 }\r
1598 \r
1599                 if( _DF1S && w >= 0x100 )\r
1600                 {                                                               /* Double byte char */\r
1601                         if( i >= ni - 1 )\r
1602                         {\r
1603                                 cf |= NS_LOSS | NS_LFN;\r
1604                                 i = ni;\r
1605                                 continue;\r
1606                         }\r
1607 \r
1608                         dj->fn[i++] = ( BYTE ) ( w >> 8 );\r
1609                 }\r
1610                 else\r
1611                 {                                                               /* Single byte char */\r
1612                         if( !w || chk_chr("+,;[=]", w) )\r
1613                         {                                                       /* Replace illegal chars for SFN */\r
1614                                 w = '_';\r
1615                                 cf |= NS_LOSS | NS_LFN; /* Lossy conversion */\r
1616                         }\r
1617                         else\r
1618                         {\r
1619                                 if( IsUpper(w) )\r
1620                                 {                                               /* ASCII large capital */\r
1621                                         b |= 2;\r
1622                                 }\r
1623                                 else\r
1624                                 {\r
1625                                         if( IsLower(w) )\r
1626                                         {                                       /* ASCII small capital */\r
1627                                                 b |= 1;\r
1628                                                 w -= 0x20;\r
1629                                         }\r
1630                                 }\r
1631                         }\r
1632                 }\r
1633 \r
1634                 dj->fn[i++] = ( BYTE ) w;\r
1635         }\r
1636 \r
1637         if( dj->fn[0] == 0xE5 )\r
1638         {\r
1639                 dj->fn[0] = 0x05;                               /* If the first char collides with deleted mark, replace it with 0x05 */\r
1640         }\r
1641 \r
1642         if( ni == 8 )\r
1643         {\r
1644                 b <<= 2;\r
1645         }\r
1646 \r
1647         if( (b & 0x0C) == 0x0C || (b & 0x03) == 0x03 )\r
1648         {                                               /* Create LFN entry when there are composite capitals */\r
1649                 cf |= NS_LFN;\r
1650         }\r
1651 \r
1652         if( !(cf & NS_LFN) )\r
1653         {                                               /* When LFN is in 8.3 format without extended char, NT flags are created */\r
1654                 if( (b & 0x03) == 0x01 )\r
1655                 {\r
1656                         cf |= NS_EXT;   /* NT flag (Extension has only small capital) */\r
1657                 }\r
1658 \r
1659                 if( (b & 0x0C) == 0x04 )\r
1660                 {\r
1661                         cf |= NS_BODY;  /* NT flag (Filename has only small capital) */\r
1662                 }\r
1663         }\r
1664 \r
1665         dj->fn[NS] = cf;                /* SFN is created */\r
1666 \r
1667         return FR_OK;\r
1668 \r
1669         #else /* Non-LFN configuration */\r
1670         BYTE            b, c, d, *sfn;\r
1671         int                     ni, si, i;\r
1672         const char      *p;\r
1673 \r
1674         /* Create file name in directory form */\r
1675         sfn = dj->fn;\r
1676         mem_set( sfn, ' ', 11 );\r
1677         si = i = b = 0;\r
1678         ni = 8;\r
1679         p = *path;\r
1680                 #if _FS_RPATH\r
1681         if( p[si] == '.' )\r
1682         {                                               /* Is this a dot entry? */\r
1683                 for( ;; )\r
1684                 {\r
1685                         c = p[si++];\r
1686                         if( c != '.' || si >= 3 )\r
1687                         {\r
1688                                 break;\r
1689                         }\r
1690 \r
1691                         sfn[i++] = c;\r
1692                 }\r
1693 \r
1694                 if( c != '/' && c != '\\' && c > ' ' )\r
1695                 {\r
1696                         return FR_INVALID_NAME;\r
1697                 }\r
1698 \r
1699                 *path = &p[si];         /* Rerurn pointer to the next segment */\r
1700                 sfn[NS] = ( c <= ' ' ) ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */\r
1701                 return FR_OK;\r
1702         }\r
1703 \r
1704                 #endif\r
1705         for( ;; )\r
1706         {\r
1707                 c = p[si++];\r
1708                 if( c <= ' ' || c == '/' || c == '\\' )\r
1709                 {\r
1710                         break;                                  /* Break on end of segment */\r
1711                 }\r
1712 \r
1713                 if( c == '.' || i >= ni )\r
1714                 {\r
1715                         if( ni != 8 || c != '.' )\r
1716                         {\r
1717                                 return FR_INVALID_NAME;\r
1718                         }\r
1719 \r
1720                         i = 8;\r
1721                         ni = 11;\r
1722                         b <<= 2;\r
1723                         continue;\r
1724                 }\r
1725 \r
1726                 if( c >= 0x80 )\r
1727                 {                                                       /* Extended char */\r
1728                                 #ifdef _EXCVT\r
1729                         c = cvt[c - 0x80];              /* Convert extend char (SBCS) */\r
1730                                 #else\r
1731                         b |= 3;                                 /* Eliminate NT flag if ext char is exist */\r
1732                                         #if !_DF1S              /* ASCII only cfg */\r
1733                         return FR_INVALID_NAME;\r
1734                                         #endif\r
1735                                 #endif\r
1736                 }\r
1737 \r
1738                 if( IsDBCS1(c) )\r
1739                 {                                                       /* DBC 1st byte? */\r
1740                         d = p[si++];                    /* Get 2nd byte */\r
1741                         if( !IsDBCS2(d) || i >= ni - 1 )\r
1742                         {                                               /* Reject invalid DBC */\r
1743                                 return FR_INVALID_NAME;\r
1744                         }\r
1745 \r
1746                         sfn[i++] = c;\r
1747                         sfn[i++] = d;\r
1748                 }\r
1749                 else\r
1750                 {                                                       /* Single byte code */\r
1751                         if( chk_chr(" \"*+,[=]|\x7F", c) )\r
1752                         {                                               /* Reject illegal chrs for SFN */\r
1753                                 return FR_INVALID_NAME;\r
1754                         }\r
1755 \r
1756                         if( IsUpper(c) )\r
1757                         {                                               /* ASCII large capital? */\r
1758                                 b |= 2;\r
1759                         }\r
1760                         else\r
1761                         {\r
1762                                 if( IsLower(c) )\r
1763                                 {                                       /* ASCII small capital? */\r
1764                                         b |= 1;\r
1765                                         c -= 0x20;\r
1766                                 }\r
1767                         }\r
1768 \r
1769                         sfn[i++] = c;\r
1770                 }\r
1771         }\r
1772 \r
1773         *path = &p[si];                                 /* Return pointer to the next segment */\r
1774         c = ( c <= ' ' ) ? NS_LAST : 0; /* Set last segment flag if end of path */\r
1775 \r
1776         if( !i )\r
1777         {\r
1778                 return FR_INVALID_NAME;         /* Reject null string */\r
1779         }\r
1780 \r
1781         if( sfn[0] == 0xE5 )\r
1782         {\r
1783                 sfn[0] = 0x05;                          /* When first char collides with 0xE5, replace it with 0x05 */\r
1784         }\r
1785 \r
1786         if( ni == 8 )\r
1787         {\r
1788                 b <<= 2;\r
1789         }\r
1790 \r
1791         if( (b & 0x03) == 0x01 )\r
1792         {\r
1793                 c |= NS_EXT;                            /* NT flag (Extension has only small capital) */\r
1794         }\r
1795 \r
1796         if( (b & 0x0C) == 0x04 )\r
1797         {\r
1798                 c |= NS_BODY;                           /* NT flag (Filename has only small capital) */\r
1799         }\r
1800 \r
1801         sfn[NS] = c;                                    /* Store NT flag, File name is created */\r
1802 \r
1803         return FR_OK;\r
1804         #endif\r
1805 }\r
1806 \r
1807 /*-----------------------------------------------------------------------*/\r
1808 \r
1809 /* Get file information from directory entry                             */\r
1810 \r
1811 /*-----------------------------------------------------------------------*/\r
1812 #if _FS_MINIMIZE <= 1\r
1813 static void get_fileinfo\r
1814         (                                       /* No return code */\r
1815                 DIR *dj,                /* Pointer to the directory object */\r
1816                 FILINFO *fno    /* Pointer to the file information to be filled */\r
1817         )\r
1818 {\r
1819         int             i;\r
1820         BYTE    c, nt, *dir;\r
1821         char    *p;\r
1822 \r
1823         p = fno->fname;\r
1824         if( dj->sect )\r
1825         {\r
1826                 dir = dj->dir;\r
1827                 nt = dir[DIR_NTres];                    /* NT flag */\r
1828                 for( i = 0; i < 8; i++ )\r
1829                 {                                                               /* Copy name body */\r
1830                         c = dir[i];\r
1831                         if( c == ' ' )\r
1832                         {\r
1833                                 break;\r
1834                         }\r
1835 \r
1836                         if( c == 0x05 )\r
1837                         {\r
1838                                 c = 0xE5;\r
1839                         }\r
1840 \r
1841                         if( _USE_LFN && (nt & NS_BODY) && IsUpper(c) )\r
1842                         {\r
1843                                 c += 0x20;\r
1844                         }\r
1845 \r
1846                         *p++ = c;\r
1847                 }\r
1848 \r
1849                 if( dir[8] != ' ' )\r
1850                 {                                                               /* Copy name extension */\r
1851                         *p++ = '.';\r
1852                         for( i = 8; i < 11; i++ )\r
1853                         {\r
1854                                 c = dir[i];\r
1855                                 if( c == ' ' )\r
1856                                 {\r
1857                                         break;\r
1858                                 }\r
1859 \r
1860                                 if( _USE_LFN && (nt & NS_EXT) && IsUpper(c) )\r
1861                                 {\r
1862                                         c += 0x20;\r
1863                                 }\r
1864 \r
1865                                 *p++ = c;\r
1866                         }\r
1867                 }\r
1868 \r
1869                 fno->fattrib = dir[DIR_Attr];   /* Attribute */\r
1870                 fno->fsize = LD_DWORD( dir + DIR_FileSize );    /* Size */\r
1871                 fno->fdate = LD_WORD( dir + DIR_WrtDate );              /* Date */\r
1872                 fno->ftime = LD_WORD( dir + DIR_WrtTime );              /* Time */\r
1873         }\r
1874 \r
1875         *p = 0;\r
1876 \r
1877                 #if _USE_LFN\r
1878         if( fno->lfname )\r
1879         {\r
1880                 XCHAR   *tp = fno->lfname;\r
1881                 WCHAR   w, *lfn;\r
1882 \r
1883                 i = 0;\r
1884                 if( dj->sect && dj->lfn_idx != 0xFFFF )\r
1885                 {               /* Get LFN if available */\r
1886                         lfn = dj->lfn;\r
1887                         while( (w = *lfn++) != 0 )\r
1888                         {       /* Get an LFN char */\r
1889                                                 #if !_LFN_UNICODE\r
1890                                 w = ff_convert( w, 0 ); /* Unicode -> OEM conversion */\r
1891                                 if( !w )\r
1892                                 {\r
1893                                         i = 0;\r
1894                                         break;\r
1895                                 }                                               /* Could not convert, no LFN */\r
1896 \r
1897                                 if( _DF1S && w >= 0x100 )\r
1898                                 {                                               /* Put 1st byte if it is a DBC */\r
1899                                         tp[i++] = ( XCHAR ) ( w >> 8 );\r
1900                                 }\r
1901 \r
1902                                                 #endif\r
1903                                 if( i >= fno->lfsize - 1 )\r
1904                                 {\r
1905                                         i = 0;\r
1906                                         break;\r
1907                                 }                                               /* Buffer overrun, no LFN */\r
1908 \r
1909                                 tp[i++] = ( XCHAR ) w;\r
1910                         }\r
1911                 }\r
1912 \r
1913                 tp[i] = 0;                                              /* Terminator */\r
1914         }\r
1915 \r
1916                 #endif\r
1917 }\r
1918 \r
1919 #endif /* _FS_MINIMIZE <= 1 */\r
1920 \r
1921 /*-----------------------------------------------------------------------*/\r
1922 \r
1923 /* Follow a file path                                                    */\r
1924 \r
1925 /*-----------------------------------------------------------------------*/\r
1926 static FRESULT follow_path\r
1927         (                                               /* FR_OK(0): successful, !=0: error code */\r
1928                 DIR *dj,                        /* Directory object to return last directory and found object */\r
1929                 const XCHAR *path       /* Full-path string to find a file or directory */\r
1930         )\r
1931 {\r
1932         FRESULT res;\r
1933         BYTE    *dir, last;\r
1934 \r
1935         while( !_USE_LFN && *path == ' ' )\r
1936         {\r
1937                 path++;                                         /* Skip leading spaces */\r
1938         }\r
1939 \r
1940         #if _FS_RPATH\r
1941         if( *path == '/' || *path == '\\' )\r
1942         {                                                               /* There is a heading separator */\r
1943                 path++;\r
1944                 dj->sclust = 0;                         /* Strip it and start from the root dir */\r
1945         }\r
1946         else\r
1947         {                                                               /* No heading saparator */\r
1948                 dj->sclust = dj->fs->cdir;      /* Start from the current dir */\r
1949         }\r
1950 \r
1951         #else\r
1952         if( *path == '/' || *path == '\\' )\r
1953         {                               /* Strip heading separator if exist */\r
1954                 path++;\r
1955         }\r
1956 \r
1957         dj->sclust = 0; /* Start from the root dir */\r
1958         #endif\r
1959         if( (UINT) * path < ' ' )\r
1960         {                               /* Null path means the start directory itself */\r
1961                 res = dir_seek( dj, 0 );\r
1962                 dj->dir = NULL;\r
1963         }\r
1964         else\r
1965         {                               /* Follow path */\r
1966                 for( ;; )\r
1967                 {\r
1968                         res = create_name( dj, &path ); /* Get a segment */\r
1969                         if( res != FR_OK )\r
1970                         {\r
1971                                 break;\r
1972                         }\r
1973 \r
1974                         res = dir_find( dj );                   /* Find it */\r
1975                         last = *( dj->fn + NS ) & NS_LAST;\r
1976                         if( res != FR_OK )\r
1977                         {                               /* Could not find the object */\r
1978                                 if( res == FR_NO_FILE && !last )\r
1979                                 {\r
1980                                         res = FR_NO_PATH;\r
1981                                 }\r
1982 \r
1983                                 break;\r
1984                         }\r
1985 \r
1986                         if( last )\r
1987                         {\r
1988                                 break;          /* Last segment match. Function completed. */\r
1989                         }\r
1990 \r
1991                         dir = dj->dir;  /* There is next segment. Follow the sub directory */\r
1992                         if( !(dir[DIR_Attr] & AM_DIR) )\r
1993                         {                               /* Cannot follow because it is a file */\r
1994                                 res = FR_NO_PATH;\r
1995                                 break;\r
1996                         }\r
1997 \r
1998                         dj->sclust = ( (DWORD) LD_WORD(dir + DIR_FstClusHI) << 16 ) | LD_WORD( dir + DIR_FstClusLO );\r
1999                 }\r
2000         }\r
2001 \r
2002         return res;\r
2003 }\r
2004 \r
2005 /*-----------------------------------------------------------------------*/\r
2006 \r
2007 /* Load boot record and check if it is an FAT boot record                */\r
2008 \r
2009 /*-----------------------------------------------------------------------*/\r
2010 static BYTE check_fs\r
2011         (                               /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */\r
2012                 FATFS *fs,      /* File system object */\r
2013                 DWORD sect      /* Sector# (lba) to check if it is an FAT boot record or not */\r
2014         )\r
2015 {\r
2016         if( disk_read(fs->drive, fs->win, sect, 1) != RES_OK )\r
2017         {       /* Load boot record */\r
2018                 return 3;\r
2019         }\r
2020 \r
2021         if( LD_WORD(&fs->win[BS_55AA]) != 0xAA55 )\r
2022         {       /* Check record signature (always placed at offset 510 even if the sector size is >512) */\r
2023                 return 2;\r
2024         }\r
2025 \r
2026         if( (LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146 )\r
2027         {       /* Check "FAT" string */\r
2028                 return 0;\r
2029         }\r
2030 \r
2031         if( (LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146 )\r
2032         {\r
2033                 return 0;\r
2034         }\r
2035 \r
2036         return 1;\r
2037 }\r
2038 \r
2039 /*-----------------------------------------------------------------------*/\r
2040 \r
2041 /* Make sure that the file system is valid                               */\r
2042 \r
2043 /*-----------------------------------------------------------------------*/\r
2044 FRESULT chk_mounted\r
2045         (                                               /* FR_OK(0): successful, !=0: any error occured */\r
2046                 const XCHAR **path, /* Pointer to pointer to the path name (drive number) */\r
2047                 FATFS **rfs,            /* Pointer to pointer to the found file system object */\r
2048                 BYTE chk_wp                     /* !=0: Check media write protection for write access */\r
2049         )\r
2050 {\r
2051         BYTE            fmt, *tbl;\r
2052         UINT            vol;\r
2053         DSTATUS         stat;\r
2054         DWORD           bsect, fsize, tsect, mclst;\r
2055         const XCHAR *p = *path;\r
2056         FATFS           *fs;\r
2057 \r
2058         /* Get logical drive number from the path name */\r
2059         vol = p[0] - '0';                       /* Is there a drive number? */\r
2060         if( vol <= 9 && p[1] == ':' )\r
2061         {                                                       /* Found a drive number, get and strip it */\r
2062                 p += 2;\r
2063                 *path = p;                              /* Return pointer to the path name */\r
2064         }\r
2065         else\r
2066         {                                                       /* No drive number is given */\r
2067                 #if _FS_RPATH\r
2068                 vol = Drive;                    /* Use current drive */\r
2069                 #else\r
2070                 vol = 0;                                /* Use drive 0 */\r
2071                 #endif\r
2072         }\r
2073 \r
2074         /* Check if the logical drive is valid or not */\r
2075         if( vol >= _DRIVES )\r
2076         {                                                       /* Is the drive number valid? */\r
2077                 return FR_INVALID_DRIVE;\r
2078         }\r
2079 \r
2080         *rfs = fs = FatFs[vol];         /* Returen pointer to the corresponding file system object */\r
2081         if( !fs )\r
2082         {\r
2083                 return FR_NOT_ENABLED;  /* Is the file system object available? */\r
2084         }\r
2085 \r
2086         ENTER_FF( fs );                         /* Lock file system */\r
2087 \r
2088         if( fs->fs_type )\r
2089         {                                               /* If the logical drive has been mounted */\r
2090                 stat = disk_status( fs->drive );\r
2091                 if( !(stat & STA_NOINIT) )\r
2092                 {                                       /* and the physical drive is kept initialized (has not been changed), */\r
2093                         #if !_FS_READONLY\r
2094                         if( chk_wp && (stat & STA_PROTECT) )\r
2095                         {                               /* Check write protection if needed */\r
2096                                 return FR_WRITE_PROTECTED;\r
2097                         }\r
2098 \r
2099                         #endif\r
2100                         return FR_OK;   /* The file system object is valid */\r
2101                 }\r
2102         }\r
2103 \r
2104         /* The logical drive must be mounted. Following code attempts to mount the volume */\r
2105         fs->fs_type = 0;                /* Clear the file system object */\r
2106         fs->drive = ( BYTE ) LD2PD( vol );              /* Bind the logical drive and a physical drive */\r
2107         stat = disk_initialize( fs->drive );    /* Initialize low level disk I/O layer */\r
2108         if( stat & STA_NOINIT )\r
2109         {                                       /* Check if the drive is ready */\r
2110                 return FR_NOT_READY;\r
2111         }\r
2112 \r
2113         #if _MAX_SS != 512      /* Get disk sector size if needed */\r
2114         if( disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS )\r
2115         {\r
2116                 return FR_NO_FILESYSTEM;\r
2117         }\r
2118 \r
2119         #endif\r
2120         #if !_FS_READONLY\r
2121         if( chk_wp && (stat & STA_PROTECT) )\r
2122         {                                       /* Check disk write protection if needed */\r
2123                 return FR_WRITE_PROTECTED;\r
2124         }\r
2125 \r
2126         #endif\r
2127 \r
2128         /* Search FAT partition on the drive */\r
2129         fmt = check_fs( fs, bsect = 0 );        /* Check sector 0 as an SFD format */\r
2130         if( fmt == 1 )\r
2131         {       /* Not an FAT boot record, it may be patitioned */\r
2132                 /* Check a partition listed in top of the partition table */\r
2133                 tbl = &fs->win[MBR_Table + LD2PT( vol ) * 16];  /* Partition table */\r
2134                 if( tbl[4] )\r
2135                 {       /* Is the partition existing? */\r
2136                         bsect = LD_DWORD( &tbl[8] );    /* Partition offset in LBA */\r
2137                         fmt = check_fs( fs, bsect );    /* Check the partition */\r
2138                 }\r
2139         }\r
2140 \r
2141         if( fmt == 3 )\r
2142         {\r
2143                 return FR_DISK_ERR;\r
2144         }\r
2145 \r
2146         if( fmt || LD_WORD(fs->win + BPB_BytsPerSec) != SS(fs) )\r
2147         {       /* No valid FAT patition is found */\r
2148                 return FR_NO_FILESYSTEM;\r
2149         }\r
2150 \r
2151         /* Initialize the file system object */\r
2152         fsize = LD_WORD( fs->win + BPB_FATSz16 );                                       /* Number of sectors per FAT */\r
2153         if( !fsize )\r
2154         {\r
2155                 fsize = LD_DWORD( fs->win + BPB_FATSz32 );\r
2156         }\r
2157 \r
2158         fs->sects_fat = fsize;\r
2159         fs->n_fats = fs->win[BPB_NumFATs];                                                      /* Number of FAT copies */\r
2160         fsize *= fs->n_fats;                                                                            /* (Number of sectors in FAT area) */\r
2161         fs->fatbase = bsect + LD_WORD( fs->win + BPB_RsvdSecCnt );      /* FAT start sector (lba) */\r
2162         fs->csize = fs->win[BPB_SecPerClus];                                            /* Number of sectors per cluster */\r
2163         fs->n_rootdir = LD_WORD( fs->win + BPB_RootEntCnt );            /* Nmuber of root directory entries */\r
2164         tsect = LD_WORD( fs->win + BPB_TotSec16 );                                      /* Number of sectors on the volume */\r
2165         if( !tsect )\r
2166         {\r
2167                 tsect = LD_DWORD( fs->win + BPB_TotSec32 );\r
2168         }\r
2169 \r
2170         fs->max_clust = mclst =\r
2171                 (\r
2172                         tsect /* Last cluster# + 1 (Number of clusters + 2) */ -\r
2173                         LD_WORD(fs->win + BPB_RsvdSecCnt) -\r
2174                         fsize -\r
2175                         fs->n_rootdir /\r
2176                         (SS(fs) / 32)\r
2177                 ) /\r
2178                 fs->csize +\r
2179                 2;\r
2180 \r
2181         fmt = FS_FAT12;         /* Determine the FAT sub type */\r
2182         if( mclst >= 0xFF7 )\r
2183         {\r
2184                 fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */\r
2185         }\r
2186 \r
2187         if( mclst >= 0xFFF7 )\r
2188         {\r
2189                 fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */\r
2190         }\r
2191 \r
2192         if( fmt == FS_FAT32 )\r
2193         {\r
2194                 fs->dirbase = LD_DWORD( fs->win + BPB_RootClus );                                       /* Root directory start cluster */\r
2195         }\r
2196         else\r
2197         {\r
2198                 fs->dirbase = fs->fatbase + fsize;                                                                      /* Root directory start sector (lba) */\r
2199         }\r
2200 \r
2201         fs->database = fs->fatbase + fsize + fs->n_rootdir / ( SS(fs) / 32 );   /* Data start sector (lba) */\r
2202 \r
2203         #if !_FS_READONLY\r
2204 \r
2205         /* Initialize allocation information */\r
2206         fs->free_clust = 0xFFFFFFFF;\r
2207         fs->wflag = 0;\r
2208 \r
2209         /* Get fsinfo if needed */\r
2210         if( fmt == FS_FAT32 )\r
2211         {\r
2212                 fs->fsi_flag = 0;\r
2213                 fs->fsi_sector = bsect + LD_WORD( fs->win + BPB_FSInfo );\r
2214                 if\r
2215                 (\r
2216                         disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&\r
2217                         LD_WORD(fs->win + BS_55AA) == 0xAA55 &&\r
2218                         LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252 &&\r
2219                         LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272\r
2220                 )\r
2221                 {\r
2222                         fs->last_clust = LD_DWORD( fs->win + FSI_Nxt_Free );\r
2223                         fs->free_clust = LD_DWORD( fs->win + FSI_Free_Count );\r
2224                 }\r
2225         }\r
2226 \r
2227         #endif\r
2228         fs->fs_type = fmt;      /* FAT sub-type */\r
2229         fs->winsect = 0;        /* Invalidate sector cache */\r
2230         #if _FS_RPATH\r
2231         fs->cdir = 0;           /* Current directory (root dir) */\r
2232         #endif\r
2233         fs->id = ++Fsid;        /* File system mount ID */\r
2234 \r
2235         return FR_OK;\r
2236 }\r
2237 \r
2238 /*-----------------------------------------------------------------------*/\r
2239 \r
2240 /* Check if the file/dir object is valid or not                          */\r
2241 \r
2242 /*-----------------------------------------------------------------------*/\r
2243 static FRESULT validate\r
2244         (                               /* FR_OK(0): The object is valid, !=0: Invalid */\r
2245                 FATFS *fs,      /* Pointer to the file system object */\r
2246                 WORD id         /* Member id of the target object to be checked */\r
2247         )\r
2248 {\r
2249         if( !fs || !fs->fs_type || fs->id != id )\r
2250         {\r
2251                 return FR_INVALID_OBJECT;\r
2252         }\r
2253 \r
2254         ENTER_FF( fs ); /* Lock file system */\r
2255 \r
2256         if( disk_status(fs->drive) & STA_NOINIT )\r
2257         {\r
2258                 return FR_NOT_READY;\r
2259         }\r
2260 \r
2261         return FR_OK;\r
2262 }\r
2263 \r
2264 /*--------------------------------------------------------------------------\r
2265 \r
2266    Public Functions\r
2267 \r
2268 --------------------------------------------------------------------------*/\r
2269 \r
2270 /*-----------------------------------------------------------------------*/\r
2271 \r
2272 /* Mount/Unmount a Locical Drive                                         */\r
2273 \r
2274 /*-----------------------------------------------------------------------*/\r
2275 FRESULT f_mount\r
2276                 (\r
2277                         BYTE    vol,    /* Logical drive number to be mounted/unmounted */\r
2278                         FATFS   *fs             /* Pointer to new file system object (NULL for unmount)*/\r
2279                 )\r
2280 {\r
2281         FATFS   *rfs;\r
2282 \r
2283         if( vol >= _DRIVES )\r
2284         {                                               /* Check if the drive number is valid */\r
2285                 return FR_INVALID_DRIVE;\r
2286         }\r
2287 \r
2288         rfs = FatFs[vol];               /* Get current fs object */\r
2289 \r
2290         if( rfs )\r
2291         {\r
2292                 #if _FS_REENTRANT       /* Discard sync object of the current volume */\r
2293                 if( !ff_del_syncobj(rfs->sobj) )\r
2294                 {\r
2295                         return FR_INT_ERR;\r
2296                 }\r
2297 \r
2298                 #endif\r
2299                 rfs->fs_type = 0;       /* Clear old fs object */\r
2300         }\r
2301 \r
2302         if( fs )\r
2303         {\r
2304                 fs->fs_type = 0;        /* Clear new fs object */\r
2305                 #if _FS_REENTRANT       /* Create sync object for the new volume */\r
2306                 if( !ff_cre_syncobj(vol, &fs->sobj) )\r
2307                 {\r
2308                         return FR_INT_ERR;\r
2309                 }\r
2310 \r
2311                 #endif\r
2312         }\r
2313 \r
2314         FatFs[vol] = fs;                /* Register new fs object */\r
2315 \r
2316         return FR_OK;\r
2317 }\r
2318 \r
2319 /*-----------------------------------------------------------------------*/\r
2320 \r
2321 /* Open or Create a File                                                 */\r
2322 \r
2323 /*-----------------------------------------------------------------------*/\r
2324 FRESULT f_open\r
2325                 (\r
2326                         FIL                     *fp,    /* Pointer to the blank file object */\r
2327                         const XCHAR *path,      /* Pointer to the file name */\r
2328                         BYTE            mode    /* Access mode and file open mode flags */\r
2329                 )\r
2330 {\r
2331         FRESULT res;\r
2332         DIR             dj;\r
2333         NAMEBUF( sfn, lfn );\r
2334 \r
2335         BYTE    *dir;\r
2336 \r
2337         fp->fs = NULL;                                  /* Clear file object */\r
2338         #if !_FS_READONLY\r
2339         mode &= ( FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW );\r
2340         res = chk_mounted( &path, &dj.fs, (BYTE) (mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) );\r
2341         #else\r
2342         mode &= FA_READ;\r
2343         res = chk_mounted( &path, &dj.fs, 0 );\r
2344         #endif\r
2345         if( res != FR_OK )\r
2346         {\r
2347                 LEAVE_FF( dj.fs, res );\r
2348         }\r
2349 \r
2350         INITBUF( dj, sfn, lfn );\r
2351         res = follow_path( &dj, path ); /* Follow the file path */\r
2352 \r
2353         #if !_FS_READONLY\r
2354 \r
2355         /* Create or Open a file */\r
2356         if( mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW) )\r
2357         {\r
2358                 DWORD   ps, cl;\r
2359 \r
2360                 if( res != FR_OK )\r
2361                 {                                       /* No file, create new */\r
2362                         if( res == FR_NO_FILE )\r
2363                         {                               /* There is no file to open, create a new entry */\r
2364                                 res = dir_register( &dj );\r
2365                         }\r
2366 \r
2367                         if( res != FR_OK )\r
2368                         {\r
2369                                 LEAVE_FF( dj.fs, res );\r
2370                         }\r
2371 \r
2372                         mode |= FA_CREATE_ALWAYS;\r
2373                         dir = dj.dir;   /* Created entry (SFN entry) */\r
2374                 }\r
2375                 else\r
2376                 {                                       /* Any object is already existing */\r
2377                         if( mode & FA_CREATE_NEW )\r
2378                         {                               /* Cannot create new */\r
2379                                 LEAVE_FF( dj.fs, FR_EXIST );\r
2380                         }\r
2381 \r
2382                         dir = dj.dir;\r
2383                         if( !dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR)) )\r
2384                         {                               /* Cannot overwrite it (R/O or DIR) */\r
2385                                 LEAVE_FF( dj.fs, FR_DENIED );\r
2386                         }\r
2387 \r
2388                         if( mode & FA_CREATE_ALWAYS )\r
2389                         {                               /* Resize it to zero on over write mode */\r
2390                                 cl = ( (DWORD) LD_WORD(dir + DIR_FstClusHI) << 16 ) | LD_WORD( dir + DIR_FstClusLO );   /* Get start cluster */\r
2391                                 ST_WORD( dir + DIR_FstClusHI, 0 );      /* cluster = 0 */\r
2392                                 ST_WORD( dir + DIR_FstClusLO, 0 );\r
2393                                 ST_DWORD( dir + DIR_FileSize, 0 );      /* size = 0 */\r
2394                                 dj.fs->wflag = 1;\r
2395                                 ps = dj.fs->winsect;                            /* Remove the cluster chain */\r
2396                                 if( cl )\r
2397                                 {\r
2398                                         res = remove_chain( dj.fs, cl );\r
2399                                         if( res )\r
2400                                         {\r
2401                                                 LEAVE_FF( dj.fs, res );\r
2402                                         }\r
2403 \r
2404                                         dj.fs->last_clust = cl - 1;             /* Reuse the cluster hole */\r
2405                                 }\r
2406 \r
2407                                 res = move_window( dj.fs, ps );\r
2408                                 if( res != FR_OK )\r
2409                                 {\r
2410                                         LEAVE_FF( dj.fs, res );\r
2411                                 }\r
2412                         }\r
2413                 }\r
2414 \r
2415                 if( mode & FA_CREATE_ALWAYS )\r
2416                 {\r
2417                         dir[DIR_Attr] = 0;                                              /* Reset attribute */\r
2418                         ps = get_fattime();\r
2419                         ST_DWORD( dir + DIR_CrtTime, ps );              /* Created time */\r
2420                         dj.fs->wflag = 1;\r
2421                         mode |= FA__WRITTEN;                                    /* Set file changed flag */\r
2422                 }\r
2423         }\r
2424 \r
2425         /* Open an existing file */\r
2426         else\r
2427         {\r
2428                 #endif /* !_FS_READONLY */\r
2429                 if( res != FR_OK )\r
2430                 {\r
2431                         LEAVE_FF( dj.fs, res );                                 /* Follow failed */\r
2432                 }\r
2433 \r
2434                 dir = dj.dir;\r
2435                 if( !dir || (dir[DIR_Attr] & AM_DIR) )\r
2436                 {       /* It is a directory */\r
2437                         LEAVE_FF( dj.fs, FR_NO_FILE );\r
2438                 }\r
2439 \r
2440                 #if !_FS_READONLY\r
2441                 if( (mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO) )\r
2442                 {       /* R/O violation */\r
2443                         LEAVE_FF( dj.fs, FR_DENIED );\r
2444                 }\r
2445         }\r
2446 \r
2447         fp->dir_sect = dj.fs->winsect;                          /* Pointer to the directory entry */\r
2448         fp->dir_ptr = dj.dir;\r
2449         #endif\r
2450         fp->flag = mode;                                                        /* File access mode */\r
2451         fp->org_clust =                                                         /* File start cluster */\r
2452         ( (DWORD) LD_WORD(dir + DIR_FstClusHI) << 16 ) | LD_WORD( dir + DIR_FstClusLO );\r
2453         fp->fsize = LD_DWORD( dir + DIR_FileSize ); /* File size */\r
2454         fp->fptr = 0;\r
2455         fp->csect = 255;        /* File pointer */\r
2456         fp->dsect = 0;\r
2457         fp->fs = dj.fs;\r
2458         fp->id = dj.fs->id; /* Owner file system object of the file */\r
2459 \r
2460         LEAVE_FF( dj.fs, FR_OK );\r
2461 }\r
2462 \r
2463 /*-----------------------------------------------------------------------*/\r
2464 \r
2465 /* Read File                                                             */\r
2466 \r
2467 /*-----------------------------------------------------------------------*/\r
2468 FRESULT f_read\r
2469                 (\r
2470                         FIL             *fp,    /* Pointer to the file object */\r
2471                         void    *buff,  /* Pointer to data buffer */\r
2472                         UINT    btr,    /* Number of bytes to read */\r
2473                         UINT    *br             /* Pointer to number of bytes read */\r
2474                 )\r
2475 {\r
2476         FRESULT res;\r
2477         DWORD   clst, sect, remain;\r
2478         UINT    rcnt, cc;\r
2479         BYTE    *rbuff = buff;\r
2480 \r
2481         *br = 0;        /* Initialize bytes read */\r
2482 \r
2483         res = validate( fp->fs, fp->id );       /* Check validity of the object */\r
2484         if( res != FR_OK )\r
2485         {\r
2486                 LEAVE_FF( fp->fs, res );\r
2487         }\r
2488 \r
2489         if( fp->flag & FA__ERROR )\r
2490         {       /* Check abort flag */\r
2491                 LEAVE_FF( fp->fs, FR_INT_ERR );\r
2492         }\r
2493 \r
2494         if( !(fp->flag & FA_READ) )\r
2495         {       /* Check access mode */\r
2496                 LEAVE_FF( fp->fs, FR_DENIED );\r
2497         }\r
2498 \r
2499         remain = fp->fsize - fp->fptr;\r
2500         if( btr > remain )\r
2501         {\r
2502                 btr = ( UINT ) remain;                          /* Truncate btr by remaining bytes */\r
2503         }\r
2504 \r
2505         for( ; btr; /* Repeat until all data transferred */ rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt )\r
2506         {\r
2507                 if( (fp->fptr % SS(fp->fs)) == 0 )\r
2508                 {                                                                       /* On the sector boundary? */\r
2509                         if( fp->csect >= fp->fs->csize )\r
2510                         {                                                               /* On the cluster boundary? */\r
2511                                 clst = ( fp->fptr == 0 ) ?      /* On the top of the file? */\r
2512                                 fp->org_clust : get_fat( fp->fs, fp->curr_clust );\r
2513                                 if( clst <= 1 )\r
2514                                 {\r
2515                                         ABORT( fp->fs, FR_INT_ERR );\r
2516                                 }\r
2517 \r
2518                                 if( clst == 0xFFFFFFFF )\r
2519                                 {\r
2520                                         ABORT( fp->fs, FR_DISK_ERR );\r
2521                                 }\r
2522 \r
2523                                 fp->curr_clust = clst;          /* Update current cluster */\r
2524                                 fp->csect = 0;                          /* Reset sector offset in the cluster */\r
2525                         }\r
2526 \r
2527                         sect = clust2sect( fp->fs, fp->curr_clust );    /* Get current sector */\r
2528                         if( !sect )\r
2529                         {\r
2530                                 ABORT( fp->fs, FR_INT_ERR );\r
2531                         }\r
2532 \r
2533                         sect += fp->csect;\r
2534                         cc = btr / SS( fp->fs );                                                /* When remaining bytes >= sector size, */\r
2535                         if( cc )\r
2536                         {               /* Read maximum contiguous sectors directly */\r
2537                                 if( fp->csect + cc > fp->fs->csize )\r
2538                                 {       /* Clip at cluster boundary */\r
2539                                         cc = fp->fs->csize - fp->csect;\r
2540                                 }\r
2541 \r
2542                                 if( disk_read(fp->fs->drive, rbuff, sect, (BYTE) cc) != RES_OK )\r
2543                                 {\r
2544                                         ABORT( fp->fs, FR_DISK_ERR );\r
2545                                 }\r
2546 \r
2547                                 #if !_FS_READONLY && _FS_MINIMIZE <= 2\r
2548                                         #if _FS_TINY\r
2549                                 if( fp->fs->wflag && fp->fs->winsect - sect < cc )\r
2550                                 {       /* Replace one of the read sectors with cached data if it contains a dirty sector */\r
2551                                         mem_cpy( rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs) );\r
2552                                 }\r
2553 \r
2554                                         #else\r
2555                                 if( (fp->flag & FA__DIRTY) && fp->dsect - sect < cc )\r
2556                                 {       /* Replace one of the read sectors with cached data if it contains a dirty sector */\r
2557                                         mem_cpy( rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs) );\r
2558                                 }\r
2559 \r
2560                                         #endif\r
2561                                 #endif\r
2562                                 fp->csect += ( BYTE ) cc;       /* Next sector address in the cluster */\r
2563                                 rcnt = SS( fp->fs ) * cc;       /* Number of bytes transferred */\r
2564                                 continue;\r
2565                         }\r
2566 \r
2567                         #if !_FS_TINY\r
2568                                 #if !_FS_READONLY\r
2569                         if( fp->flag & FA__DIRTY )\r
2570                         {                               /* Write sector I/O buffer if needed */\r
2571                                 if( disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK )\r
2572                                 {\r
2573                                         ABORT( fp->fs, FR_DISK_ERR );\r
2574                                 }\r
2575 \r
2576                                 fp->flag &= ~FA__DIRTY;\r
2577                         }\r
2578 \r
2579                                 #endif\r
2580                         if( fp->dsect != sect )\r
2581                         {                               /* Fill sector buffer with file data */\r
2582                                 if( disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK )\r
2583                                 {\r
2584                                         ABORT( fp->fs, FR_DISK_ERR );\r
2585                                 }\r
2586                         }\r
2587 \r
2588                         #endif\r
2589                         fp->dsect = sect;\r
2590                         fp->csect++;    /* Next sector address in the cluster */\r
2591                 }\r
2592 \r
2593                 rcnt = SS( fp->fs ) - ( fp->fptr % SS(fp->fs) );        /* Get partial sector data from sector buffer */\r
2594                 if( rcnt > btr )\r
2595                 {\r
2596                         rcnt = btr;\r
2597                 }\r
2598 \r
2599                 #if _FS_TINY\r
2600                 if( move_window(fp->fs, fp->dsect) )\r
2601                 {       /* Move sector window */\r
2602                         ABORT( fp->fs, FR_DISK_ERR );\r
2603                 }\r
2604 \r
2605                 mem_cpy( rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt );    /* Pick partial sector */\r
2606                 #else\r
2607                 mem_cpy( rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt );                /* Pick partial sector */\r
2608                 #endif\r
2609         }\r
2610 \r
2611         LEAVE_FF( fp->fs, FR_OK );\r
2612 }\r
2613 \r
2614 #if !_FS_READONLY\r
2615 \r
2616 /*-----------------------------------------------------------------------*/\r
2617 \r
2618 /* Write File                                                            */\r
2619 \r
2620 /*-----------------------------------------------------------------------*/\r
2621 FRESULT f_write\r
2622                 (\r
2623                         FIL                     *fp,    /* Pointer to the file object */\r
2624                         const void      *buff,  /* Pointer to the data to be written */\r
2625                         UINT            btw,    /* Number of bytes to write */\r
2626                         UINT            *bw             /* Pointer to number of bytes written */\r
2627                 )\r
2628 {\r
2629         FRESULT         res;\r
2630         DWORD           clst, sect;\r
2631         UINT            wcnt, cc;\r
2632         const BYTE      *wbuff = buff;\r
2633 \r
2634         *bw = 0;        /* Initialize bytes written */\r
2635 \r
2636         res = validate( fp->fs, fp->id );       /* Check validity of the object */\r
2637         if( res != FR_OK )\r
2638         {\r
2639                 LEAVE_FF( fp->fs, res );\r
2640         }\r
2641 \r
2642         if( fp->flag & FA__ERROR )\r
2643         {                               /* Check abort flag */\r
2644                 LEAVE_FF( fp->fs, FR_INT_ERR );\r
2645         }\r
2646 \r
2647         if( !(fp->flag & FA_WRITE) )\r
2648         {                               /* Check access mode */\r
2649                 LEAVE_FF( fp->fs, FR_DENIED );\r
2650         }\r
2651 \r
2652         if( fp->fsize + btw < fp->fsize )\r
2653         {\r
2654                 btw = 0;        /* File size cannot reach 4GB */\r
2655         }\r
2656 \r
2657         for( ; btw; /* Repeat until all data transferred */ wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt )\r
2658         {\r
2659                 if( (fp->fptr % SS(fp->fs)) == 0 )\r
2660                 {                       /* On the sector boundary? */\r
2661                         if( fp->csect >= fp->fs->csize )\r
2662                         {               /* On the cluster boundary? */\r
2663                                 if( fp->fptr == 0 )\r
2664                                 {       /* On the top of the file? */\r
2665                                         clst = fp->org_clust;   /* Follow from the origin */\r
2666                                         if( clst == 0 )\r
2667                                         {                                               /* When there is no cluster chain, */\r
2668                                                 fp->org_clust = clst = create_chain( fp->fs, 0 );       /* Create a new cluster chain */\r
2669                                         }\r
2670                                 }\r
2671                                 else\r
2672                                 {       /* Middle or end of the file */\r
2673                                         clst = create_chain( fp->fs, fp->curr_clust );  /* Follow or streach cluster chain */\r
2674                                 }\r
2675 \r
2676                                 if( clst == 0 )\r
2677                                 {\r
2678                                         break;                          /* Could not allocate a new cluster (disk full) */\r
2679                                 }\r
2680 \r
2681                                 if( clst == 1 )\r
2682                                 {\r
2683                                         ABORT( fp->fs, FR_INT_ERR );\r
2684                                 }\r
2685 \r
2686                                 if( clst == 0xFFFFFFFF )\r
2687                                 {\r
2688                                         ABORT( fp->fs, FR_DISK_ERR );\r
2689                                 }\r
2690 \r
2691                                 fp->curr_clust = clst;  /* Update current cluster */\r
2692                                 fp->csect = 0;                  /* Reset sector address in the cluster */\r
2693                         }\r
2694 \r
2695                                 #if _FS_TINY\r
2696                         if( fp->fs->winsect == fp->dsect && move_window(fp->fs, 0) )\r
2697                         {       /* Write back data buffer prior to following direct transfer */\r
2698                                 ABORT( fp->fs, FR_DISK_ERR );\r
2699                         }\r
2700 \r
2701                                 #else\r
2702                         if( fp->flag & FA__DIRTY )\r
2703                         {       /* Write back data buffer prior to following direct transfer */\r
2704                                 if( disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK )\r
2705                                 {\r
2706                                         ABORT( fp->fs, FR_DISK_ERR );\r
2707                                 }\r
2708 \r
2709                                 fp->flag &= ~FA__DIRTY;\r
2710                         }\r
2711 \r
2712                                 #endif\r
2713                         sect = clust2sect( fp->fs, fp->curr_clust );    /* Get current sector */\r
2714                         if( !sect )\r
2715                         {\r
2716                                 ABORT( fp->fs, FR_INT_ERR );\r
2717                         }\r
2718 \r
2719                         sect += fp->csect;\r
2720                         cc = btw / SS( fp->fs );                                                /* When remaining bytes >= sector size, */\r
2721                         if( cc )\r
2722                         {               /* Write maximum contiguous sectors directly */\r
2723                                 if( fp->csect + cc > fp->fs->csize )\r
2724                                 {       /* Clip at cluster boundary */\r
2725                                         cc = fp->fs->csize - fp->csect;\r
2726                                 }\r
2727 \r
2728                                 if( disk_write(fp->fs->drive, wbuff, sect, (BYTE) cc) != RES_OK )\r
2729                                 {\r
2730                                         ABORT( fp->fs, FR_DISK_ERR );\r
2731                                 }\r
2732 \r
2733                                         #if _FS_TINY\r
2734                                 if( fp->fs->winsect - sect < cc )\r
2735                                 {       /* Refill sector cache if it gets dirty by the direct write */\r
2736                                         mem_cpy( fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs) );\r
2737                                         fp->fs->wflag = 0;\r
2738                                 }\r
2739 \r
2740                                         #else\r
2741                                 if( fp->dsect - sect < cc )\r
2742                                 {       /* Refill sector cache if it gets dirty by the direct write */\r
2743                                         mem_cpy( fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs) );\r
2744                                         fp->flag &= ~FA__DIRTY;\r
2745                                 }\r
2746 \r
2747                                         #endif\r
2748                                 fp->csect += ( BYTE ) cc;       /* Next sector address in the cluster */\r
2749                                 wcnt = SS( fp->fs ) * cc;       /* Number of bytes transferred */\r
2750                                 continue;\r
2751                         }\r
2752 \r
2753                                 #if _FS_TINY\r
2754                         if( fp->fptr >= fp->fsize )\r
2755                         {                               /* Avoid silly buffer filling at growing edge */\r
2756                                 if( move_window(fp->fs, 0) )\r
2757                                 {\r
2758                                         ABORT( fp->fs, FR_DISK_ERR );\r
2759                                 }\r
2760 \r
2761                                 fp->fs->winsect = sect;\r
2762                         }\r
2763 \r
2764                                 #else\r
2765                         if( fp->dsect != sect )\r
2766                         {                               /* Fill sector buffer with file data */\r
2767                                 if( fp->fptr < fp->fsize && disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK )\r
2768                                 {\r
2769                                         ABORT( fp->fs, FR_DISK_ERR );\r
2770                                 }\r
2771                         }\r
2772 \r
2773                                 #endif\r
2774                         fp->dsect = sect;\r
2775                         fp->csect++;    /* Next sector address in the cluster */\r
2776                 }\r
2777 \r
2778                 wcnt = SS( fp->fs ) - ( fp->fptr % SS(fp->fs) );        /* Put partial sector into file I/O buffer */\r
2779                 if( wcnt > btw )\r
2780                 {\r
2781                         wcnt = btw;\r
2782                 }\r
2783 \r
2784                         #if _FS_TINY\r
2785                 if( move_window(fp->fs, fp->dsect) )\r
2786                 {       /* Move sector window */\r
2787                         ABORT( fp->fs, FR_DISK_ERR );\r
2788                 }\r
2789 \r
2790                 mem_cpy( &fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt );    /* Fit partial sector */\r
2791                 fp->fs->wflag = 1;\r
2792                         #else\r
2793                 mem_cpy( &fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt );                /* Fit partial sector */\r
2794                 fp->flag |= FA__DIRTY;\r
2795                         #endif\r
2796         }\r
2797 \r
2798         if( fp->fptr > fp->fsize )\r
2799         {\r
2800                 fp->fsize = fp->fptr;   /* Update file size if needed */\r
2801         }\r
2802 \r
2803         fp->flag |= FA__WRITTEN;        /* Set file changed flag */\r
2804 \r
2805         LEAVE_FF( fp->fs, FR_OK );\r
2806 }\r
2807 \r
2808 /*-----------------------------------------------------------------------*/\r
2809 \r
2810 /* Synchronize the File Object                                           */\r
2811 \r
2812 /*-----------------------------------------------------------------------*/\r
2813 FRESULT f_sync( FIL *fp /* Pointer to the file object */ )\r
2814 {\r
2815         FRESULT res;\r
2816         DWORD   tim;\r
2817         BYTE    *dir;\r
2818 \r
2819         res = validate( fp->fs, fp->id );       /* Check validity of the object */\r
2820         if( res == FR_OK )\r
2821         {\r
2822                 if( fp->flag & FA__WRITTEN )\r
2823                 {                                               /* Has the file been written? */\r
2824                                 #if !_FS_TINY   /* Write-back dirty buffer */\r
2825                         if( fp->flag & FA__DIRTY )\r
2826                         {\r
2827                                 if( disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK )\r
2828                                 {\r
2829                                         LEAVE_FF( fp->fs, FR_DISK_ERR );\r
2830                                 }\r
2831 \r
2832                                 fp->flag &= ~FA__DIRTY;\r
2833                         }\r
2834 \r
2835                                 #endif\r
2836 \r
2837                         /* Update the directory entry */\r
2838                         res = move_window( fp->fs, fp->dir_sect );\r
2839                         if( res == FR_OK )\r
2840                         {\r
2841                                 dir = fp->dir_ptr;\r
2842                                 dir[DIR_Attr] |= AM_ARC;                                                /* Set archive bit */\r
2843                                 ST_DWORD( dir + DIR_FileSize, fp->fsize );              /* Update file size */\r
2844                                 ST_WORD( dir + DIR_FstClusLO, fp->org_clust );  /* Update start cluster */\r
2845                                 ST_WORD( dir + DIR_FstClusHI, fp->org_clust >> 16 );\r
2846                                 tim = get_fattime();    /* Updated time */\r
2847                                 ST_DWORD( dir + DIR_WrtTime, tim );\r
2848                                 fp->flag &= ~FA__WRITTEN;\r
2849                                 fp->fs->wflag = 1;\r
2850                                 res = sync( fp->fs );\r
2851                         }\r
2852                 }\r
2853         }\r
2854 \r
2855         LEAVE_FF( fp->fs, res );\r
2856 }\r
2857 \r
2858 #endif /* !_FS_READONLY */\r
2859 \r
2860 /*-----------------------------------------------------------------------*/\r
2861 \r
2862 /* Close File                                                            */\r
2863 \r
2864 /*-----------------------------------------------------------------------*/\r
2865 FRESULT f_close( FIL *fp /* Pointer to the file object to be closed */ )\r
2866 {\r
2867         FRESULT res;\r
2868 \r
2869         #if _FS_READONLY\r
2870         res = validate( fp->fs, fp->id );\r
2871         if( res == FR_OK )\r
2872         {\r
2873                 fp->fs = NULL;\r
2874         }\r
2875 \r
2876         LEAVE_FF( fp->fs, res );\r
2877         #else\r
2878         res = f_sync( fp );\r
2879         if( res == FR_OK )\r
2880         {\r
2881                 fp->fs = NULL;\r
2882         }\r
2883 \r
2884         return res;\r
2885         #endif\r
2886 }\r
2887 \r
2888 /*-----------------------------------------------------------------------*/\r
2889 \r
2890 /* Change Current Drive/Directory                                        */\r
2891 \r
2892 /*-----------------------------------------------------------------------*/\r
2893 #if _FS_RPATH\r
2894 FRESULT f_chdrive( BYTE drv /* Drive number */ )\r
2895 {\r
2896         if( drv >= _DRIVES )\r
2897         {\r
2898                 return FR_INVALID_DRIVE;\r
2899         }\r
2900 \r
2901         Drive = drv;\r
2902 \r
2903         return FR_OK;\r
2904 }\r
2905 \r
2906 FRESULT f_chdir( const XCHAR *path /* Pointer to the directory path */ )\r
2907 {\r
2908         FRESULT res;\r
2909         DIR             dj;\r
2910         NAMEBUF( sfn, lfn );\r
2911 \r
2912         BYTE    *dir;\r
2913 \r
2914         res = chk_mounted( &path, &dj.fs, 0 );\r
2915         if( res == FR_OK )\r
2916         {\r
2917                 INITBUF( dj, sfn, lfn );\r
2918                 res = follow_path( &dj, path ); /* Follow the file path */\r
2919                 if( res == FR_OK )\r
2920                 {                                       /* Follow completed */\r
2921                         dir = dj.dir;   /* Pointer to the entry */\r
2922                         if( !dir )\r
2923                         {\r
2924                                 dj.fs->cdir = 0;                /* No entry (root dir) */\r
2925                         }\r
2926                         else\r
2927                         {\r
2928                                 if( dir[DIR_Attr] & AM_DIR )\r
2929                                 {                                               /* Reached to the dir */\r
2930                                         dj.fs->cdir = ( (DWORD) LD_WORD(dir + DIR_FstClusHI) << 16 ) | LD_WORD( dir + DIR_FstClusLO );\r
2931                                 }\r
2932                                 else\r
2933                                 {\r
2934                                         res = FR_NO_PATH;       /* Could not reach the dir (it is a file) */\r
2935                                 }\r
2936                         }\r
2937                 }\r
2938 \r
2939                 if( res == FR_NO_FILE )\r
2940                 {\r
2941                         res = FR_NO_PATH;\r
2942                 }\r
2943         }\r
2944 \r
2945         LEAVE_FF( dj.fs, res );\r
2946 }\r
2947 \r
2948 #endif\r
2949 #if _FS_MINIMIZE <= 2\r
2950 \r
2951 /*-----------------------------------------------------------------------*/\r
2952 \r
2953 /* Seek File R/W Pointer                                                 */\r
2954 \r
2955 /*-----------------------------------------------------------------------*/\r
2956 FRESULT f_lseek( FIL *fp, /* Pointer to the file object */ DWORD ofs /* File pointer from top of file */ )\r
2957 {\r
2958         FRESULT res;\r
2959         DWORD   clst, bcs, nsect, ifptr;\r
2960 \r
2961         res = validate( fp->fs, fp->id );       /* Check validity of the object */\r
2962         if( res != FR_OK )\r
2963         {\r
2964                 LEAVE_FF( fp->fs, res );\r
2965         }\r
2966 \r
2967         if( fp->flag & FA__ERROR )\r
2968         {                                       /* Check abort flag */\r
2969                 LEAVE_FF( fp->fs, FR_INT_ERR );\r
2970         }\r
2971 \r
2972         if( ofs > fp->fsize /* In read-only mode, clip offset with the file size */\r
2973                 #if !_FS_READONLY\r
2974         && !(fp->flag & FA_WRITE)\r
2975                 #endif\r
2976         )\r
2977         {\r
2978                 ofs = fp->fsize;\r
2979         }\r
2980 \r
2981         ifptr = fp->fptr;\r
2982         fp->fptr = nsect = 0;\r
2983         fp->csect = 255;\r
2984         if( ofs > 0 )\r
2985         {\r
2986                 bcs = ( DWORD ) fp->fs->csize * SS( fp->fs );   /* Cluster size (byte) */\r
2987                 if( ifptr > 0 && (ofs - 1) / bcs >= (ifptr - 1) / bcs )\r
2988                 {       /* When seek to same or following cluster, */\r
2989                         fp->fptr = ( ifptr - 1 ) &~( bcs - 1 ); /* start from the current cluster */\r
2990                         ofs -= fp->fptr;\r
2991                         clst = fp->curr_clust;\r
2992                 }\r
2993                 else\r
2994                 {       /* When seek to back cluster, */\r
2995                         clst = fp->org_clust;   /* start from the first cluster */\r
2996                                 #if !_FS_READONLY\r
2997                         if( clst == 0 )\r
2998                         {                                               /* If no cluster chain, create a new chain */\r
2999                                 clst = create_chain( fp->fs, 0 );\r
3000                                 if( clst == 1 )\r
3001                                 {\r
3002                                         ABORT( fp->fs, FR_INT_ERR );\r
3003                                 }\r
3004 \r
3005                                 if( clst == 0xFFFFFFFF )\r
3006                                 {\r
3007                                         ABORT( fp->fs, FR_DISK_ERR );\r
3008                                 }\r
3009 \r
3010                                 fp->org_clust = clst;\r
3011                         }\r
3012 \r
3013                                 #endif\r
3014                         fp->curr_clust = clst;\r
3015                 }\r
3016 \r
3017                 if( clst != 0 )\r
3018                 {\r
3019                         while( ofs > bcs )\r
3020                         {                                               /* Cluster following loop */\r
3021                                         #if !_FS_READONLY\r
3022                                 if( fp->flag & FA_WRITE )\r
3023                                 {                                       /* Check if in write mode or not */\r
3024                                         clst = create_chain( fp->fs, clst );    /* Force streached if in write mode */\r
3025                                         if( clst == 0 )\r
3026                                         {       /* When disk gets full, clip file size */\r
3027                                                 ofs = bcs;\r
3028                                                 break;\r
3029                                         }\r
3030                                 }\r
3031                                 else\r
3032                                                 #endif\r
3033                                         clst = get_fat( fp->fs, clst );         /* Follow cluster chain if not in write mode */\r
3034                                 if( clst == 0xFFFFFFFF )\r
3035                                 {\r
3036                                         ABORT( fp->fs, FR_DISK_ERR );\r
3037                                 }\r
3038 \r
3039                                 if( clst <= 1 || clst >= fp->fs->max_clust )\r
3040                                 {\r
3041                                         ABORT( fp->fs, FR_INT_ERR );\r
3042                                 }\r
3043 \r
3044                                 fp->curr_clust = clst;\r
3045                                 fp->fptr += bcs;\r
3046                                 ofs -= bcs;\r
3047                         }\r
3048 \r
3049                         fp->fptr += ofs;\r
3050                         fp->csect = ( BYTE ) ( ofs / SS(fp->fs) );      /* Sector offset in the cluster */\r
3051                         if( ofs % SS(fp->fs) )\r
3052                         {\r
3053                                 nsect = clust2sect( fp->fs, clst );             /* Current sector */\r
3054                                 if( !nsect )\r
3055                                 {\r
3056                                         ABORT( fp->fs, FR_INT_ERR );\r
3057                                 }\r
3058 \r
3059                                 nsect += fp->csect;\r
3060                                 fp->csect++;\r
3061                         }\r
3062                 }\r
3063         }\r
3064 \r
3065         if( fp->fptr % SS(fp->fs) && nsect != fp->dsect )\r
3066         {\r
3067                         #if !_FS_TINY\r
3068                                 #if !_FS_READONLY\r
3069                 if( fp->flag & FA__DIRTY )\r
3070                 {       /* Write-back dirty buffer if needed */\r
3071                         if( disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK )\r
3072                         {\r
3073                                 ABORT( fp->fs, FR_DISK_ERR );\r
3074                         }\r
3075 \r
3076                         fp->flag &= ~FA__DIRTY;\r
3077                 }\r
3078 \r
3079                                 #endif\r
3080                 if( disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK )\r
3081                 {\r
3082                         ABORT( fp->fs, FR_DISK_ERR );\r
3083                 }\r
3084 \r
3085                         #endif\r
3086                 fp->dsect = nsect;\r
3087         }\r
3088 \r
3089                 #if !_FS_READONLY\r
3090         if( fp->fptr > fp->fsize )\r
3091         {               /* Set changed flag if the file size is extended */\r
3092                 fp->fsize = fp->fptr;\r
3093                 fp->flag |= FA__WRITTEN;\r
3094         }\r
3095 \r
3096                 #endif\r
3097         LEAVE_FF( fp->fs, res );\r
3098 }\r
3099 \r
3100         #if _FS_MINIMIZE <= 1\r
3101 \r
3102 /*-----------------------------------------------------------------------*/\r
3103 \r
3104 /* Create a Directroy Object                                             */\r
3105 \r
3106 /*-----------------------------------------------------------------------*/\r
3107 FRESULT f_opendir( DIR *dj, /* Pointer to directory object to create */ const XCHAR *path /* Pointer to the directory path */ )\r
3108 {\r
3109         FRESULT res;\r
3110         NAMEBUF( sfn, lfn );\r
3111 \r
3112         BYTE    *dir;\r
3113 \r
3114         res = chk_mounted( &path, &dj->fs, 0 );\r
3115         if( res == FR_OK )\r
3116         {\r
3117                 INITBUF( (*dj), sfn, lfn );\r
3118                 res = follow_path( dj, path );  /* Follow the path to the directory */\r
3119                 if( res == FR_OK )\r
3120                 {                       /* Follow completed */\r
3121                         dir = dj->dir;\r
3122                         if( dir )\r
3123                         {               /* It is not the root dir */\r
3124                                 if( dir[DIR_Attr] & AM_DIR )\r
3125                                 {       /* The object is a directory */\r
3126                                         dj->sclust = ( (DWORD) LD_WORD(dir + DIR_FstClusHI) << 16 ) | LD_WORD( dir + DIR_FstClusLO );\r
3127                                 }\r
3128                                 else\r
3129                                 {       /* The object is not a directory */\r
3130                                         res = FR_NO_PATH;\r
3131                                 }\r
3132                         }\r
3133 \r
3134                         if( res == FR_OK )\r
3135                         {\r
3136                                 dj->id = dj->fs->id;\r
3137                                 res = dir_seek( dj, 0 );        /* Rewind dir */\r
3138                         }\r
3139                 }\r
3140 \r
3141                 if( res == FR_NO_FILE )\r
3142                 {\r
3143                         res = FR_NO_PATH;\r
3144                 }\r
3145         }\r
3146 \r
3147         LEAVE_FF( dj->fs, res );\r
3148 }\r
3149 \r
3150 /*-----------------------------------------------------------------------*/\r
3151 \r
3152 /* Read Directory Entry in Sequense                                      */\r
3153 \r
3154 /*-----------------------------------------------------------------------*/\r
3155 FRESULT f_readdir( DIR *dj, /* Pointer to the open directory object */ FILINFO *fno /* Pointer to file information to return */ )\r
3156 {\r
3157         FRESULT res;\r
3158         NAMEBUF( sfn, lfn );\r
3159 \r
3160         res = validate( dj->fs, dj->id );       /* Check validity of the object */\r
3161         if( res == FR_OK )\r
3162         {\r
3163                 INITBUF( (*dj), sfn, lfn );\r
3164                 if( !fno )\r
3165                 {\r
3166                         res = dir_seek( dj, 0 );\r
3167                 }\r
3168                 else\r
3169                 {\r
3170                         res = dir_read( dj );\r
3171                         if( res == FR_NO_FILE )\r
3172                         {\r
3173                                 dj->sect = 0;\r
3174                                 res = FR_OK;\r
3175                         }\r
3176 \r
3177                         if( res == FR_OK )\r
3178                         {       /* A valid entry is found */\r
3179                                 get_fileinfo( dj, fno );                /* Get the object information */\r
3180                                 res = dir_next( dj, FALSE );    /* Increment index for next */\r
3181                                 if( res == FR_NO_FILE )\r
3182                                 {\r
3183                                         dj->sect = 0;\r
3184                                         res = FR_OK;\r
3185                                 }\r
3186                         }\r
3187                 }\r
3188         }\r
3189 \r
3190         LEAVE_FF( dj->fs, res );\r
3191 }\r
3192 \r
3193                 #if _FS_MINIMIZE == 0\r
3194 \r
3195 /*-----------------------------------------------------------------------*/\r
3196 \r
3197 /* Get File Status                                                       */\r
3198 \r
3199 /*-----------------------------------------------------------------------*/\r
3200 FRESULT f_stat( const XCHAR *path, /* Pointer to the file path */ FILINFO *fno /* Pointer to file information to return */ )\r
3201 {\r
3202         FRESULT res;\r
3203         DIR             dj;\r
3204         NAMEBUF( sfn, lfn );\r
3205 \r
3206         res = chk_mounted( &path, &dj.fs, 0 );\r
3207         if( res == FR_OK )\r
3208         {\r
3209                 INITBUF( dj, sfn, lfn );\r
3210                 res = follow_path( &dj, path ); /* Follow the file path */\r
3211                 if( res == FR_OK )\r
3212                 {               /* Follwo completed */\r
3213                         if( dj.dir )\r
3214                         {       /* Found an object */\r
3215                                 get_fileinfo( &dj, fno );\r
3216                         }\r
3217                         else\r
3218                         {       /* It is root dir */\r
3219                                 res = FR_INVALID_NAME;\r
3220                         }\r
3221                 }\r
3222         }\r
3223 \r
3224         LEAVE_FF( dj.fs, res );\r
3225 }\r
3226 \r
3227                         #if !_FS_READONLY\r
3228 \r
3229 /*-----------------------------------------------------------------------*/\r
3230 \r
3231 /* Get Number of Free Clusters                                           */\r
3232 \r
3233 /*-----------------------------------------------------------------------*/\r
3234 FRESULT f_getfree\r
3235                 (\r
3236                         const XCHAR *path,      /* Pointer to the logical drive number (root dir) */\r
3237                         DWORD           *nclst, /* Pointer to the variable to return number of free clusters */\r
3238                         FATFS           **fatfs /* Pointer to pointer to corresponding file system object to return */\r
3239                 )\r
3240 {\r
3241         FRESULT res;\r
3242         DWORD   n, clst, sect, stat;\r
3243         UINT    i;\r
3244         BYTE    fat, *p;\r
3245 \r
3246         /* Get drive number */\r
3247         res = chk_mounted( &path, fatfs, 0 );\r
3248         if( res != FR_OK )\r
3249         {\r
3250                 LEAVE_FF( *fatfs, res );\r
3251         }\r
3252 \r
3253         /* If number of free cluster is valid, return it without cluster scan. */\r
3254         if( (*fatfs)->free_clust <= (*fatfs)->max_clust - 2 )\r
3255         {\r
3256                 *nclst = ( *fatfs )->free_clust;\r
3257                 LEAVE_FF( *fatfs, FR_OK );\r
3258         }\r
3259 \r
3260         /* Get number of free clusters */\r
3261         fat = ( *fatfs )->fs_type;\r
3262         n = 0;\r
3263         if( fat == FS_FAT12 )\r
3264         {\r
3265                 clst = 2;\r
3266                 do\r
3267                 {\r
3268                         stat = get_fat( *fatfs, clst );\r
3269                         if( stat == 0xFFFFFFFF )\r
3270                         {\r
3271                                 LEAVE_FF( *fatfs, FR_DISK_ERR );\r
3272                         }\r
3273 \r
3274                         if( stat == 1 )\r
3275                         {\r
3276                                 LEAVE_FF( *fatfs, FR_INT_ERR );\r
3277                         }\r
3278 \r
3279                         if( stat == 0 )\r
3280                         {\r
3281                                 n++;\r
3282                         }\r
3283                 } while( ++clst < (*fatfs)->max_clust );\r
3284         }\r
3285         else\r
3286         {\r
3287                 clst = ( *fatfs )->max_clust;\r
3288                 sect = ( *fatfs )->fatbase;\r
3289                 i = 0;\r
3290                 p = 0;\r
3291                 do\r
3292                 {\r
3293                         if( !i )\r
3294                         {\r
3295                                 res = move_window( *fatfs, sect++ );\r
3296                                 if( res != FR_OK )\r
3297                                 {\r
3298                                         LEAVE_FF( *fatfs, res );\r
3299                                 }\r
3300 \r
3301                                 p = ( *fatfs )->win;\r
3302                                 i = SS( *fatfs );\r
3303                         }\r
3304 \r
3305                         if( fat == FS_FAT16 )\r
3306                         {\r
3307                                 if( LD_WORD(p) == 0 )\r
3308                                 {\r
3309                                         n++;\r
3310                                 }\r
3311 \r
3312                                 p += 2;\r
3313                                 i -= 2;\r
3314                         }\r
3315                         else\r
3316                         {\r
3317                                 if( (LD_DWORD(p) & 0x0FFFFFFF) == 0 )\r
3318                                 {\r
3319                                         n++;\r
3320                                 }\r
3321 \r
3322                                 p += 4;\r
3323                                 i -= 4;\r
3324                         }\r
3325                 } while( --clst );\r
3326         }\r
3327 \r
3328         ( *fatfs )->free_clust = n;\r
3329         if( fat == FS_FAT32 )\r
3330         {\r
3331                 ( *fatfs )->fsi_flag = 1;\r
3332         }\r
3333 \r
3334         *nclst = n;\r
3335 \r
3336         LEAVE_FF( *fatfs, FR_OK );\r
3337 }\r
3338 \r
3339 /*-----------------------------------------------------------------------*/\r
3340 \r
3341 /* Truncate File                                                         */\r
3342 \r
3343 /*-----------------------------------------------------------------------*/\r
3344 FRESULT f_truncate( FIL *fp /* Pointer to the file object */ )\r
3345 {\r
3346         FRESULT res;\r
3347         DWORD   ncl;\r
3348 \r
3349         res = validate( fp->fs, fp->id );       /* Check validity of the object */\r
3350         if( res != FR_OK )\r
3351         {\r
3352                 LEAVE_FF( fp->fs, res );\r
3353         }\r
3354 \r
3355         if( fp->flag & FA__ERROR )\r
3356         {       /* Check abort flag */\r
3357                 LEAVE_FF( fp->fs, FR_INT_ERR );\r
3358         }\r
3359 \r
3360         if( !(fp->flag & FA_WRITE) )\r
3361         {       /* Check access mode */\r
3362                 LEAVE_FF( fp->fs, FR_DENIED );\r
3363         }\r
3364 \r
3365         if( fp->fsize > fp->fptr )\r
3366         {\r
3367                 fp->fsize = fp->fptr;   /* Set file size to current R/W point */\r
3368                 fp->flag |= FA__WRITTEN;\r
3369                 if( fp->fptr == 0 )\r
3370                 {                                               /* When set file size to zero, remove entire cluster chain */\r
3371                         res = remove_chain( fp->fs, fp->org_clust );\r
3372                         fp->org_clust = 0;\r
3373                 }\r
3374                 else\r
3375                 {                                               /* When truncate a part of the file, remove remaining clusters */\r
3376                         ncl = get_fat( fp->fs, fp->curr_clust );\r
3377                         res = FR_OK;\r
3378                         if( ncl == 0xFFFFFFFF )\r
3379                         {\r
3380                                 res = FR_DISK_ERR;\r
3381                         }\r
3382 \r
3383                         if( ncl == 1 )\r
3384                         {\r
3385                                 res = FR_INT_ERR;\r
3386                         }\r
3387 \r
3388                         if( res == FR_OK && ncl < fp->fs->max_clust )\r
3389                         {\r
3390                                 res = put_fat( fp->fs, fp->curr_clust, 0x0FFFFFFF );\r
3391                                 if( res == FR_OK )\r
3392                                 {\r
3393                                         res = remove_chain( fp->fs, ncl );\r
3394                                 }\r
3395                         }\r
3396                 }\r
3397         }\r
3398 \r
3399         if( res != FR_OK )\r
3400         {\r
3401                 fp->flag |= FA__ERROR;\r
3402         }\r
3403 \r
3404         LEAVE_FF( fp->fs, res );\r
3405 }\r
3406 \r
3407 /*-----------------------------------------------------------------------*/\r
3408 \r
3409 /* Delete a File or Directory                                            */\r
3410 \r
3411 /*-----------------------------------------------------------------------*/\r
3412 FRESULT f_unlink( const XCHAR *path /* Pointer to the file or directory path */ )\r
3413 {\r
3414         FRESULT res;\r
3415         DIR             dj, sdj;\r
3416         NAMEBUF( sfn, lfn );\r
3417 \r
3418         BYTE    *dir;\r
3419         DWORD   dclst;\r
3420 \r
3421         res = chk_mounted( &path, &dj.fs, 1 );\r
3422         if( res != FR_OK )\r
3423         {\r
3424                 LEAVE_FF( dj.fs, res );\r
3425         }\r
3426 \r
3427         INITBUF( dj, sfn, lfn );\r
3428         res = follow_path( &dj, path ); /* Follow the file path */\r
3429         if( _FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT) )\r
3430         {\r
3431                 res = FR_INVALID_NAME;\r
3432         }\r
3433 \r
3434         if( res != FR_OK )\r
3435         {\r
3436                 LEAVE_FF( dj.fs, res );         /* Follow failed */\r
3437         }\r
3438 \r
3439         dir = dj.dir;\r
3440         if( !dir )\r
3441         {       /* Is it the root directory? */\r
3442                 LEAVE_FF( dj.fs, FR_INVALID_NAME );\r
3443         }\r
3444 \r
3445         if( dir[DIR_Attr] & AM_RDO )\r
3446         {       /* Is it a R/O object? */\r
3447                 LEAVE_FF( dj.fs, FR_DENIED );\r
3448         }\r
3449 \r
3450         dclst = ( (DWORD) LD_WORD(dir + DIR_FstClusHI) << 16 ) | LD_WORD( dir + DIR_FstClusLO );\r
3451 \r
3452         if( dir[DIR_Attr] & AM_DIR )\r
3453         {       /* It is a sub-directory */\r
3454                 if( dclst < 2 )\r
3455                 {\r
3456                         LEAVE_FF( dj.fs, FR_INT_ERR );\r
3457                 }\r
3458 \r
3459                 mem_cpy( &sdj, &dj, sizeof(DIR) );              /* Check if the sub-dir is empty or not */\r
3460                 sdj.sclust = dclst;\r
3461                 res = dir_seek( &sdj, 2 );\r
3462                 if( res != FR_OK )\r
3463                 {\r
3464                         LEAVE_FF( dj.fs, res );\r
3465                 }\r
3466 \r
3467                 res = dir_read( &sdj );\r
3468                 if( res == FR_OK )\r
3469                 {\r
3470                         res = FR_DENIED;                                        /* Not empty sub-dir */\r
3471                 }\r
3472 \r
3473                 if( res != FR_NO_FILE )\r
3474                 {\r
3475                         LEAVE_FF( dj.fs, res );\r
3476                 }\r
3477         }\r
3478 \r
3479         res = dir_remove( &dj );                                        /* Remove directory entry */\r
3480         if( res == FR_OK )\r
3481         {\r
3482                 if( dclst )\r
3483                 {\r
3484                         res = remove_chain( dj.fs, dclst ); /* Remove the cluster chain */\r
3485                 }\r
3486 \r
3487                 if( res == FR_OK )\r
3488                 {\r
3489                         res = sync( dj.fs );\r
3490                 }\r
3491         }\r
3492 \r
3493         LEAVE_FF( dj.fs, res );\r
3494 }\r
3495 \r
3496 /*-----------------------------------------------------------------------*/\r
3497 \r
3498 /* Create a Directory                                                    */\r
3499 \r
3500 /*-----------------------------------------------------------------------*/\r
3501 FRESULT f_mkdir( const XCHAR *path /* Pointer to the directory path */ )\r
3502 {\r
3503         FRESULT res;\r
3504         DIR             dj;\r
3505         NAMEBUF( sfn, lfn );\r
3506 \r
3507         BYTE    *dir, n;\r
3508         DWORD   dsect, dclst, pclst, tim;\r
3509 \r
3510         res = chk_mounted( &path, &dj.fs, 1 );\r
3511         if( res != FR_OK )\r
3512         {\r
3513                 LEAVE_FF( dj.fs, res );\r
3514         }\r
3515 \r
3516         INITBUF( dj, sfn, lfn );\r
3517         res = follow_path( &dj, path ); /* Follow the file path */\r
3518         if( res == FR_OK )\r
3519         {\r
3520                 res = FR_EXIST;                         /* Any file or directory is already existing */\r
3521         }\r
3522 \r
3523         if( _FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT) )\r
3524         {\r
3525                 res = FR_INVALID_NAME;\r
3526         }\r
3527 \r
3528         if( res != FR_NO_FILE )\r
3529         {       /* Any error occured */\r
3530                 LEAVE_FF( dj.fs, res );\r
3531         }\r
3532 \r
3533         dclst = create_chain( dj.fs, 0 );               /* Allocate a new cluster for new directory table */\r
3534         res = FR_OK;\r
3535         if( dclst == 0 )\r
3536         {\r
3537                 res = FR_DENIED;\r
3538         }\r
3539 \r
3540         if( dclst == 1 )\r
3541         {\r
3542                 res = FR_INT_ERR;\r
3543         }\r
3544 \r
3545         if( dclst == 0xFFFFFFFF )\r
3546         {\r
3547                 res = FR_DISK_ERR;\r
3548         }\r
3549 \r
3550         if( res == FR_OK )\r
3551         {\r
3552                 res = move_window( dj.fs, 0 );\r
3553         }\r
3554 \r
3555         if( res != FR_OK )\r
3556         {\r
3557                 LEAVE_FF( dj.fs, res );\r
3558         }\r
3559 \r
3560         dsect = clust2sect( dj.fs, dclst );\r
3561 \r
3562         dir = dj.fs->win;                                               /* Initialize the new directory table */\r
3563         mem_set( dir, 0, SS(dj.fs) );\r
3564         mem_set( dir + DIR_Name, ' ', 8 + 3 );  /* Create "." entry */\r
3565         dir[DIR_Name] = '.';\r
3566         dir[DIR_Attr] = AM_DIR;\r
3567         tim = get_fattime();\r
3568         ST_DWORD( dir + DIR_WrtTime, tim );\r
3569         ST_WORD( dir + DIR_FstClusLO, dclst );\r
3570         ST_WORD( dir + DIR_FstClusHI, dclst >> 16 );\r
3571         mem_cpy( dir + 32, dir, 32 );                   /* Create ".." entry */\r
3572         dir[33] = '.';\r
3573         pclst = dj.sclust;\r
3574         if( dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase )\r
3575         {\r
3576                 pclst = 0;\r
3577         }\r
3578 \r
3579         ST_WORD( dir + 32 + DIR_FstClusLO, pclst );\r
3580         ST_WORD( dir + 32 + DIR_FstClusHI, pclst >> 16 );\r
3581         for( n = 0; n < dj.fs->csize; n++ )\r
3582         {       /* Write dot entries and clear left sectors */\r
3583                 dj.fs->winsect = dsect++;\r
3584                 dj.fs->wflag = 1;\r
3585                 res = move_window( dj.fs, 0 );\r
3586                 if( res )\r
3587                 {\r
3588                         LEAVE_FF( dj.fs, res );\r
3589                 }\r
3590 \r
3591                 mem_set( dir, 0, SS(dj.fs) );\r
3592         }\r
3593 \r
3594         res = dir_register( &dj );\r
3595         if( res != FR_OK )\r
3596         {\r
3597                 remove_chain( dj.fs, dclst );\r
3598         }\r
3599         else\r
3600         {\r
3601                 dir = dj.dir;\r
3602                 dir[DIR_Attr] = AM_DIR;                                 /* Attribute */\r
3603                 ST_DWORD( dir + DIR_WrtTime, tim );             /* Crated time */\r
3604                 ST_WORD( dir + DIR_FstClusLO, dclst );  /* Table start cluster */\r
3605                 ST_WORD( dir + DIR_FstClusHI, dclst >> 16 );\r
3606                 dj.fs->wflag = 1;\r
3607                 res = sync( dj.fs );\r
3608         }\r
3609 \r
3610         LEAVE_FF( dj.fs, res );\r
3611 }\r
3612 \r
3613 /*-----------------------------------------------------------------------*/\r
3614 \r
3615 /* Change File Attribute                                                 */\r
3616 \r
3617 /*-----------------------------------------------------------------------*/\r
3618 FRESULT f_chmod( const XCHAR *path, /* Pointer to the file path */ BYTE value, /* Attribute bits */ BYTE mask /* Attribute mask to change */ )\r
3619 {\r
3620         FRESULT res;\r
3621         DIR             dj;\r
3622         NAMEBUF( sfn, lfn );\r
3623 \r
3624         BYTE    *dir;\r
3625 \r
3626         res = chk_mounted( &path, &dj.fs, 1 );\r
3627         if( res == FR_OK )\r
3628         {\r
3629                 INITBUF( dj, sfn, lfn );\r
3630                 res = follow_path( &dj, path ); /* Follow the file path */\r
3631                 if( _FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT) )\r
3632                 {\r
3633                         res = FR_INVALID_NAME;\r
3634                 }\r
3635 \r
3636                 if( res == FR_OK )\r
3637                 {\r
3638                         dir = dj.dir;\r
3639                         if( !dir )\r
3640                         {       /* Is it a root directory? */\r
3641                                 res = FR_INVALID_NAME;\r
3642                         }\r
3643                         else\r
3644                         {       /* File or sub directory */\r
3645                                 mask &= AM_RDO | AM_HID | AM_SYS | AM_ARC;      /* Valid attribute mask */\r
3646                                 dir[DIR_Attr] = ( value & mask ) | ( dir[DIR_Attr] & (BYTE)~mask ); /* Apply attribute change */\r
3647                                 dj.fs->wflag = 1;\r
3648                                 res = sync( dj.fs );\r
3649                         }\r
3650                 }\r
3651         }\r
3652 \r
3653         LEAVE_FF( dj.fs, res );\r
3654 }\r
3655 \r
3656 /*-----------------------------------------------------------------------*/\r
3657 \r
3658 /* Change Timestamp                                                      */\r
3659 \r
3660 /*-----------------------------------------------------------------------*/\r
3661 FRESULT f_utime( const XCHAR *path, /* Pointer to the file/directory name */ const FILINFO *fno /* Pointer to the timestamp to be set */ )\r
3662 {\r
3663         FRESULT res;\r
3664         DIR             dj;\r
3665         NAMEBUF( sfn, lfn );\r
3666 \r
3667         BYTE    *dir;\r
3668 \r
3669         res = chk_mounted( &path, &dj.fs, 1 );\r
3670         if( res == FR_OK )\r
3671         {\r
3672                 INITBUF( dj, sfn, lfn );\r
3673                 res = follow_path( &dj, path ); /* Follow the file path */\r
3674                 if( _FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT) )\r
3675                 {\r
3676                         res = FR_INVALID_NAME;\r
3677                 }\r
3678 \r
3679                 if( res == FR_OK )\r
3680                 {\r
3681                         dir = dj.dir;\r
3682                         if( !dir )\r
3683                         {       /* Root directory */\r
3684                                 res = FR_INVALID_NAME;\r
3685                         }\r
3686                         else\r
3687                         {       /* File or sub-directory */\r
3688                                 ST_WORD( dir + DIR_WrtTime, fno->ftime );\r
3689                                 ST_WORD( dir + DIR_WrtDate, fno->fdate );\r
3690                                 dj.fs->wflag = 1;\r
3691                                 res = sync( dj.fs );\r
3692                         }\r
3693                 }\r
3694         }\r
3695 \r
3696         LEAVE_FF( dj.fs, res );\r
3697 }\r
3698 \r
3699 /*-----------------------------------------------------------------------*/\r
3700 \r
3701 /* Rename File/Directory                                                 */\r
3702 \r
3703 /*-----------------------------------------------------------------------*/\r
3704 FRESULT f_rename( const XCHAR *path_old, /* Pointer to the old name */ const XCHAR *path_new /* Pointer to the new name */ )\r
3705 {\r
3706         FRESULT res;\r
3707         DIR             dj_old, dj_new;\r
3708         NAMEBUF( sfn, lfn );\r
3709 \r
3710         BYTE    buf[21], *dir;\r
3711         DWORD   dw;\r
3712 \r
3713         INITBUF( dj_old, sfn, lfn );\r
3714         res = chk_mounted( &path_old, &dj_old.fs, 1 );\r
3715         if( res == FR_OK )\r
3716         {\r
3717                 dj_new.fs = dj_old.fs;\r
3718                 res = follow_path( &dj_old, path_old ); /* Check old object */\r
3719                 if( _FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT) )\r
3720                 {\r
3721                         res = FR_INVALID_NAME;\r
3722                 }\r
3723         }\r
3724 \r
3725         if( res != FR_OK )\r
3726         {\r
3727                 LEAVE_FF( dj_old.fs, res );                             /* The old object is not found */\r
3728         }\r
3729 \r
3730         if( !dj_old.dir )\r
3731         {\r
3732                 LEAVE_FF( dj_old.fs, FR_NO_FILE );              /* Is root dir? */\r
3733         }\r
3734 \r
3735         mem_cpy( buf, dj_old.dir + DIR_Attr, 21 );      /* Save the object information */\r
3736 \r
3737         mem_cpy( &dj_new, &dj_old, sizeof(DIR) );\r
3738         res = follow_path( &dj_new, path_new );         /* Check new object */\r
3739         if( res == FR_OK )\r
3740         {\r
3741                 res = FR_EXIST;                                 /* The new object name is already existing */\r
3742         }\r
3743 \r
3744         if( res == FR_NO_FILE )\r
3745         {                                                                       /* Is it a valid path and no name collision? */\r
3746                 res = dir_register( &dj_new );  /* Register the new object */\r
3747                 if( res == FR_OK )\r
3748                 {\r
3749                         dir = dj_new.dir;                       /* Copy object information into new entry */\r
3750                         mem_cpy( dir + 13, buf + 2, 19 );\r
3751                         dir[DIR_Attr] = buf[0] | AM_ARC;\r
3752                         dj_old.fs->wflag = 1;\r
3753                         if( dir[DIR_Attr] & AM_DIR )\r
3754                         {       /* Update .. entry in the directory if needed */\r
3755                                 dw = clust2sect( dj_new.fs, (DWORD) LD_WORD(dir + DIR_FstClusHI) | LD_WORD(dir + DIR_FstClusLO) );\r
3756                                 if( !dw )\r
3757                                 {\r
3758                                         res = FR_INT_ERR;\r
3759                                 }\r
3760                                 else\r
3761                                 {\r
3762                                         res = move_window( dj_new.fs, dw );\r
3763                                         dir = dj_new.fs->win + 32;\r
3764                                         if( res == FR_OK && dir[1] == '.' )\r
3765                                         {\r
3766                                                 dw = ( dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase ) ? 0 : dj_new.sclust;\r
3767                                                 ST_WORD( dir + DIR_FstClusLO, dw );\r
3768                                                 ST_WORD( dir + DIR_FstClusHI, dw >> 16 );\r
3769                                                 dj_new.fs->wflag = 1;\r
3770                                         }\r
3771                                 }\r
3772                         }\r
3773 \r
3774                         if( res == FR_OK )\r
3775                         {\r
3776                                 res = dir_remove( &dj_old );    /* Remove old entry */\r
3777                                 if( res == FR_OK )\r
3778                                 {\r
3779                                         res = sync( dj_old.fs );\r
3780                                 }\r
3781                         }\r
3782                 }\r
3783         }\r
3784 \r
3785         LEAVE_FF( dj_old.fs, res );\r
3786 }\r
3787 \r
3788                         #endif /* !_FS_READONLY */\r
3789                 #endif /* _FS_MINIMIZE == 0 */\r
3790         #endif /* _FS_MINIMIZE <= 1 */\r
3791 #endif /* _FS_MINIMIZE <= 2 */\r
3792 \r
3793 /*-----------------------------------------------------------------------*/\r
3794 \r
3795 /* Forward data to the stream directly (Available on only _FS_TINY cfg)  */\r
3796 \r
3797 /*-----------------------------------------------------------------------*/\r
3798 #if _USE_FORWARD && _FS_TINY\r
3799 FRESULT f_forward\r
3800                 (\r
3801                         FIL *fp,        /* Pointer to the file object */\r
3802                         UINT (*func) ( const BYTE *, UINT ),    /* Pointer to the streaming function */\r
3803                         UINT btr,       /* Number of bytes to forward */\r
3804                         UINT *bf        /* Pointer to number of bytes forwarded */\r
3805                 )\r
3806 {\r
3807         FRESULT res;\r
3808         DWORD   remain, clst, sect;\r
3809         UINT    rcnt;\r
3810 \r
3811         *bf = 0;\r
3812 \r
3813         res = validate( fp->fs, fp->id );       /* Check validity of the object */\r
3814         if( res != FR_OK )\r
3815         {\r
3816                 LEAVE_FF( fp->fs, res );\r
3817         }\r
3818 \r
3819         if( fp->flag & FA__ERROR )\r
3820         {       /* Check error flag */\r
3821                 LEAVE_FF( fp->fs, FR_INT_ERR );\r
3822         }\r
3823 \r
3824         if( !(fp->flag & FA_READ) )\r
3825         {       /* Check access mode */\r
3826                 LEAVE_FF( fp->fs, FR_DENIED );\r
3827         }\r
3828 \r
3829         remain = fp->fsize - fp->fptr;\r
3830         if( btr > remain )\r
3831         {\r
3832                 btr = ( UINT ) remain;          /* Truncate btr by remaining bytes */\r
3833         }\r
3834 \r
3835         for\r
3836         (\r
3837                 ;\r
3838                 btr && (*func) (NULL, 0);       /* Repeat until all data transferred or stream becomes busy */\r
3839                 fp->fptr += rcnt, *bf += rcnt, btr -= rcnt\r
3840         )\r
3841         {\r
3842                 if( (fp->fptr % SS(fp->fs)) == 0 )\r
3843                 {               /* On the sector boundary? */\r
3844                         if( fp->csect >= fp->fs->csize )\r
3845                         {       /* On the cluster boundary? */\r
3846                                 clst = ( fp->fptr == 0 ) ?                              /* On the top of the file? */\r
3847                                 fp->org_clust : get_fat( fp->fs, fp->curr_clust );\r
3848                                 if( clst <= 1 )\r
3849                                 {\r
3850                                         ABORT( fp->fs, FR_INT_ERR );\r
3851                                 }\r
3852 \r
3853                                 if( clst == 0xFFFFFFFF )\r
3854                                 {\r
3855                                         ABORT( fp->fs, FR_DISK_ERR );\r
3856                                 }\r
3857 \r
3858                                 fp->curr_clust = clst;                                  /* Update current cluster */\r
3859                                 fp->csect = 0;                                                  /* Reset sector address in the cluster */\r
3860                         }\r
3861 \r
3862                         fp->csect++;                                                            /* Next sector address in the cluster */\r
3863                 }\r
3864 \r
3865                 sect = clust2sect( fp->fs, fp->curr_clust );    /* Get current data sector */\r
3866                 if( !sect )\r
3867                 {\r
3868                         ABORT( fp->fs, FR_INT_ERR );\r
3869                 }\r
3870 \r
3871                 sect += fp->csect - 1;\r
3872                 if( move_window(fp->fs, sect) )\r
3873                 {       /* Move sector window */\r
3874                         ABORT( fp->fs, FR_DISK_ERR );\r
3875                 }\r
3876 \r
3877                 fp->dsect = sect;\r
3878                 rcnt = SS( fp->fs ) - ( WORD ) ( fp->fptr % SS(fp->fs) );       /* Forward data from sector window */\r
3879                 if( rcnt > btr )\r
3880                 {\r
3881                         rcnt = btr;\r
3882                 }\r
3883 \r
3884                 rcnt = ( *func ) ( &fp->fs->win[(WORD) fp->fptr % SS(fp->fs)], rcnt );\r
3885                 if( !rcnt )\r
3886                 {\r
3887                         ABORT( fp->fs, FR_INT_ERR );\r
3888                 }\r
3889         }\r
3890 \r
3891         LEAVE_FF( fp->fs, FR_OK );\r
3892 }\r
3893 \r
3894 #endif /* _USE_FORWARD */\r
3895 \r
3896 #if _USE_MKFS && !_FS_READONLY\r
3897 \r
3898 /*-----------------------------------------------------------------------*/\r
3899 \r
3900 /* Create File System on the Drive                                       */\r
3901 \r
3902 /*-----------------------------------------------------------------------*/\r
3903         #define N_ROOTDIR       512                     /* Multiple of 32 and <= 2048 */\r
3904         #define N_FATS          1                       /* 1 or 2 */\r
3905         #define MAX_SECTOR      131072000UL /* Maximum partition size */\r
3906         #define MIN_SECTOR      2000UL          /* Minimum partition size */\r
3907 \r
3908 FRESULT f_mkfs\r
3909                 (\r
3910                         BYTE    drv,            /* Logical drive number */\r
3911                         BYTE    partition,      /* Partitioning rule 0:FDISK, 1:SFD */\r
3912                         WORD    allocsize       /* Allocation unit size [bytes] */\r
3913                 )\r
3914 {\r
3915         static const DWORD      sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 };\r
3916         static const WORD       cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 };\r
3917         BYTE                            fmt, m, *tbl;\r
3918         DWORD                           b_part, b_fat, b_dir, b_data;   /* Area offset (LBA) */\r
3919         DWORD                           n_part, n_rsv, n_fat, n_dir;    /* Area size */\r
3920         DWORD                           n_clst, d, n;\r
3921         WORD                            as;\r
3922         FATFS                           *fs;\r
3923         DSTATUS                         stat;\r
3924 \r
3925         /* Check validity of the parameters */\r
3926         if( drv >= _DRIVES )\r
3927         {\r
3928                 return FR_INVALID_DRIVE;\r
3929         }\r
3930 \r
3931         if( partition >= 2 )\r
3932         {\r
3933                 return FR_MKFS_ABORTED;\r
3934         }\r
3935 \r
3936         /* Check mounted drive and clear work area */\r
3937         fs = FatFs[drv];\r
3938         if( !fs )\r
3939         {\r
3940                 return FR_NOT_ENABLED;\r
3941         }\r
3942 \r
3943         fs->fs_type = 0;\r
3944         drv = LD2PD( drv );\r
3945 \r
3946         /* Get disk statics */\r
3947         stat = disk_initialize( drv );\r
3948         if( stat & STA_NOINIT )\r
3949         {\r
3950                 return FR_NOT_READY;\r
3951         }\r
3952 \r
3953         if( stat & STA_PROTECT )\r
3954         {\r
3955                 return FR_WRITE_PROTECTED;\r
3956         }\r
3957 \r
3958                 #if _MAX_SS != 512                              /* Get disk sector size */\r
3959         if( disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS )\r
3960         {\r
3961                 return FR_MKFS_ABORTED;\r
3962         }\r
3963 \r
3964                 #endif\r
3965         if( disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR )\r
3966         {\r
3967                 return FR_MKFS_ABORTED;\r
3968         }\r
3969 \r
3970         if( n_part > MAX_SECTOR )\r
3971         {\r
3972                 n_part = MAX_SECTOR;\r
3973         }\r
3974 \r
3975         b_part = ( !partition ) ? 63 : 0;       /* Boot sector */\r
3976         n_part -= b_part;\r
3977         for( d = 512; d <= 32768U && d != allocsize; d <<= 1 );\r
3978 \r
3979         /* Check validity of the allocation unit size */\r
3980         if( d != allocsize )\r
3981         {\r
3982                 allocsize = 0;\r
3983         }\r
3984 \r
3985         if( !allocsize )\r
3986         {                                               /* Auto selection of cluster size */\r
3987                 d = n_part;\r
3988                 for( as = SS(fs); as > 512U; as >>= 1 )\r
3989                 {\r
3990                         d >>= 1;\r
3991                 }\r
3992 \r
3993                 for( n = 0; d < sstbl[n]; n++ );\r
3994                 allocsize = cstbl[n];\r
3995         }\r
3996 \r
3997         if( allocsize < SS(fs) )\r
3998         {\r
3999                 allocsize = SS( fs );\r
4000         }\r
4001 \r
4002         allocsize /= SS( fs );  /* Number of sectors per cluster */\r
4003 \r
4004         /* Pre-compute number of clusters and FAT type */\r
4005         n_clst = n_part / allocsize;\r
4006         fmt = FS_FAT12;\r
4007         if( n_clst >= 0xFF5 )\r
4008         {\r
4009                 fmt = FS_FAT16;\r
4010         }\r
4011 \r
4012         if( n_clst >= 0xFFF5 )\r
4013         {\r
4014                 fmt = FS_FAT32;\r
4015         }\r
4016 \r
4017         /* Determine offset and size of FAT structure */\r
4018         switch( fmt )\r
4019         {\r
4020                 case FS_FAT12:\r
4021                         n_fat = ( (n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1 ) / SS( fs );\r
4022                         n_rsv = 1 + partition;\r
4023                         n_dir = N_ROOTDIR * 32 / SS( fs );\r
4024                         break;\r
4025 \r
4026                 case FS_FAT16:\r
4027                         n_fat = ( (n_clst * 2) + 4 + SS(fs) - 1 ) / SS( fs );\r
4028                         n_rsv = 1 + partition;\r
4029                         n_dir = N_ROOTDIR * 32 / SS( fs );\r
4030                         break;\r
4031 \r
4032                 default:\r
4033                         n_fat = ( (n_clst * 4) + 8 + SS(fs) - 1 ) / SS( fs );\r
4034                         n_rsv = 33 - partition;\r
4035                         n_dir = 0;\r
4036         }\r
4037 \r
4038         b_fat = b_part + n_rsv; /* FATs start sector */\r
4039         b_dir = b_fat + n_fat * N_FATS;         /* Directory start sector */\r
4040         b_data = b_dir + n_dir;                         /* Data start sector */\r
4041 \r
4042         /* Align data start sector to erase block boundary (for flash memory media) */\r
4043         if( disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK )\r
4044         {\r
4045                 return FR_MKFS_ABORTED;\r
4046         }\r
4047 \r
4048         n = ( b_data + n - 1 ) &~( n - 1 );\r
4049         n_fat += ( n - b_data ) / N_FATS;\r
4050 \r
4051         /* b_dir and b_data are no longer used below */\r
4052 \r
4053         /* Determine number of cluster and final check of validity of the FAT type */\r
4054         n_clst = ( n_part - n_rsv - n_fat * N_FATS - n_dir ) / allocsize;\r
4055         if( (fmt == FS_FAT16 && n_clst < 0xFF5) || (fmt == FS_FAT32 && n_clst < 0xFFF5) )\r
4056         {\r
4057                 return FR_MKFS_ABORTED;\r
4058         }\r
4059 \r
4060         /* Create partition table if needed */\r
4061         if( !partition )\r
4062         {\r
4063                 DWORD   n_disk = b_part + n_part;\r
4064 \r
4065                 mem_set( fs->win, 0, SS(fs) );\r
4066                 tbl = fs->win + MBR_Table;\r
4067                 ST_DWORD( tbl, 0x00010180 );    /* Partition start in CHS */\r
4068                 if( n_disk < 63UL * 255 * 1024 )\r
4069                 {       /* Partition end in CHS */\r
4070                         n_disk = n_disk / 63 / 255;\r
4071                         tbl[7] = ( BYTE ) n_disk;\r
4072                         tbl[6] = ( BYTE ) ( (n_disk >> 2) | 63 );\r
4073                 }\r
4074                 else\r
4075                 {\r
4076                         ST_WORD( &tbl[6], 0xFFFF );\r
4077                 }\r
4078 \r
4079                 tbl[5] = 254;\r
4080                 if( fmt != FS_FAT32 )\r
4081                 {       /* System ID */\r
4082                         tbl[4] = ( n_part < 0x10000 ) ? 0x04 : 0x06;\r
4083                 }\r
4084                 else\r
4085                 {\r
4086                         tbl[4] = 0x0c;\r
4087                 }\r
4088 \r
4089                 ST_DWORD( tbl + 8, 63 );                                                        /* Partition start in LBA */\r
4090                 ST_DWORD( tbl + 12, n_part );                                           /* Partition size in LBA */\r
4091                 ST_WORD( tbl + 64, 0xAA55 );                                            /* Signature */\r
4092                 if( disk_write(drv, fs->win, 0, 1) != RES_OK )\r
4093                 {\r
4094                         return FR_DISK_ERR;\r
4095                 }\r
4096 \r
4097                 partition = 0xF8;\r
4098         }\r
4099         else\r
4100         {\r
4101                 partition = 0xF0;\r
4102         }\r
4103 \r
4104         /* Create boot record */\r
4105         tbl = fs->win;                                                                                  /* Clear buffer */\r
4106         mem_set( tbl, 0, SS(fs) );\r
4107         ST_DWORD( tbl + BS_jmpBoot, 0x90FEEB );                                 /* Boot code (jmp $, nop) */\r
4108         ST_WORD( tbl + BPB_BytsPerSec, SS(fs) );                                /* Sector size */\r
4109         tbl[BPB_SecPerClus] = ( BYTE ) allocsize;                               /* Sectors per cluster */\r
4110         ST_WORD( tbl + BPB_RsvdSecCnt, n_rsv );                                 /* Reserved sectors */\r
4111         tbl[BPB_NumFATs] = N_FATS;                                                              /* Number of FATs */\r
4112         ST_WORD( tbl + BPB_RootEntCnt, SS(fs) / 32 * n_dir );   /* Number of rootdir entries */\r
4113         if( n_part < 0x10000 )\r
4114         {       /* Number of total sectors */\r
4115                 ST_WORD( tbl + BPB_TotSec16, n_part );\r
4116         }\r
4117         else\r
4118         {\r
4119                 ST_DWORD( tbl + BPB_TotSec32, n_part );\r
4120         }\r
4121 \r
4122         tbl[BPB_Media] = partition;                                                                     /* Media descripter */\r
4123         ST_WORD( tbl + BPB_SecPerTrk, 63 );                                                     /* Number of sectors per track */\r
4124         ST_WORD( tbl + BPB_NumHeads, 255 );                                                     /* Number of heads */\r
4125         ST_DWORD( tbl + BPB_HiddSec, b_part );                                          /* Hidden sectors */\r
4126         n = get_fattime();                                                                                      /* Use current time as a VSN */\r
4127         if( fmt != FS_FAT32 )\r
4128         {\r
4129                 ST_DWORD( tbl + BS_VolID, n );                                                  /* Volume serial number */\r
4130                 ST_WORD( tbl + BPB_FATSz16, n_fat );                                    /* Number of secters per FAT */\r
4131                 tbl[BS_DrvNum] = 0x80;                                                                  /* Drive number */\r
4132                 tbl[BS_BootSig] = 0x29;                                                                 /* Extended boot signature */\r
4133                 mem_cpy( tbl + BS_VolLab, "NO NAME    FAT     ", 19 );  /* Volume lavel, FAT signature */\r
4134         }\r
4135         else\r
4136         {\r
4137                 ST_DWORD( tbl + BS_VolID32, n );                                                /* Volume serial number */\r
4138                 ST_DWORD( tbl + BPB_FATSz32, n_fat );                                   /* Number of secters per FAT */\r
4139                 ST_DWORD( tbl + BPB_RootClus, 2 );                                              /* Root directory cluster (2) */\r
4140                 ST_WORD( tbl + BPB_FSInfo, 1 );         /* FSInfo record offset (bs+1) */\r
4141                 ST_WORD( tbl + BPB_BkBootSec, 6 );      /* Backup boot record offset (bs+6) */\r
4142                 tbl[BS_DrvNum32] = 0x80;                        /* Drive number */\r
4143                 tbl[BS_BootSig32] = 0x29;                       /* Extended boot signature */\r
4144                 mem_cpy( tbl + BS_VolLab32, "NO NAME    FAT32   ", 19 );        /* Volume lavel, FAT signature */\r
4145         }\r
4146 \r
4147         ST_WORD( tbl + BS_55AA, 0xAA55 );                       /* Signature */\r
4148         if( SS(fs) > 512U )\r
4149         {\r
4150                 ST_WORD( tbl + SS(fs) - 2, 0xAA55 );\r
4151         }\r
4152 \r
4153         if( disk_write(drv, tbl, b_part + 0, 1) != RES_OK )\r
4154         {\r
4155                 return FR_DISK_ERR;\r
4156         }\r
4157 \r
4158         if( fmt == FS_FAT32 )\r
4159         {\r
4160                 disk_write( drv, tbl, b_part + 6, 1 );\r
4161         }\r
4162 \r
4163         /* Initialize FAT area */\r
4164         for( m = 0; m < N_FATS; m++ )\r
4165         {\r
4166                 mem_set( tbl, 0, SS(fs) );                              /* 1st sector of the FAT  */\r
4167                 if( fmt != FS_FAT32 )\r
4168                 {\r
4169                         n = ( fmt == FS_FAT12 ) ? 0x00FFFF00 : 0xFFFFFF00;\r
4170                         n |= partition;\r
4171                         ST_DWORD( tbl, n );                                     /* Reserve cluster #0-1 (FAT12/16) */\r
4172                 }\r
4173                 else\r
4174                 {\r
4175                         ST_DWORD( tbl + 0, 0xFFFFFFF8 );        /* Reserve cluster #0-1 (FAT32) */\r
4176                         ST_DWORD( tbl + 4, 0xFFFFFFFF );\r
4177                         ST_DWORD( tbl + 8, 0x0FFFFFFF );        /* Reserve cluster #2 for root dir */\r
4178                 }\r
4179 \r
4180                 if( disk_write(drv, tbl, b_fat++, 1) != RES_OK )\r
4181                 {\r
4182                         return FR_DISK_ERR;\r
4183                 }\r
4184 \r
4185                 mem_set( tbl, 0, SS(fs) );                              /* Following FAT entries are filled by zero */\r
4186                 for( n = 1; n < n_fat; n++ )\r
4187                 {\r
4188                         if( disk_write(drv, tbl, b_fat++, 1) != RES_OK )\r
4189                         {\r
4190                                 return FR_DISK_ERR;\r
4191                         }\r
4192                 }\r
4193         }\r
4194 \r
4195         /* Initialize Root directory */\r
4196         m = ( BYTE ) ( (fmt == FS_FAT32) ? allocsize : n_dir );\r
4197         do\r
4198         {\r
4199                 if( disk_write(drv, tbl, b_fat++, 1) != RES_OK )\r
4200                 {\r
4201                         return FR_DISK_ERR;\r
4202                 }\r
4203         } while( --m );\r
4204 \r
4205         /* Create FSInfo record if needed */\r
4206         if( fmt == FS_FAT32 )\r
4207         {\r
4208                 ST_WORD( tbl + BS_55AA, 0xAA55 );\r
4209                 ST_DWORD( tbl + FSI_LeadSig, 0x41615252 );\r
4210                 ST_DWORD( tbl + FSI_StrucSig, 0x61417272 );\r
4211                 ST_DWORD( tbl + FSI_Free_Count, n_clst - 1 );\r
4212                 ST_DWORD( tbl + FSI_Nxt_Free, 0xFFFFFFFF );\r
4213                 disk_write( drv, tbl, b_part + 1, 1 );\r
4214                 disk_write( drv, tbl, b_part + 7, 1 );\r
4215         }\r
4216 \r
4217         return( disk_ioctl(drv, CTRL_SYNC, ( void * ) NULL) == RES_OK ) ? FR_OK : FR_DISK_ERR;\r
4218 }\r
4219 \r
4220 #endif /* _USE_MKFS && !_FS_READONLY */\r
4221 \r
4222 #if _USE_STRFUNC\r
4223 \r
4224 /*-----------------------------------------------------------------------*/\r
4225 \r
4226 /* Get a string from the file                                            */\r
4227 \r
4228 /*-----------------------------------------------------------------------*/\r
4229 char *f_gets\r
4230         (\r
4231                 char    *buff,  /* Pointer to the string buffer to read */\r
4232                 int             len,    /* Size of string buffer */\r
4233                 FIL             *fil    /* Pointer to the file object */\r
4234         )\r
4235 {\r
4236         int             i = 0;\r
4237         char    *p = buff;\r
4238         UINT    rc;\r
4239 \r
4240         while( i < len - 1 )\r
4241         {                                               /* Read bytes until buffer gets filled */\r
4242                 f_read( fil, p, 1, &rc );\r
4243                 if( rc != 1 )\r
4244                 {\r
4245                         break;                  /* Break when no data to read */\r
4246                 }\r
4247 \r
4248                         #if _USE_STRFUNC >= 2\r
4249                 if( *p == '\r' )\r
4250                 {\r
4251                         continue;               /* Strip '\r' */\r
4252                 }\r
4253 \r
4254                         #endif\r
4255                 i++;\r
4256                 if( *p++ == '\n' )\r
4257                 {\r
4258                         break;                  /* Break when reached end of line */\r
4259                 }\r
4260         }\r
4261 \r
4262         *p = 0;\r
4263         return i ? buff : NULL; /* When no data read (eof or error), return with error. */\r
4264 }\r
4265 \r
4266         #if !_FS_READONLY\r
4267                 #include <stdarg.h>\r
4268 \r
4269 /*-----------------------------------------------------------------------*/\r
4270 \r
4271 /* Put a character to the file                                           */\r
4272 \r
4273 /*-----------------------------------------------------------------------*/\r
4274 int f_putc( int chr, /* A character to be output */ FIL *fil /* Ponter to the file object */ )\r
4275 {\r
4276         UINT    bw;\r
4277         char    c;\r
4278 \r
4279                         #if _USE_STRFUNC >= 2\r
4280         if( chr == '\n' )\r
4281         {\r
4282                 f_putc( '\r', fil );    /* LF -> CRLF conversion */\r
4283         }\r
4284 \r
4285                         #endif\r
4286         if( !fil )\r
4287         {       /* Special value may be used to switch the destination to any other device */\r
4288                 /*      put_console(chr);       */\r
4289                 return chr;\r
4290         }\r
4291 \r
4292         c = ( char ) chr;\r
4293         f_write( fil, &c, 1, &bw ); /* Write a byte to the file */\r
4294         return bw ? chr : EOF;          /* Return the result */\r
4295 }\r
4296 \r
4297 /*-----------------------------------------------------------------------*/\r
4298 \r
4299 /* Put a string to the file                                              */\r
4300 \r
4301 /*-----------------------------------------------------------------------*/\r
4302 int f_puts( const char *str, /* Pointer to the string to be output */ FIL *fil /* Pointer to the file object */ )\r
4303 {\r
4304         int n;\r
4305 \r
4306         for( n = 0; *str; str++, n++ )\r
4307         {\r
4308                 if( f_putc(*str, fil) == EOF )\r
4309                 {\r
4310                         return EOF;\r
4311                 }\r
4312         }\r
4313 \r
4314         return n;\r
4315 }\r
4316 \r
4317 /*-----------------------------------------------------------------------*/\r
4318 \r
4319 /* Put a formatted string to the file                                    */\r
4320 \r
4321 /*-----------------------------------------------------------------------*/\r
4322 int f_printf( FIL *fil, /* Pointer to the file object */ const char *str, /* Pointer to the format string */ ... /* Optional arguments... */ )\r
4323 {\r
4324         va_list arp;\r
4325         UCHAR   c, f, r;\r
4326         ULONG   val;\r
4327         char    s[16];\r
4328         int             i, w, res, cc;\r
4329 \r
4330         va_start( arp, str );\r
4331 \r
4332         for( cc = res = 0; cc != EOF; res += cc )\r
4333         {\r
4334                 c = *str++;\r
4335                 if( c == 0 )\r
4336                 {\r
4337                         break;  /* End of string */\r
4338                 }\r
4339 \r
4340                 if( c != '%' )\r
4341                 {                       /* Non escape cahracter */\r
4342                         cc = f_putc( c, fil );\r
4343                         if( cc != EOF )\r
4344                         {\r
4345                                 cc = 1;\r
4346                         }\r
4347 \r
4348                         continue;\r
4349                 }\r
4350 \r
4351                 w = f = 0;\r
4352                 c = *str++;\r
4353                 if( c == '0' )\r
4354                 {                       /* Flag: '0' padding */\r
4355                         f = 1;\r
4356                         c = *str++;\r
4357                 }\r
4358 \r
4359                 while( c >= '0' && c <= '9' )\r
4360                 {                       /* Precision */\r
4361                         w = w * 10 + ( c - '0' );\r
4362                         c = *str++;\r
4363                 }\r
4364 \r
4365                 if( c == 'l' )\r
4366                 {                       /* Prefix: Size is long int */\r
4367                         f |= 2;\r
4368                         c = *str++;\r
4369                 }\r
4370 \r
4371                 if( c == 's' )\r
4372                 {                       /* Type is string */\r
4373                         cc = f_puts( va_arg(arp, char *), fil );\r
4374                         continue;\r
4375                 }\r
4376 \r
4377                 if( c == 'c' )\r
4378                 {                       /* Type is character */\r
4379                         cc = f_putc( va_arg(arp, int), fil );\r
4380                         if( cc != EOF )\r
4381                         {\r
4382                                 cc = 1;\r
4383                         }\r
4384 \r
4385                         continue;\r
4386                 }\r
4387 \r
4388                 r = 0;\r
4389                 if( c == 'd' )\r
4390                 {\r
4391                         r = 10; /* Type is signed decimal */\r
4392                 }\r
4393 \r
4394                 if( c == 'u' )\r
4395                 {\r
4396                         r = 10; /* Type is unsigned decimal */\r
4397                 }\r
4398 \r
4399                 if( c == 'X' )\r
4400                 {\r
4401                         r = 16; /* Type is unsigned hexdecimal */\r
4402                 }\r
4403 \r
4404                 if( r == 0 )\r
4405                 {\r
4406                         break;  /* Unknown type */\r
4407                 }\r
4408 \r
4409                 if( f & 2 )\r
4410                 {                       /* Get the value */\r
4411                         val = ( ULONG ) va_arg( arp, long );\r
4412                 }\r
4413                 else\r
4414                 {\r
4415                         val = ( c == 'd' ) ? ( ULONG ) ( long ) va_arg( arp, int ) : ( ULONG ) va_arg( arp, unsigned int );\r
4416                 }\r
4417 \r
4418                 /* Put numeral string */\r
4419                 if( c == 'd' )\r
4420                 {\r
4421                         if( val & 0x80000000 )\r
4422                         {\r
4423                                 val = 0 - val;\r
4424                                 f |= 4;\r
4425                         }\r
4426                 }\r
4427 \r
4428                 i = sizeof( s ) - 1;\r
4429                 s[i] = 0;\r
4430                 do\r
4431                 {\r
4432                         c = ( UCHAR ) ( val % r + '0' );\r
4433                         if( c > '9' )\r
4434                         {\r
4435                                 c += 7;\r
4436                         }\r
4437 \r
4438                         s[--i] = c;\r
4439                         val /= r;\r
4440                 } while( i && val );\r
4441                 if( i && (f & 4) )\r
4442                 {\r
4443                         s[--i] = '-';\r
4444                 }\r
4445 \r
4446                 w = sizeof( s ) - 1 - w;\r
4447                 while( i && i > w )\r
4448                 {\r
4449                         s[--i] = ( f & 1 ) ? '0' : ' ';\r
4450                 }\r
4451 \r
4452                 cc = f_puts( &s[i], fil );\r
4453         }\r
4454 \r
4455         va_end( arp );\r
4456         return( cc == EOF ) ? cc : res;\r
4457 }\r
4458 \r
4459         #endif /* !_FS_READONLY */\r
4460 #endif /* _USE_STRFUNC */\r