From patchwork Fri Jan 2 00:50:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1189 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=1767315175; bh=5VI6fEqVr03mL4kqkt+xNKbOV3/eGUZuvSIEj+0rw9o=; 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=gV6fWg6w/xrOF8wO5PL/7Rs3y8/5RT86p+YVjwzAuxwUFU7HsmlMh88iVXYPNrDn1 IzEUGfzX8wKg0bV15QvE5brjvGvoifkuL/87rlIDTR3XZ2j4zBlwG/bhVWu+e5saou 0/esr4wZBtF98VS9bK5DmYm+9li+99tuUhGjoQEJtfG/AEvNBzyDIpJQsZa0q0ppwi ZtUa/0qhKGXaDHSCG/KD+yld3jj2KSJnUZkNWd/Q/mL0hfWivUD0vbLdgRJaPb/dsQ 3BY9UkwrBzp9EBew40fhMgRzYWOMrSMp8HrYwE2eoTPtdfotJ3hSI2vscG5O9iXEO/ v8UHJfxQps69w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id EFE0B69031 for ; Thu, 1 Jan 2026 17:52: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 10024) with ESMTP id rXC--gVKeFIs for ; Thu, 1 Jan 2026 17:52:55 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767315174; bh=5VI6fEqVr03mL4kqkt+xNKbOV3/eGUZuvSIEj+0rw9o=; 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=VTRd2DB1X3bQJS+ryLBn3gFnQFRgN6gJXOjvXrfzZ8Th0JLrE5XJN5os++1j5Rm2T LizKfpEfDc5d50Ut9XWU+ZNqSENxSFTVwC8DI1pfvSQw0KK1ceIju92axLboZG57Jk vXHWCm4rlKx4uFKQeYcsot7GDks/o3nsACwq//AyDR3Ik+fLv5Y1p/X6c//whsQSQ5 rsBd/nElr80GGAMr7kYUs0q3Gel4kl7FafSRM44E46GaDEqAmzbLAl/k2ZgN1rpa5h tEqsUBnbU/i5xPT8r1JX0LwgxFuMLwExQEMnCkWIh0nXztxExe4hk/XmHBmuI0gjGK Xr5WAcJUfXFCQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 00D216902A for ; Thu, 1 Jan 2026 17:52:54 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767315171; bh=mrpi9uQWsGmk+866eETjC9gQ/PSL1J8jBT133UcvOkQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qxR/NID6uJF8Hx0pWEZoDwKYI9OW/9nquPguj5fPcU+cyLGh8YiX/S8vgsaChwY+p Xyzi2uxTOecMD8ukszyUv1RvIc9JIgITbJTeX2Mlc4Q40cafBiufC40nudFRGyHMbh 9A1maDE1RSmzuwlvPdtqJy5A+RzCrEnLMQP1X1dV2X+7cq+mGydrO9TcYqGzo/pGPL 4kmQWcR28XfZazuqHIKfT3A/JEDPDzxfCvLQHHkKcRo108/WcTSe5YhqAsnTMSwZt+ 6lWg+mc884KClV9+//QYaxRh4O14gN1ac6B/Kj6/Fhsrm/yIPgjZdPhc0s92K9DYT5 HaIxdXQa/gWTw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 269EC68F53; Thu, 1 Jan 2026 17:52:51 -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 qfAj-3BxufAD; Thu, 1 Jan 2026 17:52:51 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767315170; bh=bbbLuxdRGSFbIx2pTaWKQkLBJbMP02rkvCRRXZUeiKM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WWO2LIfbZN8FZ1yPQ5Ht3BF3xSHzKevTt4i6LO9Ac34tECPk+d3FFfA3dxlu2YTU4 qdmr54ok0x9MmHlJJc2CKEiCtpEBNda3zbqkS6bWbcOfM8LX+2QXN7D2iUahkv9qcZ 9y/w4cmapLmt1f3r9W53O+0VKqDTGTXq5u3dMp2I7qxbtAFAlxb4Nihgk0FFQmOJEL W5MPuGafbvSDUeWgNV6o+Lyi8rM2q13Mz1Xl6rLKdFyJLmjwJTp7/7hHFtQKcpeRGt wvEuXm0CBskWFbeiy9Ydd9l+2GGijIdfAXmFfk5/Jo7bTAMKiB3JXB4AKwG4psI5YX WDuG3/8uar3Hw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 7889C68313; Thu, 1 Jan 2026 17:52:50 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Thu, 1 Jan 2026 17:50:55 -0700 Message-ID: <20260102005112.552256-26-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260102005112.552256-1-sjg@u-boot.org> References: <20260102005112.552256-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: HVUGR5BCZWIU65M5YOCJ2PNILGLG22MC X-Message-ID-Hash: HVUGR5BCZWIU65M5YOCJ2PNILGLG22MC 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 v2 25/30] 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 --- (no changes since v1) 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 });