From: Simon Glass <simon.glass@canonical.com> 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> --- fs/ext4l/interface.c | 43 ++++++++++++++++++++++++++++++++++++++++ fs/fs_legacy.c | 2 +- include/ext4l.h | 10 ++++++++++ test/fs/ext4l.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c index 442617a0d3e..010db3a3631 100644 --- a/fs/ext4l/interface.c +++ b/fs/ext4l/interface.c @@ -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); diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c index 1eb79b338ee..b53d420f279 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -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, diff --git a/include/ext4l.h b/include/ext4l.h index abcf17f99ad..1a12ba1ac1c 100644 --- a/include/ext4l.h +++ b/include/ext4l.h @@ -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 * diff --git a/test/fs/ext4l.c b/test/fs/ext4l.c index dec6e3340bb..5930e4234ba 100644 --- a/test/fs/ext4l.c +++ b/test/fs/ext4l.c @@ -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 }); -- 2.43.0