diff options
author | jilles <jilles@FreeBSD.org> | 2015-01-23 21:07:08 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2015-01-23 21:07:08 +0000 |
commit | 67db24d0f2b81a222335fe5bb13977ef0c2258a9 (patch) | |
tree | 319d8eb9a9510cbe3ee4c7aa7b4dfcc2f42ec514 /sys | |
parent | 4743c20fb6384559028572e519b637c0667e87ec (diff) | |
download | FreeBSD-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.c | 43 | ||||
-rw-r--r-- | sys/compat/freebsd32/syscalls.master | 5 | ||||
-rw-r--r-- | sys/kern/capabilities.conf | 4 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 5 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 126 | ||||
-rw-r--r-- | sys/sys/capsicum.h | 4 | ||||
-rw-r--r-- | sys/sys/param.h | 2 | ||||
-rw-r--r-- | sys/sys/stat.h | 8 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 5 |
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, |