Index: bin/ln/symlink.7 =================================================================== --- bin/ln/symlink.7 +++ bin/ln/symlink.7 @@ -147,9 +147,10 @@ .Dv AT_SYMLINK_NOFOLLOW flag: .Xr fchmodat 2 , -.Xr fchownat 2 +.Xr fchownat 2 , +.Xr fstatat 2 and -.Xr fstatat 2 . +.Xr utimensat 2 . .Pp The owner and group of an existing symbolic link can be changed by means of the Index: lib/libc/include/libc_private.h =================================================================== --- lib/libc/include/libc_private.h +++ lib/libc/include/libc_private.h @@ -357,6 +357,10 @@ int __libc_tcdrain(int); int __fcntl_compat(int fd, int cmd, ...); +int __sys_futimens(int fd, const struct timespec *times) __hidden; +int __sys_utimensat(int fd, const char *path, + const struct timespec *times, int flag) __hidden; + /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); Index: lib/libc/sys/Makefile.inc =================================================================== --- lib/libc/sys/Makefile.inc +++ lib/libc/sys/Makefile.inc @@ -38,6 +38,10 @@ NOASM+= ${SYSCALL_COMPAT_SRCS:S/.c/.o/} .endif +SRCS+= futimens.c utimensat.c +NOASM+= futimens.o utimensat.o +PSEUDO+= _futimens.o _utimensat.o + INTERPOSED = \ accept \ accept4 \ @@ -310,6 +314,7 @@ umask.2 \ undelete.2 \ unlink.2 \ + utimensat.2 \ utimes.2 \ utrace.2 \ uuidgen.2 \ @@ -442,6 +447,7 @@ timer_settime.2 timer_gettime.2 MLINKS+=truncate.2 ftruncate.2 MLINKS+=unlink.2 unlinkat.2 +MLINKS+=utimensat.2 futimens.2 MLINKS+=utimes.2 futimes.2 \ utimes.2 futimesat.2 \ utimes.2 lutimes.2 Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -397,7 +397,9 @@ }; FBSD_1.4 { + futimens; ppoll; + utimensat; }; FBSDprivate_1.0 { Index: lib/libc/sys/futimens.c =================================================================== --- /dev/null +++ lib/libc/sys/futimens.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2015 Jilles Tjoelker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include + +#include +#include +#include +#include "un-namespace.h" + +#include "libc_private.h" + +int +futimens(int fd, const struct timespec times[2]) +{ + struct timeval now, tv[2], *tvp; + struct stat sb; + + if (__getosreldate() >= 1100055) + return (__sys_futimens(fd, times)); + + if (times == NULL || (times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW)) + tvp = NULL; + else if (times[0].tv_nsec == UTIME_OMIT && + times[1].tv_nsec == UTIME_OMIT) + return (0); + else { + if ((times[0].tv_nsec < 0 || times[0].tv_nsec > 999999999) && + times[0].tv_nsec != UTIME_NOW && + times[0].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + if ((times[1].tv_nsec < 0 || times[1].tv_nsec > 999999999) && + times[1].tv_nsec != UTIME_NOW && + times[1].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + tv[0].tv_sec = times[0].tv_sec; + tv[0].tv_usec = times[0].tv_nsec / 1000; + tv[1].tv_sec = times[1].tv_sec; + tv[1].tv_usec = times[1].tv_nsec / 1000; + tvp = tv; + if (times[0].tv_nsec == UTIME_OMIT || + times[1].tv_nsec == UTIME_OMIT) { + if (_fstat(fd, &sb) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_OMIT) { + tv[0].tv_sec = sb.st_atim.tv_sec; + tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; + } + if (times[1].tv_nsec == UTIME_OMIT) { + tv[1].tv_sec = sb.st_mtim.tv_sec; + tv[1].tv_usec = sb.st_mtim.tv_nsec / 1000; + } + } + if (times[0].tv_nsec == UTIME_NOW || + times[1].tv_nsec == UTIME_NOW) { + if (gettimeofday(&now, NULL) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_NOW) + tv[0] = now; + if (times[1].tv_nsec == UTIME_NOW) + tv[1] = now; + } + } + return (futimes(fd, tvp)); +} Index: lib/libc/sys/utimensat.2 =================================================================== --- /dev/null +++ lib/libc/sys/utimensat.2 @@ -0,0 +1,292 @@ +.\" $NetBSD: utimes.2,v 1.13 1999/03/22 19:45:11 garbled Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 2012, Jilles Tjoelker +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)utimes.2 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd January 3, 2015 +.Dt UTIMENSAT 2 +.Os +.Sh NAME +.Nm futimens , +.Nm utimensat +.Nd set file access and modification times +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/stat.h +.Ft int +.Fn futimens "int fd" "const struct timespec times[2]" +.Ft int +.Fo utimensat +.Fa "int fd" +.Fa "const char *path" +.Fa "const struct timespec times[2]" +.Fa "int flag" +.Fc +.Sh DESCRIPTION +The access and modification times of the file named by +.Fa path +or referenced by +.Fa fd +are changed as specified by the argument +.Fa times . +The inode-change-time of the file is set to the current time. +.Pp +If +.Fa path +specifies a relative path, +it is relative to the current working directory if +.Fa fd +is +.Dv AT_FDCWD +and otherwise relative to the directory associated with the file descriptor +.Fa fd . +.Pp +The +.Va tv_nsec +field of a +.Vt timespec +structure +can be set to the special value +.Dv UTIME_NOW +to set the current time, or to +.Dv UTIME_OMIT +to leave the time unchanged. +In either case, the +.Va tv_sec +field is ignored. +.Pp +If +.Fa times +is +.No non- Ns Dv NULL , +it is assumed to point to an array of two timespec structures. +The access time is set to the value of the first element, and the +modification time is set to the value of the second element. +For file systems that support file birth (creation) times (such as +.Dv UFS2 ) , +the birth time will be set to the value of the second element +if the second element is older than the currently set birth time. +To set both a birth time and a modification time, +two calls are required; the first to set the birth time +and the second to set the (presumably newer) modification time. +Ideally a new system call will be added that allows the setting +of all three times at once. +If +.Fa times +is +.Dv NULL , +this is equivalent to passing +a pointer to an array of two timespec structures +with both +.Va tv_nsec +fields set to +.Dv UTIME_NOW . +.Pp +If both +.Va tv_nsec +fields are +.Dv UTIME_OMIT , +the timestamps remain unchanged and +no permissions are needed for the file itself, +although search permissions may be required for the path prefix. +The call may or may not succeed if the named file does not exist. +.Pp +If both +.Va tv_nsec +fields are +.Dv UTIME_NOW , +the caller must be the owner of the file, have permission to +write the file, or be the super-user. +.Pp +For all other values of the timestamps, +the caller must be the owner of the file or be the super-user. +.Pp +The values for the +.Fa flag +argument of the +.Fn utimensat +system call +are constructed by a bitwise-inclusive OR of flags from the following list, +defined in +.In fcntl.h : +.Bl -tag -width indent +.It Dv AT_SYMLINK_NOFOLLOW +If +.Fa path +names a symbolic link, the symbolic link's times are changed. +By default, +.Fn utimensat +changes the times of the file referenced by the symbolic link. +.El +.Sh RETURN VALUES +.Rv -std +.Sh COMPATIBILITY +If the running kernel does not support this system call, +a wrapper emulates it using +.Xr fstatat 2 , +.Xr futimesat 2 +and +.Xr lutimes 2 . +As a result, timestamps will be rounded down to the nearest microsecond, +.Dv UTIME_OMIT +is not atomic and +.Dv AT_SYMLINK_NOFOLLOW +is not available with a path relative to a file descriptor. +.Sh ERRORS +These system calls will fail if: +.Bl -tag -width Er +.It Bq Er EACCES +The +.Fa times +argument is +.Dv NULL , +or both +.Va tv_nsec +values are +.Dv UTIME_NOW , +and the effective user ID of the process does not +match the owner of the file, and is not the super-user, and write +access is denied. +.It Bq Er EFAULT +The +.Fa times +argument +points outside the process's allocated address space. +.It Bq Er EINVAL +The +.Va tv_usec +component of at least one of the values specified by the +.Fa times +argument has a value less than 0 or greater than 999999. +.It Bq Er EIO +An I/O error occurred while reading or writing the affected inode. +.It Bq Er EPERM +The +.Fa times +argument is not +.Dv NULL +nor are both +.Va tv_nsec +values +.Dv UTIME_NOW , +nor are both +.Va tv_nsec +values +.Dv UTIME_OMIT +and the calling process's effective user ID +does not match the owner of the file and is not the super-user. +.It Bq Er EPERM +The named file has its immutable or append-only flag set, see the +.Xr chflags 2 +manual page for more information. +.It Bq Er EROFS +The file system containing the file is mounted read-only. +.El +.Pp +The +.Fn futimens +system call +will fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa fd +argument +does not refer to a valid descriptor. +.El +.Pp +The +.Fn utimensat +system call +will fail if: +.Bl -tag -width Er +.It Bq Er EACCES +Search permission is denied for a component of the path prefix. +.It Bq Er EBADF +The +.Fa path +argument does not specify an absolute path and the +.Fa fd +argument is neither +.Dv AT_FDCWD +nor a valid file descriptor. +.It Bq Er EFAULT +The +.Fa path +argument +points outside the process's allocated address space. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded +.Dv NAME_MAX +characters, or an entire path name exceeded +.Dv PATH_MAX +characters. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er ENOTDIR +The +.Fa path +argument is not an absolute path and +.Fa fd +is neither +.Dv AT_FDCWD +nor a file descriptor associated with a directory. +.It Bq Er ENOTSUP +The running kernel does not support this system call and +.Dv AT_SYMLINK_NOFOLLOW +is used with a path relative to a file descriptor. +.El +.Sh SEE ALSO +.Xr chflags 2 , +.Xr stat 2 , +.Xr symlink 2 , +.Xr utimes 2 , +.Xr utime 3 , +.Xr symlink 7 +.Sh STANDARDS +The +.Fn futimens +and +.Fn utimensat +system calls are expected to conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn futimens +and +.Fn utimensat +system calls appeared in +.Fx 11.0 . Index: lib/libc/sys/utimensat.c =================================================================== --- /dev/null +++ lib/libc/sys/utimensat.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2015 Jilles Tjoelker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include + +#include +#include +#include +#include "un-namespace.h" + +#include "libc_private.h" + +int +utimensat(int fd, const char *path, const struct timespec times[2], int flag) +{ + struct timeval now, tv[2], *tvp; + struct stat sb; + + if (__getosreldate() >= 1100055) + return (__sys_utimensat(fd, path, times, flag)); + + if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) { + errno = EINVAL; + return (-1); + } + if (times == NULL || (times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW)) + tvp = NULL; + else if (times[0].tv_nsec == UTIME_OMIT && + times[1].tv_nsec == UTIME_OMIT) + return (0); + else { + if ((times[0].tv_nsec < 0 || times[0].tv_nsec > 999999999) && + times[0].tv_nsec != UTIME_NOW && + times[0].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + if ((times[1].tv_nsec < 0 || times[1].tv_nsec > 999999999) && + times[1].tv_nsec != UTIME_NOW && + times[1].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + tv[0].tv_sec = times[0].tv_sec; + tv[0].tv_usec = times[0].tv_nsec / 1000; + tv[1].tv_sec = times[1].tv_sec; + tv[1].tv_usec = times[1].tv_nsec / 1000; + tvp = tv; + if (times[0].tv_nsec == UTIME_OMIT || + times[1].tv_nsec == UTIME_OMIT) { + if (fstatat(fd, path, &sb, flag) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_OMIT) { + tv[0].tv_sec = sb.st_atim.tv_sec; + tv[0].tv_usec = sb.st_atim.tv_nsec / 1000; + } + if (times[1].tv_nsec == UTIME_OMIT) { + tv[1].tv_sec = sb.st_mtim.tv_sec; + tv[1].tv_usec = sb.st_mtim.tv_nsec / 1000; + } + } + if (times[0].tv_nsec == UTIME_NOW || + times[1].tv_nsec == UTIME_NOW) { + if (gettimeofday(&now, NULL) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_NOW) + tv[0] = now; + if (times[1].tv_nsec == UTIME_NOW) + tv[1] = now; + } + } + if ((flag & AT_SYMLINK_NOFOLLOW) == 0) + return (futimesat(fd, path, tvp)); + else if ((flag & AT_SYMLINK_NOFOLLOW) != 0 && + (fd == AT_FDCWD || path[0] == '/')) + return (lutimes(path, tvp)); + else { + errno = ENOTSUP; + return (-1); + } +} Index: share/man/man4/rights.4 =================================================================== --- share/man/man4/rights.4 +++ share/man/man4/rights.4 @@ -272,9 +272,13 @@ flag. .It Dv CAP_FUTIMES Permit -.Xr futimes 2 +.Xr futimens 2 and +.Xr futimes 2 , +and permit .Xr futimesat 2 +and +.Xr utimensat 2 if the .Dv CAP_LOOKUP right is also present. Index: sys/compat/freebsd32/freebsd32_misc.c =================================================================== --- sys/compat/freebsd32/freebsd32_misc.c +++ sys/compat/freebsd32/freebsd32_misc.c @@ -1300,6 +1300,49 @@ } 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; Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ 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); } Index: sys/kern/capabilities.conf =================================================================== --- sys/kern/capabilities.conf +++ sys/kern/capabilities.conf @@ -220,8 +220,9 @@ ftruncate ## -## Allow futimes(2), subject to capability rights. +## Allow futimens(2) and futimes(2), subject to capability rights. ## +futimens futimes ## @@ -453,6 +454,7 @@ renameat symlinkat unlinkat +utimensat ## ## Allow entry into open(2). This system call will fail, since access to the Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ 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 Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -95,10 +95,12 @@ 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 @@ } /* - * 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 @@ 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. */ Index: sys/sys/capsicum.h =================================================================== --- sys/sys/capsicum.h +++ 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) Index: sys/sys/param.h =================================================================== --- sys/sys/param.h +++ sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1100054 /* Master, propagated to newvers */ +#define __FreeBSD_version 1100055 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, Index: sys/sys/stat.h =================================================================== --- sys/sys/stat.h +++ sys/sys/stat.h @@ -307,6 +307,11 @@ #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 @@ #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 Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -99,6 +99,8 @@ 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 @@ 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, Index: usr.bin/kdump/kdump.c =================================================================== --- usr.bin/kdump/kdump.c +++ usr.bin/kdump/kdump.c @@ -714,6 +714,7 @@ case SYS_readlinkat: case SYS_renameat: case SYS_unlinkat: + case SYS_utimensat: putchar('('); atfdname(*ip, decimal); c = ',';