Unverified Commit 92bf5357 authored by Jeff Layton's avatar Jeff Layton Committed by Christian Brauner
Browse files

vfs: make vfs_symlink break delegations on parent dir



In order to add directory delegation support, we must break delegations
on the parent on any change to the directory.

Add a delegated_inode parameter to vfs_symlink() and have it break the
delegation. do_symlinkat() can then wait on the delegation break before
proceeding.

Reviewed-by: default avatarJan Kara <jack@suse.cz>
Reviewed-by: default avatarNeilBrown <neil@brown.name>
Signed-off-by: default avatarJeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-12-52f3feebb2f2@kernel.org


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent e8960c1b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -479,7 +479,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
	if (rc)
		goto out_lock;
	rc = vfs_symlink(&nop_mnt_idmap, lower_dir, lower_dentry,
			 encoded_symname);
			 encoded_symname, NULL);
	kfree(encoded_symname);
	if (rc || d_really_is_negative(lower_dentry))
		goto out_lock;
+1 −1
Original line number Diff line number Diff line
@@ -209,7 +209,7 @@ int __init init_symlink(const char *oldname, const char *newname)
	error = security_path_symlink(&path, dentry, oldname);
	if (!error)
		error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
				    dentry, oldname);
				    dentry, oldname, NULL);
	end_creating_path(&path, dentry);
	return error;
}
+14 −2
Original line number Diff line number Diff line
@@ -4845,6 +4845,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
 * @dir:	inode of the parent directory
 * @dentry:	dentry of the child symlink file
 * @oldname:	name of the file to link to
 * @delegated_inode: returns victim inode, if the inode is delegated.
 *
 * Create a symlink.
 *
@@ -4855,7 +4856,8 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
 * raw inode simply pass @nop_mnt_idmap.
 */
int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
		struct dentry *dentry, const char *oldname)
		struct dentry *dentry, const char *oldname,
		struct delegated_inode *delegated_inode)
{
	int error;

@@ -4870,6 +4872,10 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
	if (error)
		return error;

	error = try_break_deleg(dir, delegated_inode);
	if (error)
		return error;

	error = dir->i_op->symlink(idmap, dir, dentry, oldname);
	if (!error)
		fsnotify_create(dir, dentry);
@@ -4883,6 +4889,7 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to)
	struct dentry *dentry;
	struct path path;
	unsigned int lookup_flags = 0;
	struct delegated_inode delegated_inode = { };

	if (IS_ERR(from)) {
		error = PTR_ERR(from);
@@ -4897,8 +4904,13 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to)
	error = security_path_symlink(&path, dentry, from->name);
	if (!error)
		error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
				    dentry, from->name);
				    dentry, from->name, &delegated_inode);
	end_creating_path(&path, dentry);
	if (is_delegated(&delegated_inode)) {
		error = break_deleg_wait(&delegated_inode);
		if (!error)
			goto retry;
	}
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
+1 −1
Original line number Diff line number Diff line
@@ -1742,7 +1742,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
	err = fh_fill_pre_attrs(fhp);
	if (err != nfs_ok)
		goto out_unlock;
	host_err = vfs_symlink(&nop_mnt_idmap, d_inode(dentry), dnew, path);
	host_err = vfs_symlink(&nop_mnt_idmap, d_inode(dentry), dnew, path, NULL);
	err = nfserrno(host_err);
	cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
	if (!err)
+1 −1
Original line number Diff line number Diff line
@@ -267,7 +267,7 @@ static inline int ovl_do_symlink(struct ovl_fs *ofs,
				 struct inode *dir, struct dentry *dentry,
				 const char *oldname)
{
	int err = vfs_symlink(ovl_upper_mnt_idmap(ofs), dir, dentry, oldname);
	int err = vfs_symlink(ovl_upper_mnt_idmap(ofs), dir, dentry, oldname, NULL);

	pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
	return err;
Loading