refactoring ugly typedefs...
[ppcskel.git] / ff.c
1 /*----------------------------------------------------------------------------/
2 /  FatFs - FAT file system module  R0.07                     (C)ChaN, 2009
3 /-----------------------------------------------------------------------------/
4 / FatFs module is an open source software to implement FAT file system to
5 / small embedded systems. This is a free software and is opened for education,
6 / research and commecial developments under license policy of following trems.
7 /
8 /  Copyright (C) 2009, ChaN, all right reserved.
9 /
10 / * The FatFs module is a free software and there is NO WARRANTY.
11 / * No restriction on use. You can use, modify and redistribute it for
12 /   personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
13 / * Redistributions of source code must retain the above copyright notice.
14 /
15 /-----------------------------------------------------------------------------/
16 / Feb 26,'06 R0.00  Prototype.
17 /
18 / Apr 29,'06 R0.01  First stable version.
19 /
20 / Jun 01,'06 R0.02  Added FAT12 support.
21 /                   Removed unbuffered mode.
22 /                   Fixed a problem on small (<32M) patition.
23 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
24 /
25 / Sep 22,'06 R0.03  Added f_rename().
26 /                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
27 / Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.
28 /                   Fixed f_mkdir() creates incorrect directory on FAT32.
29 /
30 / Feb 04,'07 R0.04  Supported multiple drive system.
31 /                   Changed some interfaces for multiple drive system.
32 /                   Changed f_mountdrv() to f_mount().
33 /                   Added f_mkfs().
34 / Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.
35 /                   Added a capability of extending file size to f_lseek().
36 /                   Added minimization level 3.
37 /                   Fixed an endian sensitive code in f_mkfs().
38 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
39 /                   Added FSInfo support.
40 /                   Fixed DBCS name can result FR_INVALID_NAME.
41 /                   Fixed short seek (<= csize) collapses the file object.
42 /
43 / Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
44 /                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
45 /                   Fixed f_mkdir() on FAT32 creates incorrect directory.
46 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().
47 /                   Fixed off by one error at FAT sub-type determination.
48 /                   Fixed btr in f_read() can be mistruncated.
49 /                   Fixed cached sector is not flushed when create and close
50 /                   without write.
51 /
52 / Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
53 /                   Improved performance of f_lseek() on moving to the same
54 /                   or following cluster.
55 /
56 / Apr 01,'09 R0.07  Merged Tiny-FatFs as a buffer configuration option.
57 /                   Added long file name support.
58 /                   Added multiple code page support.
59 /                   Added re-entrancy for multitask operation.
60 /                   Added auto cluster size selection to f_mkfs().
61 /                   Added rewind option to f_readdir().
62 /                   Changed result code of critical errors.
63 /                   Renamed string functions to avoid name collision.
64 /---------------------------------------------------------------------------*/
65
66 #include "ff.h"                 /* FatFs configurations and declarations */
67 #include "diskio.h"             /* Declarations of low level disk I/O functions */
68
69
70 /*--------------------------------------------------------------------------
71
72    Module Private Definitions
73
74 ---------------------------------------------------------------------------*/
75
76 #if _EXCLUDE_LIB
77 static
78 void MemCpy (void* dst, const void* src, int cnt) {
79         char *d = (char*)dst;
80         const char *s = (const char *)src;
81         while (cnt--) *d++ = *s++;
82 }
83
84 static
85 void MemSet (void* dst, int val, int cnt) {
86         char *d = (char*)dst;
87         while (cnt--) *d++ = val;
88 }
89
90 static
91 int MemCmp (const void* dst, const void* src, int cnt) {
92         const char *d = (const char *)dst, *s = (const char *)src;
93         int r = 0;
94         while (cnt-- && !(r = *d++ - *s++));
95         return r;
96 }
97
98 static
99 char *StrChr (const char* str, int chr) {
100         while (*str && *str != chr) str++;
101         return (*str == chr) ? (char*)str : 0;
102 }
103
104 #else
105 #include "string.h"
106 #define MemCpy(x,y,z)   memcpy(x,y,z)
107 #define MemCmp(x,y,z)   memcmp(x,y,z)
108 #define MemSet(x,y,z)   memset(x,y,z)
109 #define StrChr(x,y)             strchr(x,y)
110
111 #endif
112
113 #ifndef NULL
114 #define NULL    0
115 #endif
116
117
118 #if _FS_REENTRANT
119 #if _USE_LFN == 1
120 #error Static LFN work area must not be used in re-entrant configuration.
121 #endif
122 #define ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; }
123 #define LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; }
124
125 #else
126 #define ENTER_FF(fs)
127 #define LEAVE_FF(fs, res)       return res
128
129 #endif
130
131
132 #define ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
133
134
135
136
137 /*--------------------------------------------------------------------------
138
139    Module Private Work Area
140
141 ---------------------------------------------------------------------------*/
142
143 static
144 FATFS *FatFs[_DRIVES];  /* Pointer to the file system objects (logical drives) */
145 static
146 WORD Fsid;                              /* File system mount ID */
147
148
149 #if _USE_LFN == 1       /* LFN with static LFN working buffer */
150 static
151 WORD LfnBuf[_MAX_LFN + 1];
152 #define NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR *lp = LfnBuf
153 #define INITBUF(dj,sp,lp)       dj.fn = sp; dj.lfn = lp
154
155 #elif _USE_LFN > 1      /* LFN with dynamic LFN working buffer */
156 #define NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
157 #define INITBUF(dj,sp,lp)       dj.fn = sp; dj.lfn = lp
158
159 #else                           /* No LFN */
160 #define NAMEBUF(sp,lp)  BYTE sp[12]
161 #define INITBUF(dj,sp,lp)       dj.fn = sp
162
163 #endif
164
165
166
167
168 /*--------------------------------------------------------------------------
169
170    Module Private Functions
171
172 ---------------------------------------------------------------------------*/
173
174 /*-----------------------------------------------------------------------*/
175 /* Request/Release grant to access the fs object (Platform dependent)    */
176 /*-----------------------------------------------------------------------*/
177 #if _FS_REENTRANT
178 static
179 BOOL lock_fs (
180         FATFS *fs               /* File system object */
181 )
182 {
183         return (WaitForSingleObject(fs->h_mutex, _TIMEOUT) == WAIT_OBJECT_0) ? TRUE : FALSE;
184 }
185
186
187 static
188 void unlock_fs (
189         FATFS *fs,              /* File system object */
190         FRESULT res             /* Result code to be returned */
191 )
192 {
193         if (res != FR_NOT_ENABLED &&
194                 res != FR_INVALID_DRIVE &&
195                 ree != FR_INVALID_OBJECT &&
196                 res != FR_TIMEOUT) {
197                 ReleaseMutex(fs->h_mutex);
198         }
199 }
200 #endif
201
202
203
204 /*-----------------------------------------------------------------------*/
205 /* Change window offset                                                  */
206 /*-----------------------------------------------------------------------*/
207
208 static
209 FRESULT move_window (
210         FATFS *fs,              /* File system object */
211         DWORD sector    /* Sector number to make apperance in the fs->win[] */
212 )                                       /* Move to zero only writes back dirty window */
213 {
214         DWORD wsect;
215
216
217         wsect = fs->winsect;
218         if (wsect != sector) {  /* Changed current window */
219 #if !_FS_READONLY
220                 if (fs->wflag) {        /* Write back dirty window if needed */
221                         if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
222                                 return FR_DISK_ERR;
223                         fs->wflag = 0;
224                         if (wsect < (fs->fatbase + fs->sects_fat)) {    /* In FAT area */
225                                 BYTE nf;
226                                 for (nf = fs->n_fats; nf >= 2; nf--) {  /* Refrect the change to FAT copy */
227                                         wsect += fs->sects_fat;
228                                         disk_write(fs->drive, fs->win, wsect, 1);
229                                 }
230                         }
231                 }
232 #endif
233                 if (sector) {
234                         if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK)
235                                 return FR_DISK_ERR;
236                         fs->winsect = sector;
237                 }
238         }
239
240         return FR_OK;
241 }
242
243
244
245
246 /*-----------------------------------------------------------------------*/
247 /* Clean-up cached data                                                  */
248 /*-----------------------------------------------------------------------*/
249 #if !_FS_READONLY
250 static
251 FRESULT sync (  /* FR_OK: successful, FR_DISK_ERR: failed */
252         FATFS *fs       /* File system object */
253 )
254 {
255         FRESULT res;
256
257
258         res = move_window(fs, 0);
259         if (res == FR_OK) {
260                 /* Update FSInfo sector if needed */
261                 if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
262                         fs->winsect = 0;
263                         MemSet(fs->win, 0, 512);
264                         ST_WORD(fs->win+BS_55AA, 0xAA55);
265                         ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
266                         ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
267                         ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
268                         ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
269                         disk_write(fs->drive, fs->win, fs->fsi_sector, 1);
270                         fs->fsi_flag = 0;
271                 }
272                 /* Make sure that no pending write process in the physical drive */
273                 if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK)
274                         res = FR_DISK_ERR;
275         }
276
277         return res;
278 }
279 #endif
280
281
282
283
284 /*-----------------------------------------------------------------------*/
285 /* Get a cluster status                                                  */
286 /*-----------------------------------------------------------------------*/
287
288 static
289 DWORD get_cluster (     /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */
290         FATFS *fs,              /* File system object */
291         DWORD clst              /* Cluster# to get the link information */
292 )
293 {
294         WORD wc, bc;
295         DWORD fsect;
296
297
298         if (clst < 2 || clst >= fs->max_clust)  /* Check cluster address range */
299                 return 1;
300
301         fsect = fs->fatbase;
302         switch (fs->fs_type) {
303         case FS_FAT12 :
304                 bc = (WORD)clst * 3 / 2;
305                 if (move_window(fs, fsect + (bc / SS(fs)))) break;
306                 wc = fs->win[bc & (SS(fs) - 1)]; bc++;
307                 if (move_window(fs, fsect + (bc / SS(fs)))) break;
308                 wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8;
309                 return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
310
311         case FS_FAT16 :
312                 if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break;
313                 return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]);
314
315         case FS_FAT32 :
316                 if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break;
317                 return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
318         }
319
320         return 0xFFFFFFFF;      /* An error occured at the disk I/O layer */
321 }
322
323
324
325
326 /*-----------------------------------------------------------------------*/
327 /* Change a cluster status                                               */
328 /*-----------------------------------------------------------------------*/
329 #if !_FS_READONLY
330 static
331 FRESULT put_cluster (
332         FATFS *fs,              /* File system object */
333         DWORD clst,             /* Cluster# to be changed (must be 2 to fs->max_clust-1) */
334         DWORD val               /* New value to mark the cluster */
335 )
336 {
337         WORD bc;
338         BYTE *p;
339         DWORD fsect;
340         FRESULT res;
341
342
343         if (clst < 2 || clst >= fs->max_clust) {        /* Check cluster address range */
344                 res = FR_INT_ERR;
345
346         } else {
347                 fsect = fs->fatbase;
348                 switch (fs->fs_type) {
349                 case FS_FAT12 :
350                         bc = (WORD)clst * 3 / 2;
351                         res = move_window(fs, fsect + (bc / SS(fs)));
352                         if (res != FR_OK) break;
353                         p = &fs->win[bc & (SS(fs) - 1)];
354                         *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
355                         bc++;
356                         fs->wflag = 1;
357                         res = move_window(fs, fsect + (bc / SS(fs)));
358                         if (res != FR_OK) break;
359                         p = &fs->win[bc & (SS(fs) - 1)];
360                         *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
361                         break;
362
363                 case FS_FAT16 :
364                         res = move_window(fs, fsect + (clst / (SS(fs) / 2)));
365                         if (res != FR_OK) break;
366                         ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val);
367                         break;
368
369                 case FS_FAT32 :
370                         res = move_window(fs, fsect + (clst / (SS(fs) / 4)));
371                         if (res != FR_OK) break;
372                         ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val);
373                         break;
374
375                 default :
376                         res = FR_INT_ERR;
377                 }
378                 fs->wflag = 1;
379         }
380
381         return res;
382 }
383 #endif /* !_FS_READONLY */
384
385
386
387
388 /*-----------------------------------------------------------------------*/
389 /* Remove a cluster chain                                                */
390 /*-----------------------------------------------------------------------*/
391 #if !_FS_READONLY
392 static
393 FRESULT remove_chain (
394         FATFS *fs,                      /* File system object */
395         DWORD clst                      /* Cluster# to remove chain from */
396 )
397 {
398         FRESULT res;
399         DWORD nxt;
400
401
402         if (clst < 2 || clst >= fs->max_clust) {        /* Check cluster address range */
403                 res = FR_INT_ERR;
404
405         } else {
406                 res = FR_OK;
407                 while (clst < fs->max_clust) {                  /* Not a last link? */
408                         nxt = get_cluster(fs, clst);            /* Get cluster status */
409                         if (nxt == 0) break;                            /* Empty cluster? */
410                         if (nxt == 1) { res = FR_INT_ERR; break; }      /* Internal error? */
411                         if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
412                         res = put_cluster(fs, clst, 0);         /* Mark the cluster "empty" */
413                         if (res != FR_OK) break;
414                         if (fs->free_clust != 0xFFFFFFFF) {     /* Update FSInfo */
415                                 fs->free_clust++;
416                                 fs->fsi_flag = 1;
417                         }
418                         clst = nxt;     /* Next cluster */
419                 }
420         }
421
422         return res;
423 }
424 #endif
425
426
427
428
429 /*-----------------------------------------------------------------------*/
430 /* Stretch or create a cluster chain                                     */
431 /*-----------------------------------------------------------------------*/
432 #if !_FS_READONLY
433 static
434 DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
435         FATFS *fs,                      /* File system object */
436         DWORD clst                      /* Cluster# to stretch. 0 means create a new chain. */
437 )
438 {
439         DWORD cs, ncl, scl, mcl;
440
441
442         mcl = fs->max_clust;
443         if (clst == 0) {                /* Create new chain */
444                 scl = fs->last_clust;                   /* Get suggested start point */
445                 if (scl == 0 || scl >= mcl) scl = 1;
446         }
447         else {                                  /* Stretch existing chain */
448                 cs = get_cluster(fs, clst);             /* Check the cluster status */
449                 if (cs < 2) return 1;                   /* It is an invalid cluster */
450                 if (cs < mcl) return cs;                /* It is already followed by next cluster */
451                 scl = clst;
452         }
453
454         ncl = scl;                              /* Start cluster */
455         for (;;) {
456                 ncl++;                                                  /* Next cluster */
457                 if (ncl >= mcl) {                               /* Wrap around */
458                         ncl = 2;
459                         if (ncl > scl) return 0;        /* No free custer */
460                 }
461                 cs = get_cluster(fs, ncl);              /* Get the cluster status */
462                 if (cs == 0) break;                             /* Found a free cluster */
463                 if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */
464                         return cs;
465                 if (ncl == scl) return 0;               /* No free custer */
466         }
467
468         if (put_cluster(fs, ncl, 0x0FFFFFFF))   /* Mark the new cluster "in use" */
469                 return 0xFFFFFFFF;
470         if (clst != 0) {                                                /* Link it to previous one if needed */
471                 if (put_cluster(fs, clst, ncl))
472                         return 0xFFFFFFFF;
473         }
474
475         fs->last_clust = ncl;                           /* Update FSINFO */
476         if (fs->free_clust != 0xFFFFFFFF) {
477                 fs->free_clust--;
478                 fs->fsi_flag = 1;
479         }
480
481         return ncl;             /* Return new cluster number */
482 }
483 #endif /* !_FS_READONLY */
484
485
486
487
488 /*-----------------------------------------------------------------------*/
489 /* Get sector# from cluster#                                             */
490 /*-----------------------------------------------------------------------*/
491
492 /*static*/
493 DWORD clust2sect (      /* !=0: sector number, 0: failed - invalid cluster# */
494         FATFS *fs,              /* File system object */
495         DWORD clst              /* Cluster# to be converted */
496 )
497 {
498         clst -= 2;
499         if (clst >= (fs->max_clust - 2)) return 0;              /* Invalid cluster# */
500         return clst * fs->csize + fs->database;
501 }
502
503 DWORD f_check_contig(FIL *fp)
504 {
505         DWORD nxt;
506         DWORD clust = fp->org_clust;
507         DWORD retval = 1;
508         while (clust >= 2 && clust < fp->fs->max_clust) {
509                 nxt = get_cluster(fp->fs, clust);
510                 if (nxt == 1) return retval;
511                 if (nxt != (clust + 1)) return retval;
512                 clust = nxt;
513                 retval++;
514         }
515         return retval;
516 }
517
518
519
520
521 /*-----------------------------------------------------------------------*/
522 /* Seek directory index                                                  */
523 /*-----------------------------------------------------------------------*/
524
525 static
526 FRESULT dir_seek (
527         DIR *dj,                /* Pointer to directory object */
528         WORD idx                /* Directory index number */
529 )
530 {
531         DWORD clst;
532         WORD ic;
533
534
535         dj->index = idx;
536         clst = dj->sclust;
537         if (clst == 1 || clst >= dj->fs->max_clust)     /* Check start cluster range */
538                 return FR_INT_ERR;
539
540         if (clst == 0) {        /* Static table */
541                 if (idx >= dj->fs->n_rootdir)           /* Index is out of range */
542                         return FR_INT_ERR;
543                 dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32);
544         }
545         else {                          /* Dynamic table */
546                 ic = SS(dj->fs) / 32 * dj->fs->csize;   /* Indexes per cluster */
547                 while (idx >= ic) {     /* Follow cluster chain */
548                         clst = get_cluster(dj->fs, clst);                       /* Get next cluster */
549                         if (clst == 0xFFFFFFFF) return FR_DISK_ERR;     /* Disk error */
550                         if (clst < 2 || clst >= dj->fs->max_clust)      /* Reached to end of table or int error */
551                                 return FR_INT_ERR;
552                         idx -= ic;
553                 }
554                 dj->clust = clst;
555                 dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);
556         }
557         dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;
558
559         return FR_OK;   /* Seek succeeded */
560 }
561
562
563
564
565 /*-----------------------------------------------------------------------*/
566 /* Move directory index next                                             */
567 /*-----------------------------------------------------------------------*/
568
569 static
570 FRESULT dir_next (      /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
571         DIR *dj,                /* Pointer to directory object */
572         BOOL streach    /* FALSE: Do not streach table, TRUE: Streach table if needed */
573 )
574 {
575         DWORD clst;
576         WORD i;
577
578
579         i = dj->index + 1;
580         if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
581                 return FR_NO_FILE;
582
583         if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */
584                 dj->sect++;                                     /* Next sector */
585
586                 if (dj->sclust == 0) {  /* Static table */
587                         if (i >= dj->fs->n_rootdir)     /* Report EOT when end of table */
588                                 return FR_NO_FILE;
589                 }
590                 else {                                  /* Dynamic table */
591                         if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) {     /* Cluster changed? */
592                                 clst = get_cluster(dj->fs, dj->clust);                  /* Get next cluster */
593                                 if (clst <= 1) return FR_INT_ERR;
594                                 if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
595                                 if (clst >= dj->fs->max_clust) {                                /* When it reached end of dinamic table */
596 #if !_FS_READONLY
597                                         BYTE c;
598                                         if (!streach) return FR_NO_FILE;                        /* When do not streach, report EOT */
599                                         clst = create_chain(dj->fs, dj->clust);         /* Streach cluster chain */
600                                         if (clst == 0) return FR_DENIED;                        /* No free cluster */
601                                         if (clst == 1) return FR_INT_ERR;
602                                         if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
603                                         /* Clean-up streached table */
604                                         if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */
605                                         MemSet(dj->fs->win, 0, SS(fs));                         /* Clear window buffer */
606                                         dj->fs->winsect = clust2sect(dj->fs, clst);     /* Cluster start sector */
607                                         for (c = 0; c < dj->fs->csize; c++) {           /* Fill the new cluster with 0 */
608                                                 dj->fs->wflag = 1;
609                                                 if (move_window(dj->fs, 0)) return FR_DISK_ERR;
610                                                 dj->fs->winsect++;
611                                         }
612                                         dj->fs->winsect -= c;                                           /* Rewind window address */
613 #else
614                                         return FR_NO_FILE;                      /* Report EOT */
615 #endif
616                                 }
617                                 dj->clust = clst;                               /* Initialize data for new cluster */
618                                 dj->sect = clust2sect(dj->fs, clst);
619                         }
620                 }
621         }
622
623         dj->index = i;
624         dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
625
626         return FR_OK;
627 }
628
629
630
631
632 /*-----------------------------------------------------------------------*/
633 /* Test/Pick/Fit an LFN segment from/to directory entry                  */
634 /*-----------------------------------------------------------------------*/
635 #if _USE_LFN
636 static
637 const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* Offset of LFN chars in the directory entry */
638
639
640 static
641 BOOL test_lfn (                 /* TRUE:Matched, FALSE:Not matched */
642         WCHAR *lfnbuf,          /* Pointer to the LFN to be compared */
643         BYTE *dir                       /* Pointer to the directory entry containing a part of LFN */
644 )
645 {
646         int i, s;
647         WCHAR wc1, wc2;
648
649
650         i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;  /* Offset in the LFN buffer */
651         s = 0;
652         do {
653                 if (i >= _MAX_LFN) return FALSE;        /* Out of buffer range? */
654                 wc1 = LD_WORD(dir+LfnOfs[s]);           /* Get both characters to compare */
655                 wc2 = lfnbuf[i++];
656                 if (IsLower(wc1)) wc1 -= 0x20;          /* Compare it (ignore case) */
657                 if (IsLower(wc2)) wc2 -= 0x20;
658                 if (wc1 != wc2) return FALSE;
659         } while (++s < 13 && wc1);                              /* Repeat until last char or a NUL char is processed */
660
661         return TRUE;                                                    /* The LFN entry matched */
662 }
663
664
665
666 static
667 BOOL pick_lfn (                 /* TRUE:Succeeded, FALSE:Buffer overflow */
668         WCHAR *lfnbuf,          /* Pointer to the Unicode-LFN buffer */
669         BYTE *dir                       /* Pointer to the directory entry */
670 )
671 {
672         int i, s;
673         WCHAR wchr;
674
675
676         i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;  /* Offset in the LFN buffer */
677         s = 0;
678         do {
679                 wchr = LD_WORD(dir+LfnOfs[s]);          /* Get an LFN char */
680                 if (!wchr) break;                                       /* End of LFN? */
681                 if (i >= _MAX_LFN) return FALSE;        /* Buffer overflow */
682                 lfnbuf[i++] = wchr;                                     /* Store it */
683         } while (++s < 13);                                             /* Repeat until last char is copied */
684         if (dir[LDIR_Ord] & 0x40) lfnbuf[i] = 0;        /* Put terminator if last LFN entry */
685
686         return TRUE;
687 }
688
689
690 #if !_FS_READONLY
691 static
692 void fit_lfn (
693         const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
694         BYTE *dir,                              /* Pointer to the directory entry */
695         BYTE ord,                               /* LFN order (1-20) */
696         BYTE sum                                /* SFN sum */
697 )
698 {
699         int i, s;
700         WCHAR wchr;
701
702
703         dir[LDIR_Chksum] = sum;                 /* Set check sum */
704         dir[LDIR_Attr] = AM_LFN;                /* Set attribute. LFN entry */
705         dir[LDIR_Type] = 0;
706         ST_WORD(dir+LDIR_FstClusLO, 0);
707
708         i = (ord - 1) * 13;                             /* Offset in the LFN buffer */
709         s = wchr = 0;
710         do {
711                 if (wchr != 0xFFFF) wchr = lfnbuf[i++]; /* Get an effective char */
712                 ST_WORD(dir+LfnOfs[s], wchr);   /* Put it */
713                 if (!wchr) wchr = 0xFFFF;       /* Padding chars following last char */
714         } while (++s < 13);
715         if (wchr == 0xFFFF || !lfnbuf[i]) ord |= 0x40;/* Bottom LFN part is the start of LFN sequence */
716         dir[LDIR_Ord] = ord;                    /* Set the LFN order */
717 }
718
719 #endif
720 #endif
721
722
723
724 /*-----------------------------------------------------------------------*/
725 /* Create numbered name                                                  */
726 /*-----------------------------------------------------------------------*/
727 #if _USE_LFN
728 void gen_numname (
729         BYTE *dst,                      /* Pointer to genartated SFN */
730         const BYTE *src,        /* Pointer to source SFN to be modified */
731         const WCHAR *lfn,       /* Pointer to LFN */
732         WORD num                        /* Sequense number */
733 )
734 {
735         char ns[8];
736         int i, j;
737
738
739         MemCpy(dst, src, 11);
740
741         if (num > 5) {  /* On many collisions, generate a hash number instead of sequencial number */
742                 do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn);
743         }
744
745         /* itoa */
746         i = 7;
747         do {
748                 ns[i--] = (num % 10) + '0';
749                 num /= 10;
750         } while (num);
751         ns[i] = '~';
752
753         /* Append the number */
754         for (j = 0; j < i && dst[j] != ' '; j++) {
755                 if (IsDBCS1(dst[j])) {
756                         if (j == i - 1) break;
757                         j++;
758                 }
759         }
760         do {
761                 dst[j++] = (i < 8) ? ns[i++] : ' ';
762         } while (j < 8);
763 }
764 #endif
765
766
767
768
769 /*-----------------------------------------------------------------------*/
770 /* Calculate sum of an SFN                                               */
771 /*-----------------------------------------------------------------------*/
772 #if _USE_LFN
773 static
774 BYTE sum_sfn (
775         const BYTE *dir         /* Ptr to directory entry */
776 )
777 {
778         BYTE sum = 0;
779         int n = 11;
780
781         do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
782         return sum;
783 }
784 #endif
785
786
787
788
789 /*-----------------------------------------------------------------------*/
790 /* Find an object in the directory                                       */
791 /*-----------------------------------------------------------------------*/
792
793 static
794 FRESULT dir_find (
795         DIR *dj                 /* Pointer to the directory object linked to the file name */
796 )
797 {
798         FRESULT res;
799         BYTE a, c, stat, ord, sum, *dir;
800
801         ord = sum = 0xFF; stat = *(dj->fn+11);
802         do {
803                 res = move_window(dj->fs, dj->sect);
804                 if (res != FR_OK) break;
805                 dir = dj->dir;                                  /* Ptr to the directory entry of current index */
806                 c = dir[DIR_Name];
807                 if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
808                 a = dir[DIR_Attr] & AM_MASK;
809 #if _USE_LFN    /* LFN configuration */
810                 if (c == 0xE5 || c == '.' || ((a & AM_VOL) && a != AM_LFN)) {   /* An entry without valid data */
811                         ord = 0xFF;
812                 } else {
813                         if (a == AM_LFN) {                      /* An LFN entry is found */
814                                 if (dj->lfn) {
815                                         if (c & 0x40) {         /* Is it start of LFN sequence? */
816                                                 sum = dir[LDIR_Chksum];
817                                                 c &= 0xBF; ord = c;             /* LFN start order */
818                                                 dj->lfn_idx = dj->index;
819                                         }
820                                         /* Check LFN validity. Compare LFN if it is out of 8.3 format */
821                                         ord = (c == ord && sum == dir[LDIR_Chksum] && (!(stat & 1) || test_lfn(dj->lfn, dir))) ? ord - 1 : 0xFF;
822                                 }
823                         } else {                                        /* An SFN entry is found */
824                                 if (ord || sum != sum_sfn(dir)) {       /* Did not LFN match? */
825                                         dj->lfn_idx = 0xFFFF;
826                                         ord = 0xFF;
827                                 }
828                                 if (stat & 1) {                 /* Match LFN if it is out of 8.3 format */
829                                         if (ord == 0) break;
830                                 } else {                                /* Match SFN if LFN is in 8.3 format */
831                                         if (!MemCmp(dir, dj->fn, 11)) break;
832                                 }
833                         }
834                 }
835 #else   /* Non LFN configuration */
836                 if (c != 0xE5 && c != '.' && !(a & AM_VOL) && !MemCmp(dir, dj->fn, 11)) /* Is it a valid entry? */
837                         break;
838 #endif
839                 res = dir_next(dj, FALSE);                              /* Next entry */
840         } while (res == FR_OK);
841
842         return res;
843 }
844
845
846
847
848 /*-----------------------------------------------------------------------*/
849 /* Read an object from the directory                                     */
850 /*-----------------------------------------------------------------------*/
851 #if _FS_MINIMIZE <= 2
852 static
853 FRESULT dir_read (
854         DIR *dj                 /* Pointer to the directory object to store read object name */
855 )
856 {
857         FRESULT res;
858         BYTE a, c, ord, sum, *dir;
859
860
861         ord = sum = 0xFF;
862         res = FR_NO_FILE;
863         while (dj->sect) {
864                 res = move_window(dj->fs, dj->sect);
865                 if (res != FR_OK) break;
866                 dir = dj->dir;                                  /* Ptr to the directory entry of current index */
867                 c = dir[DIR_Name];
868                 if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
869                 a = dir[DIR_Attr] & AM_MASK;
870 #if _USE_LFN    /* LFN configuration */
871                 if (c == 0xE5 || c == '.' || ((a & AM_VOL) && a != AM_LFN)) {   /* An entry without valid data */
872                         ord = 0xFF;
873                 } else {
874                         if (a == AM_LFN) {                      /* An LFN entry is found */
875                                 if (c & 0x40) {                 /* Is it start of LFN sequence? */
876                                         sum = dir[LDIR_Chksum];
877                                         c &= 0xBF; ord = c;
878                                         dj->lfn_idx = dj->index;
879                                 }
880                                 /* Check LFN validity and capture it */
881                                 ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
882                         } else {                                        /* An SFN entry is found */
883                                 if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN entry? */
884                                         dj->lfn_idx = 0xFFFF;           /* No LFN. */
885                                 break;
886                         }
887                 }
888 #else   /* Non LFN configuration */
889                 if (c != 0xE5 && c != '.' && !(a & AM_VOL))     /* Is it a valid entry? */
890                         break;
891 #endif
892                 res = dir_next(dj, FALSE);                              /* Next entry */
893                 if (res != FR_OK) break;
894         }
895
896         if (res != FR_OK) dj->sect = 0;
897
898         return res;
899 }
900 #endif
901
902
903
904 /*-----------------------------------------------------------------------*/
905 /* Register an object to the directory                                   */
906 /*-----------------------------------------------------------------------*/
907 #if !_FS_READONLY
908 static
909 FRESULT dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
910         DIR *dj                         /* Target directory with object name to be created */
911 )
912 {
913         FRESULT res;
914         BYTE c, *dir;
915
916 #if _USE_LFN    /* LFN configuration */
917         WORD n, ne, is;
918         BYTE sn[12], *fn, sum;
919         WCHAR *lfn;
920
921         fn = dj->fn; lfn = dj->lfn;
922         MemCpy(sn, fn, 12);
923         if (sn[11] & 1) {               /* When LFN is out of 8.3 format, generate a numbered name */
924                 fn[11] = 0; dj->lfn = NULL;                     /* Find only SFN */
925                 for (n = 1; n < 100; n++) {
926                         gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
927                         res = dir_seek(dj, 0);
928                         if (res != FR_OK) break;
929                         res = dir_find(dj);                             /* Check if the name collides with existing SFN */
930                         if (res != FR_OK) break;
931                 }
932                 if (n == 100) return FR_DENIED;         /* Abort if too many collisions */
933                 if (res != FR_NO_FILE) return res;      /* Abort if the result is other than 'not collided' */
934                 fn[11] = sn[11]; dj->lfn = lfn;
935         }
936         if (sn[11] & 2) {               /* When eliminate LFN, reserve only an SFN entry. */
937                 ne = 1;
938         } else {                                /* Otherwise reserve an SFN + LFN entries. */
939                 for (ne = 0; lfn[ne]; ne++) ;
940                 ne = (ne + 25) / 13;
941         }
942
943         /* Reserve contiguous entries */
944         res = dir_seek(dj, 0);
945         if (res != FR_OK) return res;
946         n = is = 0;
947         do {
948                 res = move_window(dj->fs, dj->sect);
949                 if (res != FR_OK) break;
950                 c = *dj->dir;   /* Check the entry status */
951                 if (c == 0xE5 || c == 0) {      /* Is it a blank entry? */
952                         if (n == 0) is = dj->index;     /* First index of the contigulus entry */
953                         if (++n == ne) break;   /* A contiguous entry that requiered count is found */
954                 } else {
955                         n = 0;                                  /* Not a blank entry. Restart to search */
956                 }
957                 res = dir_next(dj, TRUE);       /* Next entry with table streach */
958         } while (res == FR_OK);
959
960         if (res == FR_OK && ne > 1) {   /* Initialize LFN entry if needed */
961                 res = dir_seek(dj, is);
962                 if (res == FR_OK) {
963                         sum = sum_sfn(dj->fn);  /* Sum of the SFN tied to the LFN */
964                         ne--;
965                         do {                                    /* Store LFN entries in bottom first */
966                                 res = move_window(dj->fs, dj->sect);
967                                 if (res != FR_OK) break;
968                                 fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
969                                 dj->fs->wflag = 1;
970                                 res = dir_next(dj, FALSE);      /* Next entry */
971                         } while (res == FR_OK && --ne);
972                 }
973         }
974
975 #else   /* Non LFN configuration */
976         res = dir_seek(dj, 0);
977         if (res == FR_OK) {
978                 do {    /* Find a blank entry for the SFN */
979                         res = move_window(dj->fs, dj->sect);
980                         if (res != FR_OK) break;
981                         c = *dj->dir;
982                         if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */
983                         res = dir_next(dj, TRUE);               /* Next entry with table streach */
984                 } while (res == FR_OK);
985         }
986 #endif
987
988         if (res == FR_OK) {             /* Initialize the SFN entry */
989                 res = move_window(dj->fs, dj->sect);
990                 if (res == FR_OK) {
991                         dir = dj->dir;
992                         MemSet(dir, 0, 32);                     /* Clean the entry */
993                         MemCpy(dir, dj->fn, 11);        /* Put SFN */
994                         dir[DIR_NTres] = *(dj->fn+11) & 0x18;   /* Put NT flag */
995                         dj->fs->wflag = 1;
996                 }
997         }
998
999         return res;
1000 }
1001 #endif /* !_FS_READONLY */
1002
1003
1004
1005
1006 /*-----------------------------------------------------------------------*/
1007 /* Remove an object from the directory                                   */
1008 /*-----------------------------------------------------------------------*/
1009 #if !_FS_READONLY && !_FS_MINIMIZE
1010 static
1011 FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
1012         DIR *dj                         /* Directory object pointing the entry to be removed */
1013 )
1014 {
1015         FRESULT res;
1016
1017 #if _USE_LFN    /* LFN configuration */
1018         WORD i;
1019
1020         i = dj->index;  /* SFN index */
1021         res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));  /* Goto the SFN or top of the LFN entries */
1022         if (res == FR_OK) {
1023                 do {
1024                         res = move_window(dj->fs, dj->sect);
1025                         if (res != FR_OK) break;
1026                         *dj->dir = 0xE5;                                /* Mark the entry "deleted" */
1027                         dj->fs->wflag = 1;
1028                         if (dj->index >= i) break;              /* When SFN is deleted, all entries of the object is deleted. */
1029                         res = dir_next(dj, FALSE);              /* Next entry */
1030                 } while (res == FR_OK);
1031                 if (res == FR_NO_FILE) res = FR_INT_ERR;
1032         }
1033
1034 #else                   /* Non LFN configuration */
1035         res = dir_seek(dj, dj->index);
1036         if (res == FR_OK) {
1037                 res = move_window(dj->fs, dj->sect);
1038                 if (res == FR_OK) {
1039                         *dj->dir = 0xE5;                                /* Mark the entry "deleted" */
1040                         dj->fs->wflag = 1;
1041                 }
1042         }
1043 #endif
1044
1045         return res;
1046 }
1047 #endif /* !_FS_READONLY */
1048
1049
1050
1051
1052 /*-----------------------------------------------------------------------*/
1053 /* Pick a segment and create the object name in directory form           */
1054 /*-----------------------------------------------------------------------*/
1055
1056 static
1057 FRESULT create_name (
1058         DIR *dj,                        /* Pointer to the directory object */
1059         const char **path       /* Pointer to pointer to the segment in the path string */
1060 )
1061 {
1062 #if _USE_LFN
1063         BYTE c, b, cf, *sfn;
1064         WCHAR w, *lfn;
1065         int i, ni, si, di;
1066         const char *p;
1067
1068         /* Create LFN in Unicode */
1069         si = di = 0;
1070         p = *path;
1071         lfn = dj->lfn;
1072         for (;;) {
1073                 w = (BYTE)p[si++];                              /* Get a character */
1074                 if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
1075                 if (IsDBCS1(w)) {                               /* If it is DBC 1st byte */
1076                         c = p[si++];                            /* Get 2nd byte */
1077                         if (!IsDBCS2(c))                        /* Reject invalid DBC */
1078                                 return FR_INVALID_NAME;
1079                         w = (w << 8) + c;
1080                 } else {
1081                         if (StrChr("\"*:<>?|\x7F", w))  /* Reject unallowable chars for LFN */
1082                                 return FR_INVALID_NAME;
1083                 }
1084                 w = ff_convert(w, 1);                   /* Convert OEM to Unicode, store it */
1085                 if (!w || di >= _MAX_LFN)               /* Reject invalid code or too long name */
1086                         return FR_INVALID_NAME;
1087                 lfn[di++] = w;
1088         }
1089         *path = &p[si];                                         /* Rerurn pointer to the next segment */
1090         cf = (w < ' ') ? 4 : 0;                         /* Set last segment flag if end of path */
1091
1092         while (di) {                                            /* Strip trailing spaces and dots */
1093                 w = lfn[di - 1];
1094                 if (w != ' ' && w != '.') break;
1095                 di--;
1096         }
1097         if (!di) return FR_INVALID_NAME;        /* Reject null string */
1098
1099         lfn[di] = 0;                                            /* LFN is created */
1100
1101         /* Create SFN in directory form */
1102         sfn = dj->fn;
1103         MemSet(sfn, ' ', 11);
1104         for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;  /* Strip leading spaces and dots */
1105         if (si) cf |= 1;
1106         while (di && lfn[di - 1] != '.') di--;  /* Find extension (di<=si: no extension) */
1107
1108         b = i = 0; ni = 8;
1109         for (;;) {
1110                 w = lfn[si++];                                  /* Get an LFN char */
1111                 if (w == 0) break;                              /* Break when enf of the LFN */
1112                 if (w == ' ' || (w == '.' && si != di)) {       /* Remove spaces and dots */
1113                         cf |= 1; continue;
1114                 }
1115                 if (i >= ni || si == di) {              /* Here is extension or end of SFN */
1116                         if (ni == 11) {                         /* Extension is longer than 3 bytes */
1117                                 cf |= 1; break;
1118                         }
1119                         if (si != di) cf |= 1;          /* File name is longer than 8 bytes */
1120                         if (si > di) break;                     /* No extension */
1121                         si = di; i = 8; ni = 11;        /* Enter extension section */
1122                         b <<= 2; continue;
1123                 }
1124                 w = ff_convert(w, 0);                   /* Unicode -> OEM code */
1125                 if (w >= 0x80) cf |= 0x20;              /* If there is any extended char, force create an LFN */
1126                 if (w >= 0x100) {                               /* Double byte char */
1127                         if (i >= ni - 1) {
1128                                 cf |= 1; i = ni; continue;
1129                         }
1130                         sfn[i++] = (BYTE)(w >> 8);
1131                 } else {                                                /* Single byte char */
1132                         if (StrChr("+,;[=]", w)) {      /* Replace unallowable chars for SFN */
1133                                 w = '_'; cf |= 1;
1134                         } else {
1135                                 if (IsUpper(w)) {               /* Large capital */
1136                                         b |= 2;
1137                                 } else {
1138                                         if (IsLower(w)) {       /* Small capital */
1139                                                 b |= 1; w -= 0x20;
1140                                         }
1141                                 }
1142                         }
1143                 }
1144                 sfn[i++] = (BYTE)w;
1145         }
1146         if (sfn[0] == 0xE5) sfn[0] = 0x05;      /* When first char collides with 0xE5, replace it with 0x05 */
1147
1148         if (ni == 8) b <<= 2;
1149         if ((cf & 0x21) == 0) { /* When LFN is in 8.3 format without extended char, NT flags are created */
1150                 if ((b & 0x03) == 0x01) cf |= 0x10;     /* NT flag (Extension has only small capital) */
1151                 if ((b & 0x0C) == 0x04) cf |= 0x08;     /* NT flag (Filename has only small capital) */
1152                 if ((b & 0x0C) != 0x0C && (b & 0x03) != 0x03) cf |= 2;  /* Eliminate LFN when non composite capitals */
1153         }
1154
1155         sfn[11] = cf;           /* SFN is created */
1156
1157 #else
1158         BYTE c, d, b, *sfn;
1159         int ni, si, i;
1160         const char *p;
1161
1162         /* Create file name in directory form */
1163         sfn = dj->fn;
1164         MemSet(sfn, ' ', 11);
1165         si = i = b = 0; ni = 8;
1166         p = *path;
1167         for (;;) {
1168                 c = p[si++];
1169                 if (c < ' ' || c == '/' || c == '\\') break;    /* Break on end of segment */
1170                 if (c == '.' || i >= ni) {
1171                         if (ni != 8 || c != '.') return FR_INVALID_NAME;
1172                         i = 8; ni = 11;
1173                         b <<= 2; continue;
1174                 }
1175                 if (c >= 0x80) b |= 3;                  /* If there is any extended char, eliminate NT flag */
1176                 if (IsDBCS1(c)) {                               /* If it is DBC 1st byte */
1177                         d = p[si++];                            /* Get 2nd byte */
1178                         if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */
1179                                 return FR_INVALID_NAME;
1180                         sfn[i++] = c;
1181                         sfn[i++] = d;
1182                 } else {
1183                         if (StrChr(" +,;[=]\"*:<>?|\x7F", c))   /* Reject unallowable chrs for SFN */
1184                                 return FR_INVALID_NAME;
1185                         if (IsUpper(c)) {
1186                                 b |= 2;
1187                         } else {
1188                                 if (IsLower(c)) {
1189                                         b |= 1; c -= 0x20;
1190                                 }
1191                         }
1192                         sfn[i++] = c;
1193                 }
1194         }
1195         *path = &p[si];                                         /* Rerurn pointer to the next segment */
1196         c = (c < ' ') ? 4 : 0;                          /* Set last segment flag if end of path */
1197
1198         if (!i) return FR_INVALID_NAME;         /* Reject null string */
1199         if (sfn[0] == 0xE5) sfn[0] = 0x05;      /* When first char collides with 0xE5, replace it with 0x05 */
1200
1201         if (ni == 8) b <<= 2;
1202         if ((b & 0x03) == 0x01) c |= 0x10;      /* NT flag (Extension has only small capital) */
1203         if ((b & 0x0C) == 0x04) c |= 0x08;      /* NT flag (Filename has only small capital) */
1204
1205         sfn[11] = c;            /* Store NT flag, File name is created */
1206 #endif
1207
1208         return FR_OK;
1209 }
1210
1211
1212
1213
1214 /*-----------------------------------------------------------------------*/
1215 /* Get file information from directory entry                             */
1216 /*-----------------------------------------------------------------------*/
1217 #if _FS_MINIMIZE <= 1
1218 static
1219 void get_fileinfo (             /* No return code */
1220         DIR *dj,                        /* Pointer to the directory object */
1221         FILINFO *fno            /* Pointer to store the file information */
1222 )
1223 {
1224         int i;
1225         BYTE c, nt, *dir;
1226         char *p;
1227
1228
1229         p = fno->fname;
1230         if (dj->sect) {
1231                 dir = dj->dir;
1232                 nt = dir[DIR_NTres];            /* NT flag */
1233                 for (i = 0; i < 8; i++) {       /* Copy file name body */
1234                         c = dir[i];
1235                         if (c == ' ') break;
1236                         if (c == 0x05) c = 0xE5;
1237                         if ((nt & 0x08) && IsUpper(c)) c += 0x20;
1238                         *p++ = c;
1239                 }
1240                 if (dir[8] != ' ') {            /* Copy file name extension */
1241                         *p++ = '.';
1242                         for (i = 8; i < 11; i++) {
1243                                 c = dir[i];
1244                                 if (c == ' ') break;
1245                                 if ((nt & 0x10) && IsUpper(c)) c += 0x20;
1246                                 *p++ = c;
1247                         }
1248                 }
1249                 fno->fattrib = dir[DIR_Attr];                           /* Attribute */
1250                 fno->fsize = LD_DWORD(dir+DIR_FileSize);        /* Size */
1251                 fno->fdate = LD_WORD(dir+DIR_WrtDate);          /* Date */
1252                 fno->ftime = LD_WORD(dir+DIR_WrtTime);          /* Time */
1253         }
1254         *p = 0;
1255
1256 #if _USE_LFN
1257         p = fno->lfname;
1258         if (p) {
1259                 WCHAR wchr, *lfn;
1260
1261                 i = 0;
1262                 if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
1263                         lfn = dj->lfn;
1264                         while ((wchr = *lfn++) != 0) {          /* Get an LFN char */
1265                                 wchr = ff_convert(wchr, 0);             /* Unicode -> OEM code */
1266                                 if (!wchr) { i = 0; break; }    /* Conversion error, no LFN */
1267                                 if (_DF1S && wchr >= 0x100)             /* Put 1st byte if it is a DBC */
1268                                         p[i++] = (char)(wchr >> 8);
1269                                 p[i++] = (char)wchr;
1270                                 if (i >= fno->lfsize) { i = 0; break; } /* Buffer overrun, no LFN */
1271                         }
1272                 }
1273                 p[i] = 0;       /* Terminator */
1274         }
1275 #endif
1276 }
1277 #endif /* _FS_MINIMIZE <= 1 */
1278
1279
1280
1281
1282 /*-----------------------------------------------------------------------*/
1283 /* Follow a file path                                                    */
1284 /*-----------------------------------------------------------------------*/
1285
1286 static
1287 FRESULT follow_path (   /* FR_OK(0): successful, !=0: error code */
1288         DIR *dj,                        /* Directory object to return last directory and found object */
1289         const char *path        /* Full-path string to find a file or directory */
1290 )
1291 {
1292         FRESULT res;
1293         BYTE *dir, stat;
1294
1295
1296         if (*path == '/' || *path == '\\' ) path++;     /* Strip heading separator */
1297
1298         dj->sclust =                                            /* Set start directory (root dir) */
1299                 (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0;
1300
1301         if ((BYTE)*path < ' ') {                        /* Null path means the root directory */
1302                 res = dir_seek(dj, 0);
1303                 dj->dir = NULL;
1304
1305         } else {                                                        /* Follow path */
1306                 for (;;) {
1307                         res = dir_seek(dj, 0);                  /* Rewind directory object */
1308                         if (res != FR_OK) break;
1309                         res = create_name(dj, &path);   /* Get a segment */
1310                         if (res != FR_OK) break;
1311                         res = dir_find(dj);                             /* Find it */
1312                         stat = *(dj->fn+11);
1313                         if (res != FR_OK) {                             /* Could not find the object */
1314                                 if (res == FR_NO_FILE && !(stat & 4))
1315                                         res = FR_NO_PATH;
1316                                 break;
1317                         }
1318                         if (stat & 4) break;                    /* Last segment match. Function completed. */
1319                         dir = dj->dir;                                  /* There is next segment. Follow the sub directory */
1320                         if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
1321                                 res = FR_NO_PATH; break;
1322                         }
1323                         dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
1324                 }
1325         }
1326
1327         return res;
1328 }
1329
1330
1331
1332
1333 /*-----------------------------------------------------------------------*/
1334 /* Load boot record and check if it is an FAT boot record                */
1335 /*-----------------------------------------------------------------------*/
1336
1337 static
1338 BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
1339         FATFS *fs,      /* File system object */
1340         DWORD sect      /* Sector# (lba) to check if it is an FAT boot record or not */
1341 )
1342 {
1343         if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK)   /* Load boot record */
1344                 return 3;
1345         if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)                               /* Check record signature (always placed at offset 510 even if the sector size is >512) */
1346                 return 2;
1347
1348         if (!MemCmp(&fs->win[BS_FilSysType], "FAT", 3))         /* Check FAT signature */
1349                 return 0;
1350         if (!MemCmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80))
1351                 return 0;
1352
1353         return 1;
1354 }
1355
1356
1357
1358
1359 /*-----------------------------------------------------------------------*/
1360 /* Make sure that the file system is valid                               */
1361 /*-----------------------------------------------------------------------*/
1362
1363 static
1364 FRESULT auto_mount (    /* FR_OK(0): successful, !=0: any error occured */
1365         const char **path,      /* Pointer to pointer to the path name (drive number) */
1366         FATFS **rfs,            /* Pointer to pointer to the found file system object */
1367         BYTE chk_wp                     /* !=0: Check media write protection for write access */
1368 )
1369 {
1370         FRESULT res;
1371         BYTE drv, fmt, *tbl;
1372         DSTATUS stat;
1373         DWORD bsect, fsize, tsect, mclst;
1374         const char *p = *path;
1375         FATFS *fs;
1376
1377
1378         /* Get drive number from the path name */
1379         drv = p[0] - '0';                       /* Is there a drive number? */
1380         if (drv <= 9 && p[1] == ':') {
1381                 p += 2;                                 /* Found a drive number, get and strip it */
1382                 *path = p;                              /* Return pointer to the path name */
1383         } else {
1384                 drv = 0;                                /* No drive number is given, use drive number 0 as default */
1385         }
1386
1387         /* Check if the drive number is valid or not */
1388         if (drv >= _DRIVES) return FR_INVALID_DRIVE;    /* Is the drive number valid? */
1389         *rfs = fs = FatFs[drv];                                 /* Returen pointer to the corresponding file system object */
1390         if (!fs) return FR_NOT_ENABLED;                 /* Is the file system object registered? */
1391
1392         ENTER_FF(fs);                           /* Lock file system */
1393
1394         if (fs->fs_type) {                                              /* If the logical drive has been mounted */
1395                 stat = disk_status(fs->drive);
1396                 if (!(stat & STA_NOINIT)) {                     /* and physical drive is kept initialized (has not been changed), */
1397 #if !_FS_READONLY
1398                         if (chk_wp && (stat & STA_PROTECT))     /* Check write protection if needed */
1399                                 return FR_WRITE_PROTECTED;
1400 #endif
1401                         return FR_OK;                                   /* The file system object is valid */
1402                 }
1403         }
1404
1405         /* The logical drive must be re-mounted. Following code attempts to mount the logical drive */
1406
1407         fs->fs_type = 0;                                        /* Clear the file system object */
1408         fs->drive = LD2PD(drv);                         /* Bind the logical drive and a physical drive */
1409         stat = disk_initialize(fs->drive);      /* Initialize low level disk I/O layer */
1410         if (stat & STA_NOINIT)                          /* Check if the drive is ready */
1411                 return FR_NOT_READY;
1412 #if S_MAX_SIZ > 512                                             /* Get disk sector size if needed */
1413         if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > S_MAX_SIZ)
1414                 return FR_NO_FILESYSTEM;
1415 #endif
1416 #if !_FS_READONLY
1417         if (chk_wp && (stat & STA_PROTECT))     /* Check write protection if needed */
1418                 return FR_WRITE_PROTECTED;
1419 #endif
1420         /* Search FAT partition on the drive */
1421         fmt = check_fs(fs, bsect = 0);          /* Check sector 0 as an SFD format */
1422         if (fmt == 1) {                                         /* Not an FAT boot record, it may be patitioned */
1423                 /* Check a partition listed in top of the partition table */
1424                 tbl = &fs->win[MBR_Table + LD2PT(drv) * 16];    /* Partition table */
1425                 if (tbl[4]) {                                                                   /* Is the partition existing? */
1426                         bsect = LD_DWORD(&tbl[8]);                                      /* Partition offset in LBA */
1427                         fmt = check_fs(fs, bsect);                                      /* Check the partition */
1428                 }
1429         }
1430         if (fmt == 3) return FR_DISK_ERR;
1431         if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))   /* No valid FAT patition is found */
1432                 return FR_NO_FILESYSTEM;
1433
1434         /* Initialize the file system object */
1435         fsize = LD_WORD(fs->win+BPB_FATSz16);                           /* Number of sectors per FAT */
1436         if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32);
1437         fs->sects_fat = fsize;
1438         fs->n_fats = fs->win[BPB_NumFATs];                                      /* Number of FAT copies */
1439         fsize *= fs->n_fats;                                                            /* (Number of sectors in FAT area) */
1440         fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */
1441         fs->csize = fs->win[BPB_SecPerClus];                            /* Number of sectors per cluster */
1442         fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);        /* Nmuber of root directory entries */
1443         tsect = LD_WORD(fs->win+BPB_TotSec16);                          /* Number of sectors on the file system */
1444         if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
1445         fs->max_clust = mclst = (tsect                                          /* Last cluster# + 1 */
1446                 - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32)
1447                 ) / fs->csize + 2;
1448
1449         fmt = FS_FAT12;                                                                         /* Determine the FAT sub type */
1450         if (mclst >= 0xFF7) fmt = FS_FAT16;                             /* Number of clusters >= 0xFF5 */
1451         if (mclst >= 0xFFF7) fmt = FS_FAT32;                    /* Number of clusters >= 0xFFF5 */
1452
1453         if (fmt == FS_FAT32)
1454                 fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);   /* Root directory start cluster */
1455         else
1456                 fs->dirbase = fs->fatbase + fsize;                              /* Root directory start sector (lba) */
1457         fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32);       /* Data start sector (lba) */
1458
1459 #if !_FS_READONLY
1460         /* Initialize allocation information */
1461         fs->free_clust = 0xFFFFFFFF;
1462         fs->wflag = 0;
1463         /* Get fsinfo if needed */
1464         if (fmt == FS_FAT32) {
1465                 fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
1466                 fs->fsi_flag = 0;
1467                 if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&
1468                         LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
1469                         LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
1470                         LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
1471                         fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
1472                         fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
1473                 }
1474         }
1475 #endif
1476         fs->winsect = 0;
1477         fs->fs_type = fmt;                      /* FAT syb-type */
1478         fs->id = ++Fsid;                        /* File system mount ID */
1479         res = FR_OK;
1480
1481         return res;
1482 }
1483
1484
1485
1486
1487 /*-----------------------------------------------------------------------*/
1488 /* Check if the file/dir object is valid or not                          */
1489 /*-----------------------------------------------------------------------*/
1490
1491 static
1492 FRESULT validate (      /* FR_OK(0): The object is valid, !=0: Invalid */
1493         FATFS *fs,              /* Pointer to the file system object */
1494         WORD id                 /* Member id of the target object to be checked */
1495 )
1496 {
1497         if (!fs || !fs->fs_type || fs->id != id)
1498                 return FR_INVALID_OBJECT;
1499
1500         ENTER_FF(fs);           /* Lock file system */
1501
1502         if (disk_status(fs->drive) & STA_NOINIT)
1503                 return FR_NOT_READY;
1504
1505         return FR_OK;
1506 }
1507
1508
1509
1510
1511 /*--------------------------------------------------------------------------
1512
1513    Public Functions
1514
1515 --------------------------------------------------------------------------*/
1516
1517
1518
1519 /*-----------------------------------------------------------------------*/
1520 /* Mount/Unmount a Locical Drive                                         */
1521 /*-----------------------------------------------------------------------*/
1522
1523 FRESULT f_mount (
1524         BYTE drv,               /* Logical drive number to be mounted/unmounted */
1525         FATFS *fs               /* Pointer to new file system object (NULL for unmount)*/
1526 )
1527 {
1528         FATFS *rfs;
1529
1530
1531         if (drv >= _DRIVES)
1532                 return FR_INVALID_DRIVE;
1533
1534         rfs = FatFs[drv];
1535
1536         if (rfs) {
1537 #if _FS_REENTRANT                                       /* Discard mutex of the current fs. (Platform dependent) */
1538                 CloseHandle(rfs->h_mutex);      /* Discard mutex */
1539 #endif
1540                 rfs->fs_type = 0;                       /* Clear old fs object */
1541         }
1542
1543         if (fs) {
1544                 fs->fs_type = 0;        /* Clear new fs object */
1545 #if _FS_REENTRANT                               /* Create mutex for the new fs. (Platform dependent) */
1546                 fs->h_mutex = CreateMutex(NULL, FALSE, NULL);
1547 #endif
1548         }
1549         FatFs[drv] = fs;                /* Register new fs object */
1550
1551         return FR_OK;
1552 }
1553
1554
1555
1556
1557 /*-----------------------------------------------------------------------*/
1558 /* Open or Create a File                                                 */
1559 /*-----------------------------------------------------------------------*/
1560
1561 FRESULT f_open (
1562         FIL *fp,                        /* Pointer to the blank file object */
1563         const char *path,       /* Pointer to the file name */
1564         BYTE mode                       /* Access mode and file open mode flags */
1565 )
1566 {
1567         FRESULT res;
1568         DIR dj;
1569         NAMEBUF(sfn, lfn);
1570         BYTE *dir;
1571
1572
1573         fp->fs = NULL;          /* Clear file object */
1574 #if !_FS_READONLY
1575         mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);
1576         res = auto_mount(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));
1577 #else
1578         mode &= FA_READ;
1579         res = auto_mount(&path, &dj.fs, 0);
1580 #endif
1581         if (res != FR_OK) LEAVE_FF(dj.fs, res);
1582         INITBUF(dj, sfn, lfn);
1583         res = follow_path(&dj, path);   /* Follow the file path */
1584
1585 #if !_FS_READONLY
1586         /* Create or Open a file */
1587         if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
1588                 DWORD ps, cl;
1589
1590                 if (res != FR_OK) {             /* No file, create new */
1591                         if (res == FR_NO_FILE)
1592                                 res = dir_register(&dj);
1593                         if (res != FR_OK) LEAVE_FF(dj.fs, res);
1594                         mode |= FA_CREATE_ALWAYS;
1595                         dir = dj.dir;
1596                 }
1597                 else {                                  /* Any object is already existing */
1598                         if (mode & FA_CREATE_NEW)                       /* Cannot create new */
1599                                 LEAVE_FF(dj.fs, FR_EXIST);
1600                         dir = dj.dir;
1601                         if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR)))        /* Cannot overwrite it (R/O or DIR) */
1602                                 LEAVE_FF(dj.fs, FR_DENIED);
1603                         if (mode & FA_CREATE_ALWAYS) {          /* Resize it to zero if needed */
1604                                 cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);    /* Get start cluster */
1605                                 ST_WORD(dir+DIR_FstClusHI, 0);  /* cluster = 0 */
1606                                 ST_WORD(dir+DIR_FstClusLO, 0);
1607                                 ST_DWORD(dir+DIR_FileSize, 0);  /* size = 0 */
1608                                 dj.fs->wflag = 1;
1609                                 ps = dj.fs->winsect;                    /* Remove the cluster chain */
1610                                 if (cl) {
1611                                         res = remove_chain(dj.fs, cl);
1612                                         if (res) LEAVE_FF(dj.fs, res);
1613                                         dj.fs->last_clust = cl - 1;     /* Reuse the cluster hole */
1614                                 }
1615                                 res = move_window(dj.fs, ps);
1616                                 if (res != FR_OK) LEAVE_FF(dj.fs, res);
1617                         }
1618                 }
1619                 if (mode & FA_CREATE_ALWAYS) {
1620                         dir[DIR_Attr] = 0;                                      /* Reset attribute */
1621                         ps = get_fattime();
1622                         ST_DWORD(dir+DIR_CrtTime, ps);          /* Created time */
1623                         dj.fs->wflag = 1;
1624                         mode |= FA__WRITTEN;                            /* Set file changed flag */
1625                 }
1626         }
1627         /* Open an existing file */
1628         else {
1629 #endif /* !_FS_READONLY */
1630                 if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
1631                 dir = dj.dir;
1632                 if (!dir || (dir[DIR_Attr] & AM_DIR))   /* It is a directory */
1633                         LEAVE_FF(dj.fs, FR_NO_FILE);
1634 #if !_FS_READONLY
1635                 if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
1636                         LEAVE_FF(dj.fs, FR_DENIED);
1637         }
1638         fp->dir_sect = dj.fs->winsect;          /* Pointer to the directory entry */
1639         fp->dir_ptr = dj.dir;
1640 #endif
1641         fp->flag = mode;                                        /* File access mode */
1642         fp->org_clust =                                         /* File start cluster */
1643                 ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
1644         fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
1645         fp->fptr = 0; fp->csect = 255;          /* File pointer */
1646         fp->dsect = 0;
1647         fp->fs = dj.fs; fp->id = dj.fs->id;     /* Owner file system object of the file */
1648
1649         LEAVE_FF(dj.fs, FR_OK);
1650 }
1651
1652
1653
1654
1655 /*-----------------------------------------------------------------------*/
1656 /* Read File                                                             */
1657 /*-----------------------------------------------------------------------*/
1658
1659 FRESULT f_read (
1660         FIL *fp,                /* Pointer to the file object */
1661         void *buff,             /* Pointer to data buffer */
1662         UINT btr,               /* Number of bytes to read */
1663         UINT *br                /* Pointer to number of bytes read */
1664 )
1665 {
1666         FRESULT res;
1667         DWORD clst, sect, remain;
1668         UINT rcnt, cc;
1669         BYTE *rbuff = buff;
1670
1671
1672         *br = 0;
1673
1674         res = validate(fp->fs, fp->id);                                 /* Check validity of the object */
1675         if (res != FR_OK) LEAVE_FF(fp->fs, res);
1676         if (fp->flag & FA__ERROR)                                               /* Check abort flag */
1677                 LEAVE_FF(fp->fs, FR_INT_ERR);
1678         if (!(fp->flag & FA_READ))                                              /* Check access mode */
1679                 LEAVE_FF(fp->fs, FR_DENIED);
1680         remain = fp->fsize - fp->fptr;
1681         if (btr > remain) btr = (UINT)remain;                   /* Truncate btr by remaining bytes */
1682
1683         for ( ;  btr;                                                                   /* Repeat until all data transferred */
1684                 rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
1685                 if ((fp->fptr % SS(fp->fs)) == 0) {                     /* On the sector boundary? */
1686                         if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
1687                                 clst = (fp->fptr == 0) ?                        /* On the top of the file? */
1688                                         fp->org_clust : get_cluster(fp->fs, fp->curr_clust);
1689                                 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
1690                                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
1691                                 fp->curr_clust = clst;                          /* Update current cluster */
1692                                 fp->csect = 0;                                          /* Reset sector offset in the cluster */
1693                         }
1694                         sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current sector */
1695                         if (!sect) ABORT(fp->fs, FR_INT_ERR);
1696                         sect += fp->csect;
1697                         cc = btr / SS(fp->fs);                                  /* When remaining bytes >= sector size, */
1698                         if (cc) {                                                               /* Read maximum contiguous sectors directly */
1699                                 if (fp->csect + cc > fp->fs->csize)     /* Clip at cluster boundary */
1700                                         cc = fp->fs->csize - fp->csect;
1701                                 if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK)
1702                                         ABORT(fp->fs, FR_DISK_ERR);
1703                                 fp->csect += (BYTE)cc;                          /* Next sector address in the cluster */
1704                                 rcnt = SS(fp->fs) * cc;                         /* Number of bytes transferred */
1705                                 continue;
1706                         }
1707 #if !_FS_TINY
1708 #if !_FS_READONLY
1709                         if (fp->flag & FA__DIRTY) {                     /* Write sector I/O buffer if needed */
1710                                 if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
1711                                         ABORT(fp->fs, FR_DISK_ERR);
1712                                 fp->flag &= (BYTE)~FA__DIRTY;
1713                         }
1714 #endif
1715                         if (fp->dsect != sect) {                        /* Fill sector buffer with file data */
1716                                 if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
1717                                         ABORT(fp->fs, FR_DISK_ERR);
1718                         }
1719 #endif
1720                         fp->dsect = sect;
1721                         fp->csect++;                                                    /* Next sector address in the cluster */
1722                 }
1723                 rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Get partial sector data from sector buffer */
1724                 if (rcnt > btr) rcnt = btr;
1725 #if _FS_TINY
1726                 if (move_window(fp->fs, fp->dsect))                     /* Move sector window */
1727                         ABORT(fp->fs, FR_DISK_ERR);
1728                 MemCpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);       /* Pick partial sector */
1729 #else
1730                 MemCpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);   /* Pick partial sector */
1731 #endif
1732         }
1733
1734
1735         LEAVE_FF(fp->fs, FR_OK);
1736 }
1737
1738
1739
1740
1741 #if !_FS_READONLY
1742 /*-----------------------------------------------------------------------*/
1743 /* Write File                                                            */
1744 /*-----------------------------------------------------------------------*/
1745
1746 FRESULT f_write (
1747         FIL *fp,                        /* Pointer to the file object */
1748         const void *buff,       /* Pointer to the data to be written */
1749         UINT btw,                       /* Number of bytes to write */
1750         UINT *bw                        /* Pointer to number of bytes written */
1751 )
1752 {
1753         FRESULT res;
1754         DWORD clst, sect;
1755         UINT wcnt, cc;
1756         const BYTE *wbuff = buff;
1757
1758
1759         *bw = 0;
1760
1761         res = validate(fp->fs, fp->id);                                 /* Check validity of the object */
1762         if (res != FR_OK) LEAVE_FF(fp->fs, res);
1763         if (fp->flag & FA__ERROR)                                               /* Check abort flag */
1764                 LEAVE_FF(fp->fs, FR_INT_ERR);
1765         if (!(fp->flag & FA_WRITE))                                             /* Check access mode */
1766                 LEAVE_FF(fp->fs, FR_DENIED);
1767         if (fp->fsize + btw < fp->fsize) btw = 0;               /* File size cannot reach 4GB */
1768
1769         for ( ;  btw;                                                                   /* Repeat until all data transferred */
1770                 wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
1771                 if ((fp->fptr % SS(fp->fs)) == 0) {                     /* On the sector boundary? */
1772                         if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
1773                                 if (fp->fptr == 0) {                            /* On the top of the file? */
1774                                         clst = fp->org_clust;                   /* Follow from the origin */
1775                                         if (clst == 0)                                  /* When there is no cluster chain, */
1776                                                 fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */
1777                                 } else {                                                        /* Middle or end of the file */
1778                                         clst = create_chain(fp->fs, fp->curr_clust);                    /* Follow or streach cluster chain */
1779                                 }
1780                                 if (clst == 0) break;                           /* Could not allocate a new cluster (disk full) */
1781                                 if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
1782                                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
1783                                 fp->curr_clust = clst;                          /* Update current cluster */
1784                                 fp->csect = 0;                                          /* Reset sector address in the cluster */
1785                         }
1786 #if _FS_TINY
1787                         if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))     /* Write back data buffer prior to following direct transfer */
1788                                 ABORT(fp->fs, FR_DISK_ERR);
1789 #else
1790                         if (fp->flag & FA__DIRTY) {             /* Write back data buffer prior to following direct transfer */
1791                                 if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
1792                                         ABORT(fp->fs, FR_DISK_ERR);
1793                                 fp->flag &= (BYTE)~FA__DIRTY;
1794                         }
1795 #endif
1796                         sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current sector */
1797                         if (!sect) ABORT(fp->fs, FR_INT_ERR);
1798                         sect += fp->csect;
1799                         cc = btw / SS(fp->fs);                                  /* When remaining bytes >= sector size, */
1800                         if (cc) {                                                               /* Write maximum contiguous sectors directly */
1801                                 if (fp->csect + cc > fp->fs->csize)     /* Clip at cluster boundary */
1802                                         cc = fp->fs->csize - fp->csect;
1803                                 if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK)
1804                                         ABORT(fp->fs, FR_DISK_ERR);
1805                                 fp->csect += (BYTE)cc;                          /* Next sector address in the cluster */
1806                                 wcnt = SS(fp->fs) * cc;                         /* Number of bytes transferred */
1807                                 continue;
1808                         }
1809 #if _FS_TINY
1810                         if (fp->fptr >= fp->fsize) {                    /* Avoid silly buffer filling at growing edge */
1811                                 if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
1812                                 fp->fs->winsect = sect;
1813                         }
1814 #else
1815                         if (fp->dsect != sect) {                                /* Fill sector buffer with file data */
1816                                 if (fp->fptr < fp->fsize &&
1817                                         disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
1818                                                 ABORT(fp->fs, FR_DISK_ERR);
1819                         }
1820 #endif
1821                         fp->dsect = sect;
1822                         fp->csect++;                                                    /* Next sector address in the cluster */
1823                 }
1824                 wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Put partial sector into file I/O buffer */
1825                 if (wcnt > btw) wcnt = btw;
1826 #if _FS_TINY
1827                 if (move_window(fp->fs, fp->dsect))                     /* Move sector window */
1828                         ABORT(fp->fs, FR_DISK_ERR);
1829                 MemCpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);       /* Fit partial sector */
1830                 fp->fs->wflag = 1;
1831 #else
1832                 MemCpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);   /* Fit partial sector */
1833                 fp->flag |= FA__DIRTY;
1834 #endif
1835         }
1836
1837         if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
1838         fp->flag |= FA__WRITTEN;                                                /* Set file changed flag */
1839
1840         LEAVE_FF(fp->fs, FR_OK);
1841 }
1842
1843
1844
1845
1846 /*-----------------------------------------------------------------------*/
1847 /* Synchronize the File Object                                           */
1848 /*-----------------------------------------------------------------------*/
1849
1850 FRESULT f_sync (
1851         FIL *fp         /* Pointer to the file object */
1852 )
1853 {
1854         FRESULT res;
1855         DWORD tim;
1856         BYTE *dir;
1857
1858
1859         res = validate(fp->fs, fp->id);         /* Check validity of the object */
1860         if (res == FR_OK) {
1861                 if (fp->flag & FA__WRITTEN) {   /* Has the file been written? */
1862 #if !_FS_TINY   /* Write-back dirty buffer */
1863                         if (fp->flag & FA__DIRTY) {
1864                                 if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
1865                                         LEAVE_FF(fp->fs, FR_DISK_ERR);
1866                                 fp->flag &= (BYTE)~FA__DIRTY;
1867                         }
1868 #endif
1869                         /* Update the directory entry */
1870                         res = move_window(fp->fs, fp->dir_sect);
1871                         if (res == FR_OK) {
1872                                 dir = fp->dir_ptr;
1873                                 dir[DIR_Attr] |= AM_ARC;                                        /* Set archive bit */
1874                                 ST_DWORD(dir+DIR_FileSize, fp->fsize);          /* Update file size */
1875                                 ST_WORD(dir+DIR_FstClusLO, fp->org_clust);      /* Update start cluster */
1876                                 ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16);
1877                                 tim = get_fattime();                                    /* Updated time */
1878                                 ST_DWORD(dir+DIR_WrtTime, tim);
1879                                 fp->flag &= (BYTE)~FA__WRITTEN;
1880                                 fp->fs->wflag = 1;
1881                                 res = sync(fp->fs);
1882                         }
1883                 }
1884         }
1885
1886         LEAVE_FF(fp->fs, res);
1887 }
1888
1889 #endif /* !_FS_READONLY */
1890
1891
1892
1893
1894 /*-----------------------------------------------------------------------*/
1895 /* Close File                                                            */
1896 /*-----------------------------------------------------------------------*/
1897
1898 FRESULT f_close (
1899         FIL *fp         /* Pointer to the file object to be closed */
1900 )
1901 {
1902         FRESULT res;
1903
1904
1905 #if _FS_READONLY
1906         res = validate(fp->fs, fp->id);
1907         if (res == FR_OK) fp->fs = NULL;
1908         LEAVE_FF(fp->fs, res);
1909 #else
1910         res = f_sync(fp);
1911         if (res == FR_OK) fp->fs = NULL;
1912         return res;
1913 #endif
1914 }
1915
1916
1917
1918
1919 #if _FS_MINIMIZE <= 2
1920 /*-----------------------------------------------------------------------*/
1921 /* Seek File R/W Pointer                                                 */
1922 /*-----------------------------------------------------------------------*/
1923
1924 FRESULT f_lseek (
1925         FIL *fp,                /* Pointer to the file object */
1926         DWORD ofs               /* File pointer from top of file */
1927 )
1928 {
1929         FRESULT res;
1930         DWORD clst, bcs, nsect, ifptr;
1931
1932
1933         res = validate(fp->fs, fp->id);         /* Check validity of the object */
1934         if (res != FR_OK) LEAVE_FF(fp->fs, res);
1935         if (fp->flag & FA__ERROR)                       /* Check abort flag */
1936                 LEAVE_FF(fp->fs, FR_INT_ERR);
1937         if (ofs > fp->fsize                                     /* In read-only mode, clip offset with the file size */
1938 #if !_FS_READONLY
1939                  && !(fp->flag & FA_WRITE)
1940 #endif
1941                 ) ofs = fp->fsize;
1942
1943         ifptr = fp->fptr;
1944         fp->fptr = 0; fp->csect = 255;
1945         nsect = 0;
1946         if (ofs > 0) {
1947                 bcs = (DWORD)fp->fs->csize * SS(fp->fs);        /* Cluster size (byte) */
1948                 if (ifptr > 0 &&
1949                         (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
1950                         fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
1951                         ofs -= fp->fptr;
1952                         clst = fp->curr_clust;
1953                 } else {                                                                        /* When seek to back cluster, */
1954                         clst = fp->org_clust;                                   /* start from the first cluster */
1955 #if !_FS_READONLY
1956                         if (clst == 0) {                                                /* If no cluster chain, create a new chain */
1957                                 clst = create_chain(fp->fs, 0);
1958                                 if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
1959                                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
1960                                 fp->org_clust = clst;
1961                         }
1962 #endif
1963                         fp->curr_clust = clst;
1964                 }
1965                 if (clst != 0) {
1966                         while (ofs > bcs) {                                             /* Cluster following loop */
1967 #if !_FS_READONLY
1968                                 if (fp->flag & FA_WRITE) {                      /* Check if in write mode or not */
1969                                         clst = create_chain(fp->fs, clst);      /* Force streached if in write mode */
1970                                         if (clst == 0) {                                /* When disk gets full, clip file size */
1971                                                 ofs = bcs; break;
1972                                         }
1973                                 } else
1974 #endif
1975                                         clst = get_cluster(fp->fs, clst);       /* Follow cluster chain if not in write mode */
1976                                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
1977                                 if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR);
1978                                 fp->curr_clust = clst;
1979                                 fp->fptr += bcs;
1980                                 ofs -= bcs;
1981                         }
1982                         fp->fptr += ofs;
1983                         fp->csect = (BYTE)(ofs / SS(fp->fs));   /* Sector offset in the cluster */
1984                         if (ofs % SS(fp->fs)) {
1985                                 nsect = clust2sect(fp->fs, clst);       /* Current sector */
1986                                 if (!nsect) ABORT(fp->fs, FR_INT_ERR);
1987                                 nsect += fp->csect;
1988                                 fp->csect++;
1989                         }
1990                 }
1991         }
1992         if (nsect && nsect != fp->dsect && fp->fptr % SS(fp->fs)) {
1993 #if !_FS_TINY
1994 #if !_FS_READONLY
1995                 if (fp->flag & FA__DIRTY) {                     /* Write-back dirty buffer if needed */
1996                         if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
1997                                 ABORT(fp->fs, FR_DISK_ERR);
1998                         fp->flag &= (BYTE)~FA__DIRTY;
1999                 }
2000 #endif
2001                 if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK)
2002                         ABORT(fp->fs, FR_DISK_ERR);
2003 #endif
2004                 fp->dsect = nsect;
2005         }
2006 #if !_FS_READONLY
2007         if (fp->fptr > fp->fsize) {                     /* Set changed flag if the file size is extended */
2008                 fp->fsize = fp->fptr;
2009                 fp->flag |= FA__WRITTEN;
2010         }
2011 #endif
2012
2013         LEAVE_FF(fp->fs, res);
2014 }
2015
2016
2017
2018
2019 #if _FS_MINIMIZE <= 1
2020 /*-----------------------------------------------------------------------*/
2021 /* Create a Directroy Object                                             */
2022 /*-----------------------------------------------------------------------*/
2023
2024 FRESULT f_opendir (
2025         DIR *dj,                        /* Pointer to directory object to create */
2026         const char *path        /* Pointer to the directory path */
2027 )
2028 {
2029         FRESULT res;
2030         NAMEBUF(sfn, lfn);
2031         BYTE *dir;
2032
2033
2034         res = auto_mount(&path, &dj->fs, 0);
2035         if (res == FR_OK) {
2036                 INITBUF((*dj), sfn, lfn);
2037                 res = follow_path(dj, path);                    /* Follow the path to the directory */
2038                 if (res == FR_OK) {                                             /* Follow completed */
2039                         dir = dj->dir;
2040                         if (dir) {                                                      /* It is not the root dir */
2041                                 if (dir[DIR_Attr] & AM_DIR) {   /* The object is a directory */
2042                                         dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
2043                                 } else {                                                /* The object is not a directory */
2044                                         res = FR_NO_PATH;
2045                                 }
2046                         } else {                                                        /* It is the root dir */
2047                                 dj->sclust = (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0;
2048                         }
2049                         if (res == FR_OK) res = dir_seek(dj, 0);
2050                         dj->id = dj->fs->id;
2051                 } else {
2052                         if (res == FR_NO_FILE) res = FR_NO_PATH;
2053                 }
2054         }
2055
2056         LEAVE_FF(dj->fs, res);
2057 }
2058
2059
2060
2061
2062 /*-----------------------------------------------------------------------*/
2063 /* Read Directory Entry in Sequense                                      */
2064 /*-----------------------------------------------------------------------*/
2065
2066 FRESULT f_readdir (
2067         DIR *dj,                        /* Pointer to the open directory object */
2068         FILINFO *fno            /* Pointer to file information to return */
2069 )
2070 {
2071         FRESULT res;
2072         NAMEBUF(sfn, lfn);
2073
2074
2075         res = validate(dj->fs, dj->id);                 /* Check validity of the object */
2076         if (res == FR_OK) {
2077                 INITBUF((*dj), sfn, lfn);
2078                 if (!fno) {
2079                         res = dir_seek(dj, 0);
2080                 } else {
2081                         res = dir_read(dj);
2082                         if (res == FR_NO_FILE) {
2083                                 dj->sect = 0;
2084                                 res = FR_OK;
2085                         }
2086                         if (res == FR_OK) {                             /* A valid entry is found */
2087                                 get_fileinfo(dj, fno);          /* Get the object information */
2088                                 res = dir_next(dj, FALSE);      /* Increment index for next */
2089                                 if (res == FR_NO_FILE) {
2090                                         dj->sect = 0;
2091                                         res = FR_OK;
2092                                 }
2093                         }
2094                 }
2095         }
2096
2097         LEAVE_FF(dj->fs, res);
2098 }
2099
2100
2101
2102 #if _FS_MINIMIZE == 0
2103 /*-----------------------------------------------------------------------*/
2104 /* Get File Status                                                       */
2105 /*-----------------------------------------------------------------------*/
2106
2107 FRESULT f_stat (
2108         const char *path,       /* Pointer to the file path */
2109         FILINFO *fno            /* Pointer to file information to return */
2110 )
2111 {
2112         FRESULT res;
2113         DIR dj;
2114         NAMEBUF(sfn, lfn);
2115
2116
2117         res = auto_mount(&path, &dj.fs, 0);
2118         if (res == FR_OK) {
2119                 INITBUF(dj, sfn, lfn);
2120                 res = follow_path(&dj, path);   /* Follow the file path */
2121                 if (res == FR_OK) {                             /* Follwo completed */
2122                         if (dj.dir)     /* Found an object */
2123                                 get_fileinfo(&dj, fno);
2124                         else            /* It is root dir */
2125                                 res = FR_INVALID_NAME;
2126                 }
2127         }
2128
2129         LEAVE_FF(dj.fs, res);
2130 }
2131
2132
2133
2134 #if !_FS_READONLY
2135 /*-----------------------------------------------------------------------*/
2136 /* Truncate File                                                         */
2137 /*-----------------------------------------------------------------------*/
2138
2139 FRESULT f_truncate (
2140         FIL *fp         /* Pointer to the file object */
2141 )
2142 {
2143         FRESULT res;
2144         DWORD ncl;
2145
2146
2147         res = validate(fp->fs, fp->id);         /* Check validity of the object */
2148         if (res != FR_OK) LEAVE_FF(fp->fs, res);
2149         if (fp->flag & FA__ERROR)                       /* Check abort flag */
2150                 LEAVE_FF(fp->fs, FR_INT_ERR);
2151         if (!(fp->flag & FA_WRITE))                     /* Check access mode */
2152                 LEAVE_FF(fp->fs, FR_DENIED);
2153
2154         if (fp->fsize > fp->fptr) {
2155                 fp->fsize = fp->fptr;   /* Set file size to current R/W point */
2156                 fp->flag |= FA__WRITTEN;
2157                 if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
2158                         res = remove_chain(fp->fs, fp->org_clust);
2159                         fp->org_clust = 0;
2160                 } else {                                /* When truncate a part of the file, remove remaining clusters */
2161                         ncl = get_cluster(fp->fs, fp->curr_clust);
2162                         res = FR_OK;
2163                         if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
2164                         if (ncl == 1) res = FR_INT_ERR;
2165                         if (res == FR_OK && ncl < fp->fs->max_clust) {
2166                                 res = put_cluster(fp->fs, fp->curr_clust, 0x0FFFFFFF);
2167                                 if (res == FR_OK) res = remove_chain(fp->fs, ncl);
2168                         }
2169                 }
2170         }
2171         if (res != FR_OK) fp->flag |= FA__ERROR;
2172
2173         LEAVE_FF(fp->fs, res);
2174 }
2175
2176
2177
2178
2179 /*-----------------------------------------------------------------------*/
2180 /* Get Number of Free Clusters                                           */
2181 /*-----------------------------------------------------------------------*/
2182
2183 FRESULT f_getfree (
2184         const char *path,       /* Pointer to the logical drive number (root dir) */
2185         DWORD *nclst,           /* Pointer to the variable to return number of free clusters */
2186         FATFS **fatfs           /* Pointer to pointer to corresponding file system object to return */
2187 )
2188 {
2189         FRESULT res;
2190         DWORD n, clst, sect;
2191         BYTE fat, f, *p;
2192
2193
2194         /* Get drive number */
2195         res = auto_mount(&path, fatfs, 0);
2196         if (res != FR_OK) LEAVE_FF(*fatfs, res);
2197
2198         /* If number of free cluster is valid, return it without cluster scan. */
2199         if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) {
2200                 *nclst = (*fatfs)->free_clust;
2201                 LEAVE_FF(*fatfs, FR_OK);
2202         }
2203
2204         /* Get number of free clusters */
2205         fat = (*fatfs)->fs_type;
2206         n = 0;
2207         if (fat == FS_FAT12) {
2208                 clst = 2;
2209                 do {
2210                         if ((WORD)get_cluster(*fatfs, clst) == 0) n++;
2211                 } while (++clst < (*fatfs)->max_clust);
2212         } else {
2213                 clst = (*fatfs)->max_clust;
2214                 sect = (*fatfs)->fatbase;
2215                 f = 0; p = 0;
2216                 do {
2217                         if (!f) {
2218                                 res = move_window(*fatfs, sect++);
2219                                 if (res != FR_OK)
2220                                         LEAVE_FF(*fatfs, res);
2221                                 p = (*fatfs)->win;
2222                         }
2223                         if (fat == FS_FAT16) {
2224                                 if (LD_WORD(p) == 0) n++;
2225                                 p += 2; f += 1;
2226                         } else {
2227                                 if (LD_DWORD(p) == 0) n++;
2228                                 p += 4; f += 2;
2229                         }
2230                 } while (--clst);
2231         }
2232         (*fatfs)->free_clust = n;
2233         if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
2234         *nclst = n;
2235
2236         LEAVE_FF(*fatfs, FR_OK);
2237 }
2238
2239
2240
2241
2242 /*-----------------------------------------------------------------------*/
2243 /* Delete a File or Directory                                            */
2244 /*-----------------------------------------------------------------------*/
2245
2246 FRESULT f_unlink (
2247         const char *path                /* Pointer to the file or directory path */
2248 )
2249 {
2250         FRESULT res;
2251         DIR dj, sdj;
2252         NAMEBUF(sfn, lfn);
2253         BYTE *dir;
2254         DWORD dclst;
2255
2256
2257         res = auto_mount(&path, &dj.fs, 1);
2258         if (res != FR_OK) LEAVE_FF(dj.fs, res);
2259
2260         INITBUF(dj, sfn, lfn);
2261         res = follow_path(&dj, path);                   /* Follow the file path */
2262         if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
2263
2264         dir = dj.dir;
2265         if (!dir)                                                               /* Is it the root directory? */
2266                 LEAVE_FF(dj.fs, FR_INVALID_NAME);
2267         if (dir[DIR_Attr] & AM_RDO)                             /* Is it a R/O object? */
2268                 LEAVE_FF(dj.fs, FR_DENIED);
2269         dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
2270
2271         if (dir[DIR_Attr] & AM_DIR) {                   /* It is a sub-directory */
2272                 if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR);
2273                 MemCpy(&sdj, &dj, sizeof(DIR));         /* Check if the sub-dir is empty or not */
2274                 sdj.sclust = dclst;
2275                 res = dir_seek(&sdj, 0);
2276                 if (res != FR_OK) LEAVE_FF(dj.fs, res);
2277                 res = dir_read(&sdj);
2278                 if (res == FR_OK) res = FR_DENIED;      /* Not empty sub-dir */
2279                 if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res);
2280         }
2281
2282         res = dir_remove(&dj);                                  /* Remove directory entry */
2283         if (res == FR_OK) {
2284                 if (dclst)
2285                         res = remove_chain(dj.fs, dclst);       /* Remove the cluster chain */
2286                 if (res == FR_OK) res = sync(dj.fs);
2287         }
2288
2289         LEAVE_FF(dj.fs, FR_OK);
2290 }
2291
2292
2293
2294
2295 /*-----------------------------------------------------------------------*/
2296 /* Create a Directory                                                    */
2297 /*-----------------------------------------------------------------------*/
2298
2299 FRESULT f_mkdir (
2300         const char *path                /* Pointer to the directory path */
2301 )
2302 {
2303         FRESULT res;
2304         DIR dj;
2305         NAMEBUF(sfn, lfn);
2306         BYTE *dir, n;
2307         DWORD dsect, dclst, pclst, tim;
2308
2309
2310         res = auto_mount(&path, &dj.fs, 1);
2311         if (res != FR_OK) LEAVE_FF(dj.fs, res);
2312
2313         INITBUF(dj, sfn, lfn);
2314         res = follow_path(&dj, path);                   /* Follow the file path */
2315         if (res == FR_OK) res = FR_EXIST;               /* Any file or directory is already existing */
2316         if (res != FR_NO_FILE)                                  /* Any error occured */
2317                 LEAVE_FF(dj.fs, res);
2318
2319         dclst = create_chain(dj.fs, 0);                 /* Allocate a new cluster for new directory table */
2320         res = FR_OK;
2321         if (dclst == 0) res = FR_DENIED;
2322         if (dclst == 1) res = FR_INT_ERR;
2323         if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR;
2324         if (res == FR_OK)
2325                 res = move_window(dj.fs, 0);
2326         if (res != FR_OK) LEAVE_FF(dj.fs, res);
2327         dsect = clust2sect(dj.fs, dclst);
2328
2329         dir = dj.fs->win;                                               /* Initialize the new directory table */
2330         MemSet(dir, 0, SS(dj.fs));
2331         MemSet(dir+DIR_Name, ' ', 8+3);         /* Create "." entry */
2332         dir[DIR_Name] = '.';
2333         dir[DIR_Attr] = AM_DIR;
2334         tim = get_fattime();
2335         ST_DWORD(dir+DIR_WrtTime, tim);
2336         ST_WORD(dir+DIR_FstClusLO, dclst);
2337         ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
2338         MemCpy(dir+32, dir, 32);                        /* Create ".." entry */
2339         dir[33] = '.';
2340         pclst = dj.sclust;
2341         if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase)
2342                 pclst = 0;
2343         ST_WORD(dir+32+DIR_FstClusLO, pclst);
2344         ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16);
2345         for (n = 0; n < dj.fs->csize; n++) {    /* Write dot entries and clear left sectors */
2346                 dj.fs->winsect = dsect++;
2347                 dj.fs->wflag = 1;
2348                 res = move_window(dj.fs, 0);
2349                 if (res) LEAVE_FF(dj.fs, res);
2350                 MemSet(dir, 0, SS(dj.fs));
2351         }
2352
2353         res = dir_register(&dj);
2354         if (res != FR_OK) {
2355                 remove_chain(dj.fs, dclst);
2356         } else {
2357                 dir = dj.dir;
2358                 dir[DIR_Attr] = AM_DIR;                                 /* Attribute */
2359                 ST_DWORD(dir+DIR_WrtTime, tim);                 /* Crated time */
2360                 ST_WORD(dir+DIR_FstClusLO, dclst);              /* Table start cluster */
2361                 ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
2362                 dj.fs->wflag = 1;
2363                 res = sync(dj.fs);
2364         }
2365
2366         LEAVE_FF(dj.fs, res);
2367 }
2368
2369
2370
2371
2372 /*-----------------------------------------------------------------------*/
2373 /* Change File Attribute                                                 */
2374 /*-----------------------------------------------------------------------*/
2375
2376 FRESULT f_chmod (
2377         const char *path,       /* Pointer to the file path */
2378         BYTE value,                     /* Attribute bits */
2379         BYTE mask                       /* Attribute mask to change */
2380 )
2381 {
2382         FRESULT res;
2383         DIR dj;
2384         NAMEBUF(sfn, lfn);
2385         BYTE *dir;
2386
2387
2388         res = auto_mount(&path, &dj.fs, 1);
2389         if (res == FR_OK) {
2390                 INITBUF(dj, sfn, lfn);
2391                 res = follow_path(&dj, path);           /* Follow the file path */
2392                 if (res == FR_OK) {
2393                         dir = dj.dir;
2394                         if (!dir) {                                             /* Is it a root directory? */
2395                                 res = FR_INVALID_NAME;
2396                         } else {                                                /* File or sub directory */
2397                                 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
2398                                 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
2399                                 dj.fs->wflag = 1;
2400                                 res = sync(dj.fs);
2401                         }
2402                 }
2403         }
2404
2405         LEAVE_FF(dj.fs, res);
2406 }
2407
2408
2409
2410
2411 /*-----------------------------------------------------------------------*/
2412 /* Change Timestamp                                                      */
2413 /*-----------------------------------------------------------------------*/
2414
2415 FRESULT f_utime (
2416         const char *path,       /* Pointer to the file/directory name */
2417         const FILINFO *fno      /* Pointer to the timestamp to be set */
2418 )
2419 {
2420         FRESULT res;
2421         DIR dj;
2422         NAMEBUF(sfn, lfn);
2423         BYTE *dir;
2424
2425
2426         res = auto_mount(&path, &dj.fs, 1);
2427         if (res == FR_OK) {
2428                 INITBUF(dj, sfn, lfn);
2429                 res = follow_path(&dj, path);   /* Follow the file path */
2430                 if (res == FR_OK) {
2431                         dir = dj.dir;
2432                         if (!dir) {                             /* Root directory */
2433                                 res = FR_INVALID_NAME;
2434                         } else {                                /* File or sub-directory */
2435                                 ST_WORD(dir+DIR_WrtTime, fno->ftime);
2436                                 ST_WORD(dir+DIR_WrtDate, fno->fdate);
2437                                 dj.fs->wflag = 1;
2438                                 res = sync(dj.fs);
2439                         }
2440                 }
2441         }
2442
2443         LEAVE_FF(dj.fs, res);
2444 }
2445
2446
2447
2448
2449 /*-----------------------------------------------------------------------*/
2450 /* Rename File/Directory                                                 */
2451 /*-----------------------------------------------------------------------*/
2452
2453 FRESULT f_rename (
2454         const char *path_old,   /* Pointer to the old name */
2455         const char *path_new    /* Pointer to the new name */
2456 )
2457 {
2458         FRESULT res;
2459         DIR dj_old, dj_new;
2460         NAMEBUF(sfn, lfn);
2461         BYTE buf[21], *dir;
2462         DWORD dw;
2463
2464
2465         INITBUF(dj_old, sfn, lfn);
2466         res = auto_mount(&path_old, &dj_old.fs, 1);
2467         if (res == FR_OK) {
2468                 dj_new.fs = dj_old.fs;
2469                 res = follow_path(&dj_old, path_old);   /* Check old object */
2470         }
2471         if (res != FR_OK) LEAVE_FF(dj_old.fs, res);     /* The old object is not found */
2472
2473         if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE);       /* Is root dir? */
2474         MemCpy(buf, dj_old.dir+DIR_Attr, 21);           /* Save the object information */
2475
2476         MemCpy(&dj_new, &dj_old, sizeof(DIR));
2477         res = follow_path(&dj_new, path_new);           /* Check new object */
2478         if (res == FR_OK) res = FR_EXIST;                       /* The new object name is already existing */
2479         if (res == FR_NO_FILE) {                                        /* Is it a valid path and no name collision? */
2480                 res = dir_register(&dj_new);                    /* Register the new object */
2481                 if (res == FR_OK) {
2482                         dir = dj_new.dir;                                       /* Copy object information into new entry */
2483                         MemCpy(dir+13, buf+2, 19);
2484                         dir[DIR_Attr] = buf[0];
2485                         dj_old.fs->wflag = 1;
2486                         if (dir[DIR_Attr] & AM_DIR) {           /* Update .. entry in the directory if needed */
2487                                 dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
2488                                 if (!dw) {
2489                                         res = FR_INT_ERR;
2490                                 } else {
2491                                         res = move_window(dj_new.fs, dw);
2492                                         dir = dj_new.fs->win+32;
2493                                         if (res == FR_OK && dir[1] == '.') {
2494                                                 dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust;
2495                                                 ST_WORD(dir+DIR_FstClusLO, dw);
2496                                                 ST_WORD(dir+DIR_FstClusHI, dw >> 16);
2497                                                 dj_new.fs->wflag = 1;
2498                                         }
2499                                 }
2500                         }
2501                         if (res == FR_OK) {
2502                                 res = dir_remove(&dj_old);                      /* Remove old entry */
2503                                 if (res == FR_OK)
2504                                         res = sync(dj_old.fs);
2505                         }
2506                 }
2507         }
2508
2509         LEAVE_FF(dj_old.fs, res);
2510 }
2511
2512 #endif /* !_FS_READONLY */
2513 #endif /* _FS_MINIMIZE == 0 */
2514 #endif /* _FS_MINIMIZE <= 1 */
2515 #endif /* _FS_MINIMIZE <= 2 */
2516
2517
2518
2519 /*-----------------------------------------------------------------------*/
2520 /* Forward data to the stream directly (Available on only _FS_TINY cfg)  */
2521 /*-----------------------------------------------------------------------*/
2522 #if _USE_FORWARD && _FS_TINY
2523
2524 FRESULT f_forward (
2525         FIL *fp,                                                /* Pointer to the file object */
2526         UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
2527         UINT btr,                                               /* Number of bytes to forward */
2528         UINT *bf                                                /* Pointer to number of bytes forwarded */
2529 )
2530 {
2531         FRESULT res;
2532         DWORD remain, clst, sect;
2533         UINT rcnt;
2534
2535
2536         *bf = 0;
2537
2538         res = validate(fp->fs, fp->id);                                 /* Check validity of the object */
2539         if (res != FR_OK) LEAVE_FF(fp->fs, res);
2540         if (fp->flag & FA__ERROR)                                               /* Check error flag */
2541                 LEAVE_FF(fp->fs, FR_INT_ERR);
2542         if (!(fp->flag & FA_READ))                                              /* Check access mode */
2543                 LEAVE_FF(fp->fs, FR_DENIED);
2544
2545         remain = fp->fsize - fp->fptr;
2546         if (btr > remain) btr = (UINT)remain;                   /* Truncate btr by remaining bytes */
2547
2548         for ( ;  btr && (*func)(NULL, 0);                               /* Repeat until all data transferred or stream becomes busy */
2549                 fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
2550                 if ((fp->fptr % SS(fp->fs)) == 0) {                     /* On the sector boundary? */
2551                         if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
2552                                 clst = (fp->fptr == 0) ?                        /* On the top of the file? */
2553                                         fp->org_clust : get_cluster(fp->fs, fp->curr_clust);
2554                                 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
2555                                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
2556                                 fp->curr_clust = clst;                          /* Update current cluster */
2557                                 fp->csect = 0;                                          /* Reset sector address in the cluster */
2558                         }
2559                         fp->csect++;                                                    /* Next sector address in the cluster */
2560                 }
2561                 sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current data sector */
2562                 if (!sect) ABORT(fp->fs, FR_INT_ERR);
2563                 sect += fp->csect - 1;
2564                 if (move_window(fp->fs, sect))                          /* Move sector window */
2565                         ABORT(fp->fs, FR_DISK_ERR);
2566                 fp->dsect = sect;
2567                 rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));      /* Forward data from sector window */
2568                 if (rcnt > btr) rcnt = btr;
2569                 rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
2570                 if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
2571         }
2572
2573         LEAVE_FF(fp->fs, FR_OK);
2574 }
2575 #endif /* _USE_FORWARD */
2576
2577
2578
2579 #if _USE_MKFS && !_FS_READONLY
2580 /*-----------------------------------------------------------------------*/
2581 /* Create File System on the Drive                                       */
2582 /*-----------------------------------------------------------------------*/
2583 #define N_ROOTDIR       512                     /* Multiple of 32 and <= 2048 */
2584 #define N_FATS          1                       /* 1 or 2 */
2585 #define MAX_SECTOR      131072000UL     /* Maximum partition size */
2586 #define MIN_SECTOR      2000UL          /* Minimum partition size */
2587
2588
2589 FRESULT f_mkfs (
2590         BYTE drv,                       /* Logical drive number */
2591         BYTE partition,         /* Partitioning rule 0:FDISK, 1:SFD */
2592         WORD allocsize          /* Allocation unit size [bytes] */
2593 )
2594 {
2595         static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000,   0 };
2596         static const WORD cstbl[] =  {   32768,   16384,   8192,   4096,   2048, 16384,  8192,  4096, 2048, 1024, 512 };
2597         BYTE fmt, m, *tbl;
2598         DWORD b_part, b_fat, b_dir, b_data;             /* Area offset (LBA) */
2599         DWORD n_part, n_rsv, n_fat, n_dir;              /* Area size */
2600         DWORD n_clst, n;
2601         WORD as;
2602         FATFS *fs;
2603         DSTATUS stat;
2604
2605
2606         /* Check validity of the parameters */
2607         if (drv >= _DRIVES) return FR_INVALID_DRIVE;
2608         if (partition >= 2) return FR_MKFS_ABORTED;
2609
2610         /* Check mounted drive and clear work area */
2611         fs = FatFs[drv];
2612         if (!fs) return FR_NOT_ENABLED;
2613         fs->fs_type = 0;
2614         drv = LD2PD(drv);
2615
2616         /* Get disk statics */
2617         stat = disk_initialize(drv);
2618         if (stat & STA_NOINIT) return FR_NOT_READY;
2619         if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
2620         if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR)
2621                 return FR_MKFS_ABORTED;
2622         if (n_part > MAX_SECTOR) n_part = MAX_SECTOR;
2623         b_part = (!partition) ? 63 : 0;         /* Boot sector */
2624         n_part -= b_part;
2625 #if MAX_SS == 512
2626         if (!allocsize) {                                       /* Auto selection of cluster size */
2627                 for (n = 0; n_part < sstbl[n]; n++) ;
2628                 allocsize = cstbl[n];
2629         }
2630 #endif
2631         for (as = 512; as <= 32768U && as != allocsize; as <<= 1);
2632         if (as != allocsize) return FR_MKFS_ABORTED;
2633 #if MAX_SS > 512                                                /* Check disk sector size */
2634         if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
2635                 || SS(fs) > S_MAX_SIZ
2636                 || SS(fs) > allocsize)
2637                 return FR_MKFS_ABORTED;
2638 #endif
2639         allocsize /= SS(fs);            /* Number of sectors per cluster */
2640
2641         /* Pre-compute number of clusters and FAT type */
2642         n_clst = n_part / allocsize;
2643         fmt = FS_FAT12;
2644         if (n_clst >= 0xFF5) fmt = FS_FAT16;
2645         if (n_clst >= 0xFFF5) fmt = FS_FAT32;
2646
2647         /* Determine offset and size of FAT structure */
2648         switch (fmt) {
2649         case FS_FAT12:
2650                 n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs);
2651                 n_rsv = 1 + partition;
2652                 n_dir = N_ROOTDIR * 32 / SS(fs);
2653                 break;
2654         case FS_FAT16:
2655                 n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs);
2656                 n_rsv = 1 + partition;
2657                 n_dir = N_ROOTDIR * 32 / SS(fs);
2658                 break;
2659         default:
2660                 n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
2661                 n_rsv = 33 - partition;
2662                 n_dir = 0;
2663         }
2664         b_fat = b_part + n_rsv;                 /* FATs start sector */
2665         b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */
2666         b_data = b_dir + n_dir;                 /* Data start sector */
2667
2668         /* Align data start sector to erase block boundary (for flash memory media) */
2669         if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED;
2670         n = (b_data + n - 1) & ~(n - 1);
2671         n_fat += (n - b_data) / N_FATS;
2672         /* b_dir and b_data are no longer used below */
2673
2674         /* Determine number of cluster and final check of validity of the FAT type */
2675         n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize;
2676         if (   (fmt == FS_FAT16 && n_clst < 0xFF5)
2677                 || (fmt == FS_FAT32 && n_clst < 0xFFF5))
2678                 return FR_MKFS_ABORTED;
2679
2680         /* Create partition table if needed */
2681         if (!partition) {
2682                 DWORD n_disk = b_part + n_part;
2683
2684                 tbl = fs->win+MBR_Table;
2685                 ST_DWORD(tbl, 0x00010180);              /* Partition start in CHS */
2686                 if (n_disk < 63UL * 255 * 1024) {       /* Partition end in CHS */
2687                         n_disk = n_disk / 63 / 255;
2688                         tbl[7] = (BYTE)n_disk;
2689                         tbl[6] = (BYTE)((n_disk >> 2) | 63);
2690                 } else {
2691                         ST_WORD(&tbl[6], 0xFFFF);
2692                 }
2693                 tbl[5] = 254;
2694                 if (fmt != FS_FAT32)                    /* System ID */
2695                         tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
2696                 else
2697                         tbl[4] = 0x0c;
2698                 ST_DWORD(tbl+8, 63);                    /* Partition start in LBA */
2699                 ST_DWORD(tbl+12, n_part);               /* Partition size in LBA */
2700                 ST_WORD(tbl+64, 0xAA55);                /* Signature */
2701                 if (disk_write(drv, fs->win, 0, 1) != RES_OK)
2702                         return FR_DISK_ERR;
2703         }
2704
2705         /* Create boot record */
2706         tbl = fs->win;                                                          /* Clear buffer */
2707         MemSet(tbl, 0, SS(fs));
2708         ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB);                     /* Boot code (jmp $, nop) */
2709         memcpy(&tbl[BS_OEMName], "bootmii", 8);
2710         ST_WORD(tbl+BPB_BytsPerSec, SS(fs));            /* Sector size */
2711         tbl[BPB_SecPerClus] = (BYTE)allocsize;          /* Sectors per cluster */
2712         ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);                     /* Reserved sectors */
2713         tbl[BPB_NumFATs] = N_FATS;                                      /* Number of FATs */
2714         ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */
2715         if (n_part < 0x10000) {                                         /* Number of total sectors */
2716                 ST_WORD(tbl+BPB_TotSec16, n_part);
2717         } else {
2718                 ST_DWORD(tbl+BPB_TotSec32, n_part);
2719         }
2720         tbl[BPB_Media] = 0xF8;                                          /* Media descripter */
2721         ST_WORD(tbl+BPB_SecPerTrk, 63);                         /* Number of sectors per track */
2722         ST_WORD(tbl+BPB_NumHeads, 255);                         /* Number of heads */
2723         ST_DWORD(tbl+BPB_HiddSec, b_part);                      /* Hidden sectors */
2724         n = get_fattime();                                                      /* Use current time as a VSN */
2725         if (fmt != FS_FAT32) {
2726                 ST_DWORD(tbl+BS_VolID, n);                              /* Volume serial number */
2727                 ST_WORD(tbl+BPB_FATSz16, n_fat);                /* Number of secters per FAT */
2728                 tbl[BS_DrvNum] = 0x80;                                  /* Drive number */
2729                 tbl[BS_BootSig] = 0x29;                                 /* Extended boot signature */
2730                 MemCpy(tbl+BS_VolLab, "backupmii  FAT     ", 19);       /* Volume lavel, FAT signature */
2731         } else {
2732                 ST_DWORD(tbl+BS_VolID32, n);                    /* Volume serial number */
2733                 ST_DWORD(tbl+BPB_FATSz32, n_fat);               /* Number of secters per FAT */
2734                 ST_DWORD(tbl+BPB_RootClus, 2);                  /* Root directory cluster (2) */
2735                 ST_WORD(tbl+BPB_FSInfo, 1);                             /* FSInfo record offset (bs+1) */
2736                 ST_WORD(tbl+BPB_BkBootSec, 6);                  /* Backup boot record offset (bs+6) */
2737                 tbl[BS_DrvNum32] = 0x80;                                /* Drive number */
2738                 tbl[BS_BootSig32] = 0x29;                               /* Extended boot signature */
2739                 MemCpy(tbl+BS_VolLab32, "backupmii  FAT32   ", 19);     /* Volume lavel, FAT signature */
2740         }
2741         ST_WORD(tbl+BS_55AA, 0xAA55);                           /* Signature */
2742         if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)
2743                 return FR_DISK_ERR;
2744         if (fmt == FS_FAT32)
2745                 disk_write(drv, tbl, b_part+6, 1);
2746
2747         /* Initialize FAT area */
2748         for (m = 0; m < N_FATS; m++) {
2749                 MemSet(tbl, 0, SS(fs));         /* 1st sector of the FAT  */
2750                 if (fmt != FS_FAT32) {
2751                         n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8;
2752                         ST_DWORD(tbl, n);                               /* Reserve cluster #0-1 (FAT12/16) */
2753                 } else {
2754                         ST_DWORD(tbl+0, 0xFFFFFFF8);    /* Reserve cluster #0-1 (FAT32) */
2755                         ST_DWORD(tbl+4, 0xFFFFFFFF);
2756                         ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
2757                 }
2758                 if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
2759                         return FR_DISK_ERR;
2760                 MemSet(tbl, 0, SS(fs));         /* Following FAT entries are filled by zero */
2761                 for (n = 1; n < n_fat; n++) {
2762                         if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
2763                                 return FR_DISK_ERR;
2764                 }
2765         }
2766
2767         /* Initialize Root directory */
2768         m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir);
2769         do {
2770                 if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
2771                         return FR_DISK_ERR;
2772         } while (--m);
2773
2774         /* Create FSInfo record if needed */
2775         if (fmt == FS_FAT32) {
2776                 ST_WORD(tbl+BS_55AA, 0xAA55);
2777                 ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
2778                 ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
2779                 ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);
2780                 ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
2781                 disk_write(drv, tbl, b_part+1, 1);
2782                 disk_write(drv, tbl, b_part+7, 1);
2783         }
2784
2785         return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR;
2786 }
2787
2788 #endif /* _USE_MKFS && !_FS_READONLY */
2789
2790
2791
2792
2793 #if _USE_STRFUNC
2794 /*-----------------------------------------------------------------------*/
2795 /* Get a string from the file                                            */
2796 /*-----------------------------------------------------------------------*/
2797 char* f_gets (
2798         char* buff,     /* Pointer to the string buffer to read */
2799         int len,        /* Size of string buffer */
2800         FIL* fil        /* Pointer to the file object */
2801 )
2802 {
2803         int i = 0;
2804         char *p = buff;
2805         UINT rc;
2806
2807
2808         while (i < len - 1) {                   /* Read bytes until buffer gets filled */
2809                 f_read(fil, p, 1, &rc);
2810                 if (rc != 1) break;                     /* Break when no data to read */
2811 #if _USE_STRFUNC >= 2
2812                 if (*p == '\r') continue;       /* Strip '\r' */
2813 #endif
2814                 i++;
2815                 if (*p++ == '\n') break;        /* Break when reached end of line */
2816         }
2817         *p = 0;
2818         return i ? buff : NULL;                 /* When no data read (eof or error), return with error. */
2819 }
2820
2821
2822
2823 #if !_FS_READONLY
2824 #include <stdarg.h>
2825 /*-----------------------------------------------------------------------*/
2826 /* Put a character to the file                                           */
2827 /*-----------------------------------------------------------------------*/
2828 int f_putc (
2829         int chr,        /* A character to be output */
2830         FIL* fil        /* Ponter to the file object */
2831 )
2832 {
2833         UINT bw;
2834         char c;
2835
2836
2837 #if _USE_STRFUNC >= 2
2838         if (chr == '\n') f_putc ('\r', fil);    /* LF -> CRLF conversion */
2839 #endif
2840         if (!fil) {     /* Special value may be used to switch the destination to any other device */
2841         /*      put_console(chr);       */
2842                 return chr;
2843         }
2844         c = (char)chr;
2845         f_write(fil, &c, 1, &bw);       /* Write a byte to the file */
2846         return bw ? chr : EOF;          /* Return the result */
2847 }
2848
2849
2850
2851
2852 /*-----------------------------------------------------------------------*/
2853 /* Put a string to the file                                              */
2854 /*-----------------------------------------------------------------------*/
2855 int f_puts (
2856         const char* str,        /* Pointer to the string to be output */
2857         FIL* fil                        /* Pointer to the file object */
2858 )
2859 {
2860         int n;
2861
2862
2863         for (n = 0; *str; str++, n++) {
2864                 if (f_putc(*str, fil) == EOF) return EOF;
2865         }
2866         return n;
2867 }
2868
2869
2870
2871
2872 /*-----------------------------------------------------------------------*/
2873 /* Put a formatted string to the file                                    */
2874 /*-----------------------------------------------------------------------*/
2875 int f_printf (
2876         FIL* fil,                       /* Pointer to the file object */
2877         const char* str,        /* Pointer to the format string */
2878         ...                                     /* Optional arguments... */
2879 )
2880 {
2881         va_list arp;
2882         UCHAR c, f, r;
2883         ULONG val;
2884         char s[16];
2885         int i, w, res, cc;
2886
2887
2888         va_start(arp, str);
2889
2890         for (cc = res = 0; cc != EOF; res += cc) {
2891                 c = *str++;
2892                 if (c == 0) break;                      /* End of string */
2893                 if (c != '%') {                         /* Non escape cahracter */
2894                         cc = f_putc(c, fil);
2895                         if (cc != EOF) cc = 1;
2896                         continue;
2897                 }
2898                 w = f = 0;
2899                 c = *str++;
2900                 if (c == '0') {                         /* Flag: '0' padding */
2901                         f = 1; c = *str++;
2902                 }
2903                 while (c >= '0' && c <= '9') {  /* Precision */
2904                         w = w * 10 + (c - '0');
2905                         c = *str++;
2906                 }
2907                 if (c == 'l') {                         /* Prefix: Size is long int */
2908                         f |= 2; c = *str++;
2909                 }
2910                 if (c == 's') {                         /* Type is string */
2911                         cc = f_puts(va_arg(arp, char*), fil);
2912                         continue;
2913                 }
2914                 if (c == 'c') {                         /* Type is character */
2915                         cc = f_putc(va_arg(arp, int), fil);
2916                         if (cc != EOF) cc = 1;
2917                         continue;
2918                 }
2919                 r = 0;
2920                 if (c == 'd') r = 10;           /* Type is signed decimal */
2921                 if (c == 'u') r = 10;           /* Type is unsigned decimal */
2922                 if (c == 'X') r = 16;           /* Type is unsigned hexdecimal */
2923                 if (r == 0) break;                      /* Unknown type */
2924                 if (f & 2) {                            /* Get the value */
2925                         val = (ULONG)va_arg(arp, long);
2926                 } else {
2927                         val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);
2928                 }
2929                 /* Put numeral string */
2930                 if (c == 'd') {
2931                         if (val & 0x80000000) {
2932                                 val = 0 - val;
2933                                 f |= 4;
2934                         }
2935                 }
2936                 i = sizeof(s) - 1; s[i] = 0;
2937                 do {
2938                         c = (UCHAR)(val % r + '0');
2939                         if (c > '9') c += 7;
2940                         s[--i] = c;
2941                         val /= r;
2942                 } while (i && val);
2943                 if (i && (f & 4)) s[--i] = '-';
2944                 w = sizeof(s) - 1 - w;
2945                 while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
2946                 cc = f_puts(&s[i], fil);
2947         }
2948
2949         va_end(arp);
2950         return (cc == EOF) ? cc : res;
2951 }
2952
2953 #endif /* !_FS_READONLY */
2954 #endif /* _USE_STRFUNC */