This implementation currently does not support
moving files between directories.

Signed-off-by: Daniel Venzin <daniel.ven...@mt.com>

---

Changes in v3:
- Abort the rename if the destination is a path instead of a file

Changes in v2:
- Separate the rename implementation from changes in the filesystem layer
- Eliminate code duplication with delete_dentry_long(..)
- Ensure that the source file exists, but not the destination file
- Free the fatbuf if it has been reinitialized with fat_itr_root
- Delete orphaned FAT entry after copying file metadata

 fs/fat/fat_write.c | 125 +++++++++++++++++++++++++++++++++++++++++++++
 fs/fs.c            |   2 +-
 include/fat.h      |   1 +
 3 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index c78b80e0b7a..6e4ff7c57d4 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -1821,3 +1821,128 @@ exit:
        free(dotdent);
        return ret;
 }
+
+int file_fat_rename(const char *path, const char *new_fname)
+{
+       fsdata datablock = { .fatbuf = NULL, };
+       fat_itr *itr = NULL;
+
+       dir_entry *dentry;
+
+       char new_path[VFAT_MAXLEN_BYTES];
+       char *new_fname_copy = NULL, *new_fname_dirname, *new_fname_basename;
+       char *path_copy = NULL, *path_dirname, *path_basename;
+
+       loff_t actwrite;
+
+       __u16 starthi, start;
+       __u32 size, entry;
+
+       int ret = -1;
+
+       new_fname_copy = strdup(new_fname);
+       if (!new_fname_copy)
+               goto exit;
+
+       split_filename(new_fname_copy, &new_fname_dirname, &new_fname_basename);
+
+       if (strlen(new_fname_dirname) > 1 ||
+               (strcmp(new_fname_dirname, "/") != 0 &&
+                strcmp(new_fname_dirname, ".") != 0)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       path_copy = strdup(path);
+       if (!path_copy)
+               goto exit;
+
+       split_filename(path_copy, &path_dirname, &path_basename);
+       snprintf(new_path, sizeof(new_path), "%s/%s", path_dirname, 
new_fname_basename);
+
+       if (!fat_exists(path)) {
+               ret = -ENOENT;
+               goto exit;
+       }
+
+       if (fat_exists(new_path)) {
+               ret = -EEXIST;
+               goto exit;
+       }
+
+       ret = file_fat_write(new_path, &actwrite, 0, 0, &actwrite);
+       if (ret)
+               goto exit;
+
+       itr = malloc_cache_aligned(sizeof(*itr));
+       if (!itr) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       ret = fat_itr_root(itr, &datablock);
+       if (ret)
+               goto exit;
+
+       ret = fat_itr_resolve(itr, path, TYPE_FILE);
+       if (ret)
+               goto exit;
+
+       dentry = itr->dent;
+
+       starthi = dentry->starthi;
+       start = dentry->start;
+       size = dentry->size;
+
+       free(datablock.fatbuf);
+       datablock.fatbuf = NULL;
+
+       ret = fat_itr_root(itr, &datablock);
+       if (ret)
+               goto exit;
+
+       ret = fat_itr_resolve(itr, new_path, TYPE_FILE);
+       if (ret)
+               goto exit;
+
+       dentry = itr->dent;
+
+       fsdata *mydata = itr->fsdata;
+
+       entry = START(dentry);
+
+       dentry->starthi = starthi;
+       dentry->start = start;
+       dentry->size = size;
+
+       ret = flush_dir(itr);
+       if (ret)
+               goto exit;
+
+       clear_fatent(mydata, entry);
+       if (flush_dirty_fat_buffer(mydata) < 0) {
+               printf("Error: flush fat buffer\n");
+               ret = -EIO;
+               goto exit;
+       }
+
+       free(datablock.fatbuf);
+       datablock.fatbuf = NULL;
+
+       ret = fat_itr_root(itr, &datablock);
+       if (ret)
+               goto exit;
+
+       ret = fat_itr_resolve(itr, path, TYPE_FILE);
+       if (ret)
+               goto exit;
+
+       ret = delete_dentry_long(itr, FATENT_CLEAR_SKIP);
+
+exit:
+       free(new_fname_copy);
+       free(path_copy);
+       free(datablock.fatbuf);
+       free(itr);
+       return ret;
+}
diff --git a/fs/fs.c b/fs/fs.c
index b746d05ebcd..3576d5c6644 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -208,7 +208,7 @@ static struct fstype_info fstypes[] = {
                .write = file_fat_write,
                .unlink = fat_unlink,
                .mkdir = fat_mkdir,
-               .rename = fs_rename_unsupported,
+               .rename = file_fat_rename,
 #else
                .write = fs_write_unsupported,
                .unlink = fs_unlink_unsupported,
diff --git a/include/fat.h b/include/fat.h
index 3dce99a23cf..415ebd7b7c8 100644
--- a/include/fat.h
+++ b/include/fat.h
@@ -207,6 +207,7 @@ int fat_readdir(struct fs_dir_stream *dirs, struct 
fs_dirent **dentp);
 void fat_closedir(struct fs_dir_stream *dirs);
 int fat_unlink(const char *filename);
 int fat_mkdir(const char *dirname);
+int file_fat_rename(const char *path, const char *new_filename);
 void fat_close(void);
 void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes);
 
-- 
2.39.5

Reply via email to