From patchwork Wed Dec 31 22:29:55 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1160 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=1767220328; bh=Og4kyMgeE+lh4MRyjoxcE+6L0FluqrRkXNqohe+BC44=; 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=EIzd1CQ2iNc6qAbKmCDL+WMBReJz9glECJafPYHdlmTftIxb2h7JsqKFi0tGGNxAf E5SSfr4UwPvltlr0BhoI9ZNgmd42oh4VdAYJ3q+YldOEY2sOFvenmxc158FJ/ooC5Z CY9fpP/n61ybuGSQKln+Au5oGIzecFyxax/E+6/rJmnXczghNT4opcVCloUjPX1/TW rlZ9qCyv3J7M5leFhWmcH322f5Hs7pkC7rnAHaHy88kAAyAMf0G2rhn9TojxQve2bh j2CHJmj8Nbaf7JBZyuS285Ir3RAnRy6boJYXpQHR2w3E8F8r145I3JDVEHfl5Eu3/Z 8oawGERRvbG0w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2043168FE5 for ; Wed, 31 Dec 2025 15:32:08 -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 k2qLW8n8PiQr for ; Wed, 31 Dec 2025 15:32:08 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220328; bh=Og4kyMgeE+lh4MRyjoxcE+6L0FluqrRkXNqohe+BC44=; 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=EIzd1CQ2iNc6qAbKmCDL+WMBReJz9glECJafPYHdlmTftIxb2h7JsqKFi0tGGNxAf E5SSfr4UwPvltlr0BhoI9ZNgmd42oh4VdAYJ3q+YldOEY2sOFvenmxc158FJ/ooC5Z CY9fpP/n61ybuGSQKln+Au5oGIzecFyxax/E+6/rJmnXczghNT4opcVCloUjPX1/TW rlZ9qCyv3J7M5leFhWmcH322f5Hs7pkC7rnAHaHy88kAAyAMf0G2rhn9TojxQve2bh j2CHJmj8Nbaf7JBZyuS285Ir3RAnRy6boJYXpQHR2w3E8F8r145I3JDVEHfl5Eu3/Z 8oawGERRvbG0w== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0E70C68FD3 for ; Wed, 31 Dec 2025 15:32:08 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220326; bh=75uxdP2Rl9IrkgXz+DrzTa4xk3X45LN0IhItGUUt9Ig=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cGQl7rZr99zJZW+YeZhh4MqgX+KhmD80AuC1JJcwxcmqLh+LywucRYlYU3LEhQsMw WtxIf7f5RduP/nRAyfBDnY9M6lnsVyyFFJVmcmH8tqUbn66H3CgKpoUedQeJT8MW1k j1Pzd7ozscVecA1Ao9G0Wsiq3pI8Fe8M5RNW5UFlx7EeKBRo9mkbpfIHvypsqae6E8 n0uRBTU5B0nrvCHMLO0TgWgO6GC0yCiIRsv60crYQcBzzgt+3slKdYKYOU3ozMYuWA bt5FD0FvtauQ7Ql7kRKDhIIcSca5Q3PjnUv/uq/EikgEzAOYqh7+IWZesyEvgY7Zn7 B9cu15ZE0XPbw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7D76F68F51; Wed, 31 Dec 2025 15:32:06 -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 2uMez-7xl0Cl; Wed, 31 Dec 2025 15:32:06 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220322; bh=rXnEHhaMRR9RYKs1HTBp+zEx61herdUEhk75aPEBWkk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IamIVPdJI8FHAuR7FnLrET/saa84BMNq+XW2zp6bA1SjrrxmfjwIiOGFOd+kOSDR4 TxUO7XWkozN/5YRiS1ZH1NN+x71PzM9+vi4rgbRY1HmqEydza+SKfraKtFn5/fuBuK rMjRhimG0EwC/Q9WrCroGNCdfggy1nqGvZdEKXqtyMflrcvPLwnZYxvQpFu42v3OB0 2ce29GBortt4b+ToEHBxzKin5rV9yuMm9FPebve925iK7CV+/VCTSknyDF44/cN0Ey Ah4k/EHE7Ke8+0aKjInALED5eYsZRqE7g2pfTyaTZBTI9XUYloB4lNID216P9nOnyY SMONPIO/18zFA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5C10A68FD3; Wed, 31 Dec 2025 15:31:52 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Wed, 31 Dec 2025 15:29:55 -0700 Message-ID: <20251231223008.3251711-23-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: UXC2TVQJBYVBBQKZMMLMVJ7C4YX36ISF X-Message-ID-Hash: UXC2TVQJBYVBBQKZMMLMVJ7C4YX36ISF 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 22/26] ext4l: Add symlink support 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 Add ext4l_ln() to create symbolic links. This uses the Linux ext4_symlink() function which supports both fast symlinks (stored in inode) and regular symlinks (stored in data blocks). Fix the fscrypt_prepare_symlink() stub to properly init the disk_link structure with the symlink target, which is required for symlink creation to work correctly. Add some notes about U-Boot's argument ordering with symlinks. Co-developed-by: Claude Signed-off-by: Simon Glass --- fs/ext4l/ext4_uboot.h | 2 +- fs/ext4l/interface.c | 48 ++++++++++++++++++++++++++++++++++ fs/fs_legacy.c | 2 +- include/ext4l.h | 11 ++++++++ test/fs/ext4l.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 2 deletions(-) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index da59956eee8..5ea573fcfb8 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -1579,7 +1579,7 @@ static inline char *d_path(const struct path *path, char *buf, int buflen) #define fscrypt_dio_supported(i) (1) #define fscrypt_has_permitted_context(p, c) ({ (void)(p); (void)(c); 1; }) #define fscrypt_is_nokey_name(d) ({ (void)(d); 0; }) -#define fscrypt_prepare_symlink(d, s, l, m, dl) ({ (void)(d); (void)(s); (void)(l); (void)(m); (void)(dl); 0; }) +#define fscrypt_prepare_symlink(d, s, l, m, dl) ({ (void)(d); (void)(m); (dl)->name = (unsigned char *)(s); (dl)->len = (l) + 1; 0; }) #define fscrypt_encrypt_symlink(i, s, l, d) ({ (void)(i); (void)(s); (void)(l); (void)(d); 0; }) #define fscrypt_prepare_link(o, d, n) ({ (void)(o); (void)(d); (void)(n); 0; }) #define fscrypt_prepare_rename(od, ode, nd, nde, f) ({ (void)(od); (void)(ode); (void)(nd); (void)(nde); (void)(f); 0; }) diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c index 010db3a3631..c9dd25dd7b4 100644 --- a/fs/ext4l/interface.c +++ b/fs/ext4l/interface.c @@ -1249,6 +1249,54 @@ out: return ret; } +int ext4l_ln(const char *filename, const char *linkname) +{ + struct dentry *dentry, *dir_dentry; + char *path_copy; + int ret; + + /* + * Note: The parameter naming follows U-Boot's convention: + * - filename: the target file the link should point to + * - linkname: the path of the symlink to create + */ + if (!filename) + return -EINVAL; + + ret = ext4l_resolve_file(linkname, &dir_dentry, &dentry, &path_copy); + if (ret) + return ret; + + if (dentry->d_inode) { + /* File already exists */ + ret = -EEXIST; + goto out; + } + + /* Create the symlink - filename is what the link points to */ + ret = ext4_symlink(&nop_mnt_idmap, dir_dentry->d_inode, dentry, + filename); + if (ret) + goto out; + + /* 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 b53d420f279..a325c2631d4 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -290,7 +290,7 @@ static struct fstype_info fstypes[] = { .closedir = ext4l_closedir, .unlink = ext4l_op_ptr(ext4l_unlink, fs_unlink_unsupported), .mkdir = ext4l_op_ptr(ext4l_mkdir, fs_mkdir_unsupported), - .ln = fs_ln_unsupported, + .ln = ext4l_op_ptr(ext4l_ln, fs_ln_unsupported), .rename = fs_rename_unsupported, .statfs = ext4l_statfs, }, diff --git a/include/ext4l.h b/include/ext4l.h index 1a12ba1ac1c..d0e420c8da2 100644 --- a/include/ext4l.h +++ b/include/ext4l.h @@ -111,6 +111,17 @@ int ext4l_unlink(const char *filename); */ int ext4l_mkdir(const char *dirname); +/** + * ext4l_ln() - Create a symbolic link + * + * @filename: Path of symlink to create + * @target: Target path the symlink points to + * Return: 0 on success, -EEXIST if file already exists, + * -ENOTDIR if parent is not a directory, -EROFS if read-only, + * negative on other errors + */ +int ext4l_ln(const char *filename, const char *target); + /** * ext4l_get_uuid() - Get the filesystem UUID * diff --git a/test/fs/ext4l.c b/test/fs/ext4l.c index 5930e4234ba..1d46a752f32 100644 --- a/test/fs/ext4l.c +++ b/test/fs/ext4l.c @@ -526,3 +526,63 @@ static int fs_test_ext4l_mkdir_norun(struct unit_test_state *uts) } FS_TEST_ARGS(fs_test_ext4l_mkdir_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, { "fs_image", UT_ARG_STR }); + +/** + * fs_test_ext4l_ln_norun() - Test ext4l_ln function + * + * Verifies that ext4l can create symbolic links on the filesystem. + * + * Arguments: + * fs_image: Path to the ext4 filesystem image + */ +static int fs_test_ext4l_ln_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(EXT4L_ARG_IMAGE); + static int test_counter; + char link_name[32]; + const char *target = "/testfile.txt"; + loff_t size; + loff_t actread; + char buf[32]; + + 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 symlink names to avoid issues with test re-runs */ + snprintf(link_name, sizeof(link_name), "/testlink%d", test_counter); + test_counter++; + + /* + * Create a symbolic link. ext4l_ln follows U-Boot's ln command + * convention: ext4l_ln(target, linkname) creates linkname pointing + * to target. + */ + ut_assertok(ext4l_ln(target, link_name)); + + /* Verify symlink exists */ + ut_asserteq(1, ext4l_exists(link_name)); + + /* + * Size through symlink should be target file's size (12 bytes), + * since ext4l_resolve_path follows symlinks (like stat, not lstat) + */ + ut_assertok(ext4l_size(link_name, &size)); + ut_asserteq(12, size); + + /* Verify we can read through the symlink */ + memset(buf, '\0', sizeof(buf)); + ut_assertok(ext4l_read(link_name, buf, 0, 0, &actread)); + ut_asserteq(12, actread); + ut_asserteq_str("hello world\n", buf); + + /* Verify creating duplicate returns -EEXIST */ + ut_asserteq(-EEXIST, ext4l_ln(target, link_name)); + + /* Verify creating symlink in non-existent parent returns -ENOENT */ + ut_asserteq(-ENOENT, ext4l_ln(target, "/nonexistent/link")); + + return 0; +} +FS_TEST_ARGS(fs_test_ext4l_ln_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR });