Release 4.18 fs/utimes.c
// SPDX-License-Identifier: GPL-2.0
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/utime.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/compat.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.
*/
SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times)
{
struct timespec64 tv[2];
if (times) {
if (get_user(tv[0].tv_sec, ×->actime) ||
get_user(tv[1].tv_sec, ×->modtime))
return -EFAULT;
tv[0].tv_nsec = 0;
tv[1].tv_nsec = 0;
}
return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
}
#endif
static bool nsec_valid(long nsec)
{
if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
return true;
return nsec >= 0 && nsec <= 999999999;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Miklos Szeredi | 28 | 90.32% | 1 | 50.00% |
Michael Kerrisk | 3 | 9.68% | 1 | 50.00% |
Total | 31 | 100.00% | 2 | 100.00% |
static int utimes_common(const struct path *path, struct timespec64 *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)
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;
}
/*
* Tell setattr_prepare(), that this is an explicit time
* update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET
* were used.
*/
newattrs.ia_valid |= ATTR_TIMES_SET;
} else {
newattrs.ia_valid |= ATTR_TOUCH;
}
retry_deleg:
inode_lock(inode);
error = notify_change(path->dentry, &newattrs, &delegated_inode);
inode_unlock(inode);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
}
mnt_drop_write(path->mnt);
out:
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ulrich Drepper | 105 | 34.43% | 1 | 6.67% |
Alexey Dobriyan | 87 | 28.52% | 1 | 6.67% |
J. Bruce Fields | 34 | 11.15% | 1 | 6.67% |
Miklos Szeredi | 33 | 10.82% | 4 | 26.67% |
Michael Kerrisk | 28 | 9.18% | 2 | 13.33% |
Dave Hansen | 11 | 3.61% | 1 | 6.67% |
Al Viro | 3 | 0.98% | 2 | 13.33% |
Linus Torvalds | 2 | 0.66% | 1 | 6.67% |
Jan Kara | 1 | 0.33% | 1 | 6.67% |
Deepa Dinamani | 1 | 0.33% | 1 | 6.67% |
Total | 305 | 100.00% | 15 | 100.00% |
/*
* 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.
*/
long do_utimes(int dfd, const char __user *filename, struct timespec64 *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 fd f;
if (flags & AT_SYMLINK_NOFOLLOW)
goto out;
f = fdget(dfd);
error = -EBADF;
if (!f.file)
goto out;
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:
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Miklos Szeredi | 169 | 75.45% | 1 | 11.11% |
Jeff Layton | 20 | 8.93% | 1 | 11.11% |
Al Viro | 14 | 6.25% | 2 | 22.22% |
Alexey Dobriyan | 10 | 4.46% | 1 | 11.11% |
Ulrich Drepper | 7 | 3.12% | 1 | 11.11% |
Jan Blunck | 2 | 0.89% | 1 | 11.11% |
David Howells | 1 | 0.45% | 1 | 11.11% |
Deepa Dinamani | 1 | 0.45% | 1 | 11.11% |
Total | 224 | 100.00% | 9 | 100.00% |
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
struct timespec __user *, utimes, int, flags)
{
struct timespec64 tstimes[2];
if (utimes) {
if ((get_timespec64(&tstimes[0], &utimes[0]) ||
get_timespec64(&tstimes[1], &utimes[1])))
return -EFAULT;
/* 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);
}
static long do_futimesat(int dfd, const char __user *filename,
struct timeval __user *utimes)
{
struct timeval times[2];
struct timespec64 tstimes[2];
if (utimes) {
if (copy_from_user(×, 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;
}
return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ulrich Drepper | 117 | 65.36% | 1 | 25.00% |
Alexey Dobriyan | 42 | 23.46% | 1 | 25.00% |
Dominik Brodowski | 19 | 10.61% | 1 | 25.00% |
Deepa Dinamani | 1 | 0.56% | 1 | 25.00% |
Total | 179 | 100.00% | 4 | 100.00% |
SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename,
struct timeval __user *, utimes)
{
return do_futimesat(dfd, filename, utimes);
}
SYSCALL_DEFINE2(utimes, char __user *, filename,
struct timeval __user *, utimes)
{
return do_futimesat(AT_FDCWD, filename, utimes);
}
#ifdef CONFIG_COMPAT
/*
* Not all architectures have sys_utime, so implement this in terms
* of sys_utimes.
*/
COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
struct compat_utimbuf __user *, t)
{
struct timespec64 tv[2];
if (t) {
if (get_user(tv[0].tv_sec, &t->actime) ||
get_user(tv[1].tv_sec, &t->modtime))
return -EFAULT;
tv[0].tv_nsec = 0;
tv[1].tv_nsec = 0;
}
return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0);
}
COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags)
{
struct timespec64 tv[2];
if (t) {
if (compat_get_timespec64(&tv[0], &t[0]) ||
compat_get_timespec64(&tv[1], &t[1]))
return -EFAULT;
if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
return 0;
}
return do_utimes(dfd, filename, t ? tv : NULL, flags);
}
static long do_compat_futimesat(unsigned int dfd, const char __user *filename,
struct compat_timeval __user *t)
{
struct timespec64 tv[2];
if (t) {
if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
get_user(tv[0].tv_nsec, &t[0].tv_usec) ||
get_user(tv[1].tv_sec, &t[1].tv_sec) ||
get_user(tv[1].tv_nsec, &t[1].tv_usec))
return -EFAULT;
if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 ||
tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0)
return -EINVAL;
tv[0].tv_nsec *= 1000;
tv[1].tv_nsec *= 1000;
}
return do_utimes(dfd, filename, t ? tv : NULL, 0);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Al Viro | 168 | 88.89% | 1 | 33.33% |
Dominik Brodowski | 20 | 10.58% | 1 | 33.33% |
Deepa Dinamani | 1 | 0.53% | 1 | 33.33% |
Total | 189 | 100.00% | 3 | 100.00% |
COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd,
const char __user *, filename,
struct compat_timeval __user *, t)
{
return do_compat_futimesat(dfd, filename, t);
}
COMPAT_SYSCALL_DEFINE2(utimes, const char __user *, filename, struct compat_timeval __user *, t)
{
return do_compat_futimesat(AT_FDCWD, filename, t);
}
#endif
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Al Viro | 421 | 28.29% | 5 | 17.86% |
Ulrich Drepper | 367 | 24.66% | 1 | 3.57% |
Miklos Szeredi | 231 | 15.52% | 5 | 17.86% |
Alexey Dobriyan | 187 | 12.57% | 1 | 3.57% |
Dominik Brodowski | 97 | 6.52% | 2 | 7.14% |
Heiko Carstens | 43 | 2.89% | 2 | 7.14% |
J. Bruce Fields | 34 | 2.28% | 1 | 3.57% |
Deepa Dinamani | 33 | 2.22% | 1 | 3.57% |
Michael Kerrisk | 31 | 2.08% | 2 | 7.14% |
Jeff Layton | 20 | 1.34% | 1 | 3.57% |
Dave Hansen | 14 | 0.94% | 1 | 3.57% |
Linus Torvalds | 2 | 0.13% | 1 | 3.57% |
David Howells | 2 | 0.13% | 1 | 3.57% |
Adrian Bunk | 2 | 0.13% | 1 | 3.57% |
Jan Blunck | 2 | 0.13% | 1 | 3.57% |
Greg Kroah-Hartman | 1 | 0.07% | 1 | 3.57% |
Jan Kara | 1 | 0.07% | 1 | 3.57% |
Total | 1488 | 100.00% | 28 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.