aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-09-20 16:59:58 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-11-09 00:16:43 -0500
commit8e6d782cab50884ba94324632700e6233a252f6a (patch)
tree671b0c90795d8ab9d230018069b397c15f941af3 /fs/namei.c
parent5a14696c1795d3843673b5cf1982d0e5357a5bbf (diff)
locks: break delegations on rename
Cc: David Howells <dhowells@redhat.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>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c47
1 files changed, 43 insertions, 4 deletions
diff --git a/fs/namei.c b/fs/namei.c
index cfaeaae0f2db..ce7e580e4e14 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4022,7 +4022,8 @@ out:
4022} 4022}
4023 4023
4024static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, 4024static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
4025 struct inode *new_dir, struct dentry *new_dentry) 4025 struct inode *new_dir, struct dentry *new_dentry,
4026 struct inode **delegated_inode)
4026{ 4027{
4027 struct inode *target = new_dentry->d_inode; 4028 struct inode *target = new_dentry->d_inode;
4028 struct inode *source = old_dentry->d_inode; 4029 struct inode *source = old_dentry->d_inode;
@@ -4039,6 +4040,14 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
4039 if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) 4040 if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
4040 goto out; 4041 goto out;
4041 4042
4043 error = try_break_deleg(source, delegated_inode);
4044 if (error)
4045 goto out;
4046 if (target) {
4047 error = try_break_deleg(target, delegated_inode);
4048 if (error)
4049 goto out;
4050 }
4042 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); 4051 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
4043 if (error) 4052 if (error)
4044 goto out; 4053 goto out;
@@ -4053,8 +4062,30 @@ out:
4053 return error; 4062 return error;
4054} 4063}
4055 4064
4065/**
4066 * vfs_rename - rename a filesystem object
4067 * @old_dir: parent of source
4068 * @old_dentry: source
4069 * @new_dir: parent of destination
4070 * @new_dentry: destination
4071 * @delegated_inode: returns an inode needing a delegation break
4072 *
4073 * The caller must hold multiple mutexes--see lock_rename()).
4074 *
4075 * If vfs_rename discovers a delegation in need of breaking at either
4076 * the source or destination, it will return -EWOULDBLOCK and return a
4077 * reference to the inode in delegated_inode. The caller should then
4078 * break the delegation and retry. Because breaking a delegation may
4079 * take a long time, the caller should drop all locks before doing
4080 * so.
4081 *
4082 * Alternatively, a caller may pass NULL for delegated_inode. This may
4083 * be appropriate for callers that expect the underlying filesystem not
4084 * to be NFS exported.
4085 */
4056int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, 4086int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
4057 struct inode *new_dir, struct dentry *new_dentry) 4087 struct inode *new_dir, struct dentry *new_dentry,
4088 struct inode **delegated_inode)
4058{ 4089{
4059 int error; 4090 int error;
4060 int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry); 4091 int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry);
@@ -4082,7 +4113,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
4082 if (is_dir) 4113 if (is_dir)
4083 error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); 4114 error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
4084 else 4115 else
4085 error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); 4116 error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry,delegated_inode);
4086 if (!error) 4117 if (!error)
4087 fsnotify_move(old_dir, new_dir, old_name, is_dir, 4118 fsnotify_move(old_dir, new_dir, old_name, is_dir,
4088 new_dentry->d_inode, old_dentry); 4119 new_dentry->d_inode, old_dentry);
@@ -4098,6 +4129,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
4098 struct dentry *old_dentry, *new_dentry; 4129 struct dentry *old_dentry, *new_dentry;
4099 struct dentry *trap; 4130 struct dentry *trap;
4100 struct nameidata oldnd, newnd; 4131 struct nameidata oldnd, newnd;
4132 struct inode *delegated_inode = NULL;
4101 struct filename *from; 4133 struct filename *from;
4102 struct filename *to; 4134 struct filename *to;
4103 unsigned int lookup_flags = 0; 4135 unsigned int lookup_flags = 0;
@@ -4137,6 +4169,7 @@ retry:
4137 newnd.flags &= ~LOOKUP_PARENT; 4169 newnd.flags &= ~LOOKUP_PARENT;
4138 newnd.flags |= LOOKUP_RENAME_TARGET; 4170 newnd.flags |= LOOKUP_RENAME_TARGET;
4139 4171
4172retry_deleg:
4140 trap = lock_rename(new_dir, old_dir); 4173 trap = lock_rename(new_dir, old_dir);
4141 4174
4142 old_dentry = lookup_hash(&oldnd); 4175 old_dentry = lookup_hash(&oldnd);
@@ -4173,13 +4206,19 @@ retry:
4173 if (error) 4206 if (error)
4174 goto exit5; 4207 goto exit5;
4175 error = vfs_rename(old_dir->d_inode, old_dentry, 4208 error = vfs_rename(old_dir->d_inode, old_dentry,
4176 new_dir->d_inode, new_dentry); 4209 new_dir->d_inode, new_dentry,
4210 &delegated_inode);
4177exit5: 4211exit5:
4178 dput(new_dentry); 4212 dput(new_dentry);
4179exit4: 4213exit4:
4180 dput(old_dentry); 4214 dput(old_dentry);
4181exit3: 4215exit3:
4182 unlock_rename(new_dir, old_dir); 4216 unlock_rename(new_dir, old_dir);
4217 if (delegated_inode) {
4218 error = break_deleg_wait(&delegated_inode);
4219 if (!error)
4220 goto retry_deleg;
4221 }
4183 mnt_drop_write(oldnd.path.mnt); 4222 mnt_drop_write(oldnd.path.mnt);
4184exit2: 4223exit2:
4185 if (retry_estale(error, lookup_flags)) 4224 if (retry_estale(error, lookup_flags))