source: azure_iot_hub_f767zi/trunk/asp_baseplatform/files/ff/ff.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

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