summaryrefslogtreecommitdiff
path: root/firmware/drivers/fat.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/fat.c')
-rw-r--r--firmware/drivers/fat.c63
1 files changed, 58 insertions, 5 deletions
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index 999e6f1c9b..c9028aa762 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -1863,6 +1863,10 @@ int fat_rename(struct fat_file* file,
int rc;
struct fat_dir olddir;
struct fat_file newfile = *file;
+ unsigned char buf[SECTOR_SIZE];
+ unsigned char* entry = NULL;
+ unsigned short* clusptr = NULL;
+ unsigned int parentcluster;
#ifdef HAVE_MULTIVOLUME
struct bpb* fat_bpb = &fat_bpbs[file->volume];
@@ -1870,6 +1874,8 @@ int fat_rename(struct fat_file* file,
DEBUGF("No rename across volumes!\n");
return -1;
}
+#else
+ struct bpb* fat_bpb = &fat_bpbs[0];
#endif
if ( !file->dircluster ) {
@@ -1880,26 +1886,73 @@ int fat_rename(struct fat_file* file,
/* create a temporary file handle */
rc = fat_opendir(IF_MV2(file->volume,) &olddir, file->dircluster, NULL);
if (rc < 0)
- return rc * 10 - 3;
+ return rc * 10 - 1;
/* create new name */
rc = add_dir_entry(dir, &newfile, newname, false, false);
if (rc < 0)
- return rc * 10 - 4;
+ return rc * 10 - 2;
/* write size and cluster link */
rc = update_short_entry(&newfile, size, attr);
if (rc < 0)
- return rc * 10 - 5;
+ return rc * 10 - 3;
/* remove old name */
rc = free_direntries(file);
if (rc < 0)
- return rc * 10 - 6;
+ return rc * 10 - 4;
rc = flush_fat(IF_MV(fat_bpb));
if (rc < 0)
- return rc * 10 - 7;
+ return rc * 10 - 5;
+
+ /* if renaming a directory, update the .. entry to make sure
+ it points to its parent directory (we don't check if it was a move) */
+ if(FAT_ATTR_DIRECTORY == attr) {
+ /* open the dir that was renamed, we re-use the olddir struct */
+ rc = fat_opendir(IF_MV2(file->volume,) &olddir, newfile.firstcluster,
+ NULL);
+ if (rc < 0)
+ return rc * 10 - 6;
+
+ /* get the first sector of the dir */
+ rc = fat_seek(&olddir.file, 0);
+ if (rc < 0)
+ return rc * 10 - 7;
+
+ rc = fat_readwrite(&olddir.file, 1, buf, false);
+ if (rc < 0)
+ return rc * 10 - 8;
+
+ /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */
+ if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
+ parentcluster = 0;
+ else
+ parentcluster = dir->file.firstcluster;
+
+ entry = buf + DIR_ENTRY_SIZE;
+ if(strncmp(".. ", entry, 11))
+ {
+ /* .. entry must be second entry according to FAT spec (p.29) */
+ DEBUGF("Second dir entry is not double-dot!\n");
+ return rc * 10 - 9;
+ }
+ clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
+ *clusptr = htole16(parentcluster >> 16);
+
+ clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
+ *clusptr = htole16(parentcluster & 0xffff);
+
+ /* write back this sector */
+ rc = fat_seek(&olddir.file, 0);
+ if (rc < 0)
+ return rc * 10 - 7;
+
+ rc = fat_readwrite(&olddir.file, 1, buf, true);
+ if (rc < 1)
+ return rc * 10 - 8;
+ }
return 0;
}