From: Simon Glass <simon.glass@canonical.com> 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 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- (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 }); -- 2.43.0