diff options
author | Ulrich Drepper <drepper@redhat.com> | 2007-05-08 03:33:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:18 -0400 |
commit | 1c710c896eb461895d3c399e15bb5f20b39c9073 (patch) | |
tree | 862e210cc6dad50abffd7640f01d50c3e9f3d375 /fs/compat.c | |
parent | ade5fb818fb1861fd5f84619c761920ade762b5d (diff) |
utimensat implementation
Implement utimensat(2) which is an extension to futimesat(2) in that it
a) supports nano-second resolution for the timestamps
b) allows to selectively ignore the atime/mtime value
c) allows to selectively use the current time for either atime or mtime
d) supports changing the atime/mtime of a symlink itself along the lines
of the BSD lutimes(3) functions
For this change the internally used do_utimes() functions was changed to
accept a timespec time value and an additional flags parameter.
Additionally the sys_utime function was changed to match compat_sys_utime
which already use do_utimes instead of duplicating the work.
Also, the completely missing futimensat() functionality is added. We have
such a function in glibc but we have to resort to using /proc/self/fd/* which
not everybody likes (chroot etc).
Test application (the syscall number will need per-arch editing):
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <stddef.h>
#include <syscall.h>
#define __NR_utimensat 280
#define UTIME_NOW ((1l << 30) - 1l)
#define UTIME_OMIT ((1l << 30) - 2l)
int
main(void)
{
int status = 0;
int fd = open("ttt", O_RDWR|O_CREAT|O_EXCL, 0666);
if (fd == -1)
error (1, errno, "failed to create test file \"ttt\"");
struct stat64 st1;
if (fstat64 (fd, &st1) != 0)
error (1, errno, "fstat failed");
struct timespec t[2];
t[0].tv_sec = 0;
t[0].tv_nsec = 0;
t[1].tv_sec = 0;
t[1].tv_nsec = 0;
if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
error (1, errno, "utimensat failed");
struct stat64 st2;
if (fstat64 (fd, &st2) != 0)
error (1, errno, "fstat failed");
if (st2.st_atim.tv_sec != 0 || st2.st_atim.tv_nsec != 0)
{
puts ("atim not reset to zero");
status = 1;
}
if (st2.st_mtim.tv_sec != 0 || st2.st_mtim.tv_nsec != 0)
{
puts ("mtim not reset to zero");
status = 1;
}
if (status != 0)
goto out;
t[0] = st1.st_atim;
t[1].tv_sec = 0;
t[1].tv_nsec = UTIME_OMIT;
if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
error (1, errno, "utimensat failed");
if (fstat64 (fd, &st2) != 0)
error (1, errno, "fstat failed");
if (st2.st_atim.tv_sec != st1.st_atim.tv_sec
|| st2.st_atim.tv_nsec != st1.st_atim.tv_nsec)
{
puts ("atim not set");
status = 1;
}
if (st2.st_mtim.tv_sec != 0 || st2.st_mtim.tv_nsec != 0)
{
puts ("mtim changed from zero");
status = 1;
}
if (status != 0)
goto out;
t[0].tv_sec = 0;
t[0].tv_nsec = UTIME_OMIT;
t[1] = st1.st_mtim;
if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
error (1, errno, "utimensat failed");
if (fstat64 (fd, &st2) != 0)
error (1, errno, "fstat failed");
if (st2.st_atim.tv_sec != st1.st_atim.tv_sec
|| st2.st_atim.tv_nsec != st1.st_atim.tv_nsec)
{
puts ("mtim changed from original time");
status = 1;
}
if (st2.st_mtim.tv_sec != st1.st_mtim.tv_sec
|| st2.st_mtim.tv_nsec != st1.st_mtim.tv_nsec)
{
puts ("mtim not set");
status = 1;
}
if (status != 0)
goto out;
sleep (2);
t[0].tv_sec = 0;
t[0].tv_nsec = UTIME_NOW;
t[1].tv_sec = 0;
t[1].tv_nsec = UTIME_NOW;
if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
error (1, errno, "utimensat failed");
if (fstat64 (fd, &st2) != 0)
error (1, errno, "fstat failed");
struct timeval tv;
gettimeofday(&tv,NULL);
if (st2.st_atim.tv_sec <= st1.st_atim.tv_sec
|| st2.st_atim.tv_sec > tv.tv_sec)
{
puts ("atim not set to NOW");
status = 1;
}
if (st2.st_mtim.tv_sec <= st1.st_mtim.tv_sec
|| st2.st_mtim.tv_sec > tv.tv_sec)
{
puts ("mtim not set to NOW");
status = 1;
}
if (symlink ("ttt", "tttsym") != 0)
error (1, errno, "cannot create symlink");
t[0].tv_sec = 0;
t[0].tv_nsec = 0;
t[1].tv_sec = 0;
t[1].tv_nsec = 0;
if (syscall(__NR_utimensat, AT_FDCWD, "tttsym", t, AT_SYMLINK_NOFOLLOW) != 0)
error (1, errno, "utimensat failed");
if (lstat64 ("tttsym", &st2) != 0)
error (1, errno, "lstat failed");
if (st2.st_atim.tv_sec != 0 || st2.st_atim.tv_nsec != 0)
{
puts ("symlink atim not reset to zero");
status = 1;
}
if (st2.st_mtim.tv_sec != 0 || st2.st_mtim.tv_nsec != 0)
{
puts ("symlink mtim not reset to zero");
status = 1;
}
if (status != 0)
goto out;
t[0].tv_sec = 1;
t[0].tv_nsec = 0;
t[1].tv_sec = 1;
t[1].tv_nsec = 0;
if (syscall(__NR_utimensat, fd, NULL, t, 0) != 0)
error (1, errno, "utimensat failed");
if (fstat64 (fd, &st2) != 0)
error (1, errno, "fstat failed");
if (st2.st_atim.tv_sec != 1 || st2.st_atim.tv_nsec != 0)
{
puts ("atim not reset to one");
status = 1;
}
if (st2.st_mtim.tv_sec != 1 || st2.st_mtim.tv_nsec != 0)
{
puts ("mtim not reset to one");
status = 1;
}
if (status == 0)
puts ("all OK");
out:
close (fd);
unlink ("ttt");
unlink ("tttsym");
return status;
}
[akpm@linux-foundation.org: add missing i386 syscall table entry]
Signed-off-by: Ulrich Drepper <drepper@redhat.com>
Cc: Alexey Dobriyan <adobriyan@openvz.org>
Cc: Michael Kerrisk <mtk-manpages@gmx.net>
Cc: <linux-arch@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/compat.c')
-rw-r--r-- | fs/compat.c | 43 |
1 files changed, 35 insertions, 8 deletions
diff --git a/fs/compat.c b/fs/compat.c index 9f6248dfd9d9..9cf75df9b2bb 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -77,30 +77,57 @@ int compat_printk(const char *fmt, ...) | |||
77 | */ | 77 | */ |
78 | asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t) | 78 | asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t) |
79 | { | 79 | { |
80 | struct timeval tv[2]; | 80 | struct timespec tv[2]; |
81 | 81 | ||
82 | if (t) { | 82 | if (t) { |
83 | if (get_user(tv[0].tv_sec, &t->actime) || | 83 | if (get_user(tv[0].tv_sec, &t->actime) || |
84 | get_user(tv[1].tv_sec, &t->modtime)) | 84 | get_user(tv[1].tv_sec, &t->modtime)) |
85 | return -EFAULT; | 85 | return -EFAULT; |
86 | tv[0].tv_usec = 0; | 86 | tv[0].tv_nsec = 0; |
87 | tv[1].tv_usec = 0; | 87 | tv[1].tv_nsec = 0; |
88 | } | 88 | } |
89 | return do_utimes(AT_FDCWD, filename, t ? tv : NULL); | 89 | return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0); |
90 | } | ||
91 | |||
92 | asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, struct compat_timespec __user *t, int flags) | ||
93 | { | ||
94 | struct timespec tv[2]; | ||
95 | |||
96 | if (t) { | ||
97 | if (get_compat_timespec(&tv[0], &t[0]) || | ||
98 | get_compat_timespec(&tv[1], &t[1])) | ||
99 | return -EFAULT; | ||
100 | |||
101 | if ((tv[0].tv_nsec == UTIME_OMIT || tv[0].tv_nsec == UTIME_NOW) | ||
102 | && tv[0].tv_sec != 0) | ||
103 | return -EINVAL; | ||
104 | if ((tv[1].tv_nsec == UTIME_OMIT || tv[1].tv_nsec == UTIME_NOW) | ||
105 | && tv[1].tv_sec != 0) | ||
106 | return -EINVAL; | ||
107 | |||
108 | if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) | ||
109 | return 0; | ||
110 | } | ||
111 | return do_utimes(dfd, filename, t ? tv : NULL, flags); | ||
90 | } | 112 | } |
91 | 113 | ||
92 | asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t) | 114 | asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t) |
93 | { | 115 | { |
94 | struct timeval tv[2]; | 116 | struct timespec tv[2]; |
95 | 117 | ||
96 | if (t) { | 118 | if (t) { |
97 | if (get_user(tv[0].tv_sec, &t[0].tv_sec) || | 119 | if (get_user(tv[0].tv_sec, &t[0].tv_sec) || |
98 | get_user(tv[0].tv_usec, &t[0].tv_usec) || | 120 | get_user(tv[0].tv_nsec, &t[0].tv_usec) || |
99 | get_user(tv[1].tv_sec, &t[1].tv_sec) || | 121 | get_user(tv[1].tv_sec, &t[1].tv_sec) || |
100 | get_user(tv[1].tv_usec, &t[1].tv_usec)) | 122 | get_user(tv[1].tv_nsec, &t[1].tv_usec)) |
101 | return -EFAULT; | 123 | return -EFAULT; |
124 | if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 || | ||
125 | tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0) | ||
126 | return -EINVAL; | ||
127 | tv[0].tv_nsec *= 1000; | ||
128 | tv[1].tv_nsec *= 1000; | ||
102 | } | 129 | } |
103 | return do_utimes(dfd, filename, t ? tv : NULL); | 130 | return do_utimes(dfd, filename, t ? tv : NULL, 0); |
104 | } | 131 | } |
105 | 132 | ||
106 | asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t) | 133 | asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t) |