ext4l: Add mkdir support for directory creation
Implement ext4l_mkdir() to create directories on ext4 filesystems. The function parses the path to extract the parent directory and basename, resolves the parent inode, checks for existing entries, and calls the Linux ext4_mkdir() function to create the directory. Hook ext4l_mkdir into the filesystem layer via the .mkdir callback in fs_legacy.c, enabling the standard 'mkdir' command to work with ext4l filesystems. Add a unit test that verifies directory creation, duplicate detection (-EEXIST), nested directory creation, and error handling for non-existent parent directories (-ENOENT). Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
@@ -1206,6 +1206,49 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext4l_mkdir(const char *dirname)
|
||||
{
|
||||
struct dentry *dentry, *dir_dentry, *result;
|
||||
char *path_copy;
|
||||
int ret;
|
||||
|
||||
ret = ext4l_resolve_file(dirname, &dir_dentry, &dentry, &path_copy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dentry->d_inode) {
|
||||
/* Directory already exists */
|
||||
ret = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create the directory with mode 0755 (rwxr-xr-x) */
|
||||
result = ext4_mkdir(&nop_mnt_idmap, dir_dentry->d_inode, dentry,
|
||||
S_IFDIR | 0755);
|
||||
if (IS_ERR(result)) {
|
||||
ret = PTR_ERR(result);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
/* 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:
|
||||
kfree(dentry);
|
||||
kfree(dir_dentry);
|
||||
free(path_copy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ext4l_close(void)
|
||||
{
|
||||
ext4l_close_internal(false);
|
||||
|
||||
@@ -289,7 +289,7 @@ static struct fstype_info fstypes[] = {
|
||||
.readdir = ext4l_readdir,
|
||||
.closedir = ext4l_closedir,
|
||||
.unlink = ext4l_op_ptr(ext4l_unlink, fs_unlink_unsupported),
|
||||
.mkdir = fs_mkdir_unsupported,
|
||||
.mkdir = ext4l_op_ptr(ext4l_mkdir, fs_mkdir_unsupported),
|
||||
.ln = fs_ln_unsupported,
|
||||
.rename = fs_rename_unsupported,
|
||||
.statfs = ext4l_statfs,
|
||||
|
||||
@@ -101,6 +101,16 @@ int ext4l_write(const char *filename, void *buf, loff_t offset, loff_t len,
|
||||
*/
|
||||
int ext4l_unlink(const char *filename);
|
||||
|
||||
/**
|
||||
* ext4l_mkdir() - Create a directory
|
||||
*
|
||||
* @dirname: Path of directory to create
|
||||
* Return: 0 on success, -EEXIST if directory already exists,
|
||||
* -ENOTDIR if parent is not a directory, -EROFS if read-only,
|
||||
* negative on other errors
|
||||
*/
|
||||
int ext4l_mkdir(const char *dirname);
|
||||
|
||||
/**
|
||||
* ext4l_get_uuid() - Get the filesystem UUID
|
||||
*
|
||||
|
||||
@@ -479,3 +479,50 @@ static int fs_test_ext4l_unlink_norun(struct unit_test_state *uts)
|
||||
}
|
||||
FS_TEST_ARGS(fs_test_ext4l_unlink_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
||||
{ "fs_image", UT_ARG_STR });
|
||||
|
||||
/**
|
||||
* fs_test_ext4l_mkdir_norun() - Test ext4l_mkdir function
|
||||
*
|
||||
* Verifies that ext4l can create directories on the filesystem.
|
||||
*
|
||||
* Arguments:
|
||||
* fs_image: Path to the ext4 filesystem image
|
||||
*/
|
||||
static int fs_test_ext4l_mkdir_norun(struct unit_test_state *uts)
|
||||
{
|
||||
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
||||
static int test_counter;
|
||||
char dir_name[32];
|
||||
char subdir_name[64];
|
||||
int ret;
|
||||
|
||||
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 directory names to avoid issues with test re-runs */
|
||||
snprintf(dir_name, sizeof(dir_name), "/testdir%d", test_counter);
|
||||
snprintf(subdir_name, sizeof(subdir_name), "%s/subdir", dir_name);
|
||||
test_counter++;
|
||||
|
||||
/* Create a new directory */
|
||||
ret = ext4l_mkdir(dir_name);
|
||||
ut_assertok(ret);
|
||||
|
||||
/* Verify directory exists */
|
||||
ut_asserteq(1, ext4l_exists(dir_name));
|
||||
|
||||
/* Verify creating duplicate returns -EEXIST */
|
||||
ut_asserteq(-EEXIST, ext4l_mkdir(dir_name));
|
||||
|
||||
/* Create nested directory */
|
||||
ut_assertok(ext4l_mkdir(subdir_name));
|
||||
ut_asserteq(1, ext4l_exists(subdir_name));
|
||||
|
||||
/* Verify creating directory in non-existent parent returns -ENOENT */
|
||||
ut_asserteq(-ENOENT, ext4l_mkdir("/nonexistent/dir"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
FS_TEST_ARGS(fs_test_ext4l_mkdir_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
||||
{ "fs_image", UT_ARG_STR });
|
||||
|
||||
Reference in New Issue
Block a user