diff options
Diffstat (limited to 'fs/utimes.c')
| -rw-r--r-- | fs/utimes.c | 27 |
1 files changed, 19 insertions, 8 deletions
diff --git a/fs/utimes.c b/fs/utimes.c index 179b5869065..aa138d64560 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -53,6 +53,7 @@ static int utimes_common(struct path *path, struct timespec *times) int error; struct iattr newattrs; struct inode *inode = path->dentry->d_inode; + struct inode *delegated_inode = NULL; error = mnt_want_write(path->mnt); if (error) @@ -95,15 +96,21 @@ static int utimes_common(struct path *path, struct timespec *times) if (IS_IMMUTABLE(inode)) goto mnt_drop_write_and_out; - if (!is_owner_or_cap(inode)) { + if (!inode_owner_or_capable(inode)) { error = inode_permission(inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; } } +retry_deleg: mutex_lock(&inode->i_mutex); - error = notify_change(path->dentry, &newattrs); + error = notify_change(path->dentry, &newattrs, &delegated_inode); mutex_unlock(&inode->i_mutex); + if (delegated_inode) { + error = break_deleg_wait(&delegated_inode); + if (!error) + goto retry_deleg; + } mnt_drop_write_and_out: mnt_drop_write(path->mnt); @@ -140,31 +147,35 @@ long do_utimes(int dfd, const char __user *filename, struct timespec *times, goto out; if (filename == NULL && dfd != AT_FDCWD) { - struct file *file; + struct fd f; if (flags & AT_SYMLINK_NOFOLLOW) goto out; - file = fget(dfd); + f = fdget(dfd); error = -EBADF; - if (!file) + if (!f.file) goto out; - error = utimes_common(&file->f_path, times); - fput(file); + error = utimes_common(&f.file->f_path, times); + fdput(f); } else { struct path path; int lookup_flags = 0; if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - +retry: error = user_path_at(dfd, filename, lookup_flags, &path); if (error) goto out; error = utimes_common(&path, times); path_put(&path); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } out: |
