ext4l: Update symlink to replace existing files
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>
This commit is contained in:
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user