xref: /openbmc/linux/fs/utimes.c (revision 171fa692)
1  #include <linux/file.h>
2  #include <linux/mount.h>
3  #include <linux/namei.h>
4  #include <linux/utime.h>
5  #include <linux/syscalls.h>
6  #include <linux/uaccess.h>
7  #include <linux/compat.h>
8  #include <asm/unistd.h>
9  
10  #ifdef __ARCH_WANT_SYS_UTIME
11  
12  /*
13   * sys_utime() can be implemented in user-level using sys_utimes().
14   * Is this for backwards compatibility?  If so, why not move it
15   * into the appropriate arch directory (for those architectures that
16   * need it).
17   */
18  
19  /* If times==NULL, set access and modification to current time,
20   * must be owner or have write permission.
21   * Else, update from *times, must be owner or super user.
22   */
23  SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times)
24  {
25  	struct timespec tv[2];
26  
27  	if (times) {
28  		if (get_user(tv[0].tv_sec, &times->actime) ||
29  		    get_user(tv[1].tv_sec, &times->modtime))
30  			return -EFAULT;
31  		tv[0].tv_nsec = 0;
32  		tv[1].tv_nsec = 0;
33  	}
34  	return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
35  }
36  
37  #endif
38  
39  static bool nsec_valid(long nsec)
40  {
41  	if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
42  		return true;
43  
44  	return nsec >= 0 && nsec <= 999999999;
45  }
46  
47  static int utimes_common(const struct path *path, struct timespec *times)
48  {
49  	int error;
50  	struct iattr newattrs;
51  	struct inode *inode = path->dentry->d_inode;
52  	struct inode *delegated_inode = NULL;
53  
54  	error = mnt_want_write(path->mnt);
55  	if (error)
56  		goto out;
57  
58  	if (times && times[0].tv_nsec == UTIME_NOW &&
59  		     times[1].tv_nsec == UTIME_NOW)
60  		times = NULL;
61  
62  	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
63  	if (times) {
64  		if (times[0].tv_nsec == UTIME_OMIT)
65  			newattrs.ia_valid &= ~ATTR_ATIME;
66  		else if (times[0].tv_nsec != UTIME_NOW) {
67  			newattrs.ia_atime.tv_sec = times[0].tv_sec;
68  			newattrs.ia_atime.tv_nsec = times[0].tv_nsec;
69  			newattrs.ia_valid |= ATTR_ATIME_SET;
70  		}
71  
72  		if (times[1].tv_nsec == UTIME_OMIT)
73  			newattrs.ia_valid &= ~ATTR_MTIME;
74  		else if (times[1].tv_nsec != UTIME_NOW) {
75  			newattrs.ia_mtime.tv_sec = times[1].tv_sec;
76  			newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
77  			newattrs.ia_valid |= ATTR_MTIME_SET;
78  		}
79  		/*
80  		 * Tell setattr_prepare(), that this is an explicit time
81  		 * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET
82  		 * were used.
83  		 */
84  		newattrs.ia_valid |= ATTR_TIMES_SET;
85  	} else {
86  		newattrs.ia_valid |= ATTR_TOUCH;
87  	}
88  retry_deleg:
89  	inode_lock(inode);
90  	error = notify_change(path->dentry, &newattrs, &delegated_inode);
91  	inode_unlock(inode);
92  	if (delegated_inode) {
93  		error = break_deleg_wait(&delegated_inode);
94  		if (!error)
95  			goto retry_deleg;
96  	}
97  
98  	mnt_drop_write(path->mnt);
99  out:
100  	return error;
101  }
102  
103  /*
104   * do_utimes - change times on filename or file descriptor
105   * @dfd: open file descriptor, -1 or AT_FDCWD
106   * @filename: path name or NULL
107   * @times: new times or NULL
108   * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment)
109   *
110   * If filename is NULL and dfd refers to an open file, then operate on
111   * the file.  Otherwise look up filename, possibly using dfd as a
112   * starting point.
113   *
114   * If times==NULL, set access and modification to current time,
115   * must be owner or have write permission.
116   * Else, update from *times, must be owner or super user.
117   */
118  long do_utimes(int dfd, const char __user *filename, struct timespec *times,
119  	       int flags)
120  {
121  	int error = -EINVAL;
122  
123  	if (times && (!nsec_valid(times[0].tv_nsec) ||
124  		      !nsec_valid(times[1].tv_nsec))) {
125  		goto out;
126  	}
127  
128  	if (flags & ~AT_SYMLINK_NOFOLLOW)
129  		goto out;
130  
131  	if (filename == NULL && dfd != AT_FDCWD) {
132  		struct fd f;
133  
134  		if (flags & AT_SYMLINK_NOFOLLOW)
135  			goto out;
136  
137  		f = fdget(dfd);
138  		error = -EBADF;
139  		if (!f.file)
140  			goto out;
141  
142  		error = utimes_common(&f.file->f_path, times);
143  		fdput(f);
144  	} else {
145  		struct path path;
146  		int lookup_flags = 0;
147  
148  		if (!(flags & AT_SYMLINK_NOFOLLOW))
149  			lookup_flags |= LOOKUP_FOLLOW;
150  retry:
151  		error = user_path_at(dfd, filename, lookup_flags, &path);
152  		if (error)
153  			goto out;
154  
155  		error = utimes_common(&path, times);
156  		path_put(&path);
157  		if (retry_estale(error, lookup_flags)) {
158  			lookup_flags |= LOOKUP_REVAL;
159  			goto retry;
160  		}
161  	}
162  
163  out:
164  	return error;
165  }
166  
167  SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
168  		struct timespec __user *, utimes, int, flags)
169  {
170  	struct timespec tstimes[2];
171  
172  	if (utimes) {
173  		if (copy_from_user(&tstimes, utimes, sizeof(tstimes)))
174  			return -EFAULT;
175  
176  		/* Nothing to do, we must not even check the path.  */
177  		if (tstimes[0].tv_nsec == UTIME_OMIT &&
178  		    tstimes[1].tv_nsec == UTIME_OMIT)
179  			return 0;
180  	}
181  
182  	return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags);
183  }
184  
185  SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename,
186  		struct timeval __user *, utimes)
187  {
188  	struct timeval times[2];
189  	struct timespec tstimes[2];
190  
191  	if (utimes) {
192  		if (copy_from_user(&times, utimes, sizeof(times)))
193  			return -EFAULT;
194  
195  		/* This test is needed to catch all invalid values.  If we
196  		   would test only in do_utimes we would miss those invalid
197  		   values truncated by the multiplication with 1000.  Note
198  		   that we also catch UTIME_{NOW,OMIT} here which are only
199  		   valid for utimensat.  */
200  		if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
201  		    times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
202  			return -EINVAL;
203  
204  		tstimes[0].tv_sec = times[0].tv_sec;
205  		tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
206  		tstimes[1].tv_sec = times[1].tv_sec;
207  		tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
208  	}
209  
210  	return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
211  }
212  
213  SYSCALL_DEFINE2(utimes, char __user *, filename,
214  		struct timeval __user *, utimes)
215  {
216  	return sys_futimesat(AT_FDCWD, filename, utimes);
217  }
218  
219  #ifdef CONFIG_COMPAT
220  /*
221   * Not all architectures have sys_utime, so implement this in terms
222   * of sys_utimes.
223   */
224  COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
225  		       struct compat_utimbuf __user *, t)
226  {
227  	struct timespec tv[2];
228  
229  	if (t) {
230  		if (get_user(tv[0].tv_sec, &t->actime) ||
231  		    get_user(tv[1].tv_sec, &t->modtime))
232  			return -EFAULT;
233  		tv[0].tv_nsec = 0;
234  		tv[1].tv_nsec = 0;
235  	}
236  	return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0);
237  }
238  
239  COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags)
240  {
241  	struct timespec tv[2];
242  
243  	if  (t) {
244  		if (compat_get_timespec(&tv[0], &t[0]) ||
245  		    compat_get_timespec(&tv[1], &t[1]))
246  			return -EFAULT;
247  
248  		if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
249  			return 0;
250  	}
251  	return do_utimes(dfd, filename, t ? tv : NULL, flags);
252  }
253  
254  COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, struct compat_timeval __user *, t)
255  {
256  	struct timespec tv[2];
257  
258  	if (t) {
259  		if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
260  		    get_user(tv[0].tv_nsec, &t[0].tv_usec) ||
261  		    get_user(tv[1].tv_sec, &t[1].tv_sec) ||
262  		    get_user(tv[1].tv_nsec, &t[1].tv_usec))
263  			return -EFAULT;
264  		if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 ||
265  		    tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0)
266  			return -EINVAL;
267  		tv[0].tv_nsec *= 1000;
268  		tv[1].tv_nsec *= 1000;
269  	}
270  	return do_utimes(dfd, filename, t ? tv : NULL, 0);
271  }
272  
273  COMPAT_SYSCALL_DEFINE2(utimes, const char __user *, filename, struct compat_timeval __user *, t)
274  {
275  	return compat_sys_futimesat(AT_FDCWD, filename, t);
276  }
277  #endif
278