[PATCH v3 08/13] vfs: make vfs_symlink break delegations on parent dir

Jan Kara jack at suse.cz
Wed Oct 22 09:01:22 UTC 2025


On Tue 21-10-25 11:25:43, Jeff Layton wrote:
> 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.
> 
> Signed-off-by: Jeff Layton <jlayton at kernel.org>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack at suse.cz>

								Honza

> ---
>  fs/ecryptfs/inode.c      |  2 +-
>  fs/init.c                |  2 +-
>  fs/namei.c               | 16 ++++++++++++++--
>  fs/nfsd/vfs.c            |  2 +-
>  fs/overlayfs/overlayfs.h |  2 +-
>  include/linux/fs.h       |  2 +-
>  6 files changed, 19 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 639ae42bcd56890d04592f7269e4ffc099b44f09..d430ec5a63094ea4cd42828e7d44f0f8d918fcec 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -480,7 +480,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;
> diff --git a/fs/init.c b/fs/init.c
> index 4f02260dd65b0dfcbfbf5812d2ec6a33444a3b1f..e0f5429c0a49d046bd3f231a260954ed0f90ef44 100644
> --- a/fs/init.c
> +++ b/fs/init.c
> @@ -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;
>  }
> diff --git a/fs/namei.c b/fs/namei.c
> index 7e400cbdbc6af1c72eb684f051d0571e944a27d7..71af256cdd941e200389570538f64a3f795e6c83 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4851,6 +4851,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.
>   *
> @@ -4861,7 +4862,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 inode **delegated_inode)
>  {
>  	int error;
>  
> @@ -4876,6 +4878,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);
> @@ -4889,6 +4895,7 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to)
>  	struct dentry *dentry;
>  	struct path path;
>  	unsigned int lookup_flags = 0;
> +	struct inode *delegated_inode = NULL;
>  
>  	if (IS_ERR(from)) {
>  		error = PTR_ERR(from);
> @@ -4903,8 +4910,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 (delegated_inode) {
> +		error = break_deleg_wait(&delegated_inode);
> +		if (!error)
> +			goto retry;
> +	}
>  	if (retry_estale(error, lookup_flags)) {
>  		lookup_flags |= LOOKUP_REVAL;
>  		goto retry;
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 44debf3d0be450ddc245e2fa4f57fe076e1454a2..386f454badce7ed448399ef93e9c8edafbcc4d79 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -1829,7 +1829,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)
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index 87b82dada7ec1b8429299c68078cda24176c5607..94bb4540f7ae2e0571b3b88393c180bd73c3c09c 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -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;
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index a1e1afe39e01a46bf0a81e241b92690947402851..d8c7245da3bf3200b435c7ea6cafcf7903ebf293 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2117,7 +2117,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
>  int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
>  	      umode_t, dev_t, struct inode **);
>  int vfs_symlink(struct mnt_idmap *, struct inode *,
> -		struct dentry *, const char *);
> +		struct dentry *, const char *, struct inode **);
>  int vfs_link(struct dentry *, struct mnt_idmap *, struct inode *,
>  	     struct dentry *, struct inode **);
>  int vfs_rmdir(struct mnt_idmap *, struct inode *, struct dentry *,
> 
> -- 
> 2.51.0
> 
-- 
Jan Kara <jack at suse.com>
SUSE Labs, CR



More information about the samba-technical mailing list