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 /---------------------------------------------------------------------------*/
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
;
45 FATFS
*FatFs
; /* Pointer to the file system objects (logical drive) */
47 WORD fsid
; /* File system mount ID */
50 /*-------------------------------------------------------------------------
52 Module Private Functions
54 -------------------------------------------------------------------------*/
57 /*-----------------------------------------------------------------------*/
58 /* Change window offset */
59 /*-----------------------------------------------------------------------*/
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 */
72 { /* Changed current window */
76 { /* Write back dirty window if needed */
77 if (disk_write(0, fs
->win
, wsect
, 1) != RES_OK
)
80 if (wsect
< (fs
->fatbase
+ fs
->sects_fat
))
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);
92 if (disk_read(0, fs
->win
, sector
, 1) != RES_OK
)
103 /*-----------------------------------------------------------------------*/
104 /* Clean-up cached data */
105 /*-----------------------------------------------------------------------*/
109 FRESULT
sync (void) /* FR_OK: successful, FR_RW_ERROR: failed */
115 if (!move_window(0)) return FR_RW_ERROR
;
117 if (fs
->fs_type
== FS_FAT32
&& fs
->fsi_flag
)
118 { /* Update FSInfo sector if needed */
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);
130 if (disk_ioctl(0, CTRL_SYNC
, NULL
) != RES_OK
) return FR_RW_ERROR
;
138 /*-----------------------------------------------------------------------*/
139 /* Get a cluster status */
140 /*-----------------------------------------------------------------------*/
143 CLUST
get_cluster ( /* 0,>=2: successful, 1: failed */
144 CLUST clust
/* Cluster# to get the link information */
152 if (clust
>= 2 && clust
< fs
->max_clust
)
153 { /* Valid cluster# */
154 fatsect
= fs
->fatbase
;
158 bc
= (WORD
)clust
* 3 / 2;
159 if (!move_window(fatsect
+ bc
/ 512)) break;
160 wc
= fs
->win
[bc
% 512];
162 if (!move_window(fatsect
+ bc
/ 512)) break;
163 wc
|= (WORD
)fs
->win
[bc
% 512] << 8;
164 return (clust
& 1) ? (wc
>> 4) : (wc
& 0xFFF);
167 if (!move_window(fatsect
+ clust
/ 256)) break;
168 return LD_WORD(&fs
->win
[((WORD
)clust
* 2) % 512]);
171 if (!move_window(fatsect
+ clust
/ 128)) break;
172 return LD_DWORD(&fs
->win
[((WORD
)clust
* 4) % 512]) & 0x0FFFFFFF;
177 return 1; /* There is no cluster information, or an error occured */
183 /*-----------------------------------------------------------------------*/
184 /* Change a cluster status */
185 /*-----------------------------------------------------------------------*/
189 BOOL
put_cluster ( /* TRUE: successful, FALSE: failed */
190 CLUST clust
, /* Cluster# to change */
191 CLUST val
/* New value to mark the cluster */
200 fatsect
= fs
->fatbase
;
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
;
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));
216 if (!move_window(fatsect
+ clust
/ 256)) return FALSE
;
217 ST_WORD(&fs
->win
[((WORD
)clust
* 2) % 512], (WORD
)val
);
221 if (!move_window(fatsect
+ clust
/ 128)) return FALSE
;
222 ST_DWORD(&fs
->win
[((WORD
)clust
* 4) % 512], val
);
231 #endif /* !_FS_READONLY */
236 /*-----------------------------------------------------------------------*/
237 /* Remove a cluster chain */
238 /*-----------------------------------------------------------------------*/
242 BOOL
remove_chain ( /* TRUE: successful, FALSE: failed */
243 CLUST clust
/* Cluster# to remove chain from */
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)
271 /*-----------------------------------------------------------------------*/
272 /* Stretch or create a cluster chain */
273 /*-----------------------------------------------------------------------*/
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
;
287 { /* Create new chain */
288 scl
= fs
->last_clust
; /* Get last allocated cluster */
289 if (scl
< 2 || scl
>= mcl
) scl
= 1;
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 */
299 ncl
= scl
; /* Start cluster */
302 ncl
++; /* Next cluster */
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)
326 return ncl
; /* Return new cluster number */
328 #endif /* !_FS_READONLY */
333 /*-----------------------------------------------------------------------*/
334 /* Get sector# from cluster# */
335 /*-----------------------------------------------------------------------*/
338 DWORD
clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */
339 CLUST clust
/* Cluster# to be converted */
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 /*-----------------------------------------------------------------------*/
358 BOOL
next_dir_entry ( /* TRUE: successful, FALSE: could not move next */
359 DIR *dirobj
/* Pointer to directory object */
367 idx
= dirobj
->index
+ 1;
369 { /* Table sector changed? */
370 dirobj
->sect
++; /* Next sector */
372 { /* In static table */
373 if (idx
>= fs
->n_rootdir
) return FALSE
; /* Reached to end of table */
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 */
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 */
394 /*-----------------------------------------------------------------------*/
395 /* Get file status from directory entry */
396 /*-----------------------------------------------------------------------*/
398 #if _FS_MINIMIZE <= 1
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 */
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) */
415 if (c
== 0x05) c
= 0xE5;
416 if (a
& 0x08 && c
>= 'A' && c
<= 'Z') c
+= 0x20;
420 { /* Convert file name (extension) */
422 for (n
= 8; n
< 11; n
++)
426 if (a
& 0x10 && c
>= 'A' && c
<= 'Z') c
+= 0x20;
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 /*-----------------------------------------------------------------------*/
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)} */
455 memset(dirname
, ' ', 8+3); /* Fill buffer with spaces */
457 b
= 0x18; /* NT flag */
463 if (c
== '\0' || c
== '/')
464 { /* Reached to end of str or directory separator */
466 dirname
[11] = _USE_NTFLAG
? (a
& b
) : 0;
469 if (c
<= ' ' || c
== 0x7F) break; /* Reject invisible chars */
472 if (!(a
& 1) && n
>= 1 && n
<= 8)
473 { /* Enter extension part */
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 */
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 : ; < = > ? */
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 */
503 if (_USE_NTFLAG
) (t
== 8) ? (a
|= 0x08) : (a
|= 0x10);
517 /*-----------------------------------------------------------------------*/
518 /* Trace a file path */
519 /*-----------------------------------------------------------------------*/
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 */
534 /* Initialize directory object */
537 if (fs
->fs_type
== FS_FAT32
)
539 dirobj
->clust
= dirobj
->sclust
= clust
;
540 dirobj
->sect
= clust2sect(clust
);
545 dirobj
->clust
= dirobj
->sclust
= 0;
546 dirobj
->sect
= clust
;
552 { /* Null path means the root directory */
559 ds
= make_dirfile(&path
, fn
); /* Get a paragraph into fn[] */
560 if (ds
== 1) return FR_INVALID_NAME
;
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
;
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 */
581 ((DWORD
)LD_WORD(&dptr
[DIR_FstClusHI
]) << 16) |
583 LD_WORD(&dptr
[DIR_FstClusLO
]);
584 dirobj
->clust
= dirobj
->sclust
= clust
; /* Restart scannig with the new directory */
585 dirobj
->sect
= clust2sect(clust
);
592 /*-----------------------------------------------------------------------*/
593 /* Reserve a directory entry */
594 /*-----------------------------------------------------------------------*/
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 */
609 /* Re-initialize directory object */
610 clust
= dirobj
->sclust
;
612 { /* Dyanmic directory table */
613 dirobj
->clust
= clust
;
614 dirobj
->sect
= clust2sect(clust
);
617 { /* Static directory table */
618 dirobj
->sect
= fs
->dirbase
;
624 if (!move_window(dirobj
->sect
)) return FR_RW_ERROR
;
625 dptr
= &fs
->win
[(dirobj
->index
& 15) * 32]; /* Pointer to the directory entry */
627 if (c
== 0 || c
== 0xE5)
628 { /* Found an empty entry! */
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
)
652 #endif /* !_FS_READONLY */
657 /*-----------------------------------------------------------------------*/
658 /* Load boot record and check if it is a FAT boot record */
659 /*-----------------------------------------------------------------------*/
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 */
668 if (disk_read(0, fs
->win
, sect
, 1) != RES_OK
) /* Load boot record */
670 if (LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55) /* Check record signature */
673 if (!memcmp(&fs
->win
[BS_FilSysType
], "FAT", 3)) /* Check FAT signature */
676 if (!memcmp(&fs
->win
[BS_FilSysType32
], "FAT32", 5) && !(fs
->win
[BPB_ExtFlags
] & 0x80))
685 /*-----------------------------------------------------------------------*/
686 /* Make sure that the file system is valid */
687 /*-----------------------------------------------------------------------*/
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 */
697 DWORD bootsect
, fatsize
, totalsect
, maxclust
;
698 const char *p
= *path
;
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 */
713 stat
= disk_status(0);
714 if (!(stat
& STA_NOINIT
))
715 { /* If the physical drive is kept initialized */
717 if (chk_wp
&& (stat
& STA_PROTECT
)) /* Check write protection if needed */
718 return FR_WRITE_PROTECTED
;
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 */
731 if (chk_wp
&& (stat
& STA_PROTECT
)) /* Check write protection if needed */
732 return FR_WRITE_PROTECTED
;
735 /* Search FAT partition on the drive */
736 fmt
= check_fs(bootsect
= 0); /* Check sector 0 as an SFD format */
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)
768 return FR_NO_FILESYSTEM
;
772 fs
->dirbase
= LD_DWORD(&fs
->win
[BPB_RootClus
]); /* Root directory start cluster */
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 */
780 fs
->free_clust
= (CLUST
)0xFFFFFFFF;
782 /* Load fsinfo sector if needed */
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
]);
797 fs
->id
= ++fsid
; /* File system mount ID */
804 /*-----------------------------------------------------------------------*/
805 /* Check if the file/dir object is valid or not */
806 /*-----------------------------------------------------------------------*/
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
)
825 /*--------------------------------------------------------------------------
829 --------------------------------------------------------------------------*/
832 /*-----------------------------------------------------------------------*/
833 /* Mount/Unmount a Locical Drive */
834 /*-----------------------------------------------------------------------*/
837 BYTE drv
, /* Logical drive number to be mounted/unmounted */
838 FATFS
*fs
/* Pointer to new file system object (NULL for unmount)*/
844 if (drv
) return FR_INVALID_DRIVE
;
847 if (fsobj
) memset(fsobj
, 0, sizeof(FATFS
));
848 if (fs
) memset(fs
, 0, sizeof(FATFS
));
856 /*-----------------------------------------------------------------------*/
857 /* Open or Create a File */
858 /*-----------------------------------------------------------------------*/
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 */
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
)));
879 res
= auto_mount(&path
, 0);
881 if (res
!= FR_OK
) return res
;
883 /* Trace the file path */
884 res
= trace_path(&dirobj
, fn
, path
, &dir
); /* Trace the file path */
887 /* Create or Open a File */
888 if (mode
& (FA_CREATE_ALWAYS
|FA_OPEN_ALWAYS
|FA_CREATE_NEW
))
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
;
903 { /* Any object is already existing */
904 if (mode
& FA_CREATE_NEW
) /* Cannot create new */
906 if (dir
== NULL
|| (dir
[DIR_Attr
] & (AM_RDO
|AM_DIR
))) /* Cannot overwrite (R/O or DIR) */
908 if (mode
& FA_CREATE_ALWAYS
)
909 { /* Resize it to zero */
911 rs
= ((DWORD
)LD_WORD(&dir
[DIR_FstClusHI
]) << 16) | LD_WORD(&dir
[DIR_FstClusLO
]);
912 ST_WORD(&dir
[DIR_FstClusHI
], 0);
914 rs
= LD_WORD(&dir
[DIR_FstClusLO
]);
916 ST_WORD(&dir
[DIR_FstClusLO
], 0); /* cluster = 0 */
917 ST_DWORD(&dir
[DIR_FileSize
], 0); /* size = 0 */
919 dw
= fs
->winsect
; /* Remove the cluster chain */
920 if (!remove_chain(rs
) || !move_window(dw
))
922 fs
->last_clust
= rs
- 1; /* Reuse the cluster hole */
925 if (mode
& FA_CREATE_ALWAYS
)
927 dir
[DIR_Attr
] = AM_ARC
; /* New attribute */
929 ST_DWORD(&dir
[DIR_WrtTime
], dw
); /* Updated time */
930 ST_DWORD(&dir
[DIR_CrtTime
], dw
); /* Created time */
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 */
942 if ((mode
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
)) /* R/O violation */
946 fp
->dir_sect
= fs
->winsect
; /* Pointer to the directory entry */
949 fp
->flag
= mode
; /* File access mode */
950 fp
->org_clust
= /* File start cluster */
952 ((DWORD
)LD_WORD(&dir
[DIR_FstClusHI
]) << 16) |
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 */
959 fp
->id
= fs
->id
; /* Owner file system object of the file */
967 /*-----------------------------------------------------------------------*/
969 /*-----------------------------------------------------------------------*/
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 */
981 BYTE cc
, *rbuff
= buff
;
987 res
= validate(fs
, fp
->id
); /* Check validity of the object */
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 */
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
)
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, */
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
)
1020 fp
->sect_clust
-= cc
- 1;
1021 fp
->curr_sect
+= cc
- 1;
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
);
1034 fr_error
: /* Abort this function due to an unrecoverable error */
1035 fp
->flag
|= FA__ERROR
;
1043 /*-----------------------------------------------------------------------*/
1045 /*-----------------------------------------------------------------------*/
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 */
1059 const BYTE
*wbuff
= buff
;
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 */
1080 { /* On the cluster boundary, get next cluster */
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 */
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, */
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
)
1104 fp
->sect_clust
-= cc
- 1;
1105 fp
->curr_sect
+= cc
- 1;
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 */
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
);
1123 if (fp
->fptr
> fp
->fsize
) fp
->fsize
= fp
->fptr
; /* Update file size if needed */
1124 fp
->flag
|= FA__WRITTEN
; /* Set file changed flag */
1127 fw_error
: /* Abort this function due to an unrecoverable error */
1128 fp
->flag
|= FA__ERROR
;
1135 /*-----------------------------------------------------------------------*/
1136 /* Synchronize between File and Disk */
1137 /*-----------------------------------------------------------------------*/
1140 FIL
*fp
/* Pointer to the file object */
1149 res
= validate(fs
, fp
->id
); /* Check validity of the object */
1152 if (fp
->flag
& FA__WRITTEN
)
1153 { /* Has the file been written? */
1154 /* Update the directory entry */
1155 if (!move_window(fp
->dir_sect
))
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 */
1162 ST_WORD(&dir
[DIR_FstClusHI
], fp
->org_clust
>> 16);
1164 tim
= get_fattime(); /* Updated time */
1165 ST_DWORD(&dir
[DIR_WrtTime
], tim
);
1166 fp
->flag
&= ~FA__WRITTEN
;
1173 #endif /* !_FS_READONLY */
1178 /*-----------------------------------------------------------------------*/
1180 /*-----------------------------------------------------------------------*/
1183 FIL
*fp
/* Pointer to the file object to be closed */
1192 res
= validate(fp
->fs
, fp
->id
);
1203 #if _FS_MINIMIZE <= 2
1204 /*-----------------------------------------------------------------------*/
1205 /* Seek File Pointer */
1206 /*-----------------------------------------------------------------------*/
1209 FIL
*fp
, /* Pointer to the file object */
1210 DWORD ofs
/* File pointer from top of file */
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
;
1225 if (ofs
> fp
->fsize
&& !(fp
->flag
& FA_WRITE
))
1227 if (ofs
> fp
->fsize
)
1231 fp
->sect_clust
= 1; /* Set file R/W pointer to top of the file */
1233 /* Move file R/W pointer if needed */
1236 clust
= fp
->org_clust
; /* Get start cluster */
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
;
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 */
1249 { /* Loop to skip leading clusters */
1250 fp
->curr_clust
= clust
; /* Update current cluster */
1251 if (ofs
<= csize
) break;
1253 if (fp
->flag
& FA_WRITE
) /* Check if in write mode or not */
1254 clust
= create_chain(clust
); /* Force streached if in write mode */
1257 clust
= get_cluster(clust
); /* Only follow cluster chain if not in write mode */
1259 { /* Stop if could not follow the cluster chain */
1263 if (clust
== 1 || clust
>= fs
->max_clust
) goto fk_error
;
1264 fp
->fptr
+= csize
; /* Update R/W pointer */
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 */
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
;
1283 fk_error
: /* Abort this function due to an unrecoverable error */
1284 fp
->flag
|= FA__ERROR
;
1291 #if _FS_MINIMIZE <= 1
1292 /*-----------------------------------------------------------------------*/
1293 /* Open a directroy */
1294 /*-----------------------------------------------------------------------*/
1297 DIR *dirobj
, /* Pointer to directory object to create */
1298 const char *path
/* Pointer to the directory path */
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 */
1312 { /* Trace completed */
1314 { /* It is not the root dir */
1315 if (dir
[DIR_Attr
] & AM_DIR
)
1316 { /* The entry is a directory */
1319 ((DWORD
)LD_WORD(&dir
[DIR_FstClusHI
]) << 16) |
1321 LD_WORD(&dir
[DIR_FstClusLO
]);
1322 dirobj
->sect
= clust2sect(dirobj
->clust
);
1326 { /* The entry is not a directory */
1330 dirobj
->id
= fs
->id
;
1338 /*-----------------------------------------------------------------------*/
1339 /* Read Directory Entry in Sequense */
1340 /*-----------------------------------------------------------------------*/
1343 DIR *dirobj
, /* Pointer to the directory object */
1344 FILINFO
*finfo
/* Pointer to file information to return */
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
))
1360 dir
= &fs
->win
[(dirobj
->index
& 15) * 32]; /* pointer to the directory entry */
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 */
1375 #if _FS_MINIMIZE == 0
1376 /*-----------------------------------------------------------------------*/
1377 /* Get File Status */
1378 /*-----------------------------------------------------------------------*/
1381 const char *path
, /* Pointer to the file path */
1382 FILINFO
*finfo
/* Pointer to file information to return */
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 */
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
;
1410 /*-----------------------------------------------------------------------*/
1411 /* Get Number of Free Clusters */
1412 /*-----------------------------------------------------------------------*/
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 */
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
;
1439 /* Count number of free clusters */
1442 if (fat
== FS_FAT12
)
1447 if ((WORD
)get_cluster(clust
) == 0) n
++;
1449 while (++clust
< fs
->max_clust
);
1453 clust
= fs
->max_clust
;
1461 if (!move_window(sect
++)) return FR_RW_ERROR
;
1464 if (!_FAT32
|| fat
== FS_FAT16
)
1466 if (LD_WORD(p
) == 0) n
++;
1472 if (LD_DWORD(p
) == 0) n
++;
1481 if (fat
== FS_FAT32
) fs
->fsi_flag
= 1;
1491 /*-----------------------------------------------------------------------*/
1492 /* Delete a File or a Directory */
1493 /*-----------------------------------------------------------------------*/
1496 const char *path
/* Pointer to the file or directory path */
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
;
1518 ((DWORD
)LD_WORD(&dir
[DIR_FstClusHI
]) << 16) |
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
);
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;
1540 if (!remove_chain(dclust
)) return FR_RW_ERROR
; /* Remove the cluster chain */
1548 /*-----------------------------------------------------------------------*/
1549 /* Create a Directory */
1550 /*-----------------------------------------------------------------------*/
1553 const char *path
/* Pointer to the directory path */
1558 DWORD sect
, dsect
, tim
;
1559 CLUST dclust
, pclust
;
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
;
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
;
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
)
1589 memset(&fw
[DIR_Name
], ' ', 8+3); /* Create "." entry */
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
;
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);
1602 ST_WORD(&fw
[ DIR_FstClusLO
], dclust
);
1603 ST_WORD(&fw
[32+DIR_FstClusLO
], pclust
);
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 */
1614 ST_WORD(&dir
[DIR_FstClusHI
], dclust
>> 16);
1623 /*-----------------------------------------------------------------------*/
1624 /* Change File Attribute */
1625 /*-----------------------------------------------------------------------*/
1628 const char *path
, /* Pointer to the file path */
1629 BYTE value
, /* Attribute bits */
1630 BYTE mask
/* Attribute mask to change */
1639 res
= auto_mount(&path
, 1);
1642 res
= trace_path(&dirobj
, fn
, path
, &dir
); /* Trace the file path */
1644 { /* Trace completed */
1647 res
= FR_INVALID_NAME
;
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 */
1663 /*-----------------------------------------------------------------------*/
1664 /* Rename File/Directory */
1665 /*-----------------------------------------------------------------------*/
1668 const char *path_old
, /* Pointer to the old name */
1669 const char *path_new
/* Pointer to the new name */
1674 BYTE
*dir_old
, *dir_new
, direntry
[32-11];
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];
1700 if (!move_window(sect_old
)) return FR_RW_ERROR
; /* Remove old entry */
1701 dir_old
[DIR_Name
] = 0xE5;
1706 #endif /* !_FS_READONLY */
1707 #endif /* _FS_MINIMIZE == 0 */
1708 #endif /* _FS_MINIMIZE <= 1 */
1709 #endif /* _FS_MINIMIZE <= 2 */