summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2015-01-23 21:07:08 +0000
committerjilles <jilles@FreeBSD.org>2015-01-23 21:07:08 +0000
commit67db24d0f2b81a222335fe5bb13977ef0c2258a9 (patch)
tree319d8eb9a9510cbe3ee4c7aa7b4dfcc2f42ec514 /sys
parent4743c20fb6384559028572e519b637c0667e87ec (diff)
downloadFreeBSD-src-67db24d0f2b81a222335fe5bb13977ef0c2258a9.zip
FreeBSD-src-67db24d0f2b81a222335fe5bb13977ef0c2258a9.tar.gz
Add futimens and utimensat system calls.
The core kernel part is patch file utimes.2008.4.diff from pluknet@FreeBSD.org. I updated the code for API changes, added the manual page and added compatibility code for old kernels. There is also audit and Capsicum support. A new UTIME_* constant might allow setting birthtimes in future. Differential Revision: https://reviews.freebsd.org/D1426 Submitted by: pluknet (partially) Reviewed by: delphij, pluknet, rwatson Relnotes: yes
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c43
-rw-r--r--sys/compat/freebsd32/syscalls.master5
-rw-r--r--sys/kern/capabilities.conf4
-rw-r--r--sys/kern/syscalls.master5
-rw-r--r--sys/kern/vfs_syscalls.c126
-rw-r--r--sys/sys/capsicum.h4
-rw-r--r--sys/sys/param.h2
-rw-r--r--sys/sys/stat.h8
-rw-r--r--sys/sys/syscallsubr.h5
9 files changed, 196 insertions, 6 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index ffea283..a72612b 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -1300,6 +1300,49 @@ freebsd32_futimesat(struct thread *td, struct freebsd32_futimesat_args *uap)
}
int
+freebsd32_futimens(struct thread *td, struct freebsd32_futimens_args *uap)
+{
+ struct timespec32 ts32[2];
+ struct timespec ts[2], *tsp;
+ int error;
+
+ if (uap->times != NULL) {
+ error = copyin(uap->times, ts32, sizeof(ts32));
+ if (error)
+ return (error);
+ CP(ts32[0], ts[0], tv_sec);
+ CP(ts32[0], ts[0], tv_nsec);
+ CP(ts32[1], ts[1], tv_sec);
+ CP(ts32[1], ts[1], tv_nsec);
+ tsp = ts;
+ } else
+ tsp = NULL;
+ return (kern_futimens(td, uap->fd, tsp, UIO_SYSSPACE));
+}
+
+int
+freebsd32_utimensat(struct thread *td, struct freebsd32_utimensat_args *uap)
+{
+ struct timespec32 ts32[2];
+ struct timespec ts[2], *tsp;
+ int error;
+
+ if (uap->times != NULL) {
+ error = copyin(uap->times, ts32, sizeof(ts32));
+ if (error)
+ return (error);
+ CP(ts32[0], ts[0], tv_sec);
+ CP(ts32[0], ts[0], tv_nsec);
+ CP(ts32[1], ts[1], tv_sec);
+ CP(ts32[1], ts[1], tv_nsec);
+ tsp = ts;
+ } else
+ tsp = NULL;
+ return (kern_utimensat(td, uap->fd, uap->path, UIO_USERSPACE,
+ tsp, UIO_SYSSPACE, uap->flag));
+}
+
+int
freebsd32_adjtime(struct thread *td, struct freebsd32_adjtime_args *uap)
{
struct timeval32 tv32;
diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master
index ce655bc..91d1727 100644
--- a/sys/compat/freebsd32/syscalls.master
+++ b/sys/compat/freebsd32/syscalls.master
@@ -1069,3 +1069,8 @@
545 AUE_POLL STD { int freebsd32_ppoll(struct pollfd *fds, \
u_int nfds, const struct timespec32 *ts, \
const sigset_t *set); }
+546 AUE_FUTIMES STD { int freebsd32_futimens(int fd, \
+ struct timespec *times); }
+547 AUE_FUTIMESAT STD { int freebsd32_utimensat(int fd, \
+ char *path, \
+ struct timespec *times, int flag); }
diff --git a/sys/kern/capabilities.conf b/sys/kern/capabilities.conf
index f7a46ae..d0ea97c 100644
--- a/sys/kern/capabilities.conf
+++ b/sys/kern/capabilities.conf
@@ -220,8 +220,9 @@ fsync
ftruncate
##
-## Allow futimes(2), subject to capability rights.
+## Allow futimens(2) and futimes(2), subject to capability rights.
##
+futimens
futimes
##
@@ -453,6 +454,7 @@ readlinkat
renameat
symlinkat
unlinkat
+utimensat
##
## Allow entry into open(2). This system call will fail, since access to the
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index 089896b..09d38d4 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -983,5 +983,10 @@
545 AUE_POLL STD { int ppoll(struct pollfd *fds, u_int nfds, \
const struct timespec *ts, \
const sigset_t *set); }
+546 AUE_FUTIMES STD { int futimens(int fd, \
+ struct timespec *times); }
+547 AUE_FUTIMESAT STD { int utimensat(int fd, \
+ char *path, \
+ struct timespec *times, int flag); }
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 3105c2f..0f24819 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -95,10 +95,12 @@ SDT_PROBE_DEFINE2(vfs, , stat, mode, "char *", "int");
SDT_PROBE_DEFINE2(vfs, , stat, reg, "char *", "int");
static int chroot_refuse_vdir_fds(struct filedesc *fdp);
-static int getutimes(const struct timeval *, enum uio_seg, struct timespec *);
static int kern_chflagsat(struct thread *td, int fd, const char *path,
enum uio_seg pathseg, u_long flags, int atflag);
static int setfflags(struct thread *td, struct vnode *, u_long);
+static int getutimes(const struct timeval *, enum uio_seg, struct timespec *);
+static int getutimens(const struct timespec *, enum uio_seg,
+ struct timespec *, int *);
static int setutimes(struct thread *td, struct vnode *,
const struct timespec *, int, int);
static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred,
@@ -3007,7 +3009,53 @@ getutimes(usrtvp, tvpseg, tsp)
}
/*
- * Common implementation code for utimes(), lutimes(), and futimes().
+ * Common implementation code for futimens(), utimensat().
+ */
+#define UTIMENS_NULL 0x1
+#define UTIMENS_EXIT 0x2
+static int
+getutimens(const struct timespec *usrtsp, enum uio_seg tspseg,
+ struct timespec *tsp, int *retflags)
+{
+ struct timespec tsnow;
+ int error;
+
+ vfs_timestamp(&tsnow);
+ *retflags = 0;
+ if (usrtsp == NULL) {
+ tsp[0] = tsnow;
+ tsp[1] = tsnow;
+ *retflags |= UTIMENS_NULL;
+ return (0);
+ }
+ if (tspseg == UIO_SYSSPACE) {
+ tsp[0] = usrtsp[0];
+ tsp[1] = usrtsp[1];
+ } else if ((error = copyin(usrtsp, tsp, sizeof(*tsp) * 2)) != 0)
+ return (error);
+ if (tsp[0].tv_nsec == UTIME_OMIT && tsp[1].tv_nsec == UTIME_OMIT)
+ *retflags |= UTIMENS_EXIT;
+ if (tsp[0].tv_nsec == UTIME_NOW && tsp[1].tv_nsec == UTIME_NOW)
+ *retflags |= UTIMENS_NULL;
+ if (tsp[0].tv_nsec == UTIME_OMIT)
+ tsp[0].tv_sec = VNOVAL;
+ else if (tsp[0].tv_nsec == UTIME_NOW)
+ tsp[0] = tsnow;
+ else if (tsp[0].tv_nsec < 0 || tsp[0].tv_nsec >= 1000000000L)
+ return (EINVAL);
+ if (tsp[1].tv_nsec == UTIME_OMIT)
+ tsp[1].tv_sec = VNOVAL;
+ else if (tsp[1].tv_nsec == UTIME_NOW)
+ tsp[1] = tsnow;
+ else if (tsp[1].tv_nsec < 0 || tsp[1].tv_nsec >= 1000000000L)
+ return (EINVAL);
+
+ return (0);
+}
+
+/*
+ * Common implementation code for utimes(), lutimes(), futimes(), futimens(),
+ * and utimensat().
*/
static int
setutimes(td, vp, ts, numtimes, nullflag)
@@ -3196,6 +3244,80 @@ kern_futimes(struct thread *td, int fd, struct timeval *tptr,
return (error);
}
+int
+sys_futimens(struct thread *td, struct futimens_args *uap)
+{
+
+ return (kern_futimens(td, uap->fd, uap->times, UIO_USERSPACE));
+}
+
+int
+kern_futimens(struct thread *td, int fd, struct timespec *tptr,
+ enum uio_seg tptrseg)
+{
+ struct timespec ts[2];
+ struct file *fp;
+ cap_rights_t rights;
+ int error, flags;
+
+ AUDIT_ARG_FD(fd);
+ error = getutimens(tptr, tptrseg, ts, &flags);
+ if (error != 0)
+ return (error);
+ if (flags & UTIMENS_EXIT)
+ return (0);
+ error = getvnode(td->td_proc->p_fd, fd,
+ cap_rights_init(&rights, CAP_FUTIMES), &fp);
+ if (error != 0)
+ return (error);
+#ifdef AUDIT
+ vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY);
+ AUDIT_ARG_VNODE1(fp->f_vnode);
+ VOP_UNLOCK(fp->f_vnode, 0);
+#endif
+ error = setutimes(td, fp->f_vnode, ts, 2, flags & UTIMENS_NULL);
+ fdrop(fp, td);
+ return (error);
+}
+
+int
+sys_utimensat(struct thread *td, struct utimensat_args *uap)
+{
+
+ return (kern_utimensat(td, uap->fd, uap->path, UIO_USERSPACE,
+ uap->times, UIO_USERSPACE, uap->flag));
+}
+
+int
+kern_utimensat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
+ struct timespec *tptr, enum uio_seg tptrseg, int flag)
+{
+ struct nameidata nd;
+ struct timespec ts[2];
+ int error, flags;
+
+ if (flag & ~AT_SYMLINK_NOFOLLOW)
+ return (EINVAL);
+
+ if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0)
+ return (error);
+ NDINIT_AT(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW :
+ FOLLOW) | AUDITVNODE1, pathseg, path, fd, td);
+ if ((error = namei(&nd)) != 0)
+ return (error);
+ /*
+ * We are allowed to call namei() regardless of 2xUTIME_OMIT.
+ * POSIX states:
+ * "If both tv_nsec fields are UTIME_OMIT... EACCESS may be detected."
+ * "Search permission is denied by a component of the path prefix."
+ */
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if ((flags & UTIMENS_EXIT) == 0)
+ error = setutimes(td, nd.ni_vp, ts, 2, flags & UTIMENS_NULL);
+ vrele(nd.ni_vp);
+ return (error);
+}
+
/*
* Truncate a file given its path name.
*/
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
index de113c6..8502a96 100644
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -146,9 +146,9 @@
#define CAP_FSTATAT (CAP_FSTAT | CAP_LOOKUP)
/* Allows for fstatfs(2). */
#define CAP_FSTATFS CAPRIGHT(0, 0x0000000000100000ULL)
-/* Allows for futimes(2). */
+/* Allows for futimens(2) and futimes(2). */
#define CAP_FUTIMES CAPRIGHT(0, 0x0000000000200000ULL)
-/* Allows for futimes(2) and futimesat(2). */
+/* Allows for futimens(2), futimes(2), futimesat(2) and utimensat(2). */
#define CAP_FUTIMESAT (CAP_FUTIMES | CAP_LOOKUP)
/* Allows for linkat(2) and renameat(2) (destination directory descriptor). */
#define CAP_LINKAT (CAP_LOOKUP | 0x0000000000400000ULL)
diff --git a/sys/sys/param.h b/sys/sys/param.h
index bf59b0b..eea93c9 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -58,7 +58,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 1100055 /* Master, propagated to newvers */
+#define __FreeBSD_version 1100056 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
diff --git a/sys/sys/stat.h b/sys/sys/stat.h
index 13dea2d..7a4370d 100644
--- a/sys/sys/stat.h
+++ b/sys/sys/stat.h
@@ -307,6 +307,11 @@ struct nstat {
#endif /* __BSD_VISIBLE */
+#if __POSIX_VISIBLE >= 200809
+#define UTIME_NOW -1
+#define UTIME_OMIT -2
+#endif
+
#ifndef _KERNEL
__BEGIN_DECLS
#if __BSD_VISIBLE
@@ -322,6 +327,9 @@ int fchmod(int, mode_t);
#endif
#if __POSIX_VISIBLE >= 200809
int fchmodat(int, const char *, mode_t, int);
+int futimens(int fd, const struct timespec times[2]);
+int utimensat(int fd, const char *path, const struct timespec times[2],
+ int flag);
#endif
int fstat(int, struct stat *);
#if __BSD_VISIBLE
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index f4131a4..cb6d4e3 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -99,6 +99,8 @@ int kern_fstatfs(struct thread *td, int fd, struct statfs *buf);
int kern_ftruncate(struct thread *td, int fd, off_t length);
int kern_futimes(struct thread *td, int fd, struct timeval *tptr,
enum uio_seg tptrseg);
+int kern_futimens(struct thread *td, int fd, struct timespec *tptr,
+ enum uio_seg tptrseg);
int kern_getdirentries(struct thread *td, int fd, char *buf, u_int count,
long *basep, ssize_t *residp, enum uio_seg bufseg);
int kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
@@ -220,6 +222,9 @@ int kern_unlinkat(struct thread *td, int fd, char *path,
enum uio_seg pathseg, ino_t oldinum);
int kern_utimesat(struct thread *td, int fd, char *path,
enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg);
+int kern_utimensat(struct thread *td, int fd, char *path,
+ enum uio_seg pathseg, struct timespec *tptr, enum uio_seg tptrseg,
+ int follow);
int kern_wait(struct thread *td, pid_t pid, int *status, int options,
struct rusage *rup);
int kern_wait6(struct thread *td, enum idtype idtype, id_t id, int *status,
OpenPOWER on IntegriCloud