1 /*--------------------------------------------------------------------------/
2 / FatFs - 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 11, 2006 R0.03a Improved cluster scan algolithm to write files fast.
25 / Fixed f_mkdir() creates incorrect directory on FAT32.
26 / Feb 04, 2007 R0.04 Supported multiple drive system.
27 / Changed some interfaces for multiple drive system.
28 / Changed f_mountdrv() to f_mount().
30 / Apr 01, 2007 R0.04a Supported multiple partitions on a plysical drive.
31 / Added a capability of extending file size to f_lseek().
32 / Added minimization level 3.
33 / Fixed an endian sensitive code in f_mkfs().
34 / May 05, 2007 R0.04b Added a configuration option _USE_NTFLAG.
35 / Added FSInfo support.
36 / Fixed DBCS name can result FR_INVALID_NAME.
37 / Fixed short seek (<= csize) collapses the file object.
38 /---------------------------------------------------------------------------*/
39 #include <stdio.h> // ###
41 #include "ff.h" /* FatFs declarations */
42 #include "diskio.h" /* Include file for user provided disk functions */
45 /*--------------------------------------------------------------------------
47 Module Private Functions
49 ---------------------------------------------------------------------------*/
51 static FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */
52 static U16 fsid; /* File system mount ID */
56 /*-----------------------------------------------------------------------*/
57 /* Change window offset */
58 /*-----------------------------------------------------------------------*/
61 BOOL move_window ( /* TRUE: successful, FALSE: failed */
62 FATFS *fs, /* File system object */
63 U32 sector /* Sector number to make apperance in the fs->win[] */
64 ) /* Move to zero only writes back dirty window */
70 if (wsect != sector) { /* Changed current window */
73 if (fs->winflag) { /* Write back dirty window if needed */
74 if (diskWrite(fs->drive, fs->win, wsect, 1) != RES_OK)
77 if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */
78 for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to FAT copy */
79 wsect += fs->sects_fat;
80 diskWrite(fs->drive, fs->win, wsect, 1);
86 if (diskRead(fs->drive, fs->win, sector, 1) != RES_OK)
97 /*-----------------------------------------------------------------------*/
98 /* Clean-up cached data */
99 /*-----------------------------------------------------------------------*/
101 #if _FS_READONLY == 0
103 FRESULT sync ( /* FR_OK: successful, FR_RW_ERROR: failed */
104 FATFS *fs /* File system object */
108 if (!move_window(fs, 0)) return FR_RW_ERROR;
110 if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { /* Update FSInfo sector if needed */
112 memset(fs->win, 0, 512);
113 ST_U16(&fs->win[BS_55AA], 0xAA55);
114 ST_U32(&fs->win[FSI_LeadSig], 0x41615252);
115 ST_U32(&fs->win[FSI_StrucSig], 0x61417272);
116 ST_U32(&fs->win[FSI_Free_Count], fs->free_clust);
117 ST_U32(&fs->win[FSI_Nxt_Free], fs->last_clust);
118 diskWrite(0, fs->win, fs->fsi_sector, 1);
122 if (diskIoctl(fs->drive, CTRL_SYNC, NULL) != RES_OK) return FR_RW_ERROR;
130 /*-----------------------------------------------------------------------*/
131 /* Get a cluster status */
132 /*-----------------------------------------------------------------------*/
135 U32 get_cluster ( /* 0,>=2: successful, 1: failed */
136 FATFS *fs, /* File system object */
137 U32 clust /* Cluster# to get the link information */
144 if (clust >= 2 && clust < fs->max_clust) { /* Valid cluster# */
145 fatsect = fs->fatbase;
146 switch (fs->fs_type) {
148 bc = (U16)clust * 3 / 2;
149 if (!move_window(fs, fatsect + (bc / S_SIZ))) break;
150 wc = fs->win[bc & (S_SIZ - 1)]; bc++;
151 if (!move_window(fs, fatsect + (bc / S_SIZ))) break;
152 wc |= (U16)fs->win[bc & (S_SIZ - 1)] << 8;
153 return (clust & 1) ? (wc >> 4) : (wc & 0xFFF);
156 if (!move_window(fs, fatsect + (clust / (S_SIZ / 2)))) break;
157 return LD_U16(&fs->win[((U16)clust * 2) & (S_SIZ - 1)]);
160 if (!move_window(fs, fatsect + (clust / (S_SIZ / 4)))) break;
161 return LD_U32(&fs->win[((U16)clust * 4) & (S_SIZ - 1)]) & 0x0FFFFFFF;
165 return 1; /* There is no cluster information, or an error occured */
171 /*-----------------------------------------------------------------------*/
172 /* Change a cluster status */
173 /*-----------------------------------------------------------------------*/
175 #if _FS_READONLY == 0
177 BOOL put_cluster ( /* TRUE: successful, FALSE: failed */
178 FATFS *fs, /* File system object */
179 U32 clust, /* Cluster# to change */
180 U32 val /* New value to mark the cluster */
188 fatsect = fs->fatbase;
189 switch (fs->fs_type) {
191 bc = (U16)clust * 3 / 2;
192 if (!move_window(fs, fatsect + (bc / S_SIZ))) return FALSE;
193 p = &fs->win[bc & (S_SIZ - 1)];
194 *p = (clust & 1) ? ((*p & 0x0F) | ((U8)val << 4)) : (U8)val;
197 if (!move_window(fs, fatsect + (bc / S_SIZ))) return FALSE;
198 p = &fs->win[bc & (S_SIZ - 1)];
199 *p = (clust & 1) ? (U8)(val >> 4) : ((*p & 0xF0) | ((U8)(val >> 8) & 0x0F));
203 if (!move_window(fs, fatsect + (clust / (S_SIZ / 2)))) return FALSE;
204 ST_U16(&fs->win[((U16)clust * 2) & (S_SIZ - 1)], (U16)val);
208 if (!move_window(fs, fatsect + (clust / (S_SIZ / 4)))) return FALSE;
209 ST_U32(&fs->win[((U16)clust * 4) & (S_SIZ - 1)], val);
218 #endif /* !_FS_READONLY */
223 /*-----------------------------------------------------------------------*/
224 /* Remove a cluster chain */
225 /*-----------------------------------------------------------------------*/
227 #if _FS_READONLY == 0
229 BOOL remove_chain ( /* TRUE: successful, FALSE: failed */
230 FATFS *fs, /* File system object */
231 U32 clust /* Cluster# to remove chain from */
237 while (clust >= 2 && clust < fs->max_clust) {
238 nxt = get_cluster(fs, clust);
239 if (nxt == 1) return FALSE;
240 if (!put_cluster(fs, clust, 0)) return FALSE;
241 if (fs->free_clust != 0xFFFFFFFF) {
256 /*-----------------------------------------------------------------------*/
257 /* Stretch or create a cluster chain */
258 /*-----------------------------------------------------------------------*/
260 #if _FS_READONLY == 0
262 U32 create_chain ( /* 0: no free cluster, 1: error, >=2: new cluster number */
263 FATFS *fs, /* File system object */
264 U32 clust /* Cluster# to stretch, 0 means create new */
267 U32 cstat, ncl, scl, mcl = fs->max_clust;
270 if (clust == 0) { /* Create new chain */
271 scl = fs->last_clust; /* Get suggested start point */
272 if (scl == 0 || scl >= mcl) scl = 1;
274 else { /* Stretch existing chain */
275 cstat = get_cluster(fs, clust); /* Check the cluster status */
276 if (cstat < 2) return 1; /* It is an invalid cluster */
277 if (cstat < mcl) return cstat; /* It is already followed by next cluster */
281 ncl = scl; /* Start cluster */
283 ncl++; /* Next cluster */
284 if (ncl >= mcl) { /* Wrap around */
286 if (ncl > scl) return 0; /* No free custer */
288 cstat = get_cluster(fs, ncl); /* Get the cluster status */
289 if (cstat == 0) break; /* Found a free cluster */
290 if (cstat == 1) return 1; /* Any error occured */
291 if (ncl == scl) return 0; /* No free custer */
294 if (!put_cluster(fs, ncl, 0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */
295 if (clust && !put_cluster(fs, clust, ncl)) return 1; /* Link it to previous one if needed */
297 fs->last_clust = ncl; /* Update fsinfo */
298 if (fs->free_clust != 0xFFFFFFFF) {
305 return ncl; /* Return new cluster number */
307 #endif /* !_FS_READONLY */
312 /*-----------------------------------------------------------------------*/
313 /* Get sector# from cluster# */
314 /*-----------------------------------------------------------------------*/
317 U32 clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */
318 FATFS *fs, /* File system object */
319 U32 clust /* Cluster# to be converted */
323 if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */
324 return clust * fs->sects_clust + fs->database;
330 /*-----------------------------------------------------------------------*/
331 /* Move directory pointer to next */
332 /*-----------------------------------------------------------------------*/
335 BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */
336 DIR *dirobj /* Pointer to directory object */
341 FATFS *fs = dirobj->fs;
344 idx = dirobj->index + 1;
345 if ((idx & ((S_SIZ - 1) / 32)) == 0) { /* Table sector changed? */
346 dirobj->sect++; /* Next sector */
347 if (!dirobj->clust) { /* In static table */
348 if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */
349 } else { /* In dynamic table */
350 if (((idx / (S_SIZ / 32)) & (fs->sects_clust - 1)) == 0) { /* Cluster changed? */
351 clust = get_cluster(fs, dirobj->clust); /* Get next cluster */
352 if (clust < 2 || clust >= fs->max_clust) /* Reached to end of table */
354 dirobj->clust = clust; /* Initialize for new cluster */
355 dirobj->sect = clust2sect(fs, clust);
359 dirobj->index = idx; /* Lower 4 bit of dirobj->index indicates offset in dirobj->sect */
366 /*-----------------------------------------------------------------------*/
367 /* Get file status from directory entry */
368 /*-----------------------------------------------------------------------*/
370 #if _FS_MINIMIZE <= 1
372 void get_fileinfo ( /* No return code */
373 FILINFO *finfo, /* Ptr to store the file information */
374 const U8 *dir /* Ptr to the directory entry */
381 p = &finfo->fname[0];
382 a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */
383 for (n = 0; n < 8; n++) { /* Convert file name (body) */
386 if (c == 0x05) c = 0xE5;
387 if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20;
390 if (dir[8] != ' ') { /* Convert file name (extension) */
392 for (n = 8; n < 11; n++) {
395 if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20;
401 finfo->fattrib = dir[DIR_Attr]; /* Attribute */
402 finfo->fsize = LD_U32(&dir[DIR_FileSize]); /* Size */
403 finfo->fdate = LD_U16(&dir[DIR_WrtDate]); /* Date */
404 finfo->ftime = LD_U16(&dir[DIR_WrtTime]); /* Time */
406 #endif /* _FS_MINIMIZE <= 1 */
411 /*-----------------------------------------------------------------------*/
412 /* Pick a paragraph and create the name in format of directory entry */
413 /*-----------------------------------------------------------------------*/
416 char make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */
417 const char **path, /* Pointer to the file path pointer */
418 char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */
423 memset(dirname, ' ', 8+3); /* Fill buffer with spaces */
424 a = 0; b = 0x18; /* NT flag */
431 if (c == '\0' || c == '/') { /* Reached to end of str or directory separator */
433 dirname[11] = _USE_NTFLAG ? (a & b) : 0;
436 if (c <= ' ' || c == 0x7F) break; /* Reject invisible chars */
438 if (!(a & 1) && n >= 1 && n <= 8) { /* Enter extension part */
439 n = 8; t = 11; continue;
444 ((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code */
445 (c >= 0xE0 && c <= 0xFC))) {
446 if (n == 0 && c == 0xE5) /* Change heading \xE5 to \x05 */
450 if (c == '"') break; /* Reject " */
451 if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */
452 if (c <= ',') break; /* Reject * + , */
453 if (c <= '9') goto md_l1; /* Accept - 0-9 */
454 if (c <= '?') break; /* Reject : ; < = > ? */
455 if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */
456 if (c == '|') break; /* Reject | */
457 if (c >= '[' && c <= ']') break;/* Reject [ \ ] */
458 if (_USE_NTFLAG && c >= 'A' && c <= 'Z')
459 (t == 8) ? (b &= ~0x08) : (b &= ~0x10);
460 if (c >= 'a' && c <= 'z') { /* Convert to upper case */
462 if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10);
477 /*-----------------------------------------------------------------------*/
478 /* Trace a file path */
479 /*-----------------------------------------------------------------------*/
482 FRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */
483 DIR *dirobj, /* Pointer to directory object to return last directory */
484 char *fn, /* Pointer to last segment name to return {file(8),ext(3),attr(1)} */
485 const char *path, /* Full-path string to trace a file or directory */
486 U8 **dir /* Directory pointer in Win[] to retutn */
492 FATFS *fs = dirobj->fs; /* Get logical drive from the given DIR structure */
495 /* Initialize directory object */
497 if (fs->fs_type == FS_FAT32) {
498 dirobj->clust = dirobj->sclust = clust;
499 dirobj->sect = clust2sect(fs, clust);
501 dirobj->clust = dirobj->sclust = 0;
502 dirobj->sect = clust;
506 if (*path == '\0') { /* Null path means the root directory */
507 *dir = NULL; return FR_OK;
511 ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */
512 if (ds == 1) return FR_INVALID_NAME;
514 if (!move_window(fs, dirobj->sect)) return FR_RW_ERROR;
515 dptr = &fs->win[(dirobj->index & ((S_SIZ - 1) / 32)) * 32]; /* Pointer to the directory entry */
516 if (dptr[DIR_Name] == 0) /* Has it reached to end of dir? */
517 return !ds ? FR_NO_FILE : FR_NO_PATH;
518 if (dptr[DIR_Name] != 0xE5 /* Matched? */
519 && !(dptr[DIR_Attr] & AM_VOL)
520 && !memcmp(&dptr[DIR_Name], fn, 8+3) ) break;
521 if (!next_dir_entry(dirobj)) /* Next directory pointer */
522 return !ds ? FR_NO_FILE : FR_NO_PATH;
524 if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */
525 if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */
526 clust = ((U32)LD_U16(&dptr[DIR_FstClusHI]) << 16) | LD_U16(&dptr[DIR_FstClusLO]); /* Get cluster# of the directory */
527 dirobj->clust = dirobj->sclust = clust; /* Restart scanning at the new directory */
528 dirobj->sect = clust2sect(fs, clust);
536 /*-----------------------------------------------------------------------*/
537 /* Reserve a directory entry */
538 /*-----------------------------------------------------------------------*/
542 FRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */
543 DIR *dirobj, /* Target directory to create new entry */
544 U8 **dir /* Pointer to pointer to created entry to retutn */
549 FATFS *fs = dirobj->fs;
552 /* Re-initialize directory object */
553 clust = dirobj->sclust;
554 if (clust) { /* Dyanmic directory table */
555 dirobj->clust = clust;
556 dirobj->sect = clust2sect(fs, clust);
557 } else { /* Static directory table */
558 dirobj->sect = fs->dirbase;
563 if (!move_window(fs, dirobj->sect)) return FR_RW_ERROR;
564 dptr = &fs->win[(dirobj->index & ((S_SIZ - 1) / 32)) * 32]; /* Pointer to the directory entry */
566 if (c == 0 || c == 0xE5) { /* Found an empty entry! */
567 *dir = dptr; return FR_OK;
569 } while (next_dir_entry(dirobj)); /* Next directory pointer */
570 /* Reached to end of the directory table */
572 /* Abort when static table or could not stretch dynamic table */
573 if (!clust || !(clust = create_chain(fs, dirobj->clust))) return FR_DENIED;
574 if (clust == 1 || !move_window(fs, 0)) return FR_RW_ERROR;
576 fs->winsect = sector = clust2sect(fs, clust); /* Cleanup the expanded table */
577 memset(fs->win, 0, S_SIZ);
578 for (n = fs->sects_clust; n; n--) {
579 if (diskWrite(fs->drive, fs->win, sector, 1) != RES_OK)
587 #endif /* !_FS_READONLY */
592 /*-----------------------------------------------------------------------*/
593 /* Load boot record and check if it is a FAT boot record */
594 /*-----------------------------------------------------------------------*/
597 U8 check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */
598 FATFS *fs, /* File system object */
599 U32 sect /* Sector# (lba) to check if it is a FAT boot record or not */
602 if (diskRead(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */
604 if (LD_U16(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always offset 510) */
607 if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */
609 if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80))
618 /*-----------------------------------------------------------------------*/
619 /* Make sure that the file system is valid */
620 /*-----------------------------------------------------------------------*/
623 FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */
624 const char **path, /* Pointer to pointer to the path name (drive number) */
625 FATFS **rfs, /* Pointer to pointer to the found file system object */
626 U8 chk_wp /* !=0: Check media write protection for wrinting fuctions */
631 U32 bootsect, fatsize, totalsect, maxclust;
632 const char *p = *path;
636 /* Get drive number from the path name */
637 while (*p == ' ') p++; /* Strip leading spaces */
638 drv = p[0] - '0'; /* Is there a drive number? */
639 if (drv <= 9 && p[1] == ':')
640 p += 2; /* Found a drive number, get and strip it */
642 drv = 0; /* No drive number is given, select drive 0 in default */
643 if (*p == '/') p++; /* Strip heading slash */
644 *path = p; /* Return pointer to the path name */
646 /* Check if the drive number is valid or not */
647 if (drv >= _DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */
648 if (!(fs = FatFs[drv])) return FR_NOT_ENABLED; /* Is the file system object registered? */
649 *rfs = fs; /* Returen pointer to the corresponding file system object */
651 /* Check if the logical drive has been mounted or not */
653 stat = diskStatus(fs->drive);
654 if (!(stat & STA_NOINIT)) { /* If the physical drive is kept initialized */
656 if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
657 return FR_WRITE_PROTECTED;
659 return FR_OK; /* The file system object is valid */
663 /* The logical drive has not been mounted, following code attempts to mount the logical drive */
665 memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */
666 fs->drive = LD2PD(drv); /* Bind the logical drive and a physical drive */
667 stat = diskInitialize (fs->drive); /* Initialize low level disk I/O layer */
668 if (stat & STA_NOINIT) /* Check if the drive is ready */
670 #if S_MAX_SIZ > 512 /* Check disk sector size */
671 if (diskIoctl(drv, GET_SECTOR_SIZE, &S_SIZ) != RES_OK || S_SIZ > S_MAX_SIZ)
672 return FR_NO_FILESYSTEM;
675 if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
676 return FR_WRITE_PROTECTED;
678 /* Search FAT partition on the drive */
679 fmt = check_fs(fs, bootsect = 0); /* Check sector 0 as an SFD format */
680 if (fmt == 1) { /* Not a FAT boot record, it may be patitioned */
681 /* Check a partition listed in top of the partition table */
682 tbl = &fs->win[MBR_Table + LD2PT(drv) * 16]; /* Partition table */
683 if (tbl[4]) { /* Is the partition existing? */
684 bootsect = LD_U32(&tbl[8]); /* Partition offset in LBA */
685 fmt = check_fs(fs, bootsect); /* Check the partition */
688 if (fmt || LD_U16(&fs->win[BPB_BytsPerSec]) != S_SIZ) /* No valid FAT patition is found */
689 return FR_NO_FILESYSTEM;
691 /* Initialize the file system object */
692 fatsize = LD_U16(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT */
693 if (!fatsize) fatsize = LD_U32(&fs->win[BPB_FATSz32]);
694 fs->sects_fat = fatsize;
695 fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */
696 fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */
697 fs->fatbase = bootsect + LD_U16(&fs->win[BPB_RsvdSecCnt]); /* FAT start sector (lba) */
698 fs->sects_clust = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */
699 fs->n_rootdir = LD_U16(&fs->win[BPB_RootEntCnt]); /* Nmuber of root directory entries */
700 totalsect = LD_U16(&fs->win[BPB_TotSec16]); /* Number of sectors on the file system */
701 if (!totalsect) totalsect = LD_U32(&fs->win[BPB_TotSec32]);
702 fs->max_clust = maxclust = (totalsect /* Last cluster# + 1 */
703 - LD_U16(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / (S_SIZ/32)
704 ) / fs->sects_clust + 2;
706 fmt = FS_FAT12; /* Determine the FAT sub type */
707 if (maxclust > 0xFF7) fmt = FS_FAT16;
708 if (maxclust > 0xFFF7) fmt = FS_FAT32;
712 fs->dirbase = LD_U32(&fs->win[BPB_RootClus]); /* Root directory start cluster */
714 fs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */
715 fs->database = fs->fatbase + fatsize + fs->n_rootdir / (S_SIZ/32); /* Data start sector (lba) */
718 fs->free_clust = 0xFFFFFFFF;
720 /* Load fsinfo sector if needed */
721 if (fmt == FS_FAT32) {
722 fs->fsi_sector = bootsect + LD_U16(&fs->win[BPB_FSInfo]);
723 if (diskRead(0, fs->win, fs->fsi_sector, 1) == RES_OK &&
724 LD_U16(&fs->win[BS_55AA]) == 0xAA55 &&
725 LD_U32(&fs->win[FSI_LeadSig]) == 0x41615252 &&
726 LD_U32(&fs->win[FSI_StrucSig]) == 0x61417272) {
727 fs->last_clust = LD_U32(&fs->win[FSI_Nxt_Free]);
728 fs->free_clust = LD_U32(&fs->win[FSI_Free_Count]);
733 fs->id = ++fsid; /* File system mount ID */
740 /*-----------------------------------------------------------------------*/
741 /* Check if the file/dir object is valid or not */
742 /*-----------------------------------------------------------------------*/
745 FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Not valid */
746 const FATFS *fs, /* Pointer to the file system object */
747 U16 id /* id member of the target object to be checked */
750 if (!fs || fs->id != id)
751 return FR_INVALID_OBJECT;
752 if (diskStatus(fs->drive) & STA_NOINIT)
761 /*--------------------------------------------------------------------------
765 --------------------------------------------------------------------------*/
768 void f_printerror (FRESULT f)
772 typedef struct errorStrings_s
779 static errorStrings_t errorStrings [] =
782 { FR_NOT_READY, "NOT_READY" },
783 { FR_NO_FILE, "NO_FILE" },
784 { FR_NO_PATH, "NO_PATH" },
785 { FR_INVALID_NAME, "INVALID_NAME" },
786 { FR_INVALID_DRIVE, "INVALID_DRIVE" },
787 { FR_DENIED, "DENIED" },
788 { FR_EXIST, "EXIST" },
789 { FR_RW_ERROR, "RW_ERROR" },
790 { FR_WRITE_PROTECTED, "WRITE_PROTECTED" },
791 { FR_NOT_ENABLED, "NOT_ENABLED" },
792 { FR_NO_FILESYSTEM, "NO_FILESYSTEM" },
793 { FR_INVALID_OBJECT, "INVALID_OBJECT" },
794 { FR_MKFS_ABORTED, "MKFS_ABORTED" },
797 for (i = 0; i < arrsizeof (errorStrings); i++)
799 if (errorStrings [i].fresult == f)
801 printf ("rrc=%u FR_%s\n", f, errorStrings [f].string);
806 printf ("rrc=%u (no text equivalent)\n", f);
810 /*-----------------------------------------------------------------------*/
811 /* Mount/Unmount a Locical Drive */
812 /*-----------------------------------------------------------------------*/
814 FRESULT f_mount (U8 drv, FATFS *fs)
819 return FR_INVALID_DRIVE;
825 memset (fsobj, 0, sizeof (FATFS));
827 memset (fs, 0, sizeof (FATFS));
832 /*-----------------------------------------------------------------------*/
833 /* Open or Create a File */
834 /*-----------------------------------------------------------------------*/
837 FIL *fp, /* Pointer to the blank file object */
838 const char *path, /* Pointer to the file name */
839 U8 mode /* Access mode and file open mode flags */
851 mode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW);
852 res = auto_mount(&path, &fs, (U8)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)));
855 res = auto_mount(&path, &fs, 0);
857 if (res != FR_OK) return res;
860 /* Trace the file path */
861 res = trace_path(&dirobj, fn, path, &dir);
863 /* Create or Open a file */
864 if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) {
866 if (res != FR_OK) { /* No file, create new */
867 if (res != FR_NO_FILE) return res;
868 res = reserve_direntry(&dirobj, &dir);
869 if (res != FR_OK) return res;
870 memset(dir, 0, 32); /* Initialize the new entry with open name */
871 memcpy(&dir[DIR_Name], fn, 8+3);
872 dir[DIR_NTres] = fn[11];
873 mode |= FA_CREATE_ALWAYS;
875 else { /* Any object is already existing */
876 if (mode & FA_CREATE_NEW) /* Cannot create new */
878 if (dir == NULL || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite it (R/O or DIR) */
880 if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero if needed */
881 rs = ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]); /* Get start cluster */
882 ST_U16(&dir[DIR_FstClusHI], 0); /* cluster = 0 */
883 ST_U16(&dir[DIR_FstClusLO], 0);
884 ST_U32(&dir[DIR_FileSize], 0); /* size = 0 */
886 ps = fs->winsect; /* Remove the cluster chain */
887 if (!remove_chain(fs, rs) || !move_window(fs, ps))
889 fs->last_clust = rs - 1; /* Reuse the cluster hole */
892 if (mode & FA_CREATE_ALWAYS) {
893 dir[DIR_Attr] = AM_ARC; /* New attribute */
895 ST_U32(&dir[DIR_WrtTime], ps); /* Updated time */
896 ST_U32(&dir[DIR_CrtTime], ps); /* Created time */
900 /* Open an existing file */
902 #endif /* !_FS_READONLY */
903 if (res != FR_OK) return res; /* Trace failed */
904 if (dir == NULL || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */
907 if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
911 fp->dir_sect = fs->winsect; /* Pointer to the directory entry */
914 fp->flag = mode; /* File access mode */
915 fp->org_clust = /* File start cluster */
916 ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]);
917 fp->fsize = LD_U32(&dir[DIR_FileSize]); /* File size */
918 fp->fptr = 0; /* File ptr */
919 fp->sect_clust = 1; /* Sector counter */
920 fp->fs = fs; fp->id = fs->id; /* Owner file system object of the file */
928 /*-----------------------------------------------------------------------*/
930 /*-----------------------------------------------------------------------*/
933 FIL *fp, /* Pointer to the file object */
934 void *buff, /* Pointer to data buffer */
935 U16 btr, /* Number of bytes to read */
936 U16 *br /* Pointer to number of bytes read */
939 U32 clust, sect, remain;
941 U8 cc, *rbuff = buff;
947 res = validate(fs, fp->id); /* Check validity of the object */
949 if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */
950 if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */
951 remain = fp->fsize - fp->fptr;
952 if (btr > remain) btr = (U16)remain; /* Truncate read count by number of bytes left */
954 for ( ; btr; /* Repeat until all data transferred */
955 rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
956 if ((fp->fptr & (S_SIZ - 1)) == 0) { /* On the sector boundary */
957 if (--fp->sect_clust) { /* Decrement left sector counter */
958 sect = fp->curr_sect + 1; /* Get current sector */
959 } else { /* On the cluster boundary, get next cluster */
960 clust = (fp->fptr == 0) ?
961 fp->org_clust : get_cluster(fs, fp->curr_clust);
962 if (clust < 2 || clust >= fs->max_clust)
964 fp->curr_clust = clust; /* Current cluster */
965 sect = clust2sect(fs, clust); /* Get current sector */
966 fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */
969 if (fp->flag & FA__DIRTY) { /* Flush file I/O buffer if needed */
970 if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK)
972 fp->flag &= ~FA__DIRTY;
975 fp->curr_sect = sect; /* Update current sector */
976 cc = btr / S_SIZ; /* When left bytes >= S_SIZ, */
977 if (cc) { /* Read maximum contiguous sectors directly */
978 if (cc > fp->sect_clust) cc = fp->sect_clust;
979 if (diskRead(fs->drive, rbuff, sect, cc) != RES_OK)
981 fp->sect_clust -= cc - 1;
982 fp->curr_sect += cc - 1;
983 rcnt = cc * S_SIZ; continue;
985 if (diskRead(fs->drive, fp->buffer, sect, 1) != RES_OK) /* Load the sector into file I/O buffer */
988 rcnt = S_SIZ - ((U16)fp->fptr & (S_SIZ - 1)); /* Copy fractional bytes from file I/O buffer */
989 if (rcnt > btr) rcnt = btr;
990 memcpy(rbuff, &fp->buffer[fp->fptr & (S_SIZ - 1)], rcnt);
995 fr_error: /* Abort this file due to an unrecoverable error */
996 fp->flag |= FA__ERROR;
1004 /*-----------------------------------------------------------------------*/
1006 /*-----------------------------------------------------------------------*/
1009 FIL *fp, /* Pointer to the file object */
1010 const void *buff, /* Pointer to the data to be written */
1011 U16 btw, /* Number of bytes to write */
1012 U16 *bw /* Pointer to number of bytes written */
1019 const U8 *wbuff = buff;
1024 res = validate(fs, fp->id); /* Check validity of the object */
1025 if (res) return res;
1026 if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */
1027 if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */
1028 if (fp->fsize + btw < fp->fsize) return FR_OK; /* File size cannot reach 4GB */
1030 for ( ; btw; /* Repeat until all data transferred */
1031 wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
1032 if ((fp->fptr & (S_SIZ - 1)) == 0) { /* On the sector boundary */
1033 if (--fp->sect_clust) { /* Decrement left sector counter */
1034 sect = fp->curr_sect + 1; /* Get current sector */
1035 } else { /* On the cluster boundary, get next cluster */
1036 if (fp->fptr == 0) { /* Is top of the file */
1037 clust = fp->org_clust;
1038 if (clust == 0) /* No cluster is created yet */
1039 fp->org_clust = clust = create_chain(fs, 0); /* Create a new cluster chain */
1040 } else { /* Middle or end of file */
1041 clust = create_chain(fs, fp->curr_clust); /* Trace or streach cluster chain */
1043 if (clust == 0) break; /* Disk full */
1044 if (clust == 1 || clust >= fs->max_clust) goto fw_error;
1045 fp->curr_clust = clust; /* Current cluster */
1046 sect = clust2sect(fs, clust); /* Get current sector */
1047 fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */
1049 if (fp->flag & FA__DIRTY) { /* Flush file I/O buffer if needed */
1050 if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK)
1052 fp->flag &= ~FA__DIRTY;
1054 fp->curr_sect = sect; /* Update current sector */
1055 cc = btw / S_SIZ; /* When left bytes >= S_SIZ, */
1056 if (cc) { /* Write maximum contiguous sectors directly */
1057 if (cc > fp->sect_clust) cc = fp->sect_clust;
1058 if (diskWrite(fs->drive, wbuff, sect, cc) != RES_OK)
1060 fp->sect_clust -= cc - 1;
1061 fp->curr_sect += cc - 1;
1062 wcnt = cc * S_SIZ; continue;
1064 if (fp->fptr < fp->fsize && /* Fill sector buffer with file data if needed */
1065 diskRead(fs->drive, fp->buffer, sect, 1) != RES_OK)
1068 wcnt = S_SIZ - ((U16)fp->fptr & (S_SIZ - 1)); /* Copy fractional bytes to file I/O buffer */
1069 if (wcnt > btw) wcnt = btw;
1070 memcpy(&fp->buffer[fp->fptr & (S_SIZ - 1)], wbuff, wcnt);
1071 fp->flag |= FA__DIRTY;
1074 if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
1075 fp->flag |= FA__WRITTEN; /* Set file changed flag */
1078 fw_error: /* Abort this file due to an unrecoverable error */
1079 fp->flag |= FA__ERROR;
1086 /*-----------------------------------------------------------------------*/
1087 /* Synchronize between File and Disk */
1088 /*-----------------------------------------------------------------------*/
1091 FIL *fp /* Pointer to the file object */
1100 res = validate(fs, fp->id); /* Check validity of the object */
1102 if (fp->flag & FA__WRITTEN) { /* Has the file been written? */
1103 /* Write back data buffer if needed */
1104 if (fp->flag & FA__DIRTY) {
1105 if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK)
1107 fp->flag &= ~FA__DIRTY;
1109 /* Update the directory entry */
1110 if (!move_window(fs, fp->dir_sect))
1113 dir[DIR_Attr] |= AM_ARC; /* Set archive bit */
1114 ST_U32(&dir[DIR_FileSize], fp->fsize); /* Update file size */
1115 ST_U16(&dir[DIR_FstClusLO], fp->org_clust); /* Update start cluster */
1116 ST_U16(&dir[DIR_FstClusHI], fp->org_clust >> 16);
1117 tim = get_fattime(); /* Updated time */
1118 ST_U32(&dir[DIR_WrtTime], tim);
1119 fp->flag &= ~FA__WRITTEN;
1126 #endif /* !_FS_READONLY */
1131 /*-----------------------------------------------------------------------*/
1133 /*-----------------------------------------------------------------------*/
1136 FIL *fp /* Pointer to the file object to be closed */
1145 res = validate(fp->fs, fp->id);
1155 #if _FS_MINIMIZE <= 2
1156 /*-----------------------------------------------------------------------*/
1157 /* Seek File R/W Pointer */
1158 /*-----------------------------------------------------------------------*/
1161 FIL *fp, /* Pointer to the file object */
1162 U32 ofs /* File pointer from top of file */
1171 res = validate(fs, fp->id); /* Check validity of the object */
1172 if (res) return res;
1173 if (fp->flag & FA__ERROR) return FR_RW_ERROR;
1175 if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */
1176 if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK)
1178 fp->flag &= ~FA__DIRTY;
1180 if (ofs > fp->fsize && !(fp->flag & FA_WRITE))
1182 if (ofs > fp->fsize)
1185 fp->fptr = 0; fp->sect_clust = 1; /* Set file R/W pointer to top of the file */
1187 /* Move file R/W pointer if needed */
1189 clust = fp->org_clust; /* Get start cluster */
1191 if (!clust) { /* If the file does not have a cluster chain, create new cluster chain */
1192 clust = create_chain(fs, 0);
1193 if (clust == 1) goto fk_error;
1194 fp->org_clust = clust;
1197 if (clust) { /* If the file has a cluster chain, it can be followed */
1198 csize = (U32)fs->sects_clust * S_SIZ; /* Cluster size in unit of byte */
1199 for (;;) { /* Loop to skip leading clusters */
1200 fp->curr_clust = clust; /* Update current cluster */
1201 if (ofs <= csize) break;
1203 if (fp->flag & FA_WRITE) /* Check if in write mode or not */
1204 clust = create_chain(fs, clust); /* Force streached if in write mode */
1207 clust = get_cluster(fs, clust); /* Only follow cluster chain if not in write mode */
1208 if (clust == 0) { /* Stop if could not follow the cluster chain */
1211 if (clust == 1 || clust >= fs->max_clust) goto fk_error;
1212 fp->fptr += csize; /* Update R/W pointer */
1215 csect = (U8)((ofs - 1) / S_SIZ); /* Sector offset in the cluster */
1216 fp->curr_sect = clust2sect(fs, clust) + csect; /* Current sector */
1217 if ((ofs & (S_SIZ - 1)) && /* Load current sector if needed */
1218 diskRead(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK)
1220 fp->sect_clust = fs->sects_clust - csect; /* Left sector counter in the cluster */
1221 fp->fptr += ofs; /* Update file R/W pointer */
1225 if ((fp->flag & FA_WRITE) && fp->fptr > fp->fsize) { /* Set updated flag if in write mode */
1226 fp->fsize = fp->fptr;
1227 fp->flag |= FA__WRITTEN;
1233 fk_error: /* Abort this file due to an unrecoverable error */
1234 fp->flag |= FA__ERROR;
1241 #if _FS_MINIMIZE <= 1
1242 /*-----------------------------------------------------------------------*/
1243 /* Create a directroy object */
1244 /*-----------------------------------------------------------------------*/
1247 DIR *dirobj, /* Pointer to directory object to create */
1248 const char *path /* Pointer to the directory path */
1257 res = auto_mount(&path, &fs, 0);
1258 if (res != FR_OK) return res;
1261 res = trace_path(dirobj, fn, path, &dir); /* Trace the directory path */
1262 if (res == FR_OK) { /* Trace completed */
1263 if (dir != NULL) { /* It is not the root dir */
1264 if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */
1265 dirobj->clust = ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]);
1266 dirobj->sect = clust2sect(fs, dirobj->clust);
1268 } else { /* The entry is not a directory */
1272 dirobj->id = fs->id;
1280 /*-----------------------------------------------------------------------*/
1281 /* Read Directory Entry in Sequense */
1282 /*-----------------------------------------------------------------------*/
1285 DIR *dirobj, /* Pointer to the directory object */
1286 FILINFO *finfo /* Pointer to file information to return */
1290 FATFS *fs = dirobj->fs;
1293 res = validate(fs, dirobj->id); /* Check validity of the object */
1294 if (res) return (FRESULT) res;
1296 finfo->fname[0] = 0;
1297 while (dirobj->sect) {
1298 if (!move_window(fs, dirobj->sect))
1300 dir = &fs->win[(dirobj->index & ((S_SIZ - 1) >> 5)) * 32]; /* pointer to the directory entry */
1302 if (c == 0) break; /* Has it reached to end of dir? */
1303 if (c != 0xE5 && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */
1304 get_fileinfo(finfo, dir);
1305 if (!next_dir_entry(dirobj)) dirobj->sect = 0; /* Next entry */
1306 if (finfo->fname[0]) break; /* Found valid entry */
1315 #if _FS_MINIMIZE == 0
1316 /*-----------------------------------------------------------------------*/
1317 /* Get File Status */
1318 /*-----------------------------------------------------------------------*/
1321 const char *path, /* Pointer to the file path */
1322 FILINFO *finfo /* Pointer to file information to return */
1332 res = auto_mount(&path, &fs, 0);
1333 if (res != FR_OK) return res;
1336 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1337 if (res == FR_OK) { /* Trace completed */
1338 if (dir) /* Found an object */
1339 get_fileinfo(finfo, dir);
1340 else /* It is root dir */
1341 res = FR_INVALID_NAME;
1350 /*-----------------------------------------------------------------------*/
1351 /* Get Number of Free Clusters */
1352 /*-----------------------------------------------------------------------*/
1355 const char *drv, /* Logical drive number */
1356 U32 *nclust, /* Pointer to the double word to return number of free clusters */
1357 FATFS **fatfs /* Pointer to pointer to the file system object to return */
1366 /* Get drive number */
1367 res = auto_mount(&drv, &fs, 0);
1368 if (res != FR_OK) return res;
1371 /* If number of free cluster is valid, return it without cluster scan. */
1372 if (fs->free_clust <= fs->max_clust - 2) {
1373 *nclust = fs->free_clust;
1377 /* Count number of free clusters */
1380 if (fat == FS_FAT12) {
1383 if ((U16)get_cluster(fs, clust) == 0) n++;
1384 } while (++clust < fs->max_clust);
1386 clust = fs->max_clust;
1391 if (!move_window(fs, sect++)) return FR_RW_ERROR;
1394 if (fat == FS_FAT16) {
1395 if (LD_U16(p) == 0) n++;
1398 if (LD_U32(p) == 0) n++;
1405 if (fat == FS_FAT32) fs->fsi_flag = 1;
1415 /*-----------------------------------------------------------------------*/
1416 /* Delete a File or a Directory */
1417 /*-----------------------------------------------------------------------*/
1420 const char *path /* Pointer to the file or directory path */
1431 res = auto_mount(&path, &fs, 1);
1432 if (res != FR_OK) return res;
1435 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1436 if (res != FR_OK) return res; /* Trace failed */
1437 if (dir == NULL) return FR_INVALID_NAME; /* It is the root directory */
1438 if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */
1439 dsect = fs->winsect;
1440 dclust = ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]);
1442 if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */
1443 dirobj.clust = dclust; /* Check if the sub-dir is empty or not */
1444 dirobj.sect = clust2sect(fs, dclust);
1447 if (!move_window(fs, dirobj.sect)) return FR_RW_ERROR;
1448 sdir = &fs->win[(dirobj.index & ((S_SIZ - 1) >> 5)) * 32];
1449 if (sdir[DIR_Name] == 0) break;
1450 if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL))
1451 return FR_DENIED; /* The directory is not empty */
1452 } while (next_dir_entry(&dirobj));
1455 if (!move_window(fs, dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */
1456 dir[DIR_Name] = 0xE5;
1458 if (!remove_chain(fs, dclust)) return FR_RW_ERROR; /* Remove the cluster chain */
1466 /*-----------------------------------------------------------------------*/
1467 /* Create a Directory */
1468 /*-----------------------------------------------------------------------*/
1471 const char *path /* Pointer to the directory path */
1476 U32 sect, dsect, dclust, pclust, tim;
1481 res = auto_mount(&path, &fs, 1);
1482 if (res != FR_OK) return res;
1485 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1486 if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */
1487 if (res != FR_NO_FILE) return res;
1489 res = reserve_direntry(&dirobj, &dir); /* Reserve a directory entry */
1490 if (res != FR_OK) return res;
1493 dclust = create_chain(fs, 0); /* Allocate a cluster for new directory table */
1494 if (dclust == 1) return FR_RW_ERROR;
1496 dsect = clust2sect(fs, dclust);
1497 if (!dsect) return FR_DENIED;
1499 if (!move_window(fs, dsect)) return FR_RW_ERROR;
1502 memset(fw, 0, S_SIZ); /* Clear the new directory table */
1503 for (n = 1; n < fs->sects_clust; n++) {
1504 if (diskWrite(fs->drive, fw, ++dsect, 1) != RES_OK)
1507 memset(&fw[DIR_Name], ' ', 8+3); /* Create "." entry */
1509 fw[DIR_Attr] = AM_DIR;
1510 tim = get_fattime();
1511 ST_U32(&fw[DIR_WrtTime], tim);
1512 memcpy(&fw[32], &fw[0], 32); fw[33] = '.'; /* Create ".." entry */
1513 pclust = dirobj.sclust;
1515 ST_U16(&fw[ DIR_FstClusHI], dclust >> 16);
1516 if (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) pclust = 0;
1517 ST_U16(&fw[32+DIR_FstClusHI], pclust >> 16);
1519 ST_U16(&fw[ DIR_FstClusLO], dclust);
1520 ST_U16(&fw[32+DIR_FstClusLO], pclust);
1523 if (!move_window(fs, sect)) return FR_RW_ERROR;
1524 memset(&dir[0], 0, 32); /* Initialize the new entry */
1525 memcpy(&dir[DIR_Name], fn, 8+3); /* Name */
1526 dir[DIR_NTres] = fn[11];
1527 dir[DIR_Attr] = AM_DIR; /* Attribute */
1528 ST_U32(&dir[DIR_WrtTime], tim); /* Crated time */
1529 ST_U16(&dir[DIR_FstClusLO], dclust); /* Table start cluster */
1530 ST_U16(&dir[DIR_FstClusHI], dclust >> 16);
1538 /*-----------------------------------------------------------------------*/
1539 /* Change File Attribute */
1540 /*-----------------------------------------------------------------------*/
1543 const char *path, /* Pointer to the file path */
1544 U8 value, /* Attribute bits */
1545 U8 mask /* Attribute mask to change */
1555 res = auto_mount(&path, &fs, 1);
1558 res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */
1559 if (res == FR_OK) { /* Trace completed */
1561 res = FR_INVALID_NAME;
1563 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
1564 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (U8)~mask); /* Apply attribute change */
1575 /*-----------------------------------------------------------------------*/
1576 /* Rename File/Directory */
1577 /*-----------------------------------------------------------------------*/
1580 const char *path_old, /* Pointer to the old name */
1581 const char *path_new /* Pointer to the new name */
1586 U8 *dir_old, *dir_new, direntry[32-11];
1592 res = auto_mount(&path_old, &fs, 1);
1593 if (res != FR_OK) return res;
1596 res = trace_path(&dirobj, fn, path_old, &dir_old); /* Check old object */
1597 if (res != FR_OK) return res; /* The old object is not found */
1598 if (!dir_old) return FR_NO_FILE;
1599 sect_old = fs->winsect; /* Save the object information */
1600 memcpy(direntry, &dir_old[DIR_Attr], 32-11);
1602 res = trace_path(&dirobj, fn, path_new, &dir_new); /* Check new object */
1603 if (res == FR_OK) return FR_EXIST; /* The new object name is already existing */
1604 if (res != FR_NO_FILE) return res; /* Is there no old name? */
1605 res = reserve_direntry(&dirobj, &dir_new); /* Reserve a directory entry */
1606 if (res != FR_OK) return res;
1608 memcpy(&dir_new[DIR_Attr], direntry, 32-11); /* Create new entry */
1609 memcpy(&dir_new[DIR_Name], fn, 8+3);
1610 dir_new[DIR_NTres] = fn[11];
1613 if (!move_window(fs, sect_old)) return FR_RW_ERROR; /* Remove old entry */
1614 dir_old[DIR_Name] = 0xE5;
1622 /*-----------------------------------------------------------------------*/
1623 /* Create File System on the Drive */
1624 /*-----------------------------------------------------------------------*/
1626 #define N_ROOTDIR 512
1628 #define MAX_SECTOR 64000000UL
1629 #define MIN_SECTOR 2000UL
1630 #define ERASE_BLK 32
1634 U8 drv, /* Logical drive number */
1635 U8 partition, /* Partitioning rule 0:FDISK, 1:SFD */
1636 U8 allocsize /* Allocation unit size [sectors] */
1640 U32 b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */
1641 U32 n_part, n_rsv, n_fat, n_dir; /* Area size */
1647 /* Check and mounted drive and clear work area */
1649 return FR_INVALID_DRIVE;
1651 if (!(fs = FatFs [drv]))
1652 return FR_NOT_ENABLED;
1654 memset (fs, 0, sizeof(FATFS));
1657 /* Check validity of the parameters */
1658 for (n = 1; n <= 64 && allocsize != n; n <<= 1)
1660 if (n > 64 || partition >= 2)
1662 // printf ("line %d: n > 64 || partition >= 2. n=%d, partition=%d\n", __LINE__, n, partition); // ###
1663 return FR_MKFS_ABORTED;
1666 /* Get disk statics */
1667 stat = diskInitialize (drv);
1669 if (stat & STA_NOINIT)
1670 return FR_NOT_READY;
1671 if (stat & STA_PROTECT)
1672 return FR_WRITE_PROTECTED;
1674 DRESULT dres; // ###
1675 if ((dres = diskIoctl (drv, GET_SECTOR_COUNT, &n_part)) != RES_OK || n_part < MIN_SECTOR)
1677 // printf ("line %d: ioctl returned %d. n_part=%d, MIN_SECTOR=%d\n", __LINE__, dres, n_part, MIN_SECTOR); // ###
1678 return FR_MKFS_ABORTED;
1682 if (n_part > MAX_SECTOR)
1683 n_part = MAX_SECTOR;
1685 b_part = (!partition) ? 63 : 0;
1687 #if S_MAX_SIZ > 512 /* Check disk sector size */
1688 if (diskIoctl(drv, GET_SECTOR_SIZE, &S_SIZ) != RES_OK
1689 || S_SIZ > S_MAX_SIZ
1690 || (U32)S_SIZ * allocsize > 32768U)
1691 return FR_MKFS_ABORTED;
1694 /* Pre-compute number of clusters and FAT type */
1695 n_clust = n_part / allocsize;
1697 if (n_clust >= 0xFF7) fmt = FS_FAT16;
1698 if (n_clust >= 0xFFF7) fmt = FS_FAT32;
1701 n_fat = ((n_clust * 3 + 1) / 2 + 3 + S_SIZ - 1) / S_SIZ;
1702 n_rsv = 1 + partition;
1703 n_dir = N_ROOTDIR * 32 / S_SIZ;
1706 n_fat = ((n_clust * 2) + 4 + S_SIZ - 1) / S_SIZ;
1707 n_rsv = 1 + partition;
1708 n_dir = N_ROOTDIR * 32 / S_SIZ;
1711 n_fat = ((n_clust * 4) + 8 + S_SIZ - 1) / S_SIZ;
1712 n_rsv = 33 - partition;
1715 b_fat = b_part + n_rsv; /* FATs start sector */
1716 b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */
1717 b_data = b_dir + n_dir; /* Data start sector */
1720 /* Round up data start sector to erase block boundary */
1721 n = (b_data + ERASE_BLK - 1) & ~(ERASE_BLK - 1);
1722 b_dir += n - b_data;
1723 n_fat += (n - b_data) / N_FATS;
1725 /* Determine number of cluster and final check of validity of the FAT type */
1726 n_clust = (n_part - n_rsv - n_fat * 2 - n_dir) / allocsize;
1727 if ( (fmt == FS_FAT16 && n_clust < 0xFF7)
1728 || (fmt == FS_FAT32 && n_clust < 0xFFF7))
1730 // printf ("line %d: fmt=%d, FS_FAT16=%d, FS_FAT32=%d, n_clust=0x%x\n", __LINE__, fmt, FS_FAT16, FS_FAT32, n_clust); // ###
1731 return FR_MKFS_ABORTED;
1734 /* Create partition table if needed */
1736 U32 n_disk = b_part + n_part;
1738 tbl = &fs->win[MBR_Table];
1739 ST_U32(&tbl[0], 0x00010180); /* Partition start in CHS */
1740 if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */
1741 n_disk = n_disk / 63 / 255;
1742 tbl[7] = (U8)n_disk;
1743 tbl[6] = (U8)((n_disk >> 2) | 63);
1745 ST_U16(&tbl[6], 0xFFFF);
1748 if (fmt != FS_FAT32) /* System ID */
1749 tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
1752 ST_U32(&tbl[8], 63); /* Partition start in LBA */
1753 ST_U32(&tbl[12], n_part); /* Partition size in LBA */
1754 ST_U16(&tbl[64], 0xAA55); /* Signature */
1755 if (diskWrite(drv, fs->win, 0, 1) != RES_OK)
1759 /* Create boot record */
1760 memset(tbl = fs->win, 0, S_SIZ);
1761 ST_U32(&tbl[BS_jmpBoot], 0x90FEEB); /* Boot code (jmp $, nop) */
1762 ST_U16(&tbl[BPB_BytsPerSec], S_SIZ); /* Sector size */
1763 tbl[BPB_SecPerClus] = (U8)allocsize; /* Sectors per cluster */
1764 ST_U16(&tbl[BPB_RsvdSecCnt], n_rsv); /* Reserved sectors */
1765 tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */
1766 ST_U16(&tbl[BPB_RootEntCnt], S_SIZ / 32 * n_dir); /* Number of rootdir entries */
1767 if (n_part < 0x10000) { /* Number of total sectors */
1768 ST_U16(&tbl[BPB_TotSec16], n_part);
1770 ST_U32(&tbl[BPB_TotSec32], n_part);
1772 tbl[BPB_Media] = 0xF8; /* Media descripter */
1773 ST_U16(&tbl[BPB_SecPerTrk], 63); /* Number of sectors per track */
1774 ST_U16(&tbl[BPB_NumHeads], 255); /* Number of heads */
1775 ST_U32(&tbl[BPB_HiddSec], b_part); /* Hidden sectors */
1776 if (fmt != FS_FAT32) {
1777 ST_U16(&tbl[BPB_FATSz16], n_fat); /* Number of secters per FAT */
1778 tbl[BS_DrvNum] = 0x80; /* Drive number */
1779 tbl[BS_BootSig] = 0x29; /* Extended boot signature */
1780 memcpy(&tbl[BS_VolLab], "NO NAME FAT ", 19); /* Volume lavel, FAT signature */
1782 ST_U32(&tbl[BPB_FATSz32], n_fat); /* Number of secters per FAT */
1783 ST_U32(&tbl[BPB_RootClus], 2); /* Root directory cluster (2) */
1784 ST_U16(&tbl[BPB_FSInfo], 1); /* FSInfo record (bs+1) */
1785 ST_U16(&tbl[BPB_BkBootSec], 6); /* Backup boot record (bs+6) */
1786 tbl[BS_DrvNum32] = 0x80; /* Drive number */
1787 tbl[BS_BootSig32] = 0x29; /* Extended boot signature */
1788 memcpy(&tbl[BS_VolLab32], "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */
1790 ST_U16(&tbl[BS_55AA], 0xAA55); /* Signature */
1791 if (diskWrite(drv, tbl, b_part+0, 1) != RES_OK)
1793 if (fmt == FS_FAT32)
1794 diskWrite(drv, tbl, b_part+6, 1);
1796 /* Initialize FAT area */
1797 for (m = 0; m < N_FATS; m++) {
1798 memset(tbl, 0, S_SIZ); /* 1st sector of the FAT */
1799 if (fmt != FS_FAT32) {
1800 n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8;
1801 ST_U32(&tbl[0], n); /* Reserve cluster #0-1 (FAT12/16) */
1803 ST_U32(&tbl[0], 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */
1804 ST_U32(&tbl[4], 0xFFFFFFFF);
1805 ST_U32(&tbl[8], 0x0FFFFFFF); /* Reserve cluster #2 for root dir */
1807 if (diskWrite(drv, tbl, b_fat++, 1) != RES_OK)
1809 memset(tbl, 0, S_SIZ); /* Following FAT entries are filled by zero */
1810 for (n = 1; n < n_fat; n++) {
1811 if (diskWrite(drv, tbl, b_fat++, 1) != RES_OK)
1816 /* Initialize Root directory */
1817 for (m = 0; m < 64; m++) {
1818 if (diskWrite(drv, tbl, b_fat++, 1) != RES_OK)
1822 /* Create FSInfo record if needed */
1823 if (fmt == FS_FAT32) {
1824 ST_U16(&tbl[BS_55AA], 0xAA55);
1825 ST_U32(&tbl[FSI_LeadSig], 0x41615252);
1826 ST_U32(&tbl[FSI_StrucSig], 0x61417272);
1827 ST_U32(&tbl[FSI_Free_Count], n_clust - 1);
1828 ST_U32(&tbl[FSI_Nxt_Free], 0xFFFFFFFF);
1829 diskWrite(drv, tbl, b_part+1, 1);
1830 diskWrite(drv, tbl, b_part+7, 1);
1833 return (diskIoctl(drv, CTRL_SYNC, NULL) == RES_OK) ? FR_OK : FR_RW_ERROR;
1836 #endif /* _USE_MKFS */
1837 #endif /* !_FS_READONLY */
1838 #endif /* _FS_MINIMIZE == 0 */
1839 #endif /* _FS_MINIMIZE <= 1 */
1840 #endif /* _FS_MINIMIZE <= 2 */