diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 47 |
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 | ||
4024 | static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, | 4024 | static 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 | */ | ||
4056 | int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | 4086 | int 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 | ||
4172 | retry_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); | ||
4177 | exit5: | 4211 | exit5: |
4178 | dput(new_dentry); | 4212 | dput(new_dentry); |
4179 | exit4: | 4213 | exit4: |
4180 | dput(old_dentry); | 4214 | dput(old_dentry); |
4181 | exit3: | 4215 | exit3: |
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); |
4184 | exit2: | 4223 | exit2: |
4185 | if (retry_estale(error, lookup_flags)) | 4224 | if (retry_estale(error, lookup_flags)) |