From: Simon Glass <simon.glass@canonical.com>
The ext4l_ln() function returned -EEXIST when creating a symlink where
a file already exists. This differs from the old ext4 implementation
which deletes any existing file before creating the symlink (like ln -sf
behaviour).
Update ext4l_ln() to match this behaviour by calling __ext4_unlink() to
remove any existing non-directory file before creating the symlink.
Directories cannot be replaced with symlinks and return -EISDIR.
This allows test_symlink3 to pass.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---
(no changes since v1)
fs/ext4l/interface.c | 18 +++++++++++++++---
include/ext4l.h | 5 ++++-
test/fs/ext4l.c | 4 ++--
3 files changed, 21 insertions(+), 6 deletions(-)
@@ -1268,9 +1268,21 @@ int ext4l_ln(const char *filename, const char *linkname)
return ret;
if (dentry->d_inode) {
- /* File already exists */
- ret = -EEXIST;
- goto out;
+ /* File already exists - delete it first (like ln -sf) */
+ if (S_ISDIR(dentry->d_inode->i_mode)) {
+ /* Cannot replace a directory with a symlink */
+ ret = -EISDIR;
+ goto out;
+ }
+
+ ret = __ext4_unlink(dir_dentry->d_inode, &dentry->d_name,
+ dentry->d_inode, dentry);
+ if (ret)
+ goto out;
+
+ /* Release inode to free data blocks */
+ iput(dentry->d_inode);
+ dentry->d_inode = NULL;
}
/* Create the symlink - filename is what the link points to */
@@ -114,9 +114,12 @@ int ext4l_mkdir(const char *dirname);
/**
* ext4l_ln() - Create a symbolic link
*
+ * Creates the symlink, replacing any existing file (like ln -sf).
+ * Refuses to replace a directory.
+ *
* @filename: Path of symlink to create
* @target: Target path the symlink points to
- * Return: 0 on success, -EEXIST if file already exists,
+ * Return: 0 on success, -EISDIR if target is a directory,
* -ENOTDIR if parent is not a directory, -EROFS if read-only,
* negative on other errors
*/
@@ -576,8 +576,8 @@ static int fs_test_ext4l_ln_norun(struct unit_test_state *uts)
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 duplicate succeeds (like ln -sf) */
+ ut_assertok(ext4l_ln(target, link_name));
/* Verify creating symlink in non-existent parent returns -ENOENT */
ut_asserteq(-ENOENT, ext4l_ln(target, "/nonexistent/link"));