Index: lib/libc/include/libc_private.h =================================================================== --- lib/libc/include/libc_private.h +++ lib/libc/include/libc_private.h @@ -408,6 +408,8 @@ 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; +int __sys_utimensat2(int fd, const char *path, + const struct timespec *times, int cnt, int flag) __hidden; /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -373,6 +373,7 @@ futimens; ppoll; utimensat; + utimensat2; numa_setaffinity; numa_getaffinity; sendmmsg; Index: sys/bsm/audit_kevents.h =================================================================== --- sys/bsm/audit_kevents.h +++ sys/bsm/audit_kevents.h @@ -844,5 +844,6 @@ #define AUE_WATCHEVENT AUE_NULL #define AUE_WORKQOPEN AUE_NULL #define AUE_WORKQOPS AUE_NULL +#define AUE_FUTIMENSAT2 AUE_NULL #endif /* !_BSM_AUDIT_KEVENTS_H_ */ Index: sys/compat/freebsd32/freebsd32_misc.c =================================================================== --- sys/compat/freebsd32/freebsd32_misc.c +++ sys/compat/freebsd32/freebsd32_misc.c @@ -1768,6 +1768,28 @@ tsp, UIO_SYSSPACE, uap->flag)); } +int +freebsd32_utimensat2(struct thread *td, struct freebsd32_utimensat2_args *uap) +{ + struct timespec32 ts32[MAX_UTIMES]; + struct timespec ts[MAX_UTIMES], *tsp; + int error, i; + + if (uap->times != NULL) { + error = copyin(uap->times, ts32, sizeof(ts32)); + if (error) + return (error); + for (i = 0; i < uap->cnt; i++) { + CP(ts32[i], ts[i], tv_sec); + CP(ts32[i], ts[i], tv_nsec); + } + tsp = ts; + } else + tsp = NULL; + return (kern_utimensat2(td, uap->fd, uap->path, UIO_USERSPACE, + tsp, uap->cnt, UIO_SYSSPACE, uap->flag)); +} + int freebsd32_adjtime(struct thread *td, struct freebsd32_adjtime_args *uap) { Index: sys/compat/freebsd32/freebsd32_proto.h =================================================================== --- sys/compat/freebsd32/freebsd32_proto.h +++ sys/compat/freebsd32/freebsd32_proto.h @@ -244,7 +244,7 @@ }; struct freebsd32_lio_listio_args { char mode_l_[PADL_(int)]; int mode; char mode_r_[PADR_(int)]; - char acb_list_l_[PADL_(struct aiocb32 * const *)]; struct aiocb32 * const * acb_list; char acb_list_r_[PADR_(struct aiocb32 * const *)]; + char acb_list_l_[PADL_(struct aiocb32 *const *)]; struct aiocb32 *const * acb_list; char acb_list_r_[PADR_(struct aiocb32 *const *)]; char nent_l_[PADL_(int)]; int nent; char nent_r_[PADR_(int)]; char sig_l_[PADL_(struct sigevent32 *)]; struct sigevent32 * sig; char sig_r_[PADR_(struct sigevent32 *)]; }; @@ -278,7 +278,7 @@ char aiocbp_l_[PADL_(struct aiocb32 *)]; struct aiocb32 * aiocbp; char aiocbp_r_[PADR_(struct aiocb32 *)]; }; struct freebsd32_aio_suspend_args { - char aiocbp_l_[PADL_(struct aiocb32 * const *)]; struct aiocb32 * const * aiocbp; char aiocbp_r_[PADR_(struct aiocb32 * const *)]; + char aiocbp_l_[PADL_(struct aiocb32 *const *)]; struct aiocb32 *const * aiocbp; char aiocbp_r_[PADR_(struct aiocb32 *const *)]; char nent_l_[PADL_(int)]; int nent; char nent_r_[PADR_(int)]; char timeout_l_[PADL_(const struct timespec32 *)]; const struct timespec32 * timeout; char timeout_r_[PADR_(const struct timespec32 *)]; }; @@ -742,6 +742,13 @@ char new_l_[PADL_(void *)]; void * new; char new_r_[PADR_(void *)]; char newlen_l_[PADL_(size_t)]; size_t newlen; char newlen_r_[PADR_(size_t)]; }; +struct freebsd32_utimensat2_args { + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; + char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; + char times_l_[PADL_(struct timespec *)]; struct timespec * times; char times_r_[PADR_(struct timespec *)]; + char cnt_l_[PADL_(int)]; int cnt; char cnt_r_[PADR_(int)]; + char flag_l_[PADL_(int)]; int flag; char flag_r_[PADR_(int)]; +}; #if !defined(PAD64_REQUIRED) && !defined(__amd64__) #define PAD64_REQUIRED #endif @@ -881,6 +888,7 @@ int freebsd32_cpuset_getdomain(struct thread *, struct freebsd32_cpuset_getdomain_args *); int freebsd32_cpuset_setdomain(struct thread *, struct freebsd32_cpuset_setdomain_args *); int freebsd32___sysctlbyname(struct thread *, struct freebsd32___sysctlbyname_args *); +int freebsd32_utimensat2(struct thread *, struct freebsd32_utimensat2_args *); #ifdef COMPAT_43 @@ -1101,7 +1109,7 @@ }; struct freebsd6_freebsd32_lio_listio_args { char mode_l_[PADL_(int)]; int mode; char mode_r_[PADR_(int)]; - char acb_list_l_[PADL_(struct oaiocb32 * const *)]; struct oaiocb32 * const * acb_list; char acb_list_r_[PADR_(struct oaiocb32 * const *)]; + char acb_list_l_[PADL_(struct oaiocb32 *const *)]; struct oaiocb32 *const * acb_list; char acb_list_r_[PADR_(struct oaiocb32 *const *)]; char nent_l_[PADL_(int)]; int nent; char nent_r_[PADR_(int)]; char sig_l_[PADL_(struct osigevent32 *)]; struct osigevent32 * sig; char sig_r_[PADR_(struct osigevent32 *)]; }; @@ -1461,6 +1469,7 @@ #define FREEBSD32_SYS_AUE_freebsd32_cpuset_getdomain AUE_NULL #define FREEBSD32_SYS_AUE_freebsd32_cpuset_setdomain AUE_NULL #define FREEBSD32_SYS_AUE_freebsd32___sysctlbyname AUE_SYSCTL +#define FREEBSD32_SYS_AUE_freebsd32_utimensat2 AUE_FUTIMENSAT2 #undef PAD_ #undef PADL_ Index: sys/compat/freebsd32/freebsd32_syscall.h =================================================================== --- sys/compat/freebsd32/freebsd32_syscall.h +++ sys/compat/freebsd32/freebsd32_syscall.h @@ -503,4 +503,5 @@ #define FREEBSD32_SYS___realpathat 574 #define FREEBSD32_SYS_close_range 575 #define FREEBSD32_SYS_rpctls_syscall 576 -#define FREEBSD32_SYS_MAXSYSCALL 577 +#define FREEBSD32_SYS_freebsd32_utimensat2 577 +#define FREEBSD32_SYS_MAXSYSCALL 578 Index: sys/compat/freebsd32/freebsd32_syscalls.c =================================================================== --- sys/compat/freebsd32/freebsd32_syscalls.c +++ sys/compat/freebsd32/freebsd32_syscalls.c @@ -613,4 +613,5 @@ "__realpathat", /* 574 = __realpathat */ "close_range", /* 575 = close_range */ "rpctls_syscall", /* 576 = rpctls_syscall */ + "freebsd32_utimensat2", /* 577 = freebsd32_utimensat2 */ }; Index: sys/compat/freebsd32/freebsd32_sysent.c =================================================================== --- sys/compat/freebsd32/freebsd32_sysent.c +++ sys/compat/freebsd32/freebsd32_sysent.c @@ -666,4 +666,5 @@ { AS(__realpathat_args), (sy_call_t *)sys___realpathat, AUE_REALPATHAT, NULL, 0, 0, 0, SY_THR_STATIC }, /* 574 = __realpathat */ { AS(close_range_args), (sy_call_t *)sys_close_range, AUE_CLOSERANGE, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 575 = close_range */ { AS(rpctls_syscall_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 576 = rpctls_syscall */ + { AS(freebsd32_utimensat2_args), (sy_call_t *)freebsd32_utimensat2, AUE_FUTIMENSAT2, NULL, 0, 0, 0, SY_THR_STATIC }, /* 577 = freebsd32_utimensat2 */ }; Index: sys/compat/freebsd32/freebsd32_systrace_args.c =================================================================== --- sys/compat/freebsd32/freebsd32_systrace_args.c +++ sys/compat/freebsd32/freebsd32_systrace_args.c @@ -1278,7 +1278,7 @@ case 257: { struct freebsd32_lio_listio_args *p = params; iarg[0] = p->mode; /* int */ - uarg[1] = (intptr_t) p->acb_list; /* struct aiocb32 * const * */ + uarg[1] = (intptr_t) p->acb_list; /* struct aiocb32 *const * */ iarg[2] = p->nent; /* int */ uarg[3] = (intptr_t) p->sig; /* struct sigevent32 * */ *n_args = 4; @@ -1437,7 +1437,7 @@ /* freebsd32_aio_suspend */ case 315: { struct freebsd32_aio_suspend_args *p = params; - uarg[0] = (intptr_t) p->aiocbp; /* struct aiocb32 * const * */ + uarg[0] = (intptr_t) p->aiocbp; /* struct aiocb32 *const * */ iarg[1] = p->nent; /* int */ uarg[2] = (intptr_t) p->timeout; /* const struct timespec32 * */ *n_args = 3; @@ -3384,6 +3384,17 @@ *n_args = 2; break; } + /* freebsd32_utimensat2 */ + case 577: { + struct freebsd32_utimensat2_args *p = params; + iarg[0] = p->fd; /* int */ + uarg[1] = (intptr_t) p->path; /* const char * */ + uarg[2] = (intptr_t) p->times; /* struct timespec * */ + iarg[3] = p->cnt; /* int */ + iarg[4] = p->flag; /* int */ + *n_args = 5; + break; + } default: *n_args = 0; break; @@ -5399,7 +5410,7 @@ p = "int"; break; case 1: - p = "userland struct aiocb32 * const *"; + p = "userland struct aiocb32 *const *"; break; case 2: p = "int"; @@ -5656,7 +5667,7 @@ case 315: switch(ndx) { case 0: - p = "userland struct aiocb32 * const *"; + p = "userland struct aiocb32 *const *"; break; case 1: p = "int"; @@ -9124,6 +9135,28 @@ break; }; break; + /* freebsd32_utimensat2 */ + case 577: + switch(ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland const char *"; + break; + case 2: + p = "userland struct timespec *"; + break; + case 3: + p = "int"; + break; + case 4: + p = "int"; + break; + default: + break; + }; + break; default: break; }; @@ -11028,6 +11061,11 @@ if (ndx == 0 || ndx == 1) p = "int"; break; + /* freebsd32_utimensat2 */ + case 577: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ sys/compat/freebsd32/syscalls.master @@ -1167,5 +1167,8 @@ ; 576 is initialised by the krpc code, if present. 576 AUE_NULL NOSTD|NOPROTO { int rpctls_syscall(int op, \ const char *path); } +577 AUE_FUTIMENSAT2 STD { int freebsd32_utimensat2(int fd, \ + const char *path, \ + struct timespec *times, int cnt, int flag); } ; vim: syntax=off Index: sys/kern/init_sysent.c =================================================================== --- sys/kern/init_sysent.c +++ sys/kern/init_sysent.c @@ -632,4 +632,5 @@ { AS(__realpathat_args), (sy_call_t *)sys___realpathat, AUE_REALPATHAT, NULL, 0, 0, 0, SY_THR_STATIC }, /* 574 = __realpathat */ { AS(close_range_args), (sy_call_t *)sys_close_range, AUE_CLOSERANGE, NULL, 0, 0, SYF_CAPENABLED, SY_THR_STATIC }, /* 575 = close_range */ { AS(rpctls_syscall_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 576 = rpctls_syscall */ + { AS(utimensat2_args), (sy_call_t *)sys_utimensat2, AUE_FUTIMENSAT2, NULL, 0, 0, 0, SY_THR_STATIC }, /* 577 = utimensat2 */ }; Index: sys/kern/syscalls.c =================================================================== --- sys/kern/syscalls.c +++ sys/kern/syscalls.c @@ -583,4 +583,5 @@ "__realpathat", /* 574 = __realpathat */ "close_range", /* 575 = close_range */ "rpctls_syscall", /* 576 = rpctls_syscall */ + "utimensat2", /* 577 = utimensat2 */ }; Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -3241,6 +3241,15 @@ _In_z_ const char *path ); } +577 AUE_FUTIMENSAT2 STD { + int utimensat2( + int fd, + _In_z_char *path, + _In_reads_(3) struct timespec *times, + int cnt, + int flag + ); + } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master Index: sys/kern/systrace_args.c =================================================================== --- sys/kern/systrace_args.c +++ sys/kern/systrace_args.c @@ -3376,6 +3376,17 @@ *n_args = 2; break; } + /* utimensat2 */ + case 577: { + struct utimensat2_args *p = params; + iarg[0] = p->fd; /* int */ + uarg[1] = (intptr_t) p->path; /* char * */ + uarg[2] = (intptr_t) p->times; /* struct timespec * */ + iarg[3] = p->cnt; /* int */ + iarg[4] = p->flag; /* int */ + *n_args = 5; + break; + } default: *n_args = 0; break; @@ -9029,6 +9040,28 @@ break; }; break; + /* utimensat2 */ + case 577: + switch(ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland char *"; + break; + case 2: + p = "userland struct timespec *"; + break; + case 3: + p = "int"; + break; + case 4: + p = "int"; + break; + default: + break; + }; + break; default: break; }; @@ -10960,6 +10993,11 @@ if (ndx == 0 || ndx == 1) p = "int"; break; + /* utimensat2 */ + case 577: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -101,6 +101,8 @@ 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 getutimens2(const struct timespec *, int cnt, 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, @@ -3064,6 +3066,57 @@ return (0); } +static int +getutimens2(const struct timespec *usrtsp, int cnt, enum uio_seg tspseg, + struct timespec *tsp, int *retflags) +{ + struct timespec tsnow; + int error; + int only_omit, only_now, i; + only_omit = only_now = i = 0; + vfs_timestamp(&tsnow); + retflags = 0; + if (usrtsp == NULL) { + for (i = 0; i < cnt; i++) { + tsp[i] = tsnow; + } + *retflags |= UTIMENS_NULL; + } + if (tspseg == UIO_SYSSPACE) { + for (i = 0; i < cnt; i++) { + tsp[i] = usrtsp[i]; + } + } else if ((error = copyin(usrtsp, tsp, sizeof(*tsp) * cnt)) != 0) + return (error); + + for (i = 0; i < cnt; i++) { + if ((tsp[i].tv_nsec != UTIME_OMIT) && + (tsp[i].tv_nsec < 0 || tsp[i].tv_nsec >= 1000000000L)) { + return (EINVAL); + } + if (tsp[i].tv_nsec == UTIME_OMIT) { + tsp[i].tv_sec = VNOVAL; + only_now = 0; + } + else if (tsp[i].tv_nsec == UTIME_NOW) { + tsp[i] = tsnow; + only_omit = 0; + } + else if ((tsp[i].tv_nsec != UTIME_OMIT) && + (tsp[i].tv_nsec != UTIME_NOW)) { + only_omit = 0; + only_now = 0; + } + } + if (only_omit) { + *retflags |= UTIMENS_EXIT; + } + if (only_now) { + *retflags |= UTIMENS_NULL; + } + return (0); +} + /* * Common implementation code for utimes(), lutimes(), futimes(), futimens(), * and utimensat(). @@ -3275,6 +3328,14 @@ uap->times, UIO_USERSPACE, uap->flag)); } +int +sys_utimensat2(struct thread *td, struct utimensat2_args *uap) +{ + + return (kern_utimensat2(td, uap->fd, uap->path, UIO_USERSPACE, + uap->times, uap->cnt, UIO_USERSPACE, uap->flag)); +} + int kern_utimensat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, struct timespec *tptr, enum uio_seg tptrseg, @@ -3307,6 +3368,40 @@ return (error); } +int +kern_utimensat2(struct thread *td, int fd, const char *path, enum uio_seg pathseg, + struct timespec *tptr, int cnt, enum uio_seg tptrseg, int flag) +{ + struct nameidata nd; + struct timespec ts[MAX_UTIMES]; + int error, flags; + + if (cnt > MAX_UTIMES) + return (EINVAL); + + if (flag & ~AT_SYMLINK_NOFOLLOW) + return (EINVAL); + + if ((error = getutimens2(tptr, cnt, tptrseg, ts, &flags)) != 0) + return (error); + NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : + FOLLOW) | AUDITVNODE1, pathseg, path, fd, + &cap_futimes_rights, 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, cnt, flags & UTIMENS_NULL); + vrele(nd.ni_vp); + return (error); +} + /* * Truncate a file given its path name. */ Index: sys/sys/stat.h =================================================================== --- sys/sys/stat.h +++ sys/sys/stat.h @@ -101,6 +101,10 @@ #include #endif +#ifdef _KERNEL +#define MAX_UTIMES 3 +#endif + #ifdef _KERNEL struct ostat { __uint16_t st_dev; /* inode's device */ @@ -382,6 +386,8 @@ #if __BSD_VISIBLE int lchflags(const char *, unsigned long); int lchmod(const char *, mode_t); +int utimensat2(int fd, const char *path, const struct timespec *times, + int cnt, int flag); #endif #if __POSIX_VISIBLE >= 200112 int lstat(const char * __restrict, struct stat * __restrict); Index: sys/sys/syscall.h =================================================================== --- sys/sys/syscall.h +++ sys/sys/syscall.h @@ -512,4 +512,5 @@ #define SYS___realpathat 574 #define SYS_close_range 575 #define SYS_rpctls_syscall 576 -#define SYS_MAXSYSCALL 577 +#define SYS_utimensat2 577 +#define SYS_MAXSYSCALL 578 Index: sys/sys/syscall.mk =================================================================== --- sys/sys/syscall.mk +++ sys/sys/syscall.mk @@ -417,4 +417,5 @@ sigfastblock.o \ __realpathat.o \ close_range.o \ - rpctls_syscall.o + rpctls_syscall.o \ + utimensat2.o Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -321,6 +321,9 @@ int kern_utimensat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, struct timespec *tptr, enum uio_seg tptrseg, int follow); +int kern_utimensat2(struct thread *td, int fd, const char *path, + enum uio_seg pathseg, struct timespec *tptr, int cnt, + 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: sys/sys/sysproto.h =================================================================== --- sys/sys/sysproto.h +++ sys/sys/sysproto.h @@ -1836,6 +1836,13 @@ char op_l_[PADL_(int)]; int op; char op_r_[PADR_(int)]; char path_l_[PADL_(const char *)]; const char * path; char path_r_[PADR_(const char *)]; }; +struct utimensat2_args { + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; + char path_l_[PADL_(char *)]; char * path; char path_r_[PADR_(char *)]; + char times_l_[PADL_(struct timespec *)]; struct timespec * times; char times_r_[PADR_(struct timespec *)]; + char cnt_l_[PADL_(int)]; int cnt; char cnt_r_[PADR_(int)]; + char flag_l_[PADL_(int)]; int flag; char flag_r_[PADR_(int)]; +}; int nosys(struct thread *, struct nosys_args *); void sys_sys_exit(struct thread *, struct sys_exit_args *); int sys_fork(struct thread *, struct fork_args *); @@ -2227,6 +2234,7 @@ int sys___realpathat(struct thread *, struct __realpathat_args *); int sys_close_range(struct thread *, struct close_range_args *); int sys_rpctls_syscall(struct thread *, struct rpctls_syscall_args *); +int sys_utimensat2(struct thread *, struct utimensat2_args *); #ifdef COMPAT_43 @@ -3158,6 +3166,7 @@ #define SYS_AUE___realpathat AUE_REALPATHAT #define SYS_AUE_close_range AUE_CLOSERANGE #define SYS_AUE_rpctls_syscall AUE_NULL +#define SYS_AUE_utimensat2 AUE_FUTIMENSAT2 #undef PAD_ #undef PADL_