tcploader: added possibility to deny connection and wait for new client
[svpe-wii.git] / sdelfloader / loader / source / tff.c
blob0dcb07c8117ba4a002e6aacb2d5c245bcceab15b
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.
8 / Copyright (C) 2007, ChaN, all right reserved.
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.
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 / May 05, 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 /---------------------------------------------------------------------------*/
38 #include <string.h>
39 #include "tff.h" /* Tiny-FatFs declarations */
40 #include "diskio.h" /* Include file for user provided disk functions */
42 typedef enum { FALSE = 0, TRUE } BOOL;
44 static
45 FATFS *FatFs; /* Pointer to the file system objects (logical drive) */
46 static
47 WORD fsid; /* File system mount ID */
50 /*-------------------------------------------------------------------------
52 Module Private Functions
54 -------------------------------------------------------------------------*/
57 /*-----------------------------------------------------------------------*/
58 /* Change window offset */
59 /*-----------------------------------------------------------------------*/
61 static
62 BOOL move_window ( /* TRUE: successful, FALSE: failed */
63 DWORD sector /* Sector number to make apperance in the FatFs->win */
64 ) /* Move to zero only writes back dirty window */
66 DWORD wsect;
67 FATFS *fs = FatFs;
70 wsect = fs->winsect;
71 if (wsect != sector)
72 { /* Changed current window */
73 #if !_FS_READONLY
74 BYTE n;
75 if (fs->winflag)
76 { /* Write back dirty window if needed */
77 if (disk_write(0, fs->win, wsect, 1) != RES_OK)
78 return FALSE;
79 fs->winflag = 0;
80 if (wsect < (fs->fatbase + fs->sects_fat))
81 { /* In FAT area */
82 for (n = fs->n_fats; n >= 2; n--)
83 { /* Refrect the change to all FAT copies */
84 wsect += fs->sects_fat;
85 disk_write(0, fs->win, wsect, 1);
89 #endif
90 if (sector)
92 if (disk_read(0, fs->win, sector, 1) != RES_OK)
93 return FALSE;
94 fs->winsect = sector;
97 return TRUE;
103 /*-----------------------------------------------------------------------*/
104 /* Clean-up cached data */
105 /*-----------------------------------------------------------------------*/
107 #if !_FS_READONLY
108 static
109 FRESULT sync (void) /* FR_OK: successful, FR_RW_ERROR: failed */
111 FATFS *fs = FatFs;
114 fs->winflag = 1;
115 if (!move_window(0)) return FR_RW_ERROR;
116 #if _USE_FSINFO
117 if (fs->fs_type == FS_FAT32 && fs->fsi_flag)
118 { /* Update FSInfo sector if needed */
119 fs->winsect = 0;
120 memset(fs->win, 0, 512);
121 ST_WORD(&fs->win[BS_55AA], 0xAA55);
122 ST_DWORD(&fs->win[FSI_LeadSig], 0x41615252);
123 ST_DWORD(&fs->win[FSI_StrucSig], 0x61417272);
124 ST_DWORD(&fs->win[FSI_Free_Count], fs->free_clust);
125 ST_DWORD(&fs->win[FSI_Nxt_Free], fs->last_clust);
126 disk_write(0, fs->win, fs->fsi_sector, 1);
127 fs->fsi_flag = 0;
129 #endif
130 if (disk_ioctl(0, CTRL_SYNC, NULL) != RES_OK) return FR_RW_ERROR;
131 return FR_OK;
133 #endif
138 /*-----------------------------------------------------------------------*/
139 /* Get a cluster status */
140 /*-----------------------------------------------------------------------*/
142 static
143 CLUST get_cluster ( /* 0,>=2: successful, 1: failed */
144 CLUST clust /* Cluster# to get the link information */
147 WORD wc, bc;
148 DWORD fatsect;
149 FATFS *fs = FatFs;
152 if (clust >= 2 && clust < fs->max_clust)
153 { /* Valid cluster# */
154 fatsect = fs->fatbase;
155 switch (fs->fs_type)
157 case FS_FAT12 :
158 bc = (WORD)clust * 3 / 2;
159 if (!move_window(fatsect + bc / 512)) break;
160 wc = fs->win[bc % 512];
161 bc++;
162 if (!move_window(fatsect + bc / 512)) break;
163 wc |= (WORD)fs->win[bc % 512] << 8;
164 return (clust & 1) ? (wc >> 4) : (wc & 0xFFF);
166 case FS_FAT16 :
167 if (!move_window(fatsect + clust / 256)) break;
168 return LD_WORD(&fs->win[((WORD)clust * 2) % 512]);
169 #if _FAT32
170 case FS_FAT32 :
171 if (!move_window(fatsect + clust / 128)) break;
172 return LD_DWORD(&fs->win[((WORD)clust * 4) % 512]) & 0x0FFFFFFF;
173 #endif
177 return 1; /* There is no cluster information, or an error occured */
183 /*-----------------------------------------------------------------------*/
184 /* Change a cluster status */
185 /*-----------------------------------------------------------------------*/
187 #if !_FS_READONLY
188 static
189 BOOL put_cluster ( /* TRUE: successful, FALSE: failed */
190 CLUST clust, /* Cluster# to change */
191 CLUST val /* New value to mark the cluster */
194 WORD bc;
195 BYTE *p;
196 DWORD fatsect;
197 FATFS *fs = FatFs;
200 fatsect = fs->fatbase;
201 switch (fs->fs_type)
203 case FS_FAT12 :
204 bc = (WORD)clust * 3 / 2;
205 if (!move_window(fatsect + bc / 512)) return FALSE;
206 p = &fs->win[bc % 512];
207 *p = (clust & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
208 bc++;
209 fs->winflag = 1;
210 if (!move_window(fatsect + bc / 512)) return FALSE;
211 p = &fs->win[bc % 512];
212 *p = (clust & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
213 break;
215 case FS_FAT16 :
216 if (!move_window(fatsect + clust / 256)) return FALSE;
217 ST_WORD(&fs->win[((WORD)clust * 2) % 512], (WORD)val);
218 break;
219 #if _FAT32
220 case FS_FAT32 :
221 if (!move_window(fatsect + clust / 128)) return FALSE;
222 ST_DWORD(&fs->win[((WORD)clust * 4) % 512], val);
223 break;
224 #endif
225 default :
226 return FALSE;
228 fs->winflag = 1;
229 return TRUE;
231 #endif /* !_FS_READONLY */
236 /*-----------------------------------------------------------------------*/
237 /* Remove a cluster chain */
238 /*-----------------------------------------------------------------------*/
240 #if !_FS_READONLY
241 static
242 BOOL remove_chain ( /* TRUE: successful, FALSE: failed */
243 CLUST clust /* Cluster# to remove chain from */
246 CLUST nxt;
247 FATFS *fs = FatFs;
250 while (clust >= 2 && clust < fs->max_clust)
252 nxt = get_cluster(clust);
253 if (nxt == 1) return FALSE;
254 if (!put_cluster(clust, 0)) return FALSE;
255 if (fs->free_clust != (CLUST)0xFFFFFFFF)
257 fs->free_clust++;
258 #if _USE_FSINFO
259 fs->fsi_flag = 1;
260 #endif
262 clust = nxt;
264 return TRUE;
266 #endif
271 /*-----------------------------------------------------------------------*/
272 /* Stretch or create a cluster chain */
273 /*-----------------------------------------------------------------------*/
275 #if !_FS_READONLY
276 static
277 CLUST create_chain ( /* 0: no free cluster, 1: error, >=2: new cluster number */
278 CLUST clust /* Cluster# to stretch, 0 means create new */
281 CLUST cstat, ncl, scl, mcl;
282 FATFS *fs = FatFs;
285 mcl = fs->max_clust;
286 if (clust == 0)
287 { /* Create new chain */
288 scl = fs->last_clust; /* Get last allocated cluster */
289 if (scl < 2 || scl >= mcl) scl = 1;
291 else
292 { /* Stretch existing chain */
293 cstat = get_cluster(clust); /* Check the cluster status */
294 if (cstat < 2) return 1; /* It is an invalid cluster */
295 if (cstat < mcl) return cstat; /* It is already followed by next cluster */
296 scl = clust;
299 ncl = scl; /* Start cluster */
300 for (;;)
302 ncl++; /* Next cluster */
303 if (ncl >= mcl)
304 { /* Wrap around */
305 ncl = 2;
306 if (ncl > scl) return 0; /* No free custer */
308 cstat = get_cluster(ncl); /* Get the cluster status */
309 if (cstat == 0) break; /* Found a free cluster */
310 if (cstat == 1) return 1; /* Any error occured */
311 if (ncl == scl) return 0; /* No free custer */
314 if (!put_cluster(ncl, (CLUST)0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */
315 if (clust && !put_cluster(clust, ncl)) return 1; /* Link it to previous one if needed */
317 fs->last_clust = ncl; /* Update fsinfo */
318 if (fs->free_clust != (CLUST)0xFFFFFFFF)
320 fs->free_clust--;
321 #if _USE_FSINFO
322 fs->fsi_flag = 1;
323 #endif
326 return ncl; /* Return new cluster number */
328 #endif /* !_FS_READONLY */
333 /*-----------------------------------------------------------------------*/
334 /* Get sector# from cluster# */
335 /*-----------------------------------------------------------------------*/
337 static
338 DWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */
339 CLUST clust /* Cluster# to be converted */
342 FATFS *fs = FatFs;
345 clust -= 2;
346 if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */
347 return (DWORD)clust * fs->sects_clust + fs->database;
353 /*-----------------------------------------------------------------------*/
354 /* Move directory pointer to next */
355 /*-----------------------------------------------------------------------*/
357 static
358 BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */
359 DIR *dirobj /* Pointer to directory object */
362 CLUST clust;
363 WORD idx;
364 FATFS *fs = FatFs;
367 idx = dirobj->index + 1;
368 if ((idx & 15) == 0)
369 { /* Table sector changed? */
370 dirobj->sect++; /* Next sector */
371 if (!dirobj->clust)
372 { /* In static table */
373 if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */
375 else
376 { /* In dynamic table */
377 if (((idx / 16) & (fs->sects_clust - 1)) == 0)
378 { /* Cluster changed? */
379 clust = get_cluster(dirobj->clust); /* Get next cluster */
380 if (clust < 2 || clust >= fs->max_clust) /* Reached to end of table */
381 return FALSE;
382 dirobj->clust = clust; /* Initialize for new cluster */
383 dirobj->sect = clust2sect(clust);
387 dirobj->index = idx; /* Lower 4 bit of dirobj->index indicates offset in dirobj->sect */
388 return TRUE;
394 /*-----------------------------------------------------------------------*/
395 /* Get file status from directory entry */
396 /*-----------------------------------------------------------------------*/
398 #if _FS_MINIMIZE <= 1
399 static
400 void get_fileinfo ( /* No return code */
401 FILINFO *finfo, /* Ptr to store the File Information */
402 const BYTE *dir /* Ptr to the directory entry */
405 BYTE n, c, a;
406 char *p;
409 p = &finfo->fname[0];
410 a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */
411 for (n = 0; n < 8; n++)
412 { /* Convert file name (body) */
413 c = dir[n];
414 if (c == ' ') break;
415 if (c == 0x05) c = 0xE5;
416 if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20;
417 *p++ = c;
419 if (dir[8] != ' ')
420 { /* Convert file name (extension) */
421 *p++ = '.';
422 for (n = 8; n < 11; n++)
424 c = dir[n];
425 if (c == ' ') break;
426 if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20;
427 *p++ = c;
430 *p = '\0';
432 finfo->fattrib = dir[DIR_Attr]; /* Attribute */
433 finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size */
434 finfo->fdate = LD_WORD(&dir[DIR_WrtDate]); /* Date */
435 finfo->ftime = LD_WORD(&dir[DIR_WrtTime]); /* Time */
437 #endif /* _FS_MINIMIZE <= 1 */
442 /*-----------------------------------------------------------------------*/
443 /* Pick a paragraph and create the name in format of directory entry */
444 /*-----------------------------------------------------------------------*/
446 static
447 char make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */
448 const char **path, /* Pointer to the file path pointer */
449 char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */
452 BYTE n, t, c, a, b;
455 memset(dirname, ' ', 8+3); /* Fill buffer with spaces */
456 a = 0;
457 b = 0x18; /* NT flag */
458 n = 0;
459 t = 8;
460 for (;;)
462 c = *(*path)++;
463 if (c == '\0' || c == '/')
464 { /* Reached to end of str or directory separator */
465 if (n == 0) break;
466 dirname[11] = _USE_NTFLAG ? (a & b) : 0;
467 return c;
469 if (c <= ' ' || c == 0x7F) break; /* Reject invisible chars */
470 if (c == '.')
472 if (!(a & 1) && n >= 1 && n <= 8)
473 { /* Enter extension part */
474 n = 8;
475 t = 11;
476 continue;
478 break;
480 if (_USE_SJIS &&
481 ((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code */
482 (c >= 0xE0 && c <= 0xFC)))
484 if (n == 0 && c == 0xE5) /* Change heading \xE5 to \x05 */
485 c = 0x05;
486 a ^= 1;
487 goto md_l2;
489 if (c == '"') break; /* Reject " */
490 if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */
491 if (c <= ',') break; /* Reject * + , */
492 if (c <= '9') goto md_l1; /* Accept - 0-9 */
493 if (c <= '?') break; /* Reject : ; < = > ? */
494 if (!(a & 1))
495 { /* These checks are not applied to S-JIS 2nd byte */
496 if (c == '|') break; /* Reject | */
497 if (c >= '[' && c <= ']') break;/* Reject [ \ ] */
498 if (_USE_NTFLAG && c >= 'A' && c <= 'Z')
499 (t == 8) ? (b &= ~0x08) : (b &= ~0x10);
500 if (c >= 'a' && c <= 'z')
501 { /* Convert to upper case */
502 c -= 0x20;
503 if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10);
506 md_l1:
507 a &= ~1;
508 md_l2:
509 if (n >= t) break;
510 dirname[n++] = c;
512 return 1;
517 /*-----------------------------------------------------------------------*/
518 /* Trace a file path */
519 /*-----------------------------------------------------------------------*/
521 static
522 FRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */
523 DIR *dirobj, /* Pointer to directory object to return last directory */
524 char *fn, /* Pointer to last segment name to return */
525 const char *path, /* Full-path string to trace a file or directory */
526 BYTE **dir /* Directory pointer in Win[] to retutn */
529 CLUST clust;
530 char ds;
531 BYTE *dptr = NULL;
532 FATFS *fs = FatFs;
534 /* Initialize directory object */
535 clust = fs->dirbase;
536 #if _FAT32
537 if (fs->fs_type == FS_FAT32)
539 dirobj->clust = dirobj->sclust = clust;
540 dirobj->sect = clust2sect(clust);
542 else
543 #endif
545 dirobj->clust = dirobj->sclust = 0;
546 dirobj->sect = clust;
548 dirobj->index = 0;
549 dirobj->fs = fs;
551 if (*path == '\0')
552 { /* Null path means the root directory */
553 *dir = NULL;
554 return FR_OK;
557 for (;;)
559 ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */
560 if (ds == 1) return FR_INVALID_NAME;
561 for (;;)
563 if (!move_window(dirobj->sect)) return FR_RW_ERROR;
564 dptr = &fs->win[(dirobj->index & 15) * 32]; /* Pointer to the directory entry */
565 if (dptr[DIR_Name] == 0) /* Has it reached to end of dir? */
566 return !ds ? FR_NO_FILE : FR_NO_PATH;
567 if (dptr[DIR_Name] != 0xE5 /* Matched? */
568 && !(dptr[DIR_Attr] & AM_VOL)
569 && !memcmp(&dptr[DIR_Name], fn, 8+3) ) break;
570 if (!next_dir_entry(dirobj)) /* Next directory pointer */
571 return !ds ? FR_NO_FILE : FR_NO_PATH;
573 if (!ds)
575 *dir = dptr;
576 return FR_OK;
577 } /* Matched with end of path */
578 if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */
579 clust = /* Get cluster# of the directory */
580 #if _FAT32
581 ((DWORD)LD_WORD(&dptr[DIR_FstClusHI]) << 16) |
582 #endif
583 LD_WORD(&dptr[DIR_FstClusLO]);
584 dirobj->clust = dirobj->sclust = clust; /* Restart scannig with the new directory */
585 dirobj->sect = clust2sect(clust);
586 dirobj->index = 2;
592 /*-----------------------------------------------------------------------*/
593 /* Reserve a directory entry */
594 /*-----------------------------------------------------------------------*/
596 #if !_FS_READONLY
597 static
598 FRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */
599 DIR *dirobj, /* Target directory to create new entry */
600 BYTE **dir /* Pointer to pointer to created entry to retutn */
603 CLUST clust;
604 DWORD sector;
605 BYTE c, n, *dptr;
606 FATFS *fs = FatFs;
609 /* Re-initialize directory object */
610 clust = dirobj->sclust;
611 if (clust)
612 { /* Dyanmic directory table */
613 dirobj->clust = clust;
614 dirobj->sect = clust2sect(clust);
616 else
617 { /* Static directory table */
618 dirobj->sect = fs->dirbase;
620 dirobj->index = 0;
624 if (!move_window(dirobj->sect)) return FR_RW_ERROR;
625 dptr = &fs->win[(dirobj->index & 15) * 32]; /* Pointer to the directory entry */
626 c = dptr[DIR_Name];
627 if (c == 0 || c == 0xE5)
628 { /* Found an empty entry! */
629 *dir = dptr;
630 return FR_OK;
633 while (next_dir_entry(dirobj)); /* Next directory pointer */
634 /* Reached to end of the directory table */
636 /* Abort when static table or could not stretch dynamic table */
637 if (!clust || !(clust = create_chain(dirobj->clust))) return FR_DENIED;
638 if (clust == 1 || !move_window(0)) return FR_RW_ERROR;
640 fs->winsect = sector = clust2sect(clust); /* Cleanup the expanded table */
641 memset(fs->win, 0, 512);
642 for (n = fs->sects_clust; n; n--)
644 if (disk_write(0, fs->win, sector, 1) != RES_OK)
645 return FR_RW_ERROR;
646 sector++;
648 fs->winflag = 1;
649 *dir = fs->win;
650 return FR_OK;
652 #endif /* !_FS_READONLY */
657 /*-----------------------------------------------------------------------*/
658 /* Load boot record and check if it is a FAT boot record */
659 /*-----------------------------------------------------------------------*/
661 static
662 BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */
663 DWORD sect /* Sector# to check if it is a FAT boot record or not */
666 FATFS *fs = FatFs;
668 if (disk_read(0, fs->win, sect, 1) != RES_OK) /* Load boot record */
669 return 2;
670 if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature */
671 return 2;
673 if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */
674 return 0;
675 #if _FAT32
676 if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80))
677 return 0;
678 #endif
679 return 1;
685 /*-----------------------------------------------------------------------*/
686 /* Make sure that the file system is valid */
687 /*-----------------------------------------------------------------------*/
689 static
690 FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */
691 const char **path, /* Pointer to pointer to the path name (drive number) */
692 BYTE chk_wp /* !=0: Check media write protection for wrinting fuctions */
695 BYTE fmt;
696 DSTATUS stat;
697 DWORD bootsect, fatsize, totalsect, maxclust;
698 const char *p = *path;
699 FATFS *fs = FatFs;
703 while (*p == ' ') p++; /* Strip leading spaces */
704 if (*p == '/') p++; /* Strip heading slash */
705 *path = p; /* Return pointer to the path name */
707 /* Is the file system object registered? */
708 if (!fs) return FR_NOT_ENABLED;
710 /* Chekck if the logical drive has been mounted or not */
711 if (fs->fs_type)
713 stat = disk_status(0);
714 if (!(stat & STA_NOINIT))
715 { /* If the physical drive is kept initialized */
716 #if !_FS_READONLY
717 if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
718 return FR_WRITE_PROTECTED;
719 #endif
720 return FR_OK; /* The file system object is valid */
724 /* The logical drive has not been mounted, following code attempts to mount the logical drive */
726 memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */
727 stat = disk_initialize(0); /* Initialize low level disk I/O layer */
728 if (stat & STA_NOINIT) /* Check if the drive is ready */
729 return FR_NOT_READY;
730 #if !_FS_READONLY
731 if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
732 return FR_WRITE_PROTECTED;
733 #endif
735 /* Search FAT partition on the drive */
736 fmt = check_fs(bootsect = 0); /* Check sector 0 as an SFD format */
737 if (fmt == 1)
738 { /* Not a FAT boot record, it may be patitioned */
739 /* Check a partition listed in top of the partition table */
740 if (fs->win[MBR_Table+4])
741 { /* Is the 1st partition existing? */
742 bootsect = LD_DWORD(&fs->win[MBR_Table+8]); /* Partition offset in LBA */
743 fmt = check_fs(bootsect); /* Check the partition */
746 if (fmt || LD_WORD(&fs->win[BPB_BytsPerSec]) != 512) /* No valid FAT patition is found */
747 return FR_NO_FILESYSTEM;
749 /* Initialize the file system object */
750 fatsize = LD_WORD(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT */
751 if (!fatsize) fatsize = LD_DWORD(&fs->win[BPB_FATSz32]);
752 fs->sects_fat = (CLUST)fatsize;
753 fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */
754 fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */
755 fs->fatbase = bootsect + LD_WORD(&fs->win[BPB_RsvdSecCnt]); /* FAT start sector (lba) */
756 fs->sects_clust = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */
757 fs->n_rootdir = LD_WORD(&fs->win[BPB_RootEntCnt]); /* Nmuber of root directory entries */
758 totalsect = LD_WORD(&fs->win[BPB_TotSec16]); /* Number of sectors on the file system */
759 if (!totalsect) totalsect = LD_DWORD(&fs->win[BPB_TotSec32]);
760 fs->max_clust = maxclust = (totalsect /* Last cluster# + 1 */
761 - LD_WORD(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / 16
762 ) / fs->sects_clust + 2;
764 fmt = FS_FAT12; /* Determine the FAT sub type */
765 if (maxclust > 0xFF7) fmt = FS_FAT16;
766 if (maxclust > 0xFFF7)
767 #if !_FAT32
768 return FR_NO_FILESYSTEM;
769 #else
770 fmt = FS_FAT32;
771 if (fmt == FS_FAT32)
772 fs->dirbase = LD_DWORD(&fs->win[BPB_RootClus]); /* Root directory start cluster */
773 else
774 #endif
775 fs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */
776 fs->database = fs->fatbase + fatsize + fs->n_rootdir / 16; /* Data start sector (lba) */
777 fs->fs_type = fmt; /* FAT sub-type */
779 #if !_FS_READONLY
780 fs->free_clust = (CLUST)0xFFFFFFFF;
781 #if _USE_FSINFO
782 /* Load fsinfo sector if needed */
783 if (fmt == FS_FAT32)
785 fs->fsi_sector = bootsect + LD_WORD(&fs->win[BPB_FSInfo]);
786 if (disk_read(0, fs->win, fs->fsi_sector, 1) == RES_OK &&
787 LD_WORD(&fs->win[BS_55AA]) == 0xAA55 &&
788 LD_DWORD(&fs->win[FSI_LeadSig]) == 0x41615252 &&
789 LD_DWORD(&fs->win[FSI_StrucSig]) == 0x61417272)
791 fs->last_clust = LD_DWORD(&fs->win[FSI_Nxt_Free]);
792 fs->free_clust = LD_DWORD(&fs->win[FSI_Free_Count]);
795 #endif
796 #endif
797 fs->id = ++fsid; /* File system mount ID */
798 return FR_OK;
804 /*-----------------------------------------------------------------------*/
805 /* Check if the file/dir object is valid or not */
806 /*-----------------------------------------------------------------------*/
808 static
809 FRESULT validate ( /* FR_OK(0): The id is valid, !=0: Not valid */
810 const FATFS *fs, /* Pointer to the file system object */
811 WORD id /* id member of the target object to be checked */
814 if (!fs || fs->id != id)
815 return FR_INVALID_OBJECT;
816 if (disk_status(0) & STA_NOINIT)
817 return FR_NOT_READY;
819 return FR_OK;
825 /*--------------------------------------------------------------------------
827 Public Functions
829 --------------------------------------------------------------------------*/
832 /*-----------------------------------------------------------------------*/
833 /* Mount/Unmount a Locical Drive */
834 /*-----------------------------------------------------------------------*/
836 FRESULT f_mount (
837 BYTE drv, /* Logical drive number to be mounted/unmounted */
838 FATFS *fs /* Pointer to new file system object (NULL for unmount)*/
841 FATFS *fsobj;
844 if (drv) return FR_INVALID_DRIVE;
845 fsobj = FatFs;
846 FatFs = fs;
847 if (fsobj) memset(fsobj, 0, sizeof(FATFS));
848 if (fs) memset(fs, 0, sizeof(FATFS));
850 return FR_OK;
856 /*-----------------------------------------------------------------------*/
857 /* Open or Create a File */
858 /*-----------------------------------------------------------------------*/
860 FRESULT f_open (
861 FIL *fp, /* Pointer to the blank file object */
862 const char *path, /* Pointer to the file name */
863 BYTE mode /* Access mode and file open mode flags */
866 FRESULT res;
867 BYTE *dir;
868 DIR dirobj;
869 char fn[8+3+1];
870 FATFS *fs = FatFs;
873 fp->fs = NULL;
874 #if !_FS_READONLY
875 mode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW);
876 res = auto_mount(&path, (BYTE)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)));
877 #else
878 mode &= FA_READ;
879 res = auto_mount(&path, 0);
880 #endif
881 if (res != FR_OK) return res;
883 /* Trace the file path */
884 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
886 #if !_FS_READONLY
887 /* Create or Open a File */
888 if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW))
890 CLUST rs;
891 DWORD dw;
892 if (res != FR_OK)
893 { /* No file, create new */
894 if (res != FR_NO_FILE) return res;
895 res = reserve_direntry(&dirobj, &dir);
896 if (res != FR_OK) return res;
897 memset(dir, 0, 32); /* Initialize the new entry */
898 memcpy(&dir[DIR_Name], fn, 8+3);
899 dir[DIR_NTres] = fn[11];
900 mode |= FA_CREATE_ALWAYS;
902 else
903 { /* Any object is already existing */
904 if (mode & FA_CREATE_NEW) /* Cannot create new */
905 return FR_EXIST;
906 if (dir == NULL || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite (R/O or DIR) */
907 return FR_DENIED;
908 if (mode & FA_CREATE_ALWAYS)
909 { /* Resize it to zero */
910 #if _FAT32
911 rs = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]);
912 ST_WORD(&dir[DIR_FstClusHI], 0);
913 #else
914 rs = LD_WORD(&dir[DIR_FstClusLO]);
915 #endif
916 ST_WORD(&dir[DIR_FstClusLO], 0); /* cluster = 0 */
917 ST_DWORD(&dir[DIR_FileSize], 0); /* size = 0 */
918 fs->winflag = 1;
919 dw = fs->winsect; /* Remove the cluster chain */
920 if (!remove_chain(rs) || !move_window(dw))
921 return FR_RW_ERROR;
922 fs->last_clust = rs - 1; /* Reuse the cluster hole */
925 if (mode & FA_CREATE_ALWAYS)
927 dir[DIR_Attr] = AM_ARC; /* New attribute */
928 dw = get_fattime();
929 ST_DWORD(&dir[DIR_WrtTime], dw); /* Updated time */
930 ST_DWORD(&dir[DIR_CrtTime], dw); /* Created time */
931 fs->winflag = 1;
934 /* Open a File */
935 else
937 #endif /* !_FS_READONLY */
938 if (res != FR_OK) return res; /* Trace failed */
939 if (dir == NULL || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */
940 return FR_NO_FILE;
941 #if !_FS_READONLY
942 if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
943 return FR_DENIED;
946 fp->dir_sect = fs->winsect; /* Pointer to the directory entry */
947 fp->dir_ptr = dir;
948 #endif
949 fp->flag = mode; /* File access mode */
950 fp->org_clust = /* File start cluster */
951 #if _FAT32
952 ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |
953 #endif
954 LD_WORD(&dir[DIR_FstClusLO]);
955 fp->fsize = LD_DWORD(&dir[DIR_FileSize]); /* File size */
956 fp->fptr = 0; /* File ptr */
957 fp->sect_clust = 1; /* Sector counter */
958 fp->fs = fs;
959 fp->id = fs->id; /* Owner file system object of the file */
961 return FR_OK;
967 /*-----------------------------------------------------------------------*/
968 /* Read File */
969 /*-----------------------------------------------------------------------*/
971 FRESULT f_read (
972 FIL *fp, /* Pointer to the file object */
973 void *buff, /* Pointer to data buffer */
974 WORD btr, /* Number of bytes to read */
975 WORD *br /* Pointer to number of bytes read */
978 DWORD sect, remain;
979 WORD rcnt;
980 CLUST clust;
981 BYTE cc, *rbuff = buff;
982 FRESULT res;
983 FATFS *fs = fp->fs;
986 *br = 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_READ)) return FR_DENIED; /* Check access mode */
991 remain = fp->fsize - fp->fptr;
992 if (btr > remain) btr = (WORD)remain; /* Truncate read count by number of bytes left */
994 for ( ; btr; /* Repeat until all data transferred */
995 rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt)
997 if ((fp->fptr % 512) == 0)
998 { /* On the sector boundary */
999 if (--fp->sect_clust)
1000 { /* Decrement left sector counter */
1001 sect = fp->curr_sect + 1; /* Get current sector */
1003 else
1004 { /* On the cluster boundary, get next cluster */
1005 clust = (fp->fptr == 0) ?
1006 fp->org_clust : get_cluster(fp->curr_clust);
1007 if (clust < 2 || clust >= fs->max_clust)
1008 goto fr_error;
1009 fp->curr_clust = clust; /* Current cluster */
1010 sect = clust2sect(clust); /* Get current sector */
1011 fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */
1013 fp->curr_sect = sect; /* Update current sector */
1014 cc = btr / 512; /* When left bytes >= 512, */
1015 if (cc)
1016 { /* Read maximum contiguous sectors directly */
1017 if (cc > fp->sect_clust) cc = fp->sect_clust;
1018 if (disk_read(0, rbuff, sect, cc) != RES_OK)
1019 goto fr_error;
1020 fp->sect_clust -= cc - 1;
1021 fp->curr_sect += cc - 1;
1022 rcnt = cc * 512;
1023 continue;
1026 if (!move_window(fp->curr_sect)) goto fr_error; /* Move sector window */
1027 rcnt = 512 - (WORD)(fp->fptr % 512); /* Copy fractional bytes from sector window */
1028 if (rcnt > btr) rcnt = btr;
1029 memcpy(rbuff, &fs->win[(WORD)fp->fptr % 512], rcnt);
1032 return FR_OK;
1034 fr_error: /* Abort this function due to an unrecoverable error */
1035 fp->flag |= FA__ERROR;
1036 return FR_RW_ERROR;
1042 #if !_FS_READONLY
1043 /*-----------------------------------------------------------------------*/
1044 /* Write File */
1045 /*-----------------------------------------------------------------------*/
1047 FRESULT f_write (
1048 FIL *fp, /* Pointer to the file object */
1049 const void *buff, /* Pointer to the data to be written */
1050 WORD btw, /* Number of bytes to write */
1051 WORD *bw /* Pointer to number of bytes written */
1054 DWORD sect;
1055 WORD wcnt;
1056 CLUST clust;
1057 BYTE cc;
1058 FRESULT res;
1059 const BYTE *wbuff = buff;
1060 FATFS *fs = fp->fs;
1063 *bw = 0;
1064 res = validate(fs, fp->id); /* Check validity of the object */
1065 if (res) return res;
1066 if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */
1067 if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */
1068 if (fp->fsize + btw < fp->fsize) return FR_OK; /* File size cannot reach 4GB */
1070 for ( ; btw; /* Repeat until all data transferred */
1071 wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt)
1073 if ((fp->fptr % 512) == 0)
1074 { /* On the sector boundary */
1075 if (--(fp->sect_clust))
1076 { /* Decrement left sector counter */
1077 sect = fp->curr_sect + 1; /* Get current sector */
1079 else
1080 { /* On the cluster boundary, get next cluster */
1081 if (fp->fptr == 0)
1082 { /* Is top of the file */
1083 clust = fp->org_clust;
1084 if (clust == 0) /* No cluster is created yet */
1085 fp->org_clust = clust = create_chain(0); /* Create a new cluster chain */
1087 else
1088 { /* Middle or end of file */
1089 clust = create_chain(fp->curr_clust); /* Trace or streach cluster chain */
1091 if (clust == 0) break; /* Disk full */
1092 if (clust == 1 || clust >= fs->max_clust) goto fw_error;
1093 fp->curr_clust = clust; /* Current cluster */
1094 sect = clust2sect(clust); /* Get current sector */
1095 fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */
1097 fp->curr_sect = sect; /* Update current sector */
1098 cc = btw / 512; /* When left bytes >= 512, */
1099 if (cc)
1100 { /* Write maximum contiguous sectors directly */
1101 if (cc > fp->sect_clust) cc = fp->sect_clust;
1102 if (disk_write(0, wbuff, sect, cc) != RES_OK)
1103 goto fw_error;
1104 fp->sect_clust -= cc - 1;
1105 fp->curr_sect += cc - 1;
1106 wcnt = cc * 512;
1107 continue;
1109 if (fp->fptr >= fp->fsize)
1110 { /* Flush R/W window if needed */
1111 if (!move_window(0)) goto fw_error;
1112 fs->winsect = fp->curr_sect;
1115 if (!move_window(fp->curr_sect)) /* Move sector window */
1116 goto fw_error;
1117 wcnt = 512 - (WORD)(fp->fptr % 512); /* Copy fractional bytes bytes to sector window */
1118 if (wcnt > btw) wcnt = btw;
1119 memcpy(&fs->win[(WORD)fp->fptr % 512], wbuff, wcnt);
1120 fs->winflag = 1;
1123 if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
1124 fp->flag |= FA__WRITTEN; /* Set file changed flag */
1125 return FR_OK;
1127 fw_error: /* Abort this function due to an unrecoverable error */
1128 fp->flag |= FA__ERROR;
1129 return FR_RW_ERROR;
1135 /*-----------------------------------------------------------------------*/
1136 /* Synchronize between File and Disk */
1137 /*-----------------------------------------------------------------------*/
1139 FRESULT f_sync (
1140 FIL *fp /* Pointer to the file object */
1143 DWORD tim;
1144 BYTE *dir;
1145 FRESULT res;
1146 FATFS *fs = fp->fs;
1149 res = validate(fs, fp->id); /* Check validity of the object */
1150 if (res == FR_OK)
1152 if (fp->flag & FA__WRITTEN)
1153 { /* Has the file been written? */
1154 /* Update the directory entry */
1155 if (!move_window(fp->dir_sect))
1156 return FR_RW_ERROR;
1157 dir = fp->dir_ptr;
1158 dir[DIR_Attr] |= AM_ARC; /* Set archive bit */
1159 ST_DWORD(&dir[DIR_FileSize], fp->fsize); /* Update file size */
1160 ST_WORD(&dir[DIR_FstClusLO], fp->org_clust); /* Update start cluster */
1161 #if _FAT32
1162 ST_WORD(&dir[DIR_FstClusHI], fp->org_clust >> 16);
1163 #endif
1164 tim = get_fattime(); /* Updated time */
1165 ST_DWORD(&dir[DIR_WrtTime], tim);
1166 fp->flag &= ~FA__WRITTEN;
1167 res = sync();
1170 return res;
1173 #endif /* !_FS_READONLY */
1178 /*-----------------------------------------------------------------------*/
1179 /* Close File */
1180 /*-----------------------------------------------------------------------*/
1182 FRESULT f_close (
1183 FIL *fp /* Pointer to the file object to be closed */
1186 FRESULT res;
1189 #if !_FS_READONLY
1190 res = f_sync(fp);
1191 #else
1192 res = validate(fp->fs, fp->id);
1193 #endif
1194 if (res == FR_OK)
1195 fp->fs = NULL;
1197 return res;
1203 #if _FS_MINIMIZE <= 2
1204 /*-----------------------------------------------------------------------*/
1205 /* Seek File Pointer */
1206 /*-----------------------------------------------------------------------*/
1207 #if 0
1208 FRESULT f_lseek (
1209 FIL *fp, /* Pointer to the file object */
1210 DWORD ofs /* File pointer from top of file */
1213 CLUST clust;
1214 DWORD csize;
1215 BYTE csect;
1216 FRESULT res;
1217 FATFS *fs = fp->fs;
1220 res = validate(fs, fp->id); /* Check validity of the object */
1221 if (res) return res;
1223 if (fp->flag & FA__ERROR) return FR_RW_ERROR;
1224 #if !_FS_READONLY
1225 if (ofs > fp->fsize && !(fp->flag & FA_WRITE))
1226 #else
1227 if (ofs > fp->fsize)
1228 #endif
1229 ofs = fp->fsize;
1230 fp->fptr = 0;
1231 fp->sect_clust = 1; /* Set file R/W pointer to top of the file */
1233 /* Move file R/W pointer if needed */
1234 if (ofs)
1236 clust = fp->org_clust; /* Get start cluster */
1237 #if !_FS_READONLY
1238 if (!clust)
1239 { /* If the file does not have a cluster chain, create new cluster chain */
1240 clust = create_chain(0);
1241 if (clust == 1) goto fk_error;
1242 fp->org_clust = clust;
1244 #endif
1245 if (clust)
1246 { /* If the file has a cluster chain, it can be followed */
1247 csize = (DWORD)fs->sects_clust * 512; /* Cluster size in unit of byte */
1248 for (;;)
1249 { /* Loop to skip leading clusters */
1250 fp->curr_clust = clust; /* Update current cluster */
1251 if (ofs <= csize) break;
1252 #if !_FS_READONLY
1253 if (fp->flag & FA_WRITE) /* Check if in write mode or not */
1254 clust = create_chain(clust); /* Force streached if in write mode */
1255 else
1256 #endif
1257 clust = get_cluster(clust); /* Only follow cluster chain if not in write mode */
1258 if (clust == 0)
1259 { /* Stop if could not follow the cluster chain */
1260 ofs = csize;
1261 break;
1263 if (clust == 1 || clust >= fs->max_clust) goto fk_error;
1264 fp->fptr += csize; /* Update R/W pointer */
1265 ofs -= csize;
1267 csect = (BYTE)((ofs - 1) / 512); /* Sector offset in the cluster */
1268 fp->curr_sect = clust2sect(clust) + csect; /* Current sector */
1269 fp->sect_clust = fs->sects_clust - csect; /* Left sector counter in the cluster */
1270 fp->fptr += ofs; /* Update file R/W pointer */
1273 #if !_FS_READONLY
1274 if ((fp->flag & FA_WRITE) && fp->fptr > fp->fsize)
1275 { /* Set updated flag if in write mode */
1276 fp->fsize = fp->fptr;
1277 fp->flag |= FA__WRITTEN;
1279 #endif
1281 return FR_OK;
1283 fk_error: /* Abort this function due to an unrecoverable error */
1284 fp->flag |= FA__ERROR;
1285 return FR_RW_ERROR;
1288 #endif
1291 #if _FS_MINIMIZE <= 1
1292 /*-----------------------------------------------------------------------*/
1293 /* Open a directroy */
1294 /*-----------------------------------------------------------------------*/
1296 FRESULT f_opendir (
1297 DIR *dirobj, /* Pointer to directory object to create */
1298 const char *path /* Pointer to the directory path */
1301 BYTE *dir;
1302 char fn[8+3+1];
1303 FRESULT res;
1304 FATFS *fs = FatFs;
1307 res = auto_mount(&path, 0);
1308 if (res != FR_OK) return res;
1310 res = trace_path(dirobj, fn, path, &dir); /* Trace the directory path */
1311 if (res == FR_OK)
1312 { /* Trace completed */
1313 if (dir != NULL)
1314 { /* It is not the root dir */
1315 if (dir[DIR_Attr] & AM_DIR)
1316 { /* The entry is a directory */
1317 dirobj->clust =
1318 #if _FAT32
1319 ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |
1320 #endif
1321 LD_WORD(&dir[DIR_FstClusLO]);
1322 dirobj->sect = clust2sect(dirobj->clust);
1323 dirobj->index = 2;
1325 else
1326 { /* The entry is not a directory */
1327 res = FR_NO_FILE;
1330 dirobj->id = fs->id;
1332 return res;
1338 /*-----------------------------------------------------------------------*/
1339 /* Read Directory Entry in Sequense */
1340 /*-----------------------------------------------------------------------*/
1342 FRESULT f_readdir (
1343 DIR *dirobj, /* Pointer to the directory object */
1344 FILINFO *finfo /* Pointer to file information to return */
1347 BYTE *dir, c;
1348 FRESULT res;
1349 FATFS *fs = dirobj->fs;
1352 res = validate(fs, dirobj->id); /* Check validity of the object */
1353 if (res) return res;
1355 finfo->fname[0] = 0;
1356 while (dirobj->sect)
1358 if (!move_window(dirobj->sect))
1359 return FR_RW_ERROR;
1360 dir = &fs->win[(dirobj->index & 15) * 32]; /* pointer to the directory entry */
1361 c = dir[DIR_Name];
1362 if (c == 0) break; /* Has it reached to end of dir? */
1363 if (c != 0xE5 && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */
1364 get_fileinfo(finfo, dir);
1365 if (!next_dir_entry(dirobj)) dirobj->sect = 0; /* Next entry */
1366 if (finfo->fname[0]) break; /* Found valid entry */
1369 return FR_OK;
1375 #if _FS_MINIMIZE == 0
1376 /*-----------------------------------------------------------------------*/
1377 /* Get File Status */
1378 /*-----------------------------------------------------------------------*/
1380 FRESULT f_stat (
1381 const char *path, /* Pointer to the file path */
1382 FILINFO *finfo /* Pointer to file information to return */
1385 BYTE *dir;
1386 char fn[8+3+1];
1387 FRESULT res;
1388 DIR dirobj;
1391 res = auto_mount(&path, 0);
1392 if (res != FR_OK) return res;
1394 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1395 if (res == FR_OK)
1396 { /* Trace completed */
1397 if (dir) /* Found an object */
1398 get_fileinfo(finfo, dir);
1399 else /* It is root dir */
1400 res = FR_INVALID_NAME;
1403 return res;
1409 #if !_FS_READONLY
1410 /*-----------------------------------------------------------------------*/
1411 /* Get Number of Free Clusters */
1412 /*-----------------------------------------------------------------------*/
1414 FRESULT f_getfree (
1415 const char *drv, /* Logical drive number */
1416 DWORD *nclust, /* Pointer to the double word to return number of free clusters */
1417 FATFS **fatfs /* Pointer to pointer to the file system object to return */
1420 DWORD n, sect;
1421 CLUST clust;
1422 BYTE fat, f, *p;
1423 FRESULT res;
1424 FATFS *fs;
1427 /* Get drive number */
1428 res = auto_mount(&drv, 0);
1429 if (res != FR_OK) return res;
1430 *fatfs = fs = FatFs;
1432 /* If number of free cluster is valid, return it without cluster scan. */
1433 if (fs->free_clust <= fs->max_clust - 2)
1435 *nclust = fs->free_clust;
1436 return FR_OK;
1439 /* Count number of free clusters */
1440 fat = fs->fs_type;
1441 n = 0;
1442 if (fat == FS_FAT12)
1444 clust = 2;
1447 if ((WORD)get_cluster(clust) == 0) n++;
1449 while (++clust < fs->max_clust);
1451 else
1453 clust = fs->max_clust;
1454 sect = fs->fatbase;
1455 f = 0;
1456 p = 0;
1459 if (!f)
1461 if (!move_window(sect++)) return FR_RW_ERROR;
1462 p = fs->win;
1464 if (!_FAT32 || fat == FS_FAT16)
1466 if (LD_WORD(p) == 0) n++;
1467 p += 2;
1468 f += 1;
1470 else
1472 if (LD_DWORD(p) == 0) n++;
1473 p += 4;
1474 f += 2;
1477 while (--clust);
1479 fs->free_clust = n;
1480 #if _USE_FSINFO
1481 if (fat == FS_FAT32) fs->fsi_flag = 1;
1482 #endif
1484 *nclust = n;
1485 return FR_OK;
1491 /*-----------------------------------------------------------------------*/
1492 /* Delete a File or a Directory */
1493 /*-----------------------------------------------------------------------*/
1495 FRESULT f_unlink (
1496 const char *path /* Pointer to the file or directory path */
1499 BYTE *dir, *sdir;
1500 DWORD dsect;
1501 char fn[8+3+1];
1502 CLUST dclust;
1503 FRESULT res;
1504 DIR dirobj;
1505 FATFS *fs = FatFs;
1508 res = auto_mount(&path, 1);
1509 if (res != FR_OK) return res;
1511 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1512 if (res != FR_OK) return res; /* Trace failed */
1513 if (dir == NULL) return FR_INVALID_NAME; /* It is the root directory */
1514 if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */
1515 dsect = fs->winsect;
1516 dclust =
1517 #if _FAT32
1518 ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |
1519 #endif
1520 LD_WORD(&dir[DIR_FstClusLO]);
1521 if (dir[DIR_Attr] & AM_DIR)
1522 { /* It is a sub-directory */
1523 dirobj.clust = dclust; /* Check if the sub-dir is empty or not */
1524 dirobj.sect = clust2sect(dclust);
1525 dirobj.index = 2;
1528 if (!move_window(dirobj.sect)) return FR_RW_ERROR;
1529 sdir = &fs->win[(dirobj.index & 15) * 32];
1530 if (sdir[DIR_Name] == 0) break;
1531 if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL))
1532 return FR_DENIED; /* The directory is not empty */
1534 while (next_dir_entry(&dirobj));
1537 if (!move_window(dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */
1538 dir[DIR_Name] = 0xE5;
1539 fs->winflag = 1;
1540 if (!remove_chain(dclust)) return FR_RW_ERROR; /* Remove the cluster chain */
1542 return sync();
1548 /*-----------------------------------------------------------------------*/
1549 /* Create a Directory */
1550 /*-----------------------------------------------------------------------*/
1552 FRESULT f_mkdir (
1553 const char *path /* Pointer to the directory path */
1556 BYTE *dir, *fw, n;
1557 char fn[8+3+1];
1558 DWORD sect, dsect, tim;
1559 CLUST dclust, pclust;
1560 FRESULT res;
1561 DIR dirobj;
1562 FATFS *fs = FatFs;
1565 res = auto_mount(&path, 1);
1566 if (res != FR_OK) return res;
1568 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1569 if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */
1570 if (res != FR_NO_FILE) return res;
1572 res = reserve_direntry(&dirobj, &dir); /* Reserve a directory entry */
1573 if (res != FR_OK) return res;
1574 sect = fs->winsect;
1575 dclust = create_chain(0); /* Allocate a cluster for new directory table */
1576 if (dclust == 1) return FR_RW_ERROR;
1577 dsect = clust2sect(dclust);
1578 if (!dsect) return FR_DENIED;
1579 if (!move_window(dsect)) return FR_RW_ERROR;
1581 fw = fs->win;
1582 memset(fw, 0, 512); /* Clear the directory table */
1583 for (n = 1; n < fs->sects_clust; n++)
1585 if (disk_write(0, fw, ++dsect, 1) != RES_OK)
1586 return FR_RW_ERROR;
1589 memset(&fw[DIR_Name], ' ', 8+3); /* Create "." entry */
1590 fw[DIR_Name] = '.';
1591 fw[DIR_Attr] = AM_DIR;
1592 tim = get_fattime();
1593 ST_DWORD(&fw[DIR_WrtTime], tim);
1594 memcpy(&fw[32], &fw[0], 32);
1595 fw[33] = '.'; /* Create ".." entry */
1596 pclust = dirobj.sclust;
1597 #if _FAT32
1598 ST_WORD(&fw[ DIR_FstClusHI], dclust >> 16);
1599 if (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) pclust = 0;
1600 ST_WORD(&fw[32+DIR_FstClusHI], pclust >> 16);
1601 #endif
1602 ST_WORD(&fw[ DIR_FstClusLO], dclust);
1603 ST_WORD(&fw[32+DIR_FstClusLO], pclust);
1604 fs->winflag = 1;
1606 if (!move_window(sect)) return FR_RW_ERROR;
1607 memset(&dir[0], 0, 32); /* Clean-up the new entry */
1608 memcpy(&dir[DIR_Name], fn, 8+3); /* Name */
1609 dir[DIR_NTres] = fn[11];
1610 dir[DIR_Attr] = AM_DIR; /* Attribute */
1611 ST_DWORD(&dir[DIR_WrtTime], tim); /* Crated time */
1612 ST_WORD(&dir[DIR_FstClusLO], dclust); /* Table start cluster */
1613 #if _FAT32
1614 ST_WORD(&dir[DIR_FstClusHI], dclust >> 16);
1615 #endif
1617 return sync();
1623 /*-----------------------------------------------------------------------*/
1624 /* Change File Attribute */
1625 /*-----------------------------------------------------------------------*/
1627 FRESULT f_chmod (
1628 const char *path, /* Pointer to the file path */
1629 BYTE value, /* Attribute bits */
1630 BYTE mask /* Attribute mask to change */
1633 FRESULT res;
1634 BYTE *dir;
1635 DIR dirobj;
1636 char fn[8+3+1];
1639 res = auto_mount(&path, 1);
1640 if (res == FR_OK)
1642 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1643 if (res == FR_OK)
1644 { /* Trace completed */
1645 if (dir == NULL)
1647 res = FR_INVALID_NAME;
1649 else
1651 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
1652 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
1653 res = sync();
1657 return res;
1663 /*-----------------------------------------------------------------------*/
1664 /* Rename File/Directory */
1665 /*-----------------------------------------------------------------------*/
1667 FRESULT f_rename (
1668 const char *path_old, /* Pointer to the old name */
1669 const char *path_new /* Pointer to the new name */
1672 FRESULT res;
1673 DWORD sect_old;
1674 BYTE *dir_old, *dir_new, direntry[32-11];
1675 DIR dirobj;
1676 char fn[8+3+1];
1677 FATFS *fs = FatFs;
1680 res = auto_mount(&path_old, 1);
1681 if (res != FR_OK) return res;
1683 res = trace_path(&dirobj, fn, path_old, &dir_old); /* Check old object */
1684 if (res != FR_OK) return res; /* The old object is not found */
1685 if (!dir_old) return FR_NO_FILE;
1686 sect_old = fs->winsect; /* Save the object information */
1687 memcpy(direntry, &dir_old[11], 32-11);
1689 res = trace_path(&dirobj, fn, path_new, &dir_new); /* Check new object */
1690 if (res == FR_OK) return FR_EXIST; /* The new object name is already existing */
1691 if (res != FR_NO_FILE) return res; /* Is there no old name? */
1692 res = reserve_direntry(&dirobj, &dir_new); /* Reserve a directory entry */
1693 if (res != FR_OK) return res;
1695 memcpy(&dir_new[DIR_Attr], direntry, 32-11); /* Create new entry */
1696 memcpy(&dir_new[DIR_Name], fn, 8+3);
1697 dir_new[DIR_NTres] = fn[11];
1698 fs->winflag = 1;
1700 if (!move_window(sect_old)) return FR_RW_ERROR; /* Remove old entry */
1701 dir_old[DIR_Name] = 0xE5;
1703 return sync();
1706 #endif /* !_FS_READONLY */
1707 #endif /* _FS_MINIMIZE == 0 */
1708 #endif /* _FS_MINIMIZE <= 1 */
1709 #endif /* _FS_MINIMIZE <= 2 */