aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-09-20 17:14:31 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-11-09 00:16:43 -0500
commit146a8595c6399ee6ab4b5cc34c0d28aa4835fdc5 (patch)
treea16a703ef40d72912247d496cd2e3cb7c0f3b9a7
parent8e6d782cab50884ba94324632700e6233a252f6a (diff)
locks: break delegations on link
Cc: Tyler Hicks <tyhicks@canonical.com> Cc: Dustin Kirkland <dustin.kirkland@gazzang.com> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/ecryptfs/inode.c2
-rw-r--r--fs/namei.c36
-rw-r--r--fs/nfsd/vfs.c2
-rw-r--r--include/linux/fs.h2
4 files changed, 35 insertions, 7 deletions
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index c23b01bb7e04..1c628f023041 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -475,7 +475,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
475 dget(lower_new_dentry); 475 dget(lower_new_dentry);
476 lower_dir_dentry = lock_parent(lower_new_dentry); 476 lower_dir_dentry = lock_parent(lower_new_dentry);
477 rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, 477 rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
478 lower_new_dentry); 478 lower_new_dentry, NULL);
479 if (rc || !lower_new_dentry->d_inode) 479 if (rc || !lower_new_dentry->d_inode)
480 goto out_lock; 480 goto out_lock;
481 rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb); 481 rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb);
diff --git a/fs/namei.c b/fs/namei.c
index ce7e580e4e14..251178a1e383 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3819,7 +3819,26 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
3819 return sys_symlinkat(oldname, AT_FDCWD, newname); 3819 return sys_symlinkat(oldname, AT_FDCWD, newname);
3820} 3820}
3821 3821
3822int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) 3822/**
3823 * vfs_link - create a new link
3824 * @old_dentry: object to be linked
3825 * @dir: new parent
3826 * @new_dentry: where to create the new link
3827 * @delegated_inode: returns inode needing a delegation break
3828 *
3829 * The caller must hold dir->i_mutex
3830 *
3831 * If vfs_link discovers a delegation on the to-be-linked file in need
3832 * of breaking, it will return -EWOULDBLOCK and return a reference to the
3833 * inode in delegated_inode. The caller should then break the delegation
3834 * and retry. Because breaking a delegation may take a long time, the
3835 * caller should drop the i_mutex before doing so.
3836 *
3837 * Alternatively, a caller may pass NULL for delegated_inode. This may
3838 * be appropriate for callers that expect the underlying filesystem not
3839 * to be NFS exported.
3840 */
3841int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
3823{ 3842{
3824 struct inode *inode = old_dentry->d_inode; 3843 struct inode *inode = old_dentry->d_inode;
3825 unsigned max_links = dir->i_sb->s_max_links; 3844 unsigned max_links = dir->i_sb->s_max_links;
@@ -3855,8 +3874,11 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
3855 error = -ENOENT; 3874 error = -ENOENT;
3856 else if (max_links && inode->i_nlink >= max_links) 3875 else if (max_links && inode->i_nlink >= max_links)
3857 error = -EMLINK; 3876 error = -EMLINK;
3858 else 3877 else {
3859 error = dir->i_op->link(old_dentry, dir, new_dentry); 3878 error = try_break_deleg(inode, delegated_inode);
3879 if (!error)
3880 error = dir->i_op->link(old_dentry, dir, new_dentry);
3881 }
3860 3882
3861 if (!error && (inode->i_state & I_LINKABLE)) { 3883 if (!error && (inode->i_state & I_LINKABLE)) {
3862 spin_lock(&inode->i_lock); 3884 spin_lock(&inode->i_lock);
@@ -3883,6 +3905,7 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
3883{ 3905{
3884 struct dentry *new_dentry; 3906 struct dentry *new_dentry;
3885 struct path old_path, new_path; 3907 struct path old_path, new_path;
3908 struct inode *delegated_inode = NULL;
3886 int how = 0; 3909 int how = 0;
3887 int error; 3910 int error;
3888 3911
@@ -3921,9 +3944,14 @@ retry:
3921 error = security_path_link(old_path.dentry, &new_path, new_dentry); 3944 error = security_path_link(old_path.dentry, &new_path, new_dentry);
3922 if (error) 3945 if (error)
3923 goto out_dput; 3946 goto out_dput;
3924 error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry); 3947 error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
3925out_dput: 3948out_dput:
3926 done_path_create(&new_path, new_dentry); 3949 done_path_create(&new_path, new_dentry);
3950 if (delegated_inode) {
3951 error = break_deleg_wait(&delegated_inode);
3952 if (!error)
3953 goto retry;
3954 }
3927 if (retry_estale(error, how)) { 3955 if (retry_estale(error, how)) {
3928 how |= LOOKUP_REVAL; 3956 how |= LOOKUP_REVAL;
3929 goto retry; 3957 goto retry;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 45bf0295894d..27ba21b5f383 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1736,7 +1736,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
1736 err = nfserrno(host_err); 1736 err = nfserrno(host_err);
1737 goto out_dput; 1737 goto out_dput;
1738 } 1738 }
1739 host_err = vfs_link(dold, dirp, dnew); 1739 host_err = vfs_link(dold, dirp, dnew, NULL);
1740 if (!host_err) { 1740 if (!host_err) {
1741 err = nfserrno(commit_metadata(ffhp)); 1741 err = nfserrno(commit_metadata(ffhp));
1742 if (!err) 1742 if (!err)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 5bcff883fa90..6e36e7118ec1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1453,7 +1453,7 @@ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
1453extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); 1453extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
1454extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); 1454extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
1455extern int vfs_symlink(struct inode *, struct dentry *, const char *); 1455extern int vfs_symlink(struct inode *, struct dentry *, const char *);
1456extern int vfs_link(struct dentry *, struct inode *, struct dentry *); 1456extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
1457extern int vfs_rmdir(struct inode *, struct dentry *); 1457extern int vfs_rmdir(struct inode *, struct dentry *);
1458extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); 1458extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
1459extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **); 1459extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **);