From patchwork Fri Jan 2 00:50:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1190 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=1767315176; bh=mD4Qi+/fz5l5vE2HBPMJDAW95dP6RsMmcOF0psIH8ss=; 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=qrmKJIcQFUs9QxZNp52qodoRVoDk3hIEfl5psZu2e5X9vuS8klOUWzuJSK4HQzwna OJkNGc9tBEyUXC3zAeuvSjefohahM8pA9oawY1eXbNN30HJWlVN/Ev2kBxAAgKrQqV qRxmstkNsl3QbVWLMnlN109DrAKA+9+VW0FXBJz7xdmqBpBd+6k4GYBD6tgmMwCxIY PceqRnG55g5vXZG5zRcD63yn17+ZCcHYDgYYQaeZ26XpqQ65b3WecxujihMxO/t7vV PI/ZikMEav6uJxBPkuUyea9OSoy8Sl6ToTwdHrExh0WW4Hy6W5tw0EgTPNuyZlMtH1 UPJm+IRBLy/HQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 42E4768F53 for ; Thu, 1 Jan 2026 17:52:56 -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 m0v6lQAGRCMx for ; Thu, 1 Jan 2026 17:52:56 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767315174; bh=mD4Qi+/fz5l5vE2HBPMJDAW95dP6RsMmcOF0psIH8ss=; 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=E4thrcNKZSBl7/4SvO2/SYypBTgzDE8M56d+tmCXvPzmyHIysaN4848WEd9ZLJ6JQ 1CONhV5+Zvr2MR16uR4iRSrdiYPLu9478KrXxKXhnt2qfAG/z4GeDgUi3pvumJr64z CrRHMj00x1ljRlJ7XaZPuKmR2XfcNBfmfk+lB+F2yMwYhaUI49ZwybaiVWYpAZKz1S lj53BM2QBdC+3ROjGWkmb5Qgf9xYr7DWSk3XBXlsz/+J+tBKGFOg/Nxh+Fj7rpmS+m grAHCEHdz897enStSaf88Uov4K5clnniacvPu1G0Ic1974ygSBf/Kilol+L7QU6r4A duCD1+cCgf5rA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E4C7869035 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=1767315172; bh=Z9+tqAn85Jf41sUshstwVjfYmZgV/1HzFSxoiuwd1CY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pPqHrzil1bb/zpMxP5R3zVUXK/jXpJuX1kxhlB9XQ+QGG3Y1QuNzCCH40WR5LYvxc o++7n85MN8ljXLyCEyWomWkHAIM5yLm3YmvkklJrvPkJ+ojfMqq5qjx8ktMN8Dyj4p p5uLgF+ERfQenx9CArEaFq0vjz/a7PTjQt2J1NfBjEyjzPoErs4N7rHzcCgGP2cApU 1opf+7eWt8mgCD6L1i5NeygxUySxqZiLrOf0O/RyvHnABrudJHffkLD/Iq1LOGwKiL qG+SjxsyOQYxfhju6j21eAigZHgKDB2oZm5BVkav1MDhYLDlldC0nPaDI2V+iigQBr jooFDCeDr9alA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1C33B68F56; Thu, 1 Jan 2026 17:52:52 -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 nDwi8l0sUoru; Thu, 1 Jan 2026 17:52:52 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767315171; bh=rjRInDPryzA0EqsPnejK/20gPPDCSdmWi2FI83rNxnM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=svu9So63RbX3GdQwq6Os7UcgArgS+MJgPuUJt/2smcLLGljru5M3k3fP40ByJKaqF 8Em12d8nN/DaN+kt5kBUKSJqiQE+rUTc0UZ4SrNPhijGgxjmafXoFshVu19jm1mSf/ lXo1gSl8+HUk1mhxg5jMuJfadArNIHP21UB8Z/qQvUB1t5ZPtgx6TfqRhVP9JFecNm VqdB8hoIAZkdHoqSmXG5HivYXrnob8SI+Ya0/f6hhpYs5xfnKqIPNnhSD9Opyw6HKL 9DBHOgayeOy6GOsEiMLb0WE3dBUqvdl5eh3Is29F9MHD/EKBFwChu1zqIRHQCbGGyz tT3wwZq8yxIAw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 65D3068313; Thu, 1 Jan 2026 17:52:51 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Thu, 1 Jan 2026 17:50:56 -0700 Message-ID: <20260102005112.552256-27-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: R2ZME6WD7BYN3Y5RGAOA6VXXFPK3LVER X-Message-ID-Hash: R2ZME6WD7BYN3Y5RGAOA6VXXFPK3LVER 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 26/30] 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 --- (no changes since v1) 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 c29f582b1e0..b3da5bfa002 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -1589,7 +1589,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 });