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));
}
/*