From patchwork Wed Dec 31 22:29:54 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1159 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220317; bh=tPj6GYNalcyFdTl7J4vvbShtZ9XvV+NI2pcUnk/NF04=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Tn5IZBoi9R2JnBa6em4UWrQ+PkFshSBEbXCL0sN1r2Eg6GyKp7uIGuKPZZ6aWIsTo 2xY/TuKpUCvr83t29tmpe3LOb61JVQGAONhmbb1WVdyCCdcQSLYjvlsL6mGZ/lEHHW dX3D2jlfkHJH+fZywNyEDDE1KGef1JED+0Idg6rONChNgdIa5OoK+uYI+WE7EY0PQr GxCFsoKnwjIJcQy5ip7HOvZCc3sDqcwMuIrNu0jG28VPboF0qBCQhaUy/dOrN/dnJJ 02kKpelNJqzBII3KShPeATEl4fY4SW5hcaWdqqtsJKmXEIEbCwq1+cYUnnxjMDYsVV Csufiuhab6Uzw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6506D68FCB for ; Wed, 31 Dec 2025 15:31:57 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id w-I6IQyQxLGp for ; Wed, 31 Dec 2025 15:31:57 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220317; bh=tPj6GYNalcyFdTl7J4vvbShtZ9XvV+NI2pcUnk/NF04=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Tn5IZBoi9R2JnBa6em4UWrQ+PkFshSBEbXCL0sN1r2Eg6GyKp7uIGuKPZZ6aWIsTo 2xY/TuKpUCvr83t29tmpe3LOb61JVQGAONhmbb1WVdyCCdcQSLYjvlsL6mGZ/lEHHW dX3D2jlfkHJH+fZywNyEDDE1KGef1JED+0Idg6rONChNgdIa5OoK+uYI+WE7EY0PQr GxCFsoKnwjIJcQy5ip7HOvZCc3sDqcwMuIrNu0jG28VPboF0qBCQhaUy/dOrN/dnJJ 02kKpelNJqzBII3KShPeATEl4fY4SW5hcaWdqqtsJKmXEIEbCwq1+cYUnnxjMDYsVV Csufiuhab6Uzw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 53EC068F61 for ; Wed, 31 Dec 2025 15:31:57 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220315; bh=6X4eUpNK1yNzpzw6QIntoNRI64nY1e1AWk96HTR1qaU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ecyKcH0BnVux0YF96hN1cxF3DYRZHDGLFpLKldJqxW5wMo1fK3bINPUDL153Q4JSF fDnuttpkSq5aQdUxVkQkn8e10uhERKAQoF03g/EqWtbALygUm8KE0JdJY0y8FoHc/Z M22pMaseCJ0Vqns6PsxwrHhhUaMxvNTW82hym+PjHPV50G7g9g4j9HNMUKGjqMlV9Q nLDu+i0KPNPMMdtlplP1tR0p1yLIYaSZx38ulcitd142D0cj8tEv6K7IZE82bso6xK JIohZo+mz7Wf+6MT3OsKE/mPpDaqR5xfhX5YP9RkUuU/lvyHApBgkASWAJzKoea6oC RPTdTHrdVPzaw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3B00768F51; Wed, 31 Dec 2025 15:31:55 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id BSLDf-cZ21Jl; Wed, 31 Dec 2025 15:31:55 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220307; bh=7hFp/OfEvULsA5cG4yRUfr5qVFcH0LkJHYIYcuXqEeI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=u8ZdZ4afWPK+YNLZWSP/dWKG6PEv5mSbqBht74Eq8p7nGLHnTUUhReyw03kbez6H3 tEB99Y63hvZvGxYdjMHWYMpsBDWvq8+OqEx7f9HZ6w5gWbvWAenvAHr/5afP4sYFhx zochJBqIecIJTTkfxz4k8OOdPEnWDXTQZGDAURCNakb/UR9F2eyTd7BJdmfgf/4yl6 XGBkh6KE9aYP7yiC6npkp/1MAAvdV5DSJXmEi3RQCa0nvqxSZxti/4A7vjs1aKqSOk SxQLeNswkuvRXrVUgxOzq/7CMSD9pmZsR6rLDcqoVBm15mTK91GeopQMxiAu1FOSoS MEBT5v+PN1GVQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5D49468F61; Wed, 31 Dec 2025 15:31:47 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Wed, 31 Dec 2025 15:29:54 -0700 Message-ID: <20251231223008.3251711-22-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251231223008.3251711-1-sjg@u-boot.org> References: <20251231223008.3251711-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: K7OFRXHS6VLOR25TLOJEKJAX3ASR4LHI X-Message-ID-Hash: K7OFRXHS6VLOR25TLOJEKJAX3ASR4LHI X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 21/26] ext4l: Add mkdir support for directory creation List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass 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 Signed-off-by: Simon Glass --- 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 });