On 2/7/25 13:45, Daniel Venzin wrote:
This implementation currently does not support
moving files between directories.

Why would we make such a restriction?


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)

Below you are expecting that new_fname is a path not only a file name.

%s/new_fname/new_path/

+{
+       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)) {

No clue, why you expect "/" or "." at the beginning of a path.
In U-Boot file paths are always relative to the root directory.

Shouldn't fat_itr_resolve() take care of checking the validity of the path?

+               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);

You already determined new_fname_dirname. You must not ignore it.
You should delete above statement.

+
+       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);

Why don't we allow renaming of directories?

TYPE_FILE | TYPE_DIR

+       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);

This would not detect if new_path points to an existing or non-existing
directory.

Check that new_fname_dirname exists and then use the same itr to check
that new_fname_dirname/new_basename does not exist.

This avoids iterating once here and once in fat_rename().

+       if (ret)
+               goto exit;

We don't want to overwrite existing files or directories. Don't we
expect -ENOENT or -ENOTDIR here?

+
+       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;

Why don't you use separate variables for the old and the new itr value?
Then you would not have to resolve the old path twice.

Best regards

Heinrich

+
+       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);


Reply via email to