From patchwork Wed Dec 31 22:29:57 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1162 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=1767220333; bh=E7k+1wq8yD44dI9ImAmkMlB/IHdMy/mXXT6b5FU0gic=; 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=XTBWTe6Foalt+2Q5AcCk+qzOMrrg5GrE7ZVrM1CQhO8J7Gvy907Z2vorx4mpIRHq8 afdykWTu1lRvYy4v29XG7pxvVsN6+qyf18IyJhKUXpJXHexgiPHTq1dlGU7zvlciZN Z6+Kz6bUnYN8qVhrZHBZCp9G088hCBhhpFU+t2fhlZ+nSL5b1h5cxfcp6xYMugevwp GP+RQtG9GktIQgIo8nXcw4VkK4pdf3cMOqAsTI9GbZ9easCUSC0xFrCvgKg/GR+2N2 1yW00p84ZsRaBHP4HEBtbOwJ32LFxV34ioXm+32AtWrsmjEUL0Fm913azm0lv9H8kj 1jY3FpbmBnrvA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 32D3D68FE5 for ; Wed, 31 Dec 2025 15:32:13 -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 ibnYp6Es9Z5D for ; Wed, 31 Dec 2025 15:32:13 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220332; bh=E7k+1wq8yD44dI9ImAmkMlB/IHdMy/mXXT6b5FU0gic=; 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=LiYujfU/P8XMjrNFprqHr3BSegK6JO2aXXIzSDGaByJBD9JO5KQbNoK436Ytetm62 Zxd6CJvEvAQWBYCkqOvMK882lZ9+ZSXkqHiDmOLenykp5M8/GPwjM/KOxFt2RYvs3Y INHL+wvyp0G/MnZqPMi2VFFy+6ma9T1lWRJs/Ubp+LjPd4GhMZ2O9TvxfhHpP7rfPj /6uxZngX7GdQ2B5H5UyLO2fmOa3dE2+iWMD5F0e+SR87wAKFVUaSDpyyMESgYjXVxp r43La8HN/fP967UCrr88v7GFMfnWUOsN2W4pTcOgglFggOxtC3+k+57VCBdDwQYILj OIEW5GfJKPthw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D69FD68F65 for ; Wed, 31 Dec 2025 15:32:12 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220330; bh=ej3orwGJz/HU8LzdAfTNM7w1BYh7cJ/D90Fegs+kCxc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vNFADdZTxqemiW4N07u88oPPEA9HcS9+GE7/Tjunzahm2gx+fA7n3MfpwnGBPdhJi qorLkCgIBX1iIOHHKh/l26sMogxLW0Nh6fdTH6Jw2HQQEVx8gsfedaLSl6UHT3/2Ed bEOUgZ6M0t90KFrTLdmF+sEgZZmtbJsM7fKrcIx0xXTq5QARzgVPuo7HbnAxFjYvTK 9DO6kWpZQhmXU/YWpn1oMTPt5oBKhNT3ZfkfLSzst2jrr7TFF/Z+Y9gO268arlxgK7 QSBh/H8U/us0NfoVXojKBpo7bRA55gprPQjs3E1JVztR0MdBXADMZST7BPgwwv6qlb wIJGuU/JqLMJA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BF32A68FF3; Wed, 31 Dec 2025 15:32:10 -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 l8Mxm93c6JcL; Wed, 31 Dec 2025 15:32:10 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767220328; bh=N26EpYAENKDZZnSDKnX/DmBkjxHzowMSxYEkqTJtTEw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qox20++bvZaR/ntHbkty3D9SW6QNqVvBDHrDriJsaERZBvg3foccQwux6AjmJBA0Q CbML30dL8Wgg4TJLuaidKHyMMPeLnFblVcoQX3kyH4cgTRMlmyPGFsx7PbDzcVu8CV M9lywdXBsHCH6CtoE6Ln6PcaziZJNzWHf50r4IbGu1cOBqAx98P36KKYpLC4yrCj4N d1wKackmVfTBwNqUUKN6DUEJWUonwf8FcZTSDZc5BHaM4is+CvoymbFdzKfbk8Rvgg PF7+6wiRtagTNttmK+5iEpRTqpZYRJDvl6Dqdky26qjcFSTZREOV5u8PHv5esSnVgM gl1SLb2tD80YA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id AB84E68F61; Wed, 31 Dec 2025 15:32:07 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Wed, 31 Dec 2025 15:29:57 -0700 Message-ID: <20251231223008.3251711-25-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: 66D25O53XAONNTN4K2Q5TWQELIUV24X2 X-Message-ID-Hash: 66D25O53XAONNTN4K2Q5TWQELIUV24X2 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 24/26] ext4l: Add rename 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_rename() to rename files and directories, including moves across directories. This uses the Linux ext4_rename() function. Also fix the symlink test to verify reading through symlinks works correctly, since ext4l_resolve_path follows symlinks (stat behavior). Add Python test wrappers for mkdir, ln, and rename tests. Co-developed-by: Claude Signed-off-by: Simon Glass --- fs/ext4l/interface.c | 54 +++++++++++++++++++++++++ fs/fs_legacy.c | 2 +- include/ext4l.h | 11 ++++++ test/fs/ext4l.c | 61 +++++++++++++++++++++++++++++ test/py/tests/test_fs/test_ext4l.py | 15 +++++++ 5 files changed, 142 insertions(+), 1 deletion(-) diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c index acd9ba5511e..f581c32359e 100644 --- a/fs/ext4l/interface.c +++ b/fs/ext4l/interface.c @@ -1309,6 +1309,60 @@ out: return ret; } +int ext4l_rename(const char *old_path, const char *new_path) +{ + struct dentry *old_dentry, *new_dentry; + struct dentry *old_dir_dentry, *new_dir_dentry; + char *old_path_copy, *new_path_copy; + int ret; + + /* Check new_path before ext4l_resolve_file checks old_path */ + if (!new_path) + return -EINVAL; + + ret = ext4l_resolve_file(old_path, &old_dir_dentry, &old_dentry, + &old_path_copy); + if (ret) + return ret; + + if (!old_dentry->d_inode) { + /* Source file doesn't exist */ + ret = -ENOENT; + goto out_old; + } + + ret = ext4l_resolve_file(new_path, &new_dir_dentry, &new_dentry, + &new_path_copy); + if (ret) + goto out_old; + + /* Perform the rename */ + ret = ext4_rename(&nop_mnt_idmap, old_dir_dentry->d_inode, old_dentry, + new_dir_dentry->d_inode, new_dentry, 0); + if (ret) + goto out_new; + + /* 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_new: + kfree(new_dentry); + kfree(new_dir_dentry); + free(new_path_copy); +out_old: + kfree(old_dentry); + kfree(old_dir_dentry); + free(old_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 a325c2631d4..6e1bb449410 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -291,7 +291,7 @@ static struct fstype_info fstypes[] = { .unlink = ext4l_op_ptr(ext4l_unlink, fs_unlink_unsupported), .mkdir = ext4l_op_ptr(ext4l_mkdir, fs_mkdir_unsupported), .ln = ext4l_op_ptr(ext4l_ln, fs_ln_unsupported), - .rename = fs_rename_unsupported, + .rename = ext4l_op_ptr(ext4l_rename, fs_rename_unsupported), .statfs = ext4l_statfs, }, #endif diff --git a/include/ext4l.h b/include/ext4l.h index 882a27dad42..59990a3a6f4 100644 --- a/include/ext4l.h +++ b/include/ext4l.h @@ -125,6 +125,17 @@ int ext4l_mkdir(const char *dirname); */ int ext4l_ln(const char *filename, const char *target); +/** + * ext4l_rename() - Rename a file or directory + * + * @old_path: Current path of file or directory + * @new_path: New path for file or directory + * Return: 0 on success, -ENOENT if source not found, + * -ENOTDIR if parent is not a directory, -EROFS if read-only, + * negative on other errors + */ +int ext4l_rename(const char *old_path, const char *new_path); + /** * ext4l_get_uuid() - Get the filesystem UUID * diff --git a/test/fs/ext4l.c b/test/fs/ext4l.c index c1d10dcc816..0843ba1d5ba 100644 --- a/test/fs/ext4l.c +++ b/test/fs/ext4l.c @@ -586,3 +586,64 @@ static int fs_test_ext4l_ln_norun(struct unit_test_state *uts) } FS_TEST_ARGS(fs_test_ext4l_ln_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, { "fs_image", UT_ARG_STR }); + +/** + * fs_test_ext4l_rename_norun() - Test ext4l_rename function + * + * Verifies that ext4l can rename files and directories on the filesystem. + * + * Arguments: + * fs_image: Path to the ext4 filesystem image + */ +static int fs_test_ext4l_rename_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(EXT4L_ARG_IMAGE); + const char *test_data = "rename test\n"; + size_t test_len = strlen(test_data); + static int test_counter; + char old_name[32], new_name[32], subdir_name[32], moved_name[64]; + loff_t actwrite, size; + + 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 names to avoid issues with test re-runs */ + snprintf(old_name, sizeof(old_name), "/renameme%d.txt", test_counter); + snprintf(new_name, sizeof(new_name), "/renamed%d.txt", test_counter); + snprintf(subdir_name, sizeof(subdir_name), "/renamedir%d", test_counter); + snprintf(moved_name, sizeof(moved_name), "%s/moved.txt", subdir_name); + test_counter++; + + /* Create a file to rename */ + ut_assertok(ext4l_write(old_name, (void *)test_data, 0, + test_len, &actwrite)); + ut_asserteq(test_len, actwrite); + + /* Verify file exists */ + ut_asserteq(1, ext4l_exists(old_name)); + + /* Rename the file */ + ut_assertok(ext4l_rename(old_name, new_name)); + + /* Verify old name no longer exists, new name does */ + ut_asserteq(0, ext4l_exists(old_name)); + ut_asserteq(1, ext4l_exists(new_name)); + + /* Verify file size is preserved */ + ut_assertok(ext4l_size(new_name, &size)); + ut_asserteq(test_len, size); + + /* Verify renaming non-existent file returns -ENOENT */ + ut_asserteq(-ENOENT, ext4l_rename("/nonexistent", "/newname")); + + /* Test cross-directory rename */ + ut_assertok(ext4l_mkdir(subdir_name)); + ut_assertok(ext4l_rename(new_name, moved_name)); + ut_asserteq(0, ext4l_exists(new_name)); + ut_asserteq(1, ext4l_exists(moved_name)); + + return 0; +} +FS_TEST_ARGS(fs_test_ext4l_rename_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); diff --git a/test/py/tests/test_fs/test_ext4l.py b/test/py/tests/test_fs/test_ext4l.py index edea38f7faf..0930ebd01ea 100644 --- a/test/py/tests/test_fs/test_ext4l.py +++ b/test/py/tests/test_fs/test_ext4l.py @@ -128,3 +128,18 @@ class TestExt4l: """Test that ext4l can delete files.""" with ubman.log.section('Test ext4l unlink'): ubman.run_ut('fs', 'fs_test_ext4l_unlink', fs_image=ext4_image) + + def test_mkdir(self, ubman, ext4_image): + """Test that ext4l can create directories.""" + with ubman.log.section('Test ext4l mkdir'): + ubman.run_ut('fs', 'fs_test_ext4l_mkdir', fs_image=ext4_image) + + def test_ln(self, ubman, ext4_image): + """Test that ext4l can create symbolic links.""" + with ubman.log.section('Test ext4l ln'): + ubman.run_ut('fs', 'fs_test_ext4l_ln', fs_image=ext4_image) + + def test_rename(self, ubman, ext4_image): + """Test that ext4l can rename files and directories.""" + with ubman.log.section('Test ext4l rename'): + ubman.run_ut('fs', 'fs_test_ext4l_rename', fs_image=ext4_image)