=== modified file 'fs/compat.c' --- fs/compat.c 2008-11-21 10:57:31 +0000 +++ fs/compat.c 2008-11-21 11:08:01 +0000 @@ -84,31 +84,6 @@ return ret; } -asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, - struct compat_timespec __user *t, int flags) -{ - struct timeval tv[2]; - - if (t) { - if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_usec, &t[0].tv_nsec) || - get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_usec, &t[1].tv_nsec)) - return -EFAULT; - - if ((tv[0].tv_usec == UTIME_OMIT || tv[0].tv_usec == UTIME_NOW) - && tv[0].tv_sec != 0) - return -EINVAL; - if ((tv[1].tv_usec == UTIME_OMIT || tv[1].tv_usec == UTIME_NOW) - && tv[1].tv_sec != 0) - return -EINVAL; - - if (tv[0].tv_usec == UTIME_OMIT && tv[1].tv_usec == UTIME_OMIT) - return 0; - } - tv[0].tv_usec/=1000; /* nsec->usec */ - tv[1].tv_usec/=1000; - return do_utimes(dfd, filename, t ? tv : NULL, flags); -} - /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. === modified file 'fs/utimes.c' --- fs/utimes.c 2008-11-21 10:57:31 +0000 +++ fs/utimes.c 2008-11-21 11:08:02 +0000 @@ -1,43 +1,183 @@ -#include #include #include -#include #include -#include #include #include #include -#include - - -asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags) + +static int nsec_valid(long nsec) +{ + if (nsec == UTIME_OMIT || nsec == UTIME_NOW) + return 1; + + return nsec >= 0 && nsec <= 999999999; +} + +static int utimes_common(struct dentry *dentry, struct timespec *times) +{ + int error; + struct iattr newattrs; + struct inode *inode = dentry->d_inode; + + error = -EROFS; + if (IS_RDONLY(inode)) + goto out; + + if (times && times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW) + times = NULL; + + newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; + if (times) { + if (times[0].tv_nsec == UTIME_OMIT) + newattrs.ia_valid &= ~ATTR_ATIME; + else if (times[0].tv_nsec != UTIME_NOW) { + newattrs.ia_atime.tv_sec = times[0].tv_sec; + newattrs.ia_atime.tv_nsec = times[0].tv_nsec; + newattrs.ia_valid |= ATTR_ATIME_SET; + } + + if (times[1].tv_nsec == UTIME_OMIT) + newattrs.ia_valid &= ~ATTR_MTIME; + else if (times[1].tv_nsec != UTIME_NOW) { + newattrs.ia_mtime.tv_sec = times[1].tv_sec; + newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; + newattrs.ia_valid |= ATTR_MTIME_SET; + } + /* + * if neither ATTR_ATIME_SET nor ATTR_MTIME_SET were used + * we need to check permissions, because + * inode_change_ok() won't do it. + */ + if (!(newattrs.ia_valid & (ATTR_ATIME_SET | ATTR_MTIME_SET))) { + error = -EPERM; + if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + goto out; + } + } else { + /* + * If times is NULL (or both times are UTIME_NOW), + * then we need to check permissions, because + * inode_change_ok() won't do it. + */ + error = -EACCES; + if (IS_IMMUTABLE(inode)) + goto out; + + if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) { + error = permission(inode, MAY_WRITE, NULL); + if (error) + goto out; + } + } + mutex_lock(&inode->i_mutex); + error = notify_change(dentry, &newattrs); + mutex_unlock(&inode->i_mutex); + +out: + return error; +} + +/* + * do_utimes - change times on filename or file descriptor + * @dfd: open file descriptor, -1 or AT_FDCWD + * @filename: path name or NULL + * @times: new times or NULL + * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment) + * + * If filename is NULL and dfd refers to an open file, then operate on + * the file. Otherwise look up filename, possibly using dfd as a + * starting point. + * + * 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. + */ +static long __do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) +{ + int 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) { + struct file *file; + + if (flags & AT_SYMLINK_NOFOLLOW) + goto out; + + file = fget(dfd); + error = -EBADF; + if (!file) + goto out; + + error = utimes_common(file->f_dentry, times); + fput(file); + } else { + struct nameidata nd; + int lookup_flags = 0; + + if (!(flags & AT_SYMLINK_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + + error = __user_walk_fd(dfd, filename, lookup_flags, &nd); + if (error) + goto out; + + error = utimes_common(nd.dentry, times); + path_release(&nd); + } + +out: + return error; +} + +asmlinkage long sys_utimensat(int dfd, char __user *filename, + struct timespec __user *utimes, int flags) { struct timespec tstimes[2]; - struct timeval time[2]; + if (utimes) { if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) return -EFAULT; - if ((tstimes[0].tv_nsec == UTIME_OMIT || - tstimes[0].tv_nsec == UTIME_NOW) && - tstimes[0].tv_sec != 0) - return -EINVAL; - if ((tstimes[1].tv_nsec == UTIME_OMIT || - tstimes[1].tv_nsec == UTIME_NOW) && - tstimes[1].tv_sec != 0) - return -EINVAL; - - /* Nothing to do, we must not even check the path. */ - if (tstimes[0].tv_nsec == UTIME_OMIT && - tstimes[1].tv_nsec == UTIME_OMIT) - return 0; - } - -/* Note: declaration of lutimes from glibc is: -int lutimes(const char *path, const struct timeval *times); -while 2.6.23 had timespec instead of timeval, but sizeof(timespec)==sizeof(timeval) */ - time[0].tv_sec =tstimes[0].tv_sec; - time[0].tv_usec=tstimes[0].tv_nsec/1000; - time[1].tv_sec =tstimes[1].tv_sec; - time[1].tv_usec=tstimes[1].tv_nsec/1000; - return do_utimes(dfd, filename, utimes ? time : NULL, flags); -} + + /* Nothing to do, we must not even check the path. */ + if (tstimes[0].tv_nsec == UTIME_OMIT && + tstimes[1].tv_nsec == UTIME_OMIT) + return 0; + } + + return __do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); +} + +#ifdef CONFIG_COMPAT + +asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, + struct compat_timespec __user *t, int flags) +{ + struct timespec tv[2]; + + if (t) { + if (get_compat_timespec(&tv[0], &t[0]) || + get_compat_timespec(&tv[1], &t[1])) + return -EFAULT; + + if ((tv[0].tv_nsec == UTIME_OMIT || tv[0].tv_nsec == UTIME_NOW) + && tv[0].tv_sec != 0) + return -EINVAL; + if ((tv[1].tv_nsec == UTIME_OMIT || tv[1].tv_nsec == UTIME_NOW) + && tv[1].tv_sec != 0) + return -EINVAL; + + if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) + return 0; + } + return __do_utimes(dfd, filename, t ? tv : NULL, flags); +} + +#endif === modified file 'include/linux/compat.h' --- include/linux/compat.h 2008-11-21 10:57:31 +0000 +++ include/linux/compat.h 2008-11-21 11:08:02 +0000 @@ -230,7 +230,9 @@ extern int ve_compat_printk(int dst, const char *fmt, ...); extern long compat_nanosleep_restart(struct restart_block *restart); -asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename,struct compat_timespec __user *t, int flags); + +asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, + struct compat_timespec __user *t, int flags); /* * epoll (fs/eventpoll.c) compat bits follow ... === modified file 'include/linux/stat.h' --- include/linux/stat.h 2008-11-21 10:57:31 +0000 +++ include/linux/stat.h 2008-11-21 11:08:02 +0000 @@ -53,8 +53,8 @@ #define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) #define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) -#define UTIME_NOW ((1l << 30) - 1l) -#define UTIME_OMIT ((1l << 30) - 2l) +#define UTIME_NOW ((1l << 30) - 1l) +#define UTIME_OMIT ((1l << 30) - 2l) #include #include === modified file 'include/linux/syscalls.h' --- include/linux/syscalls.h 2008-11-21 10:57:31 +0000 +++ include/linux/syscalls.h 2008-11-21 11:09:53 +0000 @@ -577,6 +577,8 @@ struct stat64 __user *statbuf, int flag); asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz); +asmlinkage long sys_utimensat(int dfd, char __user *filename, + struct timespec __user *utimes, int flags); asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t); asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename, @@ -603,6 +605,5 @@ asmlinkage long sys_set_robust_list(struct robust_list_head __user *head, size_t len); asmlinkage long sys_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *cache); -asmlinkage long sys_utimensat(int dfd, char __user *filename,struct timespec __user *utimes, int flags); #endif