Rev Author Line No. Line
1234 kaklik 1 /*--------------------------------------------------------------------------/
2 / FatFs - Tiny FAT file system module R0.04b (C)ChaN, 2007
3 /---------------------------------------------------------------------------/
4 / The FatFs module is an experimenal project to implement FAT file system to
5 / cheap microcontrollers. This is a free software and is opened for education,
6 / research and development under license policy of following trems.
7 /
8 / Copyright (C) 2007, ChaN, all right reserved.
9 /
10 / * The FatFs module is a free software and there is no warranty.
11 / * You can use, modify and/or redistribute it for personal, non-profit or
12 / profit use without any restriction under your responsibility.
13 / * Redistributions of source code must retain the above copyright notice.
14 /
15 /---------------------------------------------------------------------------/
16 / Feb 26, 2006 R0.00 Prototype.
17 / Apr 29, 2006 R0.01 First stable version.
18 / Jun 01, 2006 R0.02 Added FAT12 support.
19 / Removed unbuffered mode.
20 / Fixed a problem on small (<32M) patition.
21 / Jun 10, 2006 R0.02a Added a configuration option (_FS_MINIMUM).
22 / Sep 22, 2006 R0.03 Added f_rename().
23 / Changed option _FS_MINIMUM to _FS_MINIMIZE.
24 / Dec 09, 2006 R0.03a Improved cluster scan algolithm to write files fast.
25 / Feb 04, 2007 R0.04 Added FAT32 supprt.
26 / Changed some interfaces incidental to FatFs.
27 / Changed f_mountdrv() to f_mount().
28 / Apr 01, 2007 R0.04a Added a capability of extending file size to f_lseek().
29 / Added minimization level 3.
30 / Fixed a problem in FAT32 support.
31 / xxx xx, 2007 R0.04b Added a configuration option _USE_NTFLAG.
32 / Added FSInfo support.
33 / Fixed some problems corresponds to FAT32 support.
34 / Fixed DBCS name can result FR_INVALID_NAME.
35 / Fixed short seek (<= csize) collapses the file object.
36 /---------------------------------------------------------------------------*/
37  
38 #include <string.h>
39 #include "tff.h" /* Tiny-FatFs declarations */
40 #include "diskio.h" /* Include file for user provided disk functions */
41  
42  
43 static
44 FATFS *FatFs; /* Pointer to the file system objects (logical drive) */
45 static
46 WORD fsid; /* File system mount ID */
47  
48  
49 /*-------------------------------------------------------------------------
50  
51 Module Private Functions
52  
53 -------------------------------------------------------------------------*/
54  
55  
56 /*-----------------------------------------------------------------------*/
57 /* Change window offset */
58 /*-----------------------------------------------------------------------*/
59  
60 static
61 BOOL move_window ( /* TRUE: successful, FALSE: failed */
62 DWORD sector /* Sector number to make apperance in the FatFs->win */
63 ) /* Move to zero only writes back dirty window */
64 {
65 DWORD wsect;
66 FATFS *fs = FatFs;
67  
68  
69 wsect = fs->winsect;
70 if (wsect != sector) { /* Changed current window */
71 #if !_FS_READONLY
72 BYTE n;
73 if (fs->winflag) { /* Write back dirty window if needed */
74 if (disk_write(0, fs->win, wsect, 1) != RES_OK)
75 return FALSE;
76 fs->winflag = 0;
77 if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */
78 for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to all FAT copies */
79 wsect += fs->sects_fat;
80 disk_write(0, fs->win, wsect, 1);
81 }
82 }
83 }
84 #endif
85 if (sector) {
86 if (disk_read(0, fs->win, sector, 1) != RES_OK)
87 return FALSE;
88 fs->winsect = sector;
89 }
90 }
91 return TRUE;
92 }
93  
94  
95  
96  
97 /*-----------------------------------------------------------------------*/
98 /* Clean-up cached data */
99 /*-----------------------------------------------------------------------*/
100  
101 #if !_FS_READONLY
102 static
103 FRESULT sync (void) /* FR_OK: successful, FR_RW_ERROR: failed */
104 {
105 FATFS *fs = FatFs;
106  
107  
108 fs->winflag = 1;
109 if (!move_window(0)) return FR_RW_ERROR;
110 #if _USE_FSINFO
111 if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { /* Update FSInfo sector if needed */
112 fs->winsect = 0;
113 memset(fs->win, 0, 512);
114 ST_WORD(&fs->win[BS_55AA], 0xAA55);
115 ST_DWORD(&fs->win[FSI_LeadSig], 0x41615252);
116 ST_DWORD(&fs->win[FSI_StrucSig], 0x61417272);
117 ST_DWORD(&fs->win[FSI_Free_Count], fs->free_clust);
118 ST_DWORD(&fs->win[FSI_Nxt_Free], fs->last_clust);
119 disk_write(0, fs->win, fs->fsi_sector, 1);
120 fs->fsi_flag = 0;
121 }
122 #endif
123 if (disk_ioctl(0, CTRL_SYNC, NULL) != RES_OK) return FR_RW_ERROR;
124 return FR_OK;
125 }
126 #endif
127  
128  
129  
130  
131 /*-----------------------------------------------------------------------*/
132 /* Get a cluster status */
133 /*-----------------------------------------------------------------------*/
134  
135 static
136 CLUST get_cluster ( /* 0,>=2: successful, 1: failed */
137 CLUST clust /* Cluster# to get the link information */
138 )
139 {
140 WORD wc, bc;
141 DWORD fatsect;
142 FATFS *fs = FatFs;
143  
144  
145 if (clust >= 2 && clust < fs->max_clust) { /* Valid cluster# */
146 fatsect = fs->fatbase;
147 switch (fs->fs_type) {
148 case FS_FAT12 :
149 bc = (WORD)clust * 3 / 2;
150 if (!move_window(fatsect + bc / 512)) break;
151 wc = fs->win[bc % 512]; bc++;
152 if (!move_window(fatsect + bc / 512)) break;
153 wc |= (WORD)fs->win[bc % 512] << 8;
154 return (clust & 1) ? (wc >> 4) : (wc & 0xFFF);
155  
156 case FS_FAT16 :
157 if (!move_window(fatsect + clust / 256)) break;
158 return LD_WORD(&fs->win[((WORD)clust * 2) % 512]);
159 #if _FAT32
160 case FS_FAT32 :
161 if (!move_window(fatsect + clust / 128)) break;
162 return LD_DWORD(&fs->win[((WORD)clust * 4) % 512]) & 0x0FFFFFFF;
163 #endif
164 }
165 }
166  
167 return 1; /* There is no cluster information, or an error occured */
168 }
169  
170  
171  
172  
173 /*-----------------------------------------------------------------------*/
174 /* Change a cluster status */
175 /*-----------------------------------------------------------------------*/
176  
177 #if !_FS_READONLY
178 static
179 BOOL put_cluster ( /* TRUE: successful, FALSE: failed */
180 CLUST clust, /* Cluster# to change */
181 CLUST val /* New value to mark the cluster */
182 )
183 {
184 WORD bc;
185 BYTE *p;
186 DWORD fatsect;
187 FATFS *fs = FatFs;
188  
189  
190 fatsect = fs->fatbase;
191 switch (fs->fs_type) {
192 case FS_FAT12 :
193 bc = (WORD)clust * 3 / 2;
194 if (!move_window(fatsect + bc / 512)) return FALSE;
195 p = &fs->win[bc % 512];
196 *p = (clust & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
197 bc++;
198 fs->winflag = 1;
199 if (!move_window(fatsect + bc / 512)) return FALSE;
200 p = &fs->win[bc % 512];
201 *p = (clust & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
202 break;
203  
204 case FS_FAT16 :
205 if (!move_window(fatsect + clust / 256)) return FALSE;
206 ST_WORD(&fs->win[((WORD)clust * 2) % 512], (WORD)val);
207 break;
208 #if _FAT32
209 case FS_FAT32 :
210 if (!move_window(fatsect + clust / 128)) return FALSE;
211 ST_DWORD(&fs->win[((WORD)clust * 4) % 512], val);
212 break;
213 #endif
214 default :
215 return FALSE;
216 }
217 fs->winflag = 1;
218 return TRUE;
219 }
220 #endif /* !_FS_READONLY */
221  
222  
223  
224  
225 /*-----------------------------------------------------------------------*/
226 /* Remove a cluster chain */
227 /*-----------------------------------------------------------------------*/
228  
229 #if !_FS_READONLY
230 static
231 BOOL remove_chain ( /* TRUE: successful, FALSE: failed */
232 CLUST clust /* Cluster# to remove chain from */
233 )
234 {
235 CLUST nxt;
236 FATFS *fs = FatFs;
237  
238  
239 while (clust >= 2 && clust < fs->max_clust) {
240 nxt = get_cluster(clust);
241 if (nxt == 1) return FALSE;
242 if (!put_cluster(clust, 0)) return FALSE;
243 if (fs->free_clust != (CLUST)0xFFFFFFFF) {
244 fs->free_clust++;
245 #if _USE_FSINFO
246 fs->fsi_flag = 1;
247 #endif
248 }
249 clust = nxt;
250 }
251 return TRUE;
252 }
253 #endif
254  
255  
256  
257  
258 /*-----------------------------------------------------------------------*/
259 /* Stretch or create a cluster chain */
260 /*-----------------------------------------------------------------------*/
261  
262 #if !_FS_READONLY
263 static
264 CLUST create_chain ( /* 0: no free cluster, 1: error, >=2: new cluster number */
265 CLUST clust /* Cluster# to stretch, 0 means create new */
266 )
267 {
268 CLUST cstat, ncl, scl, mcl;
269 FATFS *fs = FatFs;
270  
271  
272 mcl = fs->max_clust;
273 if (clust == 0) { /* Create new chain */
274 scl = fs->last_clust; /* Get last allocated cluster */
275 if (scl < 2 || scl >= mcl) scl = 1;
276 }
277 else { /* Stretch existing chain */
278 cstat = get_cluster(clust); /* Check the cluster status */
279 if (cstat < 2) return 1; /* It is an invalid cluster */
280 if (cstat < mcl) return cstat; /* It is already followed by next cluster */
281 scl = clust;
282 }
283  
284 ncl = scl; /* Start cluster */
285 for (;;) {
286 ncl++; /* Next cluster */
287 if (ncl >= mcl) { /* Wrap around */
288 ncl = 2;
289 if (ncl > scl) return 0; /* No free custer */
290 }
291 cstat = get_cluster(ncl); /* Get the cluster status */
292 if (cstat == 0) break; /* Found a free cluster */
293 if (cstat == 1) return 1; /* Any error occured */
294 if (ncl == scl) return 0; /* No free custer */
295 }
296  
297 if (!put_cluster(ncl, (CLUST)0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */
298 if (clust && !put_cluster(clust, ncl)) return 1; /* Link it to previous one if needed */
299  
300 fs->last_clust = ncl; /* Update fsinfo */
301 if (fs->free_clust != (CLUST)0xFFFFFFFF) {
302 fs->free_clust--;
303 #if _USE_FSINFO
304 fs->fsi_flag = 1;
305 #endif
306 }
307  
308 return ncl; /* Return new cluster number */
309 }
310 #endif /* !_FS_READONLY */
311  
312  
313  
314  
315 /*-----------------------------------------------------------------------*/
316 /* Get sector# from cluster# */
317 /*-----------------------------------------------------------------------*/
318  
319 static
320 DWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */
321 CLUST clust /* Cluster# to be converted */
322 )
323 {
324 FATFS *fs = FatFs;
325  
326  
327 clust -= 2;
328 if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */
329 return (DWORD)clust * fs->sects_clust + fs->database;
330 }
331  
332  
333  
334  
335 /*-----------------------------------------------------------------------*/
336 /* Move directory pointer to next */
337 /*-----------------------------------------------------------------------*/
338  
339 static
340 BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */
341 DIR *dirobj /* Pointer to directory object */
342 )
343 {
344 CLUST clust;
345 WORD idx;
346 FATFS *fs = FatFs;
347  
348  
349 idx = dirobj->index + 1;
350 if ((idx & 15) == 0) { /* Table sector changed? */
351 dirobj->sect++; /* Next sector */
352 if (!dirobj->clust) { /* In static table */
353 if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */
354 } else { /* In dynamic table */
355 if (((idx / 16) & (fs->sects_clust - 1)) == 0) { /* Cluster changed? */
356 clust = get_cluster(dirobj->clust); /* Get next cluster */
357 if (clust < 2 || clust >= fs->max_clust) /* Reached to end of table */
358 return FALSE;
359 dirobj->clust = clust; /* Initialize for new cluster */
360 dirobj->sect = clust2sect(clust);
361 }
362 }
363 }
364 dirobj->index = idx; /* Lower 4 bit of dirobj->index indicates offset in dirobj->sect */
365 return TRUE;
366 }
367  
368  
369  
370  
371 /*-----------------------------------------------------------------------*/
372 /* Get file status from directory entry */
373 /*-----------------------------------------------------------------------*/
374  
375 #if _FS_MINIMIZE <= 1
376 static
377 void get_fileinfo ( /* No return code */
378 FILINFO *finfo, /* Ptr to store the File Information */
379 const BYTE *dir /* Ptr to the directory entry */
380 )
381 {
382 BYTE n, c, a;
383 char *p;
384  
385  
386 p = &finfo->fname[0];
387 a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */
388 for (n = 0; n < 8; n++) { /* Convert file name (body) */
389 c = dir[n];
390 if (c == ' ') break;
391 if (c == 0x05) c = 0xE5;
392 if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20;
393 *p++ = c;
394 }
395 if (dir[8] != ' ') { /* Convert file name (extension) */
396 *p++ = '.';
397 for (n = 8; n < 11; n++) {
398 c = dir[n];
399 if (c == ' ') break;
400 if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20;
401 *p++ = c;
402 }
403 }
404 *p = '\0';
405  
406 finfo->fattrib = dir[DIR_Attr]; /* Attribute */
407 finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size */
408 finfo->fdate = LD_WORD(&dir[DIR_WrtDate]); /* Date */
409 finfo->ftime = LD_WORD(&dir[DIR_WrtTime]); /* Time */
410 }
411 #endif /* _FS_MINIMIZE <= 1 */
412  
413  
414  
415  
416 /*-----------------------------------------------------------------------*/
417 /* Pick a paragraph and create the name in format of directory entry */
418 /*-----------------------------------------------------------------------*/
419  
420 static
421 char make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */
422 const char **path, /* Pointer to the file path pointer */
423 char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */
424 )
425 {
426 BYTE n, t, c, a, b;
427  
428  
429 memset(dirname, ' ', 8+3); /* Fill buffer with spaces */
430 a = 0; b = 0x18; /* NT flag */
431 n = 0; t = 8;
432 for (;;) {
433 c = *(*path)++;
434 if (c == '\0' || c == '/') { /* Reached to end of str or directory separator */
435 if (n == 0) break;
436 dirname[11] = _USE_NTFLAG ? (a & b) : 0;
437 return c;
438 }
439 if (c <= ' ' || c == 0x7F) break; /* Reject invisible chars */
440 if (c == '.') {
441 if (!(a & 1) && n >= 1 && n <= 8) { /* Enter extension part */
442 n = 8; t = 11; continue;
443 }
444 break;
445 }
446 if (_USE_SJIS &&
447 ((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code */
448 (c >= 0xE0 && c <= 0xFC))) {
449 if (n == 0 && c == 0xE5) /* Change heading \xE5 to \x05 */
450 c = 0x05;
451 a ^= 1; goto md_l2;
452 }
453 if (c == '"') break; /* Reject " */
454 if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */
455 if (c <= ',') break; /* Reject * + , */
456 if (c <= '9') goto md_l1; /* Accept - 0-9 */
457 if (c <= '?') break; /* Reject : ; < = > ? */
458 if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */
459 if (c == '|') break; /* Reject | */
460 if (c >= '[' && c <= ']') break;/* Reject [ \ ] */
461 if (_USE_NTFLAG && c >= 'A' && c <= 'Z')
462 (t == 8) ? (b &= ~0x08) : (b &= ~0x10);
463 if (c >= 'a' && c <= 'z') { /* Convert to upper case */
464 c -= 0x20;
465 if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10);
466 }
467 }
468 md_l1:
469 a &= ~1;
470 md_l2:
471 if (n >= t) break;
472 dirname[n++] = c;
473 }
474 return 1;
475 }
476  
477  
478  
479 /*-----------------------------------------------------------------------*/
480 /* Trace a file path */
481 /*-----------------------------------------------------------------------*/
482  
483 static
484 FRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */
485 DIR *dirobj, /* Pointer to directory object to return last directory */
486 char *fn, /* Pointer to last segment name to return */
487 const char *path, /* Full-path string to trace a file or directory */
488 BYTE **dir /* Directory pointer in Win[] to retutn */
489 )
490 {
491 CLUST clust;
492 char ds;
493 BYTE *dptr = NULL;
494 FATFS *fs = FatFs;
495  
496 /* Initialize directory object */
497 clust = fs->dirbase;
498 #if _FAT32
499 if (fs->fs_type == FS_FAT32) {
500 dirobj->clust = dirobj->sclust = clust;
501 dirobj->sect = clust2sect(clust);
502 } else
503 #endif
504 {
505 dirobj->clust = dirobj->sclust = 0;
506 dirobj->sect = clust;
507 }
508 dirobj->index = 0;
509 dirobj->fs = fs;
510  
511 if (*path == '\0') { /* Null path means the root directory */
512 *dir = NULL; return FR_OK;
513 }
514  
515 for (;;) {
516 ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */
517 if (ds == 1) return FR_INVALID_NAME;
518 for (;;) {
519 if (!move_window(dirobj->sect)) return FR_RW_ERROR;
520 dptr = &fs->win[(dirobj->index & 15) * 32]; /* Pointer to the directory entry */
521 if (dptr[DIR_Name] == 0) /* Has it reached to end of dir? */
522 return !ds ? FR_NO_FILE : FR_NO_PATH;
523 if (dptr[DIR_Name] != 0xE5 /* Matched? */
524 && !(dptr[DIR_Attr] & AM_VOL)
525 && !memcmp(&dptr[DIR_Name], fn, 8+3) ) break;
526 if (!next_dir_entry(dirobj)) /* Next directory pointer */
527 return !ds ? FR_NO_FILE : FR_NO_PATH;
528 }
529 if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */
530 if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */
531 clust = /* Get cluster# of the directory */
532 #if _FAT32
533 ((DWORD)LD_WORD(&dptr[DIR_FstClusHI]) << 16) |
534 #endif
535 LD_WORD(&dptr[DIR_FstClusLO]);
536 dirobj->clust = dirobj->sclust = clust; /* Restart scannig with the new directory */
537 dirobj->sect = clust2sect(clust);
538 dirobj->index = 2;
539 }
540 }
541  
542  
543  
544 /*-----------------------------------------------------------------------*/
545 /* Reserve a directory entry */
546 /*-----------------------------------------------------------------------*/
547  
548 #if !_FS_READONLY
549 static
550 FRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */
551 DIR *dirobj, /* Target directory to create new entry */
552 BYTE **dir /* Pointer to pointer to created entry to retutn */
553 )
554 {
555 CLUST clust;
556 DWORD sector;
557 BYTE c, n, *dptr;
558 FATFS *fs = FatFs;
559  
560  
561 /* Re-initialize directory object */
562 clust = dirobj->sclust;
563 if (clust) { /* Dyanmic directory table */
564 dirobj->clust = clust;
565 dirobj->sect = clust2sect(clust);
566 } else { /* Static directory table */
567 dirobj->sect = fs->dirbase;
568 }
569 dirobj->index = 0;
570  
571 do {
572 if (!move_window(dirobj->sect)) return FR_RW_ERROR;
573 dptr = &fs->win[(dirobj->index & 15) * 32]; /* Pointer to the directory entry */
574 c = dptr[DIR_Name];
575 if (c == 0 || c == 0xE5) { /* Found an empty entry! */
576 *dir = dptr; return FR_OK;
577 }
578 } while (next_dir_entry(dirobj)); /* Next directory pointer */
579 /* Reached to end of the directory table */
580  
581 /* Abort when static table or could not stretch dynamic table */
582 if (!clust || !(clust = create_chain(dirobj->clust))) return FR_DENIED;
583 if (clust == 1 || !move_window(0)) return FR_RW_ERROR;
584  
585 fs->winsect = sector = clust2sect(clust); /* Cleanup the expanded table */
586 memset(fs->win, 0, 512);
587 for (n = fs->sects_clust; n; n--) {
588 if (disk_write(0, fs->win, sector, 1) != RES_OK)
589 return FR_RW_ERROR;
590 sector++;
591 }
592 fs->winflag = 1;
593 *dir = fs->win;
594 return FR_OK;
595 }
596 #endif /* !_FS_READONLY */
597  
598  
599  
600  
601 /*-----------------------------------------------------------------------*/
602 /* Load boot record and check if it is a FAT boot record */
603 /*-----------------------------------------------------------------------*/
604  
605 static
606 BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */
607 DWORD sect /* Sector# to check if it is a FAT boot record or not */
608 )
609 {
610 FATFS *fs = FatFs;
611  
612 if (disk_read(0, fs->win, sect, 1) != RES_OK) /* Load boot record */
613 return 2;
614 if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature */
615 return 2;
616  
617 if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */
618 return 0;
619 #if _FAT32
620 if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80))
621 return 0;
622 #endif
623 return 1;
624 }
625  
626  
627  
628  
629 /*-----------------------------------------------------------------------*/
630 /* Make sure that the file system is valid */
631 /*-----------------------------------------------------------------------*/
632  
633 static
634 FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */
635 const char **path, /* Pointer to pointer to the path name (drive number) */
636 BYTE chk_wp /* !=0: Check media write protection for wrinting fuctions */
637 )
638 {
639 BYTE fmt;
640 DSTATUS stat;
641 DWORD bootsect, fatsize, totalsect, maxclust;
642 const char *p = *path;
643 FATFS *fs = FatFs;
644  
645  
646  
647 while (*p == ' ') p++; /* Strip leading spaces */
648 if (*p == '/') p++; /* Strip heading slash */
649 *path = p; /* Return pointer to the path name */
650  
651 /* Is the file system object registered? */
652 if (!fs) return FR_NOT_ENABLED;
653  
654 /* Chekck if the logical drive has been mounted or not */
655 if (fs->fs_type) {
656 stat = disk_status(0);
657 if (!(stat & STA_NOINIT)) { /* If the physical drive is kept initialized */
658 #if !_FS_READONLY
659 if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
660 return FR_WRITE_PROTECTED;
661 #endif
662 return FR_OK; /* The file system object is valid */
663 }
664 }
665  
666 /* The logical drive has not been mounted, following code attempts to mount the logical drive */
667  
668 memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */
669 stat = disk_initialize(0); /* Initialize low level disk I/O layer */
670 if (stat & STA_NOINIT) /* Check if the drive is ready */
671 return FR_NOT_READY;
672 #if !_FS_READONLY
673 if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
674 return FR_WRITE_PROTECTED;
675 #endif
676  
677 /* Search FAT partition on the drive */
678 fmt = check_fs(bootsect = 0); /* Check sector 0 as an SFD format */
679 if (fmt == 1) { /* Not a FAT boot record, it may be patitioned */
680 /* Check a partition listed in top of the partition table */
681 if (fs->win[MBR_Table+4]) { /* Is the 1st partition existing? */
682 bootsect = LD_DWORD(&fs->win[MBR_Table+8]); /* Partition offset in LBA */
683 fmt = check_fs(bootsect); /* Check the partition */
684 }
685 }
686 if (fmt || LD_WORD(&fs->win[BPB_BytsPerSec]) != 512) /* No valid FAT patition is found */
687 return FR_NO_FILESYSTEM;
688  
689 /* Initialize the file system object */
690 fatsize = LD_WORD(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT */
691 if (!fatsize) fatsize = LD_DWORD(&fs->win[BPB_FATSz32]);
692 fs->sects_fat = (CLUST)fatsize;
693 fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */
694 fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */
695 fs->fatbase = bootsect + LD_WORD(&fs->win[BPB_RsvdSecCnt]); /* FAT start sector (lba) */
696 fs->sects_clust = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */
697 fs->n_rootdir = LD_WORD(&fs->win[BPB_RootEntCnt]); /* Nmuber of root directory entries */
698 totalsect = LD_WORD(&fs->win[BPB_TotSec16]); /* Number of sectors on the file system */
699 if (!totalsect) totalsect = LD_DWORD(&fs->win[BPB_TotSec32]);
700 fs->max_clust = maxclust = (totalsect /* Last cluster# + 1 */
701 - LD_WORD(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / 16
702 ) / fs->sects_clust + 2;
703  
704 fmt = FS_FAT12; /* Determine the FAT sub type */
705 if (maxclust > 0xFF7) fmt = FS_FAT16;
706 if (maxclust > 0xFFF7)
707 #if !_FAT32
708 return FR_NO_FILESYSTEM;
709 #else
710 fmt = FS_FAT32;
711 if (fmt == FS_FAT32)
712 fs->dirbase = LD_DWORD(&fs->win[BPB_RootClus]); /* Root directory start cluster */
713 else
714 #endif
715 fs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */
716 fs->database = fs->fatbase + fatsize + fs->n_rootdir / 16; /* Data start sector (lba) */
717 fs->fs_type = fmt; /* FAT sub-type */
718  
719 #if !_FS_READONLY
720 fs->free_clust = (CLUST)0xFFFFFFFF;
721 #if _USE_FSINFO
722 /* Load fsinfo sector if needed */
723 if (fmt == FS_FAT32) {
724 fs->fsi_sector = bootsect + LD_WORD(&fs->win[BPB_FSInfo]);
725 if (disk_read(0, fs->win, fs->fsi_sector, 1) == RES_OK &&
726 LD_WORD(&fs->win[BS_55AA]) == 0xAA55 &&
727 LD_DWORD(&fs->win[FSI_LeadSig]) == 0x41615252 &&
728 LD_DWORD(&fs->win[FSI_StrucSig]) == 0x61417272) {
729 fs->last_clust = LD_DWORD(&fs->win[FSI_Nxt_Free]);
730 fs->free_clust = LD_DWORD(&fs->win[FSI_Free_Count]);
731 }
732 }
733 #endif
734 #endif
735 fs->id = ++fsid; /* File system mount ID */
736 return FR_OK;
737 }
738  
739  
740  
741  
742 /*-----------------------------------------------------------------------*/
743 /* Check if the file/dir object is valid or not */
744 /*-----------------------------------------------------------------------*/
745  
746 static
747 FRESULT validate ( /* FR_OK(0): The id is valid, !=0: Not valid */
748 const FATFS *fs, /* Pointer to the file system object */
749 WORD id /* id member of the target object to be checked */
750 )
751 {
752 if (!fs || fs->id != id)
753 return FR_INVALID_OBJECT;
754 if (disk_status(0) & STA_NOINIT)
755 return FR_NOT_READY;
756  
757 return FR_OK;
758 }
759  
760  
761  
762  
763 /*--------------------------------------------------------------------------
764  
765 Public Functions
766  
767 --------------------------------------------------------------------------*/
768  
769  
770 /*-----------------------------------------------------------------------*/
771 /* Mount/Unmount a Locical Drive */
772 /*-----------------------------------------------------------------------*/
773  
774 FRESULT f_mount (
775 BYTE drv, /* Logical drive number to be mounted/unmounted */
776 FATFS *fs /* Pointer to new file system object (NULL for unmount)*/
777 )
778 {
779 FATFS *fsobj;
780  
781  
782 if (drv) return FR_INVALID_DRIVE;
783 fsobj = FatFs;
784 FatFs = fs;
785 if (fsobj) memset(fsobj, 0, sizeof(FATFS));
786 if (fs) memset(fs, 0, sizeof(FATFS));
787  
788 return FR_OK;
789 }
790  
791  
792  
793  
794 /*-----------------------------------------------------------------------*/
795 /* Open or Create a File */
796 /*-----------------------------------------------------------------------*/
797  
798 FRESULT f_open (
799 FIL *fp, /* Pointer to the blank file object */
800 const char *path, /* Pointer to the file name */
801 BYTE mode /* Access mode and file open mode flags */
802 )
803 {
804 FRESULT res;
805 BYTE *dir;
806 DIR dirobj;
807 char fn[8+3+1];
808 FATFS *fs = FatFs;
809  
810  
811 fp->fs = NULL;
812 #if !_FS_READONLY
813 mode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW);
814 res = auto_mount(&path, (BYTE)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)));
815 #else
816 mode &= FA_READ;
817 res = auto_mount(&path, 0);
818 #endif
819 if (res != FR_OK) return res;
820  
821 /* Trace the file path */
822 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
823  
824 #if !_FS_READONLY
825 /* Create or Open a File */
826 if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) {
827 CLUST rs;
828 DWORD dw;
829 if (res != FR_OK) { /* No file, create new */
830 if (res != FR_NO_FILE) return res;
831 res = reserve_direntry(&dirobj, &dir);
832 if (res != FR_OK) return res;
833 memset(dir, 0, 32); /* Initialize the new entry */
834 memcpy(&dir[DIR_Name], fn, 8+3);
835 dir[DIR_NTres] = fn[11];
836 mode |= FA_CREATE_ALWAYS;
837 } else { /* Any object is already existing */
838 if (mode & FA_CREATE_NEW) /* Cannot create new */
839 return FR_EXIST;
840 if (dir == NULL || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite (R/O or DIR) */
841 return FR_DENIED;
842 if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero */
843 #if _FAT32
844 rs = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]);
845 ST_WORD(&dir[DIR_FstClusHI], 0);
846 #else
847 rs = LD_WORD(&dir[DIR_FstClusLO]);
848 #endif
849 ST_WORD(&dir[DIR_FstClusLO], 0); /* cluster = 0 */
850 ST_DWORD(&dir[DIR_FileSize], 0); /* size = 0 */
851 fs->winflag = 1;
852 dw = fs->winsect; /* Remove the cluster chain */
853 if (!remove_chain(rs) || !move_window(dw))
854 return FR_RW_ERROR;
855 fs->last_clust = rs - 1; /* Reuse the cluster hole */
856 }
857 }
858 if (mode & FA_CREATE_ALWAYS) {
859 dir[DIR_Attr] = AM_ARC; /* New attribute */
860 dw = get_fattime();
861 ST_DWORD(&dir[DIR_WrtTime], dw); /* Updated time */
862 ST_DWORD(&dir[DIR_CrtTime], dw); /* Created time */
863 fs->winflag = 1;
864 }
865 }
866 /* Open a File */
867 else {
868 #endif /* !_FS_READONLY */
869 if (res != FR_OK) return res; /* Trace failed */
870 if (dir == NULL || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */
871 return FR_NO_FILE;
872 #if !_FS_READONLY
873 if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
874 return FR_DENIED;
875 }
876  
877 fp->dir_sect = fs->winsect; /* Pointer to the directory entry */
878 fp->dir_ptr = dir;
879 #endif
880 fp->flag = mode; /* File access mode */
881 fp->org_clust = /* File start cluster */
882 #if _FAT32
883 ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |
884 #endif
885 LD_WORD(&dir[DIR_FstClusLO]);
886 fp->fsize = LD_DWORD(&dir[DIR_FileSize]); /* File size */
887 fp->fptr = 0; /* File ptr */
888 fp->sect_clust = 1; /* Sector counter */
889 fp->fs = fs; fp->id = fs->id; /* Owner file system object of the file */
890  
891 return FR_OK;
892 }
893  
894  
895  
896  
897 /*-----------------------------------------------------------------------*/
898 /* Read File */
899 /*-----------------------------------------------------------------------*/
900  
901 FRESULT f_read (
902 FIL *fp, /* Pointer to the file object */
903 void *buff, /* Pointer to data buffer */
904 WORD btr, /* Number of bytes to read */
905 WORD *br /* Pointer to number of bytes read */
906 )
907 {
908 DWORD sect, remain;
909 WORD rcnt;
910 CLUST clust;
911 BYTE cc, *rbuff = buff;
912 FRESULT res;
913 FATFS *fs = fp->fs;
914  
915  
916 *br = 0;
917 res = validate(fs, fp->id); /* Check validity of the object */
918 if (res) return res;
919 if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */
920 if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */
921 remain = fp->fsize - fp->fptr;
922 if (btr > remain) btr = (WORD)remain; /* Truncate read count by number of bytes left */
923  
924 for ( ; btr; /* Repeat until all data transferred */
925 rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
926 if ((fp->fptr % 512) == 0) { /* On the sector boundary */
927 if (--fp->sect_clust) { /* Decrement left sector counter */
928 sect = fp->curr_sect + 1; /* Get current sector */
929 } else { /* On the cluster boundary, get next cluster */
930 clust = (fp->fptr == 0) ?
931 fp->org_clust : get_cluster(fp->curr_clust);
932 if (clust < 2 || clust >= fs->max_clust)
933 goto fr_error;
934 fp->curr_clust = clust; /* Current cluster */
935 sect = clust2sect(clust); /* Get current sector */
936 fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */
937 }
938 fp->curr_sect = sect; /* Update current sector */
939 cc = btr / 512; /* When left bytes >= 512, */
940 if (cc) { /* Read maximum contiguous sectors directly */
941 if (cc > fp->sect_clust) cc = fp->sect_clust;
942 if (disk_read(0, rbuff, sect, cc) != RES_OK)
943 goto fr_error;
944 fp->sect_clust -= cc - 1;
945 fp->curr_sect += cc - 1;
946 rcnt = cc * 512; continue;
947 }
948 }
949 if (!move_window(fp->curr_sect)) goto fr_error; /* Move sector window */
950 rcnt = 512 - (WORD)(fp->fptr % 512); /* Copy fractional bytes from sector window */
951 if (rcnt > btr) rcnt = btr;
952 memcpy(rbuff, &fs->win[(WORD)fp->fptr % 512], rcnt);
953 }
954  
955 return FR_OK;
956  
957 fr_error: /* Abort this function due to an unrecoverable error */
958 fp->flag |= FA__ERROR;
959 return FR_RW_ERROR;
960 }
961  
962  
963  
964  
965 #if !_FS_READONLY
966 /*-----------------------------------------------------------------------*/
967 /* Write File */
968 /*-----------------------------------------------------------------------*/
969  
970 FRESULT f_write (
971 FIL *fp, /* Pointer to the file object */
972 const void *buff, /* Pointer to the data to be written */
973 WORD btw, /* Number of bytes to write */
974 WORD *bw /* Pointer to number of bytes written */
975 )
976 {
977 DWORD sect;
978 WORD wcnt;
979 CLUST clust;
980 BYTE cc;
981 FRESULT res;
982 const BYTE *wbuff = buff;
983 FATFS *fs = fp->fs;
984  
985  
986 *bw = 0;
987 res = validate(fs, fp->id); /* Check validity of the object */
988 if (res) return res;
989 if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */
990 if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */
991 if (fp->fsize + btw < fp->fsize) return FR_OK; /* File size cannot reach 4GB */
992  
993 for ( ; btw; /* Repeat until all data transferred */
994 wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
995 if ((fp->fptr % 512) == 0) { /* On the sector boundary */
996 if (--(fp->sect_clust)) { /* Decrement left sector counter */
997 sect = fp->curr_sect + 1; /* Get current sector */
998 } else { /* On the cluster boundary, get next cluster */
999 if (fp->fptr == 0) { /* Is top of the file */
1000 clust = fp->org_clust;
1001 if (clust == 0) /* No cluster is created yet */
1002 fp->org_clust = clust = create_chain(0); /* Create a new cluster chain */
1003 } else { /* Middle or end of file */
1004 clust = create_chain(fp->curr_clust); /* Trace or streach cluster chain */
1005 }
1006 if (clust == 0) break; /* Disk full */
1007 if (clust == 1 || clust >= fs->max_clust) goto fw_error;
1008 fp->curr_clust = clust; /* Current cluster */
1009 sect = clust2sect(clust); /* Get current sector */
1010 fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */
1011 }
1012 fp->curr_sect = sect; /* Update current sector */
1013 cc = btw / 512; /* When left bytes >= 512, */
1014 if (cc) { /* Write maximum contiguous sectors directly */
1015 if (cc > fp->sect_clust) cc = fp->sect_clust;
1016 if (disk_write(0, wbuff, sect, cc) != RES_OK)
1017 goto fw_error;
1018 fp->sect_clust -= cc - 1;
1019 fp->curr_sect += cc - 1;
1020 wcnt = cc * 512; continue;
1021 }
1022 if (fp->fptr >= fp->fsize) { /* Flush R/W window if needed */
1023 if (!move_window(0)) goto fw_error;
1024 fs->winsect = fp->curr_sect;
1025 }
1026 }
1027 if (!move_window(fp->curr_sect)) /* Move sector window */
1028 goto fw_error;
1029 wcnt = 512 - (WORD)(fp->fptr % 512); /* Copy fractional bytes bytes to sector window */
1030 if (wcnt > btw) wcnt = btw;
1031 memcpy(&fs->win[(WORD)fp->fptr % 512], wbuff, wcnt);
1032 fs->winflag = 1;
1033 }
1034  
1035 if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
1036 fp->flag |= FA__WRITTEN; /* Set file changed flag */
1037 return FR_OK;
1038  
1039 fw_error: /* Abort this function due to an unrecoverable error */
1040 fp->flag |= FA__ERROR;
1041 return FR_RW_ERROR;
1042 }
1043  
1044  
1045  
1046  
1047 /*-----------------------------------------------------------------------*/
1048 /* Synchronize between File and Disk */
1049 /*-----------------------------------------------------------------------*/
1050  
1051 FRESULT f_sync (
1052 FIL *fp /* Pointer to the file object */
1053 )
1054 {
1055 DWORD tim;
1056 BYTE *dir;
1057 FRESULT res;
1058 FATFS *fs = fp->fs;
1059  
1060  
1061 res = validate(fs, fp->id); /* Check validity of the object */
1062 if (res == FR_OK) {
1063 if (fp->flag & FA__WRITTEN) { /* Has the file been written? */
1064 /* Update the directory entry */
1065 if (!move_window(fp->dir_sect))
1066 return FR_RW_ERROR;
1067 dir = fp->dir_ptr;
1068 dir[DIR_Attr] |= AM_ARC; /* Set archive bit */
1069 ST_DWORD(&dir[DIR_FileSize], fp->fsize); /* Update file size */
1070 ST_WORD(&dir[DIR_FstClusLO], fp->org_clust); /* Update start cluster */
1071 #if _FAT32
1072 ST_WORD(&dir[DIR_FstClusHI], fp->org_clust >> 16);
1073 #endif
1074 tim = get_fattime(); /* Updated time */
1075 ST_DWORD(&dir[DIR_WrtTime], tim);
1076 fp->flag &= ~FA__WRITTEN;
1077 res = sync();
1078 }
1079 }
1080 return res;
1081 }
1082  
1083 #endif /* !_FS_READONLY */
1084  
1085  
1086  
1087  
1088 /*-----------------------------------------------------------------------*/
1089 /* Close File */
1090 /*-----------------------------------------------------------------------*/
1091  
1092 FRESULT f_close (
1093 FIL *fp /* Pointer to the file object to be closed */
1094 )
1095 {
1096 FRESULT res;
1097  
1098  
1099 #if !_FS_READONLY
1100 res = f_sync(fp);
1101 #else
1102 res = validate(fp->fs, fp->id);
1103 #endif
1104 if (res == FR_OK)
1105 fp->fs = NULL;
1106  
1107 return res;
1108 }
1109  
1110  
1111  
1112  
1113 #if _FS_MINIMIZE <= 2
1114 /*-----------------------------------------------------------------------*/
1115 /* Seek File Pointer */
1116 /*-----------------------------------------------------------------------*/
1117  
1118 FRESULT f_lseek (
1119 FIL *fp, /* Pointer to the file object */
1120 DWORD ofs /* File pointer from top of file */
1121 )
1122 {
1123 CLUST clust;
1124 DWORD csize;
1125 BYTE csect;
1126 FRESULT res;
1127 FATFS *fs = fp->fs;
1128  
1129  
1130 res = validate(fs, fp->id); /* Check validity of the object */
1131 if (res) return res;
1132  
1133 if (fp->flag & FA__ERROR) return FR_RW_ERROR;
1134 #if !_FS_READONLY
1135 if (ofs > fp->fsize && !(fp->flag & FA_WRITE))
1136 #else
1137 if (ofs > fp->fsize)
1138 #endif
1139 ofs = fp->fsize;
1140 fp->fptr = 0; fp->sect_clust = 1; /* Set file R/W pointer to top of the file */
1141  
1142 /* Move file R/W pointer if needed */
1143 if (ofs) {
1144 clust = fp->org_clust; /* Get start cluster */
1145 #if !_FS_READONLY
1146 if (!clust) { /* If the file does not have a cluster chain, create new cluster chain */
1147 clust = create_chain(0);
1148 if (clust == 1) goto fk_error;
1149 fp->org_clust = clust;
1150 }
1151 #endif
1152 if (clust) { /* If the file has a cluster chain, it can be followed */
1153 csize = (DWORD)fs->sects_clust * 512; /* Cluster size in unit of byte */
1154 for (;;) { /* Loop to skip leading clusters */
1155 fp->curr_clust = clust; /* Update current cluster */
1156 if (ofs <= csize) break;
1157 #if !_FS_READONLY
1158 if (fp->flag & FA_WRITE) /* Check if in write mode or not */
1159 clust = create_chain(clust); /* Force streached if in write mode */
1160 else
1161 #endif
1162 clust = get_cluster(clust); /* Only follow cluster chain if not in write mode */
1163 if (clust == 0) { /* Stop if could not follow the cluster chain */
1164 ofs = csize; break;
1165 }
1166 if (clust == 1 || clust >= fs->max_clust) goto fk_error;
1167 fp->fptr += csize; /* Update R/W pointer */
1168 ofs -= csize;
1169 }
1170 csect = (BYTE)((ofs - 1) / 512); /* Sector offset in the cluster */
1171 fp->curr_sect = clust2sect(clust) + csect; /* Current sector */
1172 fp->sect_clust = fs->sects_clust - csect; /* Left sector counter in the cluster */
1173 fp->fptr += ofs; /* Update file R/W pointer */
1174 }
1175 }
1176 #if !_FS_READONLY
1177 if ((fp->flag & FA_WRITE) && fp->fptr > fp->fsize) { /* Set updated flag if in write mode */
1178 fp->fsize = fp->fptr;
1179 fp->flag |= FA__WRITTEN;
1180 }
1181 #endif
1182  
1183 return FR_OK;
1184  
1185 fk_error: /* Abort this function due to an unrecoverable error */
1186 fp->flag |= FA__ERROR;
1187 return FR_RW_ERROR;
1188 }
1189  
1190  
1191  
1192  
1193 #if _FS_MINIMIZE <= 1
1194 /*-----------------------------------------------------------------------*/
1195 /* Open a directroy */
1196 /*-----------------------------------------------------------------------*/
1197  
1198 FRESULT f_opendir (
1199 DIR *dirobj, /* Pointer to directory object to create */
1200 const char *path /* Pointer to the directory path */
1201 )
1202 {
1203 BYTE *dir;
1204 char fn[8+3+1];
1205 FRESULT res;
1206 FATFS *fs = FatFs;
1207  
1208  
1209 res = auto_mount(&path, 0);
1210 if (res != FR_OK) return res;
1211  
1212 res = trace_path(dirobj, fn, path, &dir); /* Trace the directory path */
1213 if (res == FR_OK) { /* Trace completed */
1214 if (dir != NULL) { /* It is not the root dir */
1215 if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */
1216 dirobj->clust =
1217 #if _FAT32
1218 ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |
1219 #endif
1220 LD_WORD(&dir[DIR_FstClusLO]);
1221 dirobj->sect = clust2sect(dirobj->clust);
1222 dirobj->index = 2;
1223 } else { /* The entry is not a directory */
1224 res = FR_NO_FILE;
1225 }
1226 }
1227 dirobj->id = fs->id;
1228 }
1229 return res;
1230 }
1231  
1232  
1233  
1234  
1235 /*-----------------------------------------------------------------------*/
1236 /* Read Directory Entry in Sequense */
1237 /*-----------------------------------------------------------------------*/
1238  
1239 FRESULT f_readdir (
1240 DIR *dirobj, /* Pointer to the directory object */
1241 FILINFO *finfo /* Pointer to file information to return */
1242 )
1243 {
1244 BYTE *dir, c;
1245 FRESULT res;
1246 FATFS *fs = dirobj->fs;
1247  
1248  
1249 res = validate(fs, dirobj->id); /* Check validity of the object */
1250 if (res) return res;
1251  
1252 finfo->fname[0] = 0;
1253 while (dirobj->sect) {
1254 if (!move_window(dirobj->sect))
1255 return FR_RW_ERROR;
1256 dir = &fs->win[(dirobj->index & 15) * 32]; /* pointer to the directory entry */
1257 c = dir[DIR_Name];
1258 if (c == 0) break; /* Has it reached to end of dir? */
1259 if (c != 0xE5 && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */
1260 get_fileinfo(finfo, dir);
1261 if (!next_dir_entry(dirobj)) dirobj->sect = 0; /* Next entry */
1262 if (finfo->fname[0]) break; /* Found valid entry */
1263 }
1264  
1265 return FR_OK;
1266 }
1267  
1268  
1269  
1270  
1271 #if _FS_MINIMIZE == 0
1272 /*-----------------------------------------------------------------------*/
1273 /* Get File Status */
1274 /*-----------------------------------------------------------------------*/
1275  
1276 FRESULT f_stat (
1277 const char *path, /* Pointer to the file path */
1278 FILINFO *finfo /* Pointer to file information to return */
1279 )
1280 {
1281 BYTE *dir;
1282 char fn[8+3+1];
1283 FRESULT res;
1284 DIR dirobj;
1285  
1286  
1287 res = auto_mount(&path, 0);
1288 if (res != FR_OK) return res;
1289  
1290 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1291 if (res == FR_OK) { /* Trace completed */
1292 if (dir) /* Found an object */
1293 get_fileinfo(finfo, dir);
1294 else /* It is root dir */
1295 res = FR_INVALID_NAME;
1296 }
1297  
1298 return res;
1299 }
1300  
1301  
1302  
1303  
1304 #if !_FS_READONLY
1305 /*-----------------------------------------------------------------------*/
1306 /* Get Number of Free Clusters */
1307 /*-----------------------------------------------------------------------*/
1308  
1309 FRESULT f_getfree (
1310 const char *drv, /* Logical drive number */
1311 DWORD *nclust, /* Pointer to the double word to return number of free clusters */
1312 FATFS **fatfs /* Pointer to pointer to the file system object to return */
1313 )
1314 {
1315 DWORD n, sect;
1316 CLUST clust;
1317 BYTE fat, f, *p;
1318 FRESULT res;
1319 FATFS *fs;
1320  
1321  
1322 /* Get drive number */
1323 res = auto_mount(&drv, 0);
1324 if (res != FR_OK) return res;
1325 *fatfs = fs = FatFs;
1326  
1327 /* If number of free cluster is valid, return it without cluster scan. */
1328 if (fs->free_clust <= fs->max_clust - 2) {
1329 *nclust = fs->free_clust;
1330 return FR_OK;
1331 }
1332  
1333 /* Count number of free clusters */
1334 fat = fs->fs_type;
1335 n = 0;
1336 if (fat == FS_FAT12) {
1337 clust = 2;
1338 do {
1339 if ((WORD)get_cluster(clust) == 0) n++;
1340 } while (++clust < fs->max_clust);
1341 } else {
1342 clust = fs->max_clust;
1343 sect = fs->fatbase;
1344 f = 0; p = 0;
1345 do {
1346 if (!f) {
1347 if (!move_window(sect++)) return FR_RW_ERROR;
1348 p = fs->win;
1349 }
1350 if (!_FAT32 || fat == FS_FAT16) {
1351 if (LD_WORD(p) == 0) n++;
1352 p += 2; f += 1;
1353 } else {
1354 if (LD_DWORD(p) == 0) n++;
1355 p += 4; f += 2;
1356 }
1357 } while (--clust);
1358 }
1359 fs->free_clust = n;
1360 #if _USE_FSINFO
1361 if (fat == FS_FAT32) fs->fsi_flag = 1;
1362 #endif
1363  
1364 *nclust = n;
1365 return FR_OK;
1366 }
1367  
1368  
1369  
1370  
1371 /*-----------------------------------------------------------------------*/
1372 /* Delete a File or a Directory */
1373 /*-----------------------------------------------------------------------*/
1374  
1375 FRESULT f_unlink (
1376 const char *path /* Pointer to the file or directory path */
1377 )
1378 {
1379 BYTE *dir, *sdir;
1380 DWORD dsect;
1381 char fn[8+3+1];
1382 CLUST dclust;
1383 FRESULT res;
1384 DIR dirobj;
1385 FATFS *fs = FatFs;
1386  
1387  
1388 res = auto_mount(&path, 1);
1389 if (res != FR_OK) return res;
1390  
1391 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1392 if (res != FR_OK) return res; /* Trace failed */
1393 if (dir == NULL) return FR_INVALID_NAME; /* It is the root directory */
1394 if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */
1395 dsect = fs->winsect;
1396 dclust =
1397 #if _FAT32
1398 ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |
1399 #endif
1400 LD_WORD(&dir[DIR_FstClusLO]);
1401 if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */
1402 dirobj.clust = dclust; /* Check if the sub-dir is empty or not */
1403 dirobj.sect = clust2sect(dclust);
1404 dirobj.index = 2;
1405 do {
1406 if (!move_window(dirobj.sect)) return FR_RW_ERROR;
1407 sdir = &fs->win[(dirobj.index & 15) * 32];
1408 if (sdir[DIR_Name] == 0) break;
1409 if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL))
1410 return FR_DENIED; /* The directory is not empty */
1411 } while (next_dir_entry(&dirobj));
1412 }
1413  
1414 if (!move_window(dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */
1415 dir[DIR_Name] = 0xE5;
1416 fs->winflag = 1;
1417 if (!remove_chain(dclust)) return FR_RW_ERROR; /* Remove the cluster chain */
1418  
1419 return sync();
1420 }
1421  
1422  
1423  
1424  
1425 /*-----------------------------------------------------------------------*/
1426 /* Create a Directory */
1427 /*-----------------------------------------------------------------------*/
1428  
1429 FRESULT f_mkdir (
1430 const char *path /* Pointer to the directory path */
1431 )
1432 {
1433 BYTE *dir, *fw, n;
1434 char fn[8+3+1];
1435 DWORD sect, dsect, tim;
1436 CLUST dclust, pclust;
1437 FRESULT res;
1438 DIR dirobj;
1439 FATFS *fs = FatFs;
1440  
1441  
1442 res = auto_mount(&path, 1);
1443 if (res != FR_OK) return res;
1444  
1445 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1446 if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */
1447 if (res != FR_NO_FILE) return res;
1448  
1449 res = reserve_direntry(&dirobj, &dir); /* Reserve a directory entry */
1450 if (res != FR_OK) return res;
1451 sect = fs->winsect;
1452 dclust = create_chain(0); /* Allocate a cluster for new directory table */
1453 if (dclust == 1) return FR_RW_ERROR;
1454 dsect = clust2sect(dclust);
1455 if (!dsect) return FR_DENIED;
1456 if (!move_window(dsect)) return FR_RW_ERROR;
1457  
1458 fw = fs->win;
1459 memset(fw, 0, 512); /* Clear the directory table */
1460 for (n = 1; n < fs->sects_clust; n++) {
1461 if (disk_write(0, fw, ++dsect, 1) != RES_OK)
1462 return FR_RW_ERROR;
1463 }
1464  
1465 memset(&fw[DIR_Name], ' ', 8+3); /* Create "." entry */
1466 fw[DIR_Name] = '.';
1467 fw[DIR_Attr] = AM_DIR;
1468 tim = get_fattime();
1469 ST_DWORD(&fw[DIR_WrtTime], tim);
1470 memcpy(&fw[32], &fw[0], 32); fw[33] = '.'; /* Create ".." entry */
1471 pclust = dirobj.sclust;
1472 #if _FAT32
1473 ST_WORD(&fw[ DIR_FstClusHI], dclust >> 16);
1474 if (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) pclust = 0;
1475 ST_WORD(&fw[32+DIR_FstClusHI], pclust >> 16);
1476 #endif
1477 ST_WORD(&fw[ DIR_FstClusLO], dclust);
1478 ST_WORD(&fw[32+DIR_FstClusLO], pclust);
1479 fs->winflag = 1;
1480  
1481 if (!move_window(sect)) return FR_RW_ERROR;
1482 memset(&dir[0], 0, 32); /* Clean-up the new entry */
1483 memcpy(&dir[DIR_Name], fn, 8+3); /* Name */
1484 dir[DIR_NTres] = fn[11];
1485 dir[DIR_Attr] = AM_DIR; /* Attribute */
1486 ST_DWORD(&dir[DIR_WrtTime], tim); /* Crated time */
1487 ST_WORD(&dir[DIR_FstClusLO], dclust); /* Table start cluster */
1488 #if _FAT32
1489 ST_WORD(&dir[DIR_FstClusHI], dclust >> 16);
1490 #endif
1491  
1492 return sync();
1493 }
1494  
1495  
1496  
1497  
1498 /*-----------------------------------------------------------------------*/
1499 /* Change File Attribute */
1500 /*-----------------------------------------------------------------------*/
1501  
1502 FRESULT f_chmod (
1503 const char *path, /* Pointer to the file path */
1504 BYTE value, /* Attribute bits */
1505 BYTE mask /* Attribute mask to change */
1506 )
1507 {
1508 FRESULT res;
1509 BYTE *dir;
1510 DIR dirobj;
1511 char fn[8+3+1];
1512  
1513  
1514 res = auto_mount(&path, 1);
1515 if (res == FR_OK) {
1516 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1517 if (res == FR_OK) { /* Trace completed */
1518 if (dir == NULL) {
1519 res = FR_INVALID_NAME;
1520 } else {
1521 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
1522 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
1523 res = sync();
1524 }
1525 }
1526 }
1527 return res;
1528 }
1529  
1530  
1531  
1532  
1533 /*-----------------------------------------------------------------------*/
1534 /* Rename File/Directory */
1535 /*-----------------------------------------------------------------------*/
1536  
1537 FRESULT f_rename (
1538 const char *path_old, /* Pointer to the old name */
1539 const char *path_new /* Pointer to the new name */
1540 )
1541 {
1542 FRESULT res;
1543 DWORD sect_old;
1544 BYTE *dir_old, *dir_new, direntry[32-11];
1545 DIR dirobj;
1546 char fn[8+3+1];
1547 FATFS *fs = FatFs;
1548  
1549  
1550 res = auto_mount(&path_old, 1);
1551 if (res != FR_OK) return res;
1552  
1553 res = trace_path(&dirobj, fn, path_old, &dir_old); /* Check old object */
1554 if (res != FR_OK) return res; /* The old object is not found */
1555 if (!dir_old) return FR_NO_FILE;
1556 sect_old = fs->winsect; /* Save the object information */
1557 memcpy(direntry, &dir_old[11], 32-11);
1558  
1559 res = trace_path(&dirobj, fn, path_new, &dir_new); /* Check new object */
1560 if (res == FR_OK) return FR_EXIST; /* The new object name is already existing */
1561 if (res != FR_NO_FILE) return res; /* Is there no old name? */
1562 res = reserve_direntry(&dirobj, &dir_new); /* Reserve a directory entry */
1563 if (res != FR_OK) return res;
1564  
1565 memcpy(&dir_new[DIR_Attr], direntry, 32-11); /* Create new entry */
1566 memcpy(&dir_new[DIR_Name], fn, 8+3);
1567 dir_new[DIR_NTres] = fn[11];
1568 fs->winflag = 1;
1569  
1570 if (!move_window(sect_old)) return FR_RW_ERROR; /* Remove old entry */
1571 dir_old[DIR_Name] = 0xE5;
1572  
1573 return sync();
1574 }
1575  
1576 #endif /* !_FS_READONLY */
1577 #endif /* _FS_MINIMIZE == 0 */
1578 #endif /* _FS_MINIMIZE <= 1 */
1579 #endif /* _FS_MINIMIZE <= 2 */
1580