utimes.c 5.1 KB
Newer Older
A
Alexey Dobriyan 已提交
1
#include <linux/compiler.h>
U
Ulrich Drepper 已提交
2
#include <linux/file.h>
A
Alexey Dobriyan 已提交
3 4 5
#include <linux/fs.h>
#include <linux/linkage.h>
#include <linux/namei.h>
6
#include <linux/sched.h>
U
Ulrich Drepper 已提交
7
#include <linux/stat.h>
A
Alexey Dobriyan 已提交
8
#include <linux/utime.h>
9
#include <linux/syscalls.h>
A
Alexey Dobriyan 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#include <asm/uaccess.h>
#include <asm/unistd.h>

#ifdef __ARCH_WANT_SYS_UTIME

/*
 * sys_utime() can be implemented in user-level using sys_utimes().
 * Is this for backwards compatibility?  If so, why not move it
 * into the appropriate arch directory (for those architectures that
 * need it).
 */

/* 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.
 */
U
Ulrich Drepper 已提交
26
asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times)
A
Alexey Dobriyan 已提交
27
{
U
Ulrich Drepper 已提交
28
	struct timespec tv[2];
A
Alexey Dobriyan 已提交
29 30

	if (times) {
U
Ulrich Drepper 已提交
31 32 33 34 35
		if (get_user(tv[0].tv_sec, &times->actime) ||
		    get_user(tv[1].tv_sec, &times->modtime))
			return -EFAULT;
		tv[0].tv_nsec = 0;
		tv[1].tv_nsec = 0;
A
Alexey Dobriyan 已提交
36
	}
U
Ulrich Drepper 已提交
37
	return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
A
Alexey Dobriyan 已提交
38 39 40 41
}

#endif

42 43 44 45 46 47 48 49
static bool nsec_valid(long nsec)
{
	if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
		return true;

	return nsec >= 0 && nsec <= 999999999;
}

A
Alexey Dobriyan 已提交
50 51 52 53
/* 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.
 */
U
Ulrich Drepper 已提交
54
long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
A
Alexey Dobriyan 已提交
55 56 57
{
	int error;
	struct nameidata nd;
U
Ulrich Drepper 已提交
58 59
	struct dentry *dentry;
	struct inode *inode;
A
Alexey Dobriyan 已提交
60
	struct iattr newattrs;
U
Ulrich Drepper 已提交
61
	struct file *f = NULL;
A
Alexey Dobriyan 已提交
62

U
Ulrich Drepper 已提交
63
	error = -EINVAL;
64 65 66 67 68
	if (times && (!nsec_valid(times[0].tv_nsec) ||
		      !nsec_valid(times[1].tv_nsec))) {
		goto out;
	}

U
Ulrich Drepper 已提交
69
	if (flags & ~AT_SYMLINK_NOFOLLOW)
A
Alexey Dobriyan 已提交
70
		goto out;
U
Ulrich Drepper 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

	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;
	} else {
		error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
		if (error)
			goto out;

87
		dentry = nd.path.dentry;
U
Ulrich Drepper 已提交
88 89 90
	}

	inode = dentry->d_inode;
A
Alexey Dobriyan 已提交
91 92 93 94 95 96 97 98 99 100 101 102

	error = -EROFS;
	if (IS_RDONLY(inode))
		goto dput_and_out;

	/* Don't worry, the checks are done in inode_change_ok() */
	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
	if (times) {
		error = -EPERM;
                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
                        goto dput_and_out;

U
Ulrich Drepper 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
		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;
		}
A
Alexey Dobriyan 已提交
118 119 120 121 122
	} else {
		error = -EACCES;
                if (IS_IMMUTABLE(inode))
                        goto dput_and_out;

123
		if (!is_owner_or_cap(inode)) {
124 125 126 127 128 129 130 131 132
			if (f) {
				if (!(f->f_mode & FMODE_WRITE))
					goto dput_and_out;
			} else {
				error = vfs_permission(&nd, MAY_WRITE);
				if (error)
					goto dput_and_out;
			}
		}
A
Alexey Dobriyan 已提交
133 134
	}
	mutex_lock(&inode->i_mutex);
U
Ulrich Drepper 已提交
135
	error = notify_change(dentry, &newattrs);
A
Alexey Dobriyan 已提交
136 137
	mutex_unlock(&inode->i_mutex);
dput_and_out:
U
Ulrich Drepper 已提交
138 139 140 141
	if (f)
		fput(f);
	else
		path_release(&nd);
A
Alexey Dobriyan 已提交
142 143 144 145
out:
	return error;
}

U
Ulrich Drepper 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags)
{
	struct timespec tstimes[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;
	}

	return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags);
}

A
Alexey Dobriyan 已提交
171 172 173
asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes)
{
	struct timeval times[2];
U
Ulrich Drepper 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	struct timespec tstimes[2];

	if (utimes) {
		if (copy_from_user(&times, utimes, sizeof(times)))
			return -EFAULT;

		/* This test is needed to catch all invalid values.  If we
		   would test only in do_utimes we would miss those invalid
		   values truncated by the multiplication with 1000.  Note
		   that we also catch UTIME_{NOW,OMIT} here which are only
		   valid for utimensat.  */
		if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
		    times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
			return -EINVAL;

		tstimes[0].tv_sec = times[0].tv_sec;
		tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
		tstimes[1].tv_sec = times[1].tv_sec;
		tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
	}
A
Alexey Dobriyan 已提交
194

U
Ulrich Drepper 已提交
195
	return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
A
Alexey Dobriyan 已提交
196 197 198 199 200 201
}

asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes)
{
	return sys_futimesat(AT_FDCWD, filename, utimes);
}