Diff below adds support for the two special values UTIME_NOW and
UTIME_OMIT, which make it possible to update either just the access
time or the modification time of a file.  Additionally, it adds the
futimens(2) system call, which is the futimes(2) analog to
utimensat(2).

I'm unsure what values are best to pick for UTIME_NOW and UTIME_OMIT.
The only restriction is that they be distinct integer values outside
the valid range for tv_nsec values (i.e., [0, 999999999]).  Values
below are from Linux; OpenSolaris uses UTIME_NOW == -1 and UTIME_OMIT
== -2.

Kernel changes made by this diff:

  - Switch doutimensat() to use timespec values instead of timeval, so
    that utimensat(2) and futimens(2) can operate a full nanosecond
    precision.

  - Refactor part of doutimensat() into a dovutimens() method that
    operates on a vnode that's already been looked up from the
    filesystem or from the file descriptor table, as appropriate.

  - Simplify the NULL timeval/timespec pointer handling somewhat:
    instead of continuing to pass NULL pointers around, just translate
    this into UTIME_NOW/UTIME_NOW early on.

  - futimes(2) now only holds a reference to the vnode during the
    operation, rather than the struct file.

As with the AT_* variables now, UTIME_NOW and UTIME_OMIT are only
visible to the kernel until the system call stubs are added to libc.

ok?


Index: sys/stat.h
===================================================================
RCS file: /cvs/src/sys/sys/stat.h,v
retrieving revision 1.17
diff -u -p -r1.17 stat.h
--- sys/stat.h  29 Jan 2009 22:08:45 -0000      1.17
+++ sys/stat.h  14 Jul 2011 18:52:39 -0000
@@ -221,6 +221,13 @@ struct stat {
 #endif /* _KERNEL */
 #endif /* __BSD_VISIBLE */
 
+#ifdef _KERNEL /* XXX */
+#if __POSIX_VISIBLE >= 200809
+#define        UTIME_NOW       ((1L << 30) - 1)
+#define        UTIME_OMIT      ((1L << 30) - 2)
+#endif /* __POSIX_VISIBLE */
+#endif
+
 #ifndef _KERNEL
 __BEGIN_DECLS
 int    chmod(const char *, mode_t);
Index: kern/syscalls.master
===================================================================
RCS file: /cvs/src/sys/kern/syscalls.master,v
retrieving revision 1.117
diff -u -p -r1.117 syscalls.master
--- kern/syscalls.master        9 Jul 2011 05:46:26 -0000       1.117
+++ kern/syscalls.master        14 Jul 2011 18:52:39 -0000
@@ -572,3 +572,5 @@
                            int flag); }
 326    STD             { int sys_utimensat(int fd, const char *path, \
                            const struct timespec *times, int flag); }
+327    STD             { int sys_futimens(int fd, \
+                           const struct timespec *times); }
Index: kern/vfs_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.176
diff -u -p -r1.176 vfs_syscalls.c
--- kern/vfs_syscalls.c 14 Jul 2011 18:03:06 -0000      1.176
+++ kern/vfs_syscalls.c 14 Jul 2011 18:52:39 -0000
@@ -90,8 +90,11 @@ int dofchownat(struct proc *, int, const
 int dorenameat(struct proc *, int, const char *, int, const char *,
     register_t *);
 int domkdirat(struct proc *, int, const char *, mode_t, register_t *);
-int doutimensat(struct proc *, int, const char *, const struct timeval *,
+int doutimensat(struct proc *, int, const char *, struct timespec *,
     int, register_t *);
+int dovutimens(struct proc *, struct vnode *, struct timespec *ts,
+    register_t *);
+int dofutimens(struct proc *, int, struct timespec *ts, register_t *);
 
 /*
  * Virtual File System System Calls
@@ -2211,6 +2214,7 @@ sys_utimes(struct proc *p, void *v, regi
                syscallarg(const struct timeval *) tptr;
        } */ *uap = v;
 
+       struct timespec ts[2];
        struct timeval tv[2];
        const struct timeval *tvp;
        int error;
@@ -2220,10 +2224,12 @@ sys_utimes(struct proc *p, void *v, regi
                error = copyin(tvp, tv, sizeof(tv));
                if (error)
                        return (error);
-               tvp = tv;
-       }
+               TIMEVAL_TO_TIMESPEC(&tv[0], &ts[0]);
+               TIMEVAL_TO_TIMESPEC(&tv[1], &ts[1]);
+       } else
+               ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
 
-       return (doutimensat(p, AT_FDCWD, SCARG(uap, path), tvp, 0, retval));
+       return (doutimensat(p, AT_FDCWD, SCARG(uap, path), ts, 0, retval));
 }
 
 int
@@ -2236,9 +2242,7 @@ sys_utimensat(struct proc *p, void *v, r
                syscallarg(int) flag;
        } */ *uap = v;
 
-       struct timeval tv[2];
        struct timespec ts[2];
-       const struct timeval *tvp = NULL;
        const struct timespec *tsp;
        int error;
 
@@ -2247,56 +2251,79 @@ sys_utimensat(struct proc *p, void *v, r
                error = copyin(tsp, ts, sizeof(ts));
                if (error)
                        return (error);
-               tv[0].tv_sec = ts[0].tv_sec;
-               tv[0].tv_usec = ts[0].tv_nsec / 1000;
-               tv[1].tv_sec = ts[1].tv_sec;
-               tv[1].tv_usec = ts[1].tv_nsec / 1000;
-               tvp = tv;
-       }
+       } else
+               ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
 
-       return (doutimensat(p, SCARG(uap, fd), SCARG(uap, path), tvp,
+       return (doutimensat(p, SCARG(uap, fd), SCARG(uap, path), ts,
            SCARG(uap, flag), retval));
 }
 
 int
 doutimensat(struct proc *p, int fd, const char *path,
-    const struct timeval *tvp, int flag, register_t *retval)
+    struct timespec ts[2], int flag, register_t *retval)
 {
        struct vnode *vp;
-       struct timeval tv[2];
-       struct vattr vattr;
        int error, follow;
        struct nameidata nd;
 
        if (flag & ~AT_SYMLINK_NOFOLLOW)
                return (EINVAL);
 
-       VATTR_NULL(&vattr);
-       if (tvp == NULL) {
-               getmicrotime(&tv[0]);
-               tv[1] = tv[0];
-               vattr.va_vaflags |= VA_UTIMES_NULL;
-       } else {
-               bcopy(tvp, tv, sizeof(tv));
-               /* XXX workaround timeval matching the VFS constant VNOVAL */
-               if (tv[0].tv_sec == VNOVAL)
-                       tv[0].tv_sec = VNOVAL - 1;
-               if (tv[1].tv_sec == VNOVAL)
-                       tv[1].tv_sec = VNOVAL - 1;
-       }
        follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
        NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd, path, p);
        if ((error = namei(&nd)) != 0)
                return (error);
        vp = nd.ni_vp;
+
+       return (dovutimens(p, vp, ts, retval));
+}
+
+int
+dovutimens(struct proc *p, struct vnode *vp, struct timespec ts[2],
+    register_t *retval)
+{
+       struct vattr vattr;
+       struct timespec now;
+       int error;
+
+       VATTR_NULL(&vattr);
+       if (ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) {
+               if (ts[0].tv_nsec == UTIME_NOW && ts[1].tv_nsec == UTIME_NOW)
+                       vattr.va_vaflags |= VA_UTIMES_NULL;
+
+               getnanotime(&now);
+               if (ts[0].tv_nsec == UTIME_NOW)
+                       ts[0] = now;
+               if (ts[1].tv_nsec == UTIME_NOW)
+                       ts[1] = now;
+       }
+
+       /*
+        * XXX: Ideally the filesystem code would check tv_nsec ==
+        * UTIME_OMIT instead of tv_sec == VNOVAL, but until then we
+        * need to fudge tv_sec if it happens to equal VNOVAL.
+        */
+
+       if (ts[0].tv_sec == VNOVAL)
+               ts[0].tv_sec = VNOVAL - 1;
+       if (ts[1].tv_sec == VNOVAL)
+               ts[1].tv_sec = VNOVAL - 1;
+
+       if (ts[0].tv_nsec == UTIME_OMIT) {
+               ts[0].tv_sec = VNOVAL;
+               ts[0].tv_nsec = VNOVAL;
+       }
+       if (ts[1].tv_nsec == UTIME_OMIT) {
+               ts[1].tv_sec = VNOVAL;
+               ts[1].tv_nsec = VNOVAL;
+       }
+
        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        else {
-               vattr.va_atime.tv_sec = tv[0].tv_sec;
-               vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000;
-               vattr.va_mtime.tv_sec = tv[1].tv_sec;
-               vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000;
+               vattr.va_atime = ts[0];
+               vattr.va_mtime = ts[1];
                error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
        }
        vput(vp);
@@ -2314,44 +2341,60 @@ sys_futimes(struct proc *p, void *v, reg
                syscallarg(int) fd;
                syscallarg(const struct timeval *) tptr;
        } */ *uap = v;
-       struct vnode *vp;
        struct timeval tv[2];
-       struct vattr vattr;
+       struct timespec ts[2];
+       const struct timeval *tvp;
        int error;
-       struct file *fp;
 
-       VATTR_NULL(&vattr);
-       if (SCARG(uap, tptr) == NULL) {
-               getmicrotime(&tv[0]);
-               tv[1] = tv[0];
-               vattr.va_vaflags |= VA_UTIMES_NULL;
-       } else {
-               error = copyin(SCARG(uap, tptr), tv,
-                   sizeof(tv));
+       tvp = SCARG(uap, tptr);
+       if (tvp != NULL) {
+               error = copyin(tvp, tv, sizeof(tv));
                if (error)
                        return (error);
-               /* XXX workaround timeval matching the VFS constant VNOVAL */
-               if (tv[0].tv_sec == VNOVAL)
-                       tv[0].tv_sec = VNOVAL - 1;
-               if (tv[1].tv_sec == VNOVAL)
-                       tv[1].tv_sec = VNOVAL - 1;
-       }
-       if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0)
+               TIMEVAL_TO_TIMESPEC(&tv[0], &ts[0]);
+               TIMEVAL_TO_TIMESPEC(&tv[1], &ts[1]);
+       } else
+               ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
+
+       return (dofutimens(p, SCARG(uap, fd), ts, retval));
+}
+
+int
+sys_futimens(struct proc *p, void *v, register_t *retval)
+{
+       struct sys_futimens_args /* {
+               syscallarg(int) fd;
+               syscallarg(const struct timespec *) times;
+       } */ *uap = v;
+       struct timespec ts[2];
+       const struct timespec *tsp;
+       int error;
+
+       tsp = SCARG(uap, times);
+       if (tsp != NULL) {
+               error = copyin(tsp, ts, sizeof(ts));
+               if (error)
+                       return (error);
+       } else
+               ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
+
+       return (dofutimens(p, SCARG(uap, fd), ts, retval));
+}
+
+int
+dofutimens(struct proc *p, int fd, struct timespec ts[2], register_t *retval)
+{
+       struct file *fp;
+       struct vnode *vp;
+       int error;
+
+       if ((error = getvnode(p->p_fd, fd, &fp)) != 0)
                return (error);
        vp = (struct vnode *)fp->f_data;
-       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
-       if (vp->v_mount && vp->v_mount->mnt_flag & MNT_RDONLY)
-               error = EROFS;
-       else {
-               vattr.va_atime.tv_sec = tv[0].tv_sec;
-               vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000;
-               vattr.va_mtime.tv_sec = tv[1].tv_sec;
-               vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000;
-               error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
-       }
-       VOP_UNLOCK(vp, 0, p);
+       vref(vp);
        FRELE(fp);
-       return (error);
+
+       return (dovutimens(p, vp, ts, retval));
 }
 
 /*

Reply via email to