diff options
author | Peter D'Hoye <peter.dhoye@gmail.com> | 2007-07-05 18:30:55 +0000 |
---|---|---|
committer | Peter D'Hoye <peter.dhoye@gmail.com> | 2007-07-05 18:30:55 +0000 |
commit | e3d7c5c464e495a2d8e9371c9c218bbd0ac3cbf9 (patch) | |
tree | a3c9098cf2f09366dbe49660402164c3e85df9c1 /firmware/drivers/fat.c | |
parent | b0ce5ad8569aeff7230a1aa4c0959f661abe3b8e (diff) |
Fix a (minor) disk corruption when moving directories around. Fixes FS #7359
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13797 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/fat.c')
-rw-r--r-- | firmware/drivers/fat.c | 63 |
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; } |