ext4l: Add rename support
Add ext4l_rename() to rename files and directories, including moves across directories. This uses the Linux ext4_rename() function. Also fix the symlink test to verify reading through symlinks works correctly, since ext4l_resolve_path follows symlinks (stat behavior). Add Python test wrappers for mkdir, ln, and rename tests. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
@@ -1309,6 +1309,60 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext4l_rename(const char *old_path, const char *new_path)
|
||||
{
|
||||
struct dentry *old_dentry, *new_dentry;
|
||||
struct dentry *old_dir_dentry, *new_dir_dentry;
|
||||
char *old_path_copy, *new_path_copy;
|
||||
int ret;
|
||||
|
||||
/* Check new_path before ext4l_resolve_file checks old_path */
|
||||
if (!new_path)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ext4l_resolve_file(old_path, &old_dir_dentry, &old_dentry,
|
||||
&old_path_copy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!old_dentry->d_inode) {
|
||||
/* Source file doesn't exist */
|
||||
ret = -ENOENT;
|
||||
goto out_old;
|
||||
}
|
||||
|
||||
ret = ext4l_resolve_file(new_path, &new_dir_dentry, &new_dentry,
|
||||
&new_path_copy);
|
||||
if (ret)
|
||||
goto out_old;
|
||||
|
||||
/* Perform the rename */
|
||||
ret = ext4_rename(&nop_mnt_idmap, old_dir_dentry->d_inode, old_dentry,
|
||||
new_dir_dentry->d_inode, new_dentry, 0);
|
||||
if (ret)
|
||||
goto out_new;
|
||||
|
||||
/* Sync all dirty buffers */
|
||||
{
|
||||
int sync_ret = bh_cache_sync();
|
||||
|
||||
if (sync_ret)
|
||||
ret = sync_ret;
|
||||
/* Commit superblock with updated free counts */
|
||||
ext4_commit_super(ext4l_sb);
|
||||
}
|
||||
|
||||
out_new:
|
||||
kfree(new_dentry);
|
||||
kfree(new_dir_dentry);
|
||||
free(new_path_copy);
|
||||
out_old:
|
||||
kfree(old_dentry);
|
||||
kfree(old_dir_dentry);
|
||||
free(old_path_copy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ext4l_close(void)
|
||||
{
|
||||
ext4l_close_internal(false);
|
||||
|
||||
@@ -291,7 +291,7 @@ static struct fstype_info fstypes[] = {
|
||||
.unlink = ext4l_op_ptr(ext4l_unlink, fs_unlink_unsupported),
|
||||
.mkdir = ext4l_op_ptr(ext4l_mkdir, fs_mkdir_unsupported),
|
||||
.ln = ext4l_op_ptr(ext4l_ln, fs_ln_unsupported),
|
||||
.rename = fs_rename_unsupported,
|
||||
.rename = ext4l_op_ptr(ext4l_rename, fs_rename_unsupported),
|
||||
.statfs = ext4l_statfs,
|
||||
},
|
||||
#endif
|
||||
|
||||
@@ -125,6 +125,17 @@ int ext4l_mkdir(const char *dirname);
|
||||
*/
|
||||
int ext4l_ln(const char *filename, const char *target);
|
||||
|
||||
/**
|
||||
* ext4l_rename() - Rename a file or directory
|
||||
*
|
||||
* @old_path: Current path of file or directory
|
||||
* @new_path: New path for file or directory
|
||||
* Return: 0 on success, -ENOENT if source not found,
|
||||
* -ENOTDIR if parent is not a directory, -EROFS if read-only,
|
||||
* negative on other errors
|
||||
*/
|
||||
int ext4l_rename(const char *old_path, const char *new_path);
|
||||
|
||||
/**
|
||||
* ext4l_get_uuid() - Get the filesystem UUID
|
||||
*
|
||||
|
||||
@@ -586,3 +586,64 @@ static int fs_test_ext4l_ln_norun(struct unit_test_state *uts)
|
||||
}
|
||||
FS_TEST_ARGS(fs_test_ext4l_ln_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
||||
{ "fs_image", UT_ARG_STR });
|
||||
|
||||
/**
|
||||
* fs_test_ext4l_rename_norun() - Test ext4l_rename function
|
||||
*
|
||||
* Verifies that ext4l can rename files and directories on the filesystem.
|
||||
*
|
||||
* Arguments:
|
||||
* fs_image: Path to the ext4 filesystem image
|
||||
*/
|
||||
static int fs_test_ext4l_rename_norun(struct unit_test_state *uts)
|
||||
{
|
||||
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
||||
const char *test_data = "rename test\n";
|
||||
size_t test_len = strlen(test_data);
|
||||
static int test_counter;
|
||||
char old_name[32], new_name[32], subdir_name[32], moved_name[64];
|
||||
loff_t actwrite, size;
|
||||
|
||||
ut_assertnonnull(fs_image);
|
||||
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
||||
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
||||
|
||||
/* Use unique names to avoid issues with test re-runs */
|
||||
snprintf(old_name, sizeof(old_name), "/renameme%d.txt", test_counter);
|
||||
snprintf(new_name, sizeof(new_name), "/renamed%d.txt", test_counter);
|
||||
snprintf(subdir_name, sizeof(subdir_name), "/renamedir%d", test_counter);
|
||||
snprintf(moved_name, sizeof(moved_name), "%s/moved.txt", subdir_name);
|
||||
test_counter++;
|
||||
|
||||
/* Create a file to rename */
|
||||
ut_assertok(ext4l_write(old_name, (void *)test_data, 0,
|
||||
test_len, &actwrite));
|
||||
ut_asserteq(test_len, actwrite);
|
||||
|
||||
/* Verify file exists */
|
||||
ut_asserteq(1, ext4l_exists(old_name));
|
||||
|
||||
/* Rename the file */
|
||||
ut_assertok(ext4l_rename(old_name, new_name));
|
||||
|
||||
/* Verify old name no longer exists, new name does */
|
||||
ut_asserteq(0, ext4l_exists(old_name));
|
||||
ut_asserteq(1, ext4l_exists(new_name));
|
||||
|
||||
/* Verify file size is preserved */
|
||||
ut_assertok(ext4l_size(new_name, &size));
|
||||
ut_asserteq(test_len, size);
|
||||
|
||||
/* Verify renaming non-existent file returns -ENOENT */
|
||||
ut_asserteq(-ENOENT, ext4l_rename("/nonexistent", "/newname"));
|
||||
|
||||
/* Test cross-directory rename */
|
||||
ut_assertok(ext4l_mkdir(subdir_name));
|
||||
ut_assertok(ext4l_rename(new_name, moved_name));
|
||||
ut_asserteq(0, ext4l_exists(new_name));
|
||||
ut_asserteq(1, ext4l_exists(moved_name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
FS_TEST_ARGS(fs_test_ext4l_rename_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
||||
{ "fs_image", UT_ARG_STR });
|
||||
|
||||
@@ -128,3 +128,18 @@ class TestExt4l:
|
||||
"""Test that ext4l can delete files."""
|
||||
with ubman.log.section('Test ext4l unlink'):
|
||||
ubman.run_ut('fs', 'fs_test_ext4l_unlink', fs_image=ext4_image)
|
||||
|
||||
def test_mkdir(self, ubman, ext4_image):
|
||||
"""Test that ext4l can create directories."""
|
||||
with ubman.log.section('Test ext4l mkdir'):
|
||||
ubman.run_ut('fs', 'fs_test_ext4l_mkdir', fs_image=ext4_image)
|
||||
|
||||
def test_ln(self, ubman, ext4_image):
|
||||
"""Test that ext4l can create symbolic links."""
|
||||
with ubman.log.section('Test ext4l ln'):
|
||||
ubman.run_ut('fs', 'fs_test_ext4l_ln', fs_image=ext4_image)
|
||||
|
||||
def test_rename(self, ubman, ext4_image):
|
||||
"""Test that ext4l can rename files and directories."""
|
||||
with ubman.log.section('Test ext4l rename'):
|
||||
ubman.run_ut('fs', 'fs_test_ext4l_rename', fs_image=ext4_image)
|
||||
|
||||
Reference in New Issue
Block a user