From: Miklos Szeredi <mszeredi@xxxxxxx> Untange the mess that is do_utimes() Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxx> CC: Ulrich Drepper <drepper@xxxxxxxxxx> --- fs/utimes.c | 163 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 72 deletions(-) Index: linux-2.6/fs/utimes.c =================================================================== --- linux-2.6.orig/fs/utimes.c 2008-05-05 11:29:26.000000000 +0200 +++ linux-2.6/fs/utimes.c 2008-05-05 11:29:28.000000000 +0200 @@ -53,54 +53,10 @@ static bool nsec_valid(long nsec) return nsec >= 0 && nsec <= 999999999; } -/* If times==NULL, set access and modification to current time, - * must be owner or have write permission. - * Else, update from *times, must be owner or super user. - */ -long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) +static int utimes_common(struct path *path, struct timespec *times) { int error; - struct nameidata nd; - struct dentry *dentry; - struct inode *inode; struct iattr newattrs; - struct file *f = NULL; - struct vfsmount *mnt; - - error = -EINVAL; - if (times && (!nsec_valid(times[0].tv_nsec) || - !nsec_valid(times[1].tv_nsec))) { - goto out; - } - - if (flags & ~AT_SYMLINK_NOFOLLOW) - goto out; - - if (filename == NULL && dfd != AT_FDCWD) { - error = -EINVAL; - if (flags & AT_SYMLINK_NOFOLLOW) - goto out; - - error = -EBADF; - f = fget(dfd); - if (!f) - goto out; - dentry = f->f_path.dentry; - mnt = f->f_path.mnt; - } else { - error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); - if (error) - goto out; - - dentry = nd.path.dentry; - mnt = nd.path.mnt; - } - - inode = dentry->d_inode; - - error = mnt_want_write(mnt); - if (error) - goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; @@ -121,40 +77,103 @@ long do_utimes(int dfd, char __user *fil newattrs.ia_valid |= ATTR_MTIME_SET; } } + mutex_lock(&path->dentry->d_inode->i_mutex); + error = mnt_want_write(path->mnt); + if (!error) { + error = notify_change(path->dentry, &newattrs); + mnt_drop_write(path->mnt); + } + mutex_unlock(&path->dentry->d_inode->i_mutex); + + return error; +} + +/* + * If times is NULL or both times are either UTIME_OMIT or UTIME_NOW, then + * need to check permissions, because inode_change_ok() won't do it. + */ +static bool utimes_need_permission(struct timespec *times) +{ + return !times || (nsec_special(times[0].tv_nsec) && + nsec_special(times[1].tv_nsec)); +} + +static int do_fd_utimes(int fd, struct timespec *times) +{ + int error; + struct file *file = fget(fd); + + if (!file) + return -EBADF; + + if (utimes_need_permission(times)) { + struct inode *inode = file->f_path.dentry->d_inode; + + error = -EACCES; + if (IS_IMMUTABLE(inode)) + goto out_fput; + + if (!is_owner_or_cap(inode) && !(file->f_mode & FMODE_WRITE)) + goto out_fput; + } + error = utimes_common(&file->f_path, times); + + out_fput: + fput(file); + + return error; +} + +/* If times==NULL, set access and modification to current time, + * must be owner or have write permission. + * Else, update from *times, must be owner or super user. + */ +long do_utimes(int dfd, char __user *filename, struct timespec *times, + int flags) +{ + int error; + struct nameidata nd; + int lookup_flags; + + if (times && (!nsec_valid(times[0].tv_nsec) || + !nsec_valid(times[1].tv_nsec))) { + return -EINVAL; + } + + if (filename == NULL && dfd != AT_FDCWD) { + if (flags) + return -EINVAL; + + return do_fd_utimes(dfd, times); + } + + if (flags & ~AT_SYMLINK_NOFOLLOW) + return -EINVAL; + + lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; + error = __user_walk_fd(dfd, filename, lookup_flags, &nd); + if (error) + return error; + + + if (utimes_need_permission(times)) { + struct inode *inode = nd.path.dentry->d_inode; - /* - * If times is NULL or both times are either UTIME_OMIT or - * UTIME_NOW, then need to check permissions, because - * inode_change_ok() won't do it. - */ - if (!times || (nsec_special(times[0].tv_nsec) && - nsec_special(times[1].tv_nsec))) { error = -EACCES; if (IS_IMMUTABLE(inode)) - goto mnt_drop_write_and_out; + goto out_path_put; if (!is_owner_or_cap(inode)) { - if (f) { - if (!(f->f_mode & FMODE_WRITE)) - goto mnt_drop_write_and_out; - } else { - error = vfs_permission(&nd, MAY_WRITE); - if (error) - goto mnt_drop_write_and_out; - } + error = vfs_permission(&nd, MAY_WRITE); + if (error) + goto out_path_put; } } - mutex_lock(&inode->i_mutex); - error = notify_change(dentry, &newattrs); - mutex_unlock(&inode->i_mutex); -mnt_drop_write_and_out: - mnt_drop_write(mnt); -dput_and_out: - if (f) - fput(f); - else - path_put(&nd.path); -out: + error = utimes_common(&nd.path, times); + + out_path_put: + path_put(&nd.path); + return error; } -- -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html