From e40f73e4318d58df0894a1e4b98ab72121e8df75 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 20 May 2015 13:53:19 +0100 Subject: [PATCH] Add utimensat() support There is more work to be done in this area, namely adding support for futimens(2) and refactoring some common code out into separate functions. --- lib/libc/sys/Makefile.inc | 2 +- lib/libc/sys/utimensat.2 | 252 ++++++++++++++++++++++++++++++++++++++++++++++ sys/kern/init_sysent.c | 1 + sys/kern/syscalls.c | 1 + sys/kern/syscalls.master | 1 + sys/kern/vfs_syscalls.c | 84 ++++++++++++++++ sys/sys/kern_syscall.h | 2 + sys/sys/stat.h | 6 ++ sys/sys/syscall.h | 3 +- sys/sys/syscall.mk | 3 +- sys/sys/sysproto.h | 10 ++ sys/sys/sysunion.h | 1 + 12 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 lib/libc/sys/utimensat.2 diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 57b1415..8596479 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -92,7 +92,7 @@ MAN+= _exit.2 accept.2 access.2 acct.2 adjtime.2 \ sigstack.2 sigsuspend.2 socket.2 socketpair.2 stat.2 statfs.2 \ statvfs.2 swapon.2 symlink.2 sync.2 sysarch.2 syscall.2 \ truncate.2 tls.2 umask.2 umtx.2 undelete.2 \ - unlink.2 unlinkat.2 utimes.2 utrace.2 usched_set.2 \ + unlink.2 unlinkat.2 utimensat.2 utimes.2 utrace.2 usched_set.2 \ uuidgen.2 \ varsym.2 vfork.2 vquotactl.2 wait.2 write.2 diff --git a/lib/libc/sys/utimensat.2 b/lib/libc/sys/utimensat.2 new file mode 100644 index 0000000..9cde4ca --- /dev/null +++ b/lib/libc/sys/utimensat.2 @@ -0,0 +1,252 @@ +.\" $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 May 20, 2015 +.Dt UTIMENSAT 2 +.Os +.Sh NAME +.Nm utimensat +.Nd set file access and modification times +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/stat.h +.Ft int +.Fo utimensat +.Fa "int fd" +.Fa "const char *path" +.Fa "const struct timespec times[2]" +.Fa "int flags" +.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. +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 ERRORS +This system call 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 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 utimensat +system call is expected to conform to +.St -p1003.1-2008 . +.Sh HISTORY +The +.Fn utimensat +system call appeared in +.Dx 4.1 . diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index ef08a77..0d6714b 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -575,4 +575,5 @@ struct sysent sysent[] = { { AS(procctl_args), (sy_call_t *)sys_procctl }, /* 536 = procctl */ { AS(chflagsat_args), (sy_call_t *)sys_chflagsat }, /* 537 = chflagsat */ { AS(pipe2_args), (sy_call_t *)sys_pipe2 }, /* 538 = pipe2 */ + { AS(utimensat_args), (sy_call_t *)sys_utimensat }, /* 539 = utimensat */ }; diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index eddd64b..db6d668 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -547,4 +547,5 @@ const char *syscallnames[] = { "procctl", /* 536 = procctl */ "chflagsat", /* 537 = chflagsat */ "pipe2", /* 538 = pipe2 */ + "utimensat", /* 539 = utimensat */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 462cce6..d430ad8 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -745,3 +745,4 @@ 536 STD { int procctl(idtype_t idtype, id_t id, int cmd, void *data); } 537 STD { int chflagsat(int fd, const char *path, int flags, int atflags);} 538 STD { int pipe2(int *fildes, int flags); } +539 STD { int utimensat(int fd, const char *path, const struct timespec *ts, int flags); } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index b95a3b4..2833302 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -3562,6 +3562,90 @@ sys_futimes(struct futimes_args *uap) } int +kern_utimensat(struct nlookupdata *nd, int fd, const char *path, + const struct timespec *usrts, int flags) +{ + struct timespec ts[2], tsnow; + struct vnode *vp; + struct vattr vattr; + int nullflag = 0; + int error; + + if (flags & ~AT_SYMLINK_NOFOLLOW) + return (EINVAL); + + nanotime(&tsnow); + if (!usrts) { + ts[0] = tsnow; + ts[1] = tsnow; + nullflag = 1; + } else { + error = copyin(usrts, ts, sizeof(ts)); + if (error) + return (error); + + if (ts[0].tv_nsec == UTIME_OMIT && ts[1].tv_nsec == UTIME_OMIT) + return 0; + if (ts[0].tv_nsec == UTIME_NOW && ts[1].tv_nsec == UTIME_NOW) + nullflag = 1; + + if (ts[0].tv_nsec == UTIME_OMIT) + ts[0].tv_sec = VNOVAL; + else if (ts[0].tv_nsec == UTIME_NOW) + ts[0] = tsnow; + else if (ts[0].tv_nsec < 0 || ts[0].tv_nsec >= 1000000000ULL) + return (EINVAL); + + if (ts[1].tv_nsec == UTIME_OMIT) + ts[1].tv_sec = VNOVAL; + else if (ts[1].tv_nsec == UTIME_NOW) + ts[1] = tsnow; + else if (ts[1].tv_nsec < 0 || ts[1].tv_nsec >= 1000000000ULL) + return (EINVAL); + } + + nd->nl_flags |= NLC_OWN | NLC_WRITE; + if ((error = nlookup(nd)) != 0) + return (error); + if ((error = ncp_writechk(&nd->nl_nch)) != 0) + return (error); + if ((error = cache_vref(&nd->nl_nch, nd->nl_cred, &vp)) != 0) + return (error); + if ((error = vn_writechk(vp, &nd->nl_nch)) == 0) { + error = vget(vp, LK_EXCLUSIVE); + if (error == 0) { + error = setutimes(vp, &vattr, ts, nullflag); + vput(vp); + } + } + vrele(vp); + return (error); +} + +/* + * utimensat_args(int fd, const char *path, const struct timespec *ts, int flags); + * + * Set file access and modification times of a file. + */ +int +sys_utimensat(struct utimensat_args *uap) +{ + struct nlookupdata nd; + struct file *fp; + int error; + int flags; + + flags = (uap->flags & AT_SYMLINK_NOFOLLOW) ? 0 : NLC_FOLLOW; + error = nlookup_init_at(&nd, &fp, uap->fd, uap->path, + UIO_USERSPACE, flags); + if (error == 0) + error = kern_utimensat(&nd, uap->fd, uap->path, + uap->ts, uap->flags); + nlookup_done_at(&nd, fp); + return (error); +} + +int kern_truncate(struct nlookupdata *nd, off_t length) { struct vnode *vp; diff --git a/sys/sys/kern_syscall.h b/sys/sys/kern_syscall.h index dbef8c6..ed4fce8 100644 --- a/sys/sys/kern_syscall.h +++ b/sys/sys/kern_syscall.h @@ -167,6 +167,8 @@ int kern_statvfs(struct nlookupdata *nd, struct statvfs *buf); int kern_symlink(struct nlookupdata *nd, char *path, int mode); int kern_truncate(struct nlookupdata *nd, off_t length); int kern_unlink(struct nlookupdata *nd); +int kern_utimensat(struct nlookupdata *nd, int fd, const char *path, + const struct timespec *ts, int flag); int kern_utimes(struct nlookupdata *nd, struct timeval *tptr); struct uuid *kern_uuidgen(struct uuid *store, size_t count); diff --git a/sys/sys/stat.h b/sys/sys/stat.h index d653ffa..74b376f 100644 --- a/sys/sys/stat.h +++ b/sys/sys/stat.h @@ -207,6 +207,11 @@ struct stat { #endif /* !_POSIX_SOURCE */ +#if __POSIX_VISIBLE >= 200809 +#define UTIME_NOW -1 +#define UTIME_OMIT -2 +#endif + #if !defined(_KERNEL) || defined(_KERNEL_VIRTUAL) #include @@ -214,6 +219,7 @@ __BEGIN_DECLS int chmod (const char *, mode_t); #if __POSIX_VISIBLE >= 200809 int fchmodat (int, const char *, mode_t, int); +int utimensat(int, const char *, const struct timespec *, int); #endif int fstat (int, struct stat *); int mkdir (const char *, mode_t); diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index fcaf955..93e47f5 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -378,4 +378,5 @@ #define SYS_procctl 536 #define SYS_chflagsat 537 #define SYS_pipe2 538 -#define SYS_MAXSYSCALL 539 +#define SYS_utimensat 539 +#define SYS_MAXSYSCALL 540 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index f4cf937..60fa682 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -301,4 +301,5 @@ MIASM = \ vmm_guest_sync_addr.o \ procctl.o \ chflagsat.o \ - pipe2.o + pipe2.o \ + utimensat.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index 1fc7a63..8a585a8 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -2285,6 +2285,15 @@ struct pipe2_args { int * fildes; char fildes_[PAD_(int *)]; int flags; char flags_[PAD_(int)]; }; +struct utimensat_args { +#ifdef _KERNEL + struct sysmsg sysmsg; +#endif + int fd; char fd_[PAD_(int)]; + const char * path; char path_[PAD_(const char *)]; + const struct timespec * ts; char ts_[PAD_(const struct timespec *)]; + int flags; char flags_[PAD_(int)]; +}; #ifdef COMPAT_43 @@ -2893,6 +2902,7 @@ int sys_vmm_guest_sync_addr (struct vmm_guest_sync_addr_args *); int sys_procctl (struct procctl_args *); int sys_chflagsat (struct chflagsat_args *); int sys_pipe2 (struct pipe2_args *); +int sys_utimensat (struct utimensat_args *); #endif /* !_SYS_SYSPROTO_H_ */ #undef PAD_ diff --git a/sys/sys/sysunion.h b/sys/sys/sysunion.h index a12b9a7..447a450 100644 --- a/sys/sys/sysunion.h +++ b/sys/sys/sysunion.h @@ -409,4 +409,5 @@ union sysunion { struct procctl_args procctl; struct chflagsat_args chflagsat; struct pipe2_args pipe2; + struct utimensat_args utimensat; }; -- 2.4.0