From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: Waldemar Kozaczuk <jwkozac...@gmail.com>
Branch: master

syscalls: fix utimensat

The Linux manual for utimensat states this:

"On Linux, futimens() is a library function implemented on top of
 the utimensat() system call.  To support this, the Linux
 utimensat() system call implements a nonstandard feature: if
 pathname is NULL, then the call modifies the timestamps of the
 file referred to by the file descriptor dirfd (which may refer to
 any type of file).  Using this feature, the call
 futimens(fd, times) is implemented as:

     utimensat(fd, NULL, times, 0);

 Note, however, that the glibc wrapper for utimensat() disallows
 passing NULL as the value for pathname: the wrapper function
 returns the error EINVAL in this case."

To accomodate it, we intruduce new function utimensat4() which is
what linux syscall wrapper delegates to. The new utimensat4() calls
modified sys_utimensat() with the syscall flag argument equal to true.
The modified sys_utimensat() treats the dirfd accordingly in such case.

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>

---
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -2060,7 +2060,22 @@ int utimensat(int dirfd, const char *pathname, const 
struct timespec times[2], i
 {
     trace_vfs_utimensat(pathname);
 
-    auto error = sys_utimensat(dirfd, pathname, times, flags);
+    auto error = sys_utimensat(dirfd, pathname, times, flags, false);
+    if (error) {
+        trace_vfs_utimensat_err(error);
+        errno = error;
+        return -1;
+    }
+
+    trace_vfs_utimensat_ret();
+    return 0;
+}
+
+int utimensat4(int dirfd, const char *pathname, const struct timespec 
times[2], int flags)
+{
+    trace_vfs_utimensat(pathname);
+
+    auto error = sys_utimensat(dirfd, pathname, times, flags, true);
     if (error) {
         trace_vfs_utimensat_err(error);
         errno = error;
diff --git a/fs/vfs/vfs.h b/fs/vfs/vfs.h
--- a/fs/vfs/vfs.h
+++ b/fs/vfs/vfs.h
@@ -120,7 +120,7 @@ int  sys_truncate(char *path, off_t length);
 int     sys_readlink(char *path, char *buf, size_t bufsize, ssize_t *size);
 int  sys_utimes(char *path, const struct timeval times[2], int flags);
 int  sys_utimensat(int dirfd, const char *pathname,
-                   const struct timespec times[2], int flags);
+                   const struct timespec times[2], int flags, bool syscall);
 int  sys_futimens(int fd, const struct timespec times[2]);
 int  sys_fallocate(struct file *fp, int mode, loff_t offset, loff_t len);
 
diff --git a/fs/vfs/vfs_syscalls.cc b/fs/vfs/vfs_syscalls.cc
--- a/fs/vfs/vfs_syscalls.cc
+++ b/fs/vfs/vfs_syscalls.cc
@@ -1317,7 +1317,7 @@ void init_timespec(struct timespec &_times, const struct 
timespec *times)
 }
 
 int
-sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], 
int flags)
+sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], 
int flags, bool syscall)
 {
     int error;
     std::string ap;
@@ -1356,7 +1356,7 @@ sys_utimensat(int dirfd, const char *pathname, const 
struct timespec times[2], i
        if(!fp->f_dentry)
            return EBADF;
 
-       if (!(fp->f_dentry->d_vnode->v_type & VDIR))
+       if (!syscall && !(fp->f_dentry->d_vnode->v_type & VDIR))
            return ENOTDIR;
 
        if (pathname)
@@ -1407,7 +1407,7 @@ sys_futimens(int fd, const struct timespec times[2])
         return EBADF;
 
     std::string pathname = fp->f_dentry->d_path;
-    auto error = sys_utimensat(AT_FDCWD, pathname.c_str(), times, 0);
+    auto error = sys_utimensat(AT_FDCWD, pathname.c_str(), times, 0, false);
     return error;
 }
 
diff --git a/linux.cc b/linux.cc
--- a/linux.cc
+++ b/linux.cc
@@ -574,6 +574,9 @@ static long sys_brk(void *addr)
     }
 }
 
+#define __NR_utimensat4 __NR_utimensat
+extern int utimensat4(int dirfd, const char *pathname, const struct timespec 
times[2], int flags);
+
 #ifdef SYS_open
 TRACEPOINT(trace_syscall_open, "%d <= \"%s\" 0x%x", int, const char *, int);
 #endif
@@ -692,7 +695,7 @@ TRACEPOINT(trace_syscall_chdir, "%d <= \"%s\"", int, const 
char *);
 TRACEPOINT(trace_syscall_faccessat, "%d <= %d \"%s\" %d %d", int, int, const 
char *, int, int);
 TRACEPOINT(trace_syscall_kill, "%d <= %d %d", int, pid_t, int);
 TRACEPOINT(trace_syscall_alarm, "%d <= %u", int, unsigned int);
-TRACEPOINT(trace_syscall_utimensat, "%d <= %d \"%s\" %p %d", int, int, const 
char *, const struct timespec*, int);
+TRACEPOINT(trace_syscall_utimensat4, "%d <= %d \"%s\" %p %d", int, int, const 
char *, const struct timespec*, int);
 TRACEPOINT(trace_syscall_symlink, "%d <= \"%s\" \"%s\"", int, const char *, 
const char *);
 TRACEPOINT(trace_syscall_rmdir, "%d <= \"%s\"", int, const char *);
 TRACEPOINT(trace_syscall_sethostname, "%d <= \"%s\" %d", int, const char *, 
int);
@@ -843,7 +846,7 @@ OSV_LIBC_API long syscall(long number, ...)
     SYSCALL4(faccessat, int, const char *, int, int);
     SYSCALL2(kill, pid_t, int);
     SYSCALL1(alarm, unsigned int);
-    SYSCALL4(utimensat, int, const char *, const struct timespec*, int);
+    SYSCALL4(utimensat4, int, const char *, const struct timespec*, int);
     SYSCALL2(symlink, const char *, const char *);
     SYSCALL1(rmdir, const char *);
     SYSCALL2(sethostname, const char *, int);

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/000000000000a53765060c90821d%40google.com.

Reply via email to