diff options
-rw-r--r-- | include/unistd.h | 1 | ||||
-rw-r--r-- | lib/libc/sys/Makefile.inc | 1 | ||||
-rw-r--r-- | lib/libc/sys/Symbol.map | 1 | ||||
-rw-r--r-- | lib/libc/sys/unlink.2 | 41 | ||||
-rw-r--r-- | sys/cddl/compat/opensolaris/sys/vnode.h | 3 | ||||
-rw-r--r-- | sys/compat/cloudabi/cloudabi_file.c | 6 | ||||
-rw-r--r-- | sys/compat/freebsd32/syscalls.master | 2 | ||||
-rw-r--r-- | sys/compat/linux/linux_file.c | 9 | ||||
-rw-r--r-- | sys/kern/capabilities.conf | 1 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 8 | ||||
-rw-r--r-- | sys/kern/vfs_mountroot.c | 2 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 115 | ||||
-rw-r--r-- | sys/sys/fcntl.h | 10 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 4 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 7 |
15 files changed, 170 insertions, 41 deletions
diff --git a/include/unistd.h b/include/unistd.h index 835a6dffc471..091ff16f4f92 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -585,6 +585,7 @@ off_t __syscall(quad_t, ...); int undelete(const char *); int unwhiteout(const char *); void *valloc(size_t); /* obsoleted by malloc() */ +int funlinkat(int, const char *, int, int); #ifndef _OPTRESET_DECLARED #define _OPTRESET_DECLARED diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index bc8299eea8f2..22737fcb8949 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -485,6 +485,7 @@ MLINKS+=timer_settime.2 timer_getoverrun.2 \ MLINKS+=thr_kill.2 thr_kill2.2 MLINKS+=truncate.2 ftruncate.2 MLINKS+=unlink.2 unlinkat.2 +MLINKS+=unlink.2 funlinkat.2 MLINKS+=utimensat.2 futimens.2 MLINKS+=utimes.2 futimes.2 \ utimes.2 futimesat.2 \ diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index a48c693575f8..74b68afdedef 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -406,6 +406,7 @@ FBSD_1.6 { fhlinkat; fhreadlink; getfhat; + funlinkat; }; FBSDprivate_1.0 { diff --git a/lib/libc/sys/unlink.2 b/lib/libc/sys/unlink.2 index c6c6a150c1a2..83823f1d78ae 100644 --- a/lib/libc/sys/unlink.2 +++ b/lib/libc/sys/unlink.2 @@ -28,7 +28,7 @@ .\" @(#)unlink.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd November 11, 2018 +.Dd April 6, 2019 .Dt UNLINK 2 .Os .Sh NAME @@ -42,7 +42,9 @@ .Ft int .Fn unlink "const char *path" .Ft int -.Fn unlinkat "int fd" "const char *path" "int flag" +.Fn unlinkat "int dfd" "const char *path" "int flag" +.Ft int +.Fn funlinkat "int dfd" "const char *path" "int fd" "int flag" .Sh DESCRIPTION The .Fn unlink @@ -74,7 +76,7 @@ except in the case where specifies a relative path. In this case the directory entry to be removed is determined relative to the directory associated with the file descriptor -.Fa fd +.Fa dfd instead of the current working directory. .Pp The values for @@ -113,6 +115,26 @@ or respectively, depending on whether or not the .Dv AT_REMOVEDIR bit is set in flag. +.Pp +The +.Fn funlinkat +system call can be used to unlink an already-opened file, unless that +file has been replaced since it was opened. +It is equivalent to +.Fn unlinkat +in the case where +.Fa path +is already open as the file descriptor +.Fa fd . +Otherwise, the path will not be removed and an error will be returned. +The +.Fa fd +can be set the +.Dv FD_NONE . +In that case +.Fn funlinkat +behaves exactly like +.Fn unlinkat . .Sh RETURN VALUES .Rv -std unlink .Sh ERRORS @@ -227,6 +249,15 @@ or the relative .Fa path escapes it. .El +.Pp +In addition to the errors returned by +.Fn unlinkat , +.Fn funlinkat +may fail if: +.Bl -tag -width Er +.It Bq Er EDEADLK +The file descriptor is not associated with the path. +.El .Sh SEE ALSO .Xr chflags 2 , .Xr close 2 , @@ -246,6 +277,10 @@ The .Fn unlinkat system call appeared in .Fx 8.0 . +The +.Fn funlinkat +system call appeared in +.Fx 13.0 . .Pp The .Fn unlink diff --git a/sys/cddl/compat/opensolaris/sys/vnode.h b/sys/cddl/compat/opensolaris/sys/vnode.h index 54e78f2ffe9f..b337f96e9ae7 100644 --- a/sys/cddl/compat/opensolaris/sys/vnode.h +++ b/sys/cddl/compat/opensolaris/sys/vnode.h @@ -278,7 +278,8 @@ vn_remove(char *fnamep, enum uio_seg seg, enum rm dirflag) ASSERT(seg == UIO_SYSSPACE); ASSERT(dirflag == RMFILE); - return (kern_unlinkat(curthread, AT_FDCWD, fnamep, seg, 0, 0)); + return (kern_funlinkat(curthread, AT_FDCWD, fnamep, FD_NONE, seg, 0, + 0)); } #endif /* _KERNEL */ diff --git a/sys/compat/cloudabi/cloudabi_file.c b/sys/compat/cloudabi/cloudabi_file.c index 602ae5042962..0b5231bb885b 100644 --- a/sys/compat/cloudabi/cloudabi_file.c +++ b/sys/compat/cloudabi/cloudabi_file.c @@ -752,9 +752,11 @@ cloudabi_sys_file_unlink(struct thread *td, return (error); if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) - error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE, 0); + error = kern_frmdirat(td, uap->fd, path, FD_NONE, + UIO_SYSSPACE, 0); else - error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0, 0); + error = kern_funlinkat(td, uap->fd, path, FD_NONE, + UIO_SYSSPACE, 0, 0); cloudabi_freestr(path); return (error); } diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 26e2432c300a..94bea54d84c3 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -1145,5 +1145,7 @@ const char *to); } 567 AUE_NULL NOPROTO { int fhreadlink( struct fhandle *fhp, char *buf, \ size_t bufsize); } +568 AUE_UNLINKAT NOPROTO { int funlinkat(int dfd, const char *path, int fd, \ + int flag); } ; vim: syntax=off diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index ecc95370d4c1..e27f167aaba9 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -590,7 +590,7 @@ linux_unlink(struct thread *td, struct linux_unlink_args *args) printf(ARGS(unlink, "%s"), path); #endif - error = kern_unlinkat(td, AT_FDCWD, path, UIO_SYSSPACE, 0, 0); + error = kern_funlinkat(td, AT_FDCWD, path, FD_NONE, UIO_SYSSPACE, 0, 0); if (error == EPERM) { /* Introduce POSIX noncompliant behaviour of Linux */ if (kern_statat(td, 0, AT_FDCWD, path, UIO_SYSSPACE, &st, @@ -623,9 +623,10 @@ linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args) #endif if (args->flag & LINUX_AT_REMOVEDIR) - error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE, 0); + error = kern_frmdirat(td, dfd, path, FD_NONE, UIO_SYSSPACE, 0); else - error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE, 0, 0); + error = kern_funlinkat(td, dfd, path, FD_NONE, UIO_SYSSPACE, 0, + 0); if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) { /* Introduce POSIX noncompliant behaviour of Linux */ if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path, @@ -741,7 +742,7 @@ linux_rmdir(struct thread *td, struct linux_rmdir_args *args) if (ldebug(rmdir)) printf(ARGS(rmdir, "%s"), path); #endif - error = kern_rmdirat(td, AT_FDCWD, path, UIO_SYSSPACE, 0); + error = kern_frmdirat(td, AT_FDCWD, path, FD_NONE, UIO_SYSSPACE, 0); LFREEPATH(path); return (error); } diff --git a/sys/kern/capabilities.conf b/sys/kern/capabilities.conf index b5198b7a341c..927f5f66d07e 100644 --- a/sys/kern/capabilities.conf +++ b/sys/kern/capabilities.conf @@ -468,6 +468,7 @@ readlinkat renameat symlinkat unlinkat +funlinkat utimensat ## diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index e9aae2c4c395..57af35814f64 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -3167,6 +3167,14 @@ size_t bufsize ); } +568 AUE_UNLINKAT STD { + int funlinkat( + int dfd, + _In_z_ const char *path, + int fd, + int flag + ); + } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/kern/vfs_mountroot.c b/sys/kern/vfs_mountroot.c index 20ddeb63db2b..200015b4d016 100644 --- a/sys/kern/vfs_mountroot.c +++ b/sys/kern/vfs_mountroot.c @@ -389,7 +389,7 @@ vfs_mountroot_shuffle(struct thread *td, struct mount *mpdevfs) if (mporoot == mpdevfs) { vfs_unbusy(mpdevfs); /* Unlink the no longer needed /dev/dev -> / symlink */ - error = kern_unlinkat(td, AT_FDCWD, "/dev/dev", + error = kern_funlinkat(td, AT_FDCWD, "/dev/dev", FD_NONE, UIO_SYSSPACE, 0, 0); if (error) printf("mountroot: unable to unlink /dev/dev " diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index ef01081601f2..49db5b350cd3 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1751,7 +1751,22 @@ int sys_unlink(struct thread *td, struct unlink_args *uap) { - return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0, 0)); + return (kern_funlinkat(td, AT_FDCWD, uap->path, FD_NONE, UIO_USERSPACE, + 0, 0)); +} + +static int +kern_funlinkat_ex(struct thread *td, int dfd, const char *path, int fd, + int flag, enum uio_seg pathseg, ino_t oldinum) +{ + + if ((flag & ~AT_REMOVEDIR) != 0) + return (EINVAL); + + if ((flag & AT_REMOVEDIR) != 0) + return (kern_frmdirat(td, dfd, path, fd, UIO_USERSPACE, 0)); + + return (kern_funlinkat(td, dfd, path, fd, UIO_USERSPACE, 0, 0)); } #ifndef _SYS_SYSPROTO_H_ @@ -1764,46 +1779,69 @@ struct unlinkat_args { int sys_unlinkat(struct thread *td, struct unlinkat_args *uap) { - int fd, flag; - const char *path; - flag = uap->flag; - fd = uap->fd; - path = uap->path; + return (kern_funlinkat_ex(td, uap->fd, uap->path, FD_NONE, uap->flag, + UIO_USERSPACE, 0)); +} - if ((flag & ~(AT_REMOVEDIR | AT_BENEATH)) != 0) - return (EINVAL); +#ifndef _SYS_SYSPROTO_H_ +struct funlinkat_args { + int dfd; + const char *path; + int fd; + int flag; +}; +#endif +int +sys_funlinkat(struct thread *td, struct funlinkat_args *uap) +{ - if ((uap->flag & AT_REMOVEDIR) != 0) - return (kern_rmdirat(td, fd, path, UIO_USERSPACE, flag)); - else - return (kern_unlinkat(td, fd, path, UIO_USERSPACE, flag, 0)); + return (kern_funlinkat_ex(td, uap->dfd, uap->path, uap->fd, uap->flag, + UIO_USERSPACE, 0)); } int -kern_unlinkat(struct thread *td, int fd, const char *path, +kern_funlinkat(struct thread *td, int dfd, const char *path, int fd, enum uio_seg pathseg, int flag, ino_t oldinum) { struct mount *mp; + struct file *fp; struct vnode *vp; struct nameidata nd; struct stat sb; + cap_rights_t rights; int error; + fp = NULL; + if (fd != FD_NONE) { + error = getvnode(td, fd, cap_rights_init(&rights, CAP_LOOKUP), + &fp); + if (error != 0) + return (error); + } + restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), - pathseg, path, fd, &cap_unlinkat_rights, td); - if ((error = namei(&nd)) != 0) - return (error == EINVAL ? EPERM : error); + pathseg, path, dfd, &cap_unlinkat_rights, td); + if ((error = namei(&nd)) != 0) { + if (error == EINVAL) + error = EPERM; + goto fdout; + } vp = nd.ni_vp; if (vp->v_type == VDIR && oldinum == 0) { error = EPERM; /* POSIX */ } else if (oldinum != 0 && ((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) && sb.st_ino != oldinum) { - error = EIDRM; /* Identifier removed */ + error = EIDRM; /* Identifier removed */ + } else if (fp != NULL && fp->f_vnode != vp) { + if ((fp->f_vnode->v_iflag & VI_DOOMED) != 0) + error = EBADF; + else + error = EDEADLK; } else { /* * The root of a mounted filesystem cannot be deleted. @@ -1822,8 +1860,9 @@ restart: else vput(vp); if ((error = vn_start_write(NULL, &mp, - V_XSLEEP | PCATCH)) != 0) - return (error); + V_XSLEEP | PCATCH)) != 0) { + goto fdout; + } goto restart; } #ifdef MAC @@ -1845,6 +1884,9 @@ out: vrele(vp); else vput(vp); +fdout: + if (fp != NULL) + fdrop(fp, td); return (error); } @@ -3704,25 +3746,36 @@ int sys_rmdir(struct thread *td, struct rmdir_args *uap) { - return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0)); + return (kern_frmdirat(td, AT_FDCWD, uap->path, FD_NONE, UIO_USERSPACE, + 0)); } int -kern_rmdirat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, - int flag) +kern_frmdirat(struct thread *td, int dfd, const char *path, int fd, + enum uio_seg pathseg, int flag) { struct mount *mp; struct vnode *vp; + struct file *fp; struct nameidata nd; + cap_rights_t rights; int error; + fp = NULL; + if (fd != FD_NONE) { + error = getvnode(td, fd, cap_rights_init(&rights, CAP_LOOKUP), + &fp); + if (error != 0) + return (error); + } + restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), - pathseg, path, fd, &cap_unlinkat_rights, td); + pathseg, path, dfd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) - return (error); + goto fdout; vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; @@ -3742,6 +3795,15 @@ restart: error = EBUSY; goto out; } + + if (fp != NULL && fp->f_vnode != vp) { + if ((fp->f_vnode->v_iflag & VI_DOOMED) != 0) + error = EBADF; + else + error = EDEADLK; + goto out; + } + #ifdef MAC error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp, &nd.ni_cnd); @@ -3756,7 +3818,7 @@ restart: else vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - return (error); + goto fdout; goto restart; } vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); @@ -3769,6 +3831,9 @@ out: vrele(nd.ni_dvp); else vput(nd.ni_dvp); +fdout: + if (fp != NULL) + fdrop(fp, td); return (error); } diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h index 04eab5e27ed1..b2e7f94199d0 100644 --- a/sys/sys/fcntl.h +++ b/sys/sys/fcntl.h @@ -316,6 +316,16 @@ struct __oflock { #define POSIX_FADV_NOREUSE 5 /* access data only once */ #endif + +#ifdef __BSD_VISIBLE +/* + * Magic value that specify that corresponding file descriptor to filename + * is unknown and sanitary check should be omitted in the funlinkat() and + * similar syscalls. + */ +#define FD_NONE -200 +#endif + #ifndef _KERNEL __BEGIN_DECLS int open(const char *, int, ...); diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 3545745f5a7f..f82500087f08 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -218,7 +218,7 @@ int kern_recvit(struct thread *td, int s, struct msghdr *mp, enum uio_seg fromseg, struct mbuf **controlp); int kern_renameat(struct thread *td, int oldfd, const char *old, int newfd, const char *new, enum uio_seg pathseg); -int kern_rmdirat(struct thread *td, int fd, const char *path, +int kern_frmdirat(struct thread *td, int dfd, const char *path, int fd, enum uio_seg pathseg, int flag); int kern_sched_getparam(struct thread *td, struct thread *targettd, struct sched_param *param); @@ -285,7 +285,7 @@ int kern_thr_new(struct thread *td, struct thr_param *param); int kern_thr_suspend(struct thread *td, struct timespec *tsp); int kern_truncate(struct thread *td, const char *path, enum uio_seg pathseg, off_t length); -int kern_unlinkat(struct thread *td, int fd, const char *path, +int kern_funlinkat(struct thread *td, int dfd, const char *path, int fd, enum uio_seg pathseg, int flag, ino_t oldinum); int kern_utimesat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg); diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index f4bdd5789ab5..7882a86ebbce 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -3431,14 +3431,15 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) } #endif /* DEBUG */ /* - * kern_unlinkat will do its own start/finish writes and + * kern_funlinkat will do its own start/finish writes and * they do not nest, so drop ours here. Setting mp == NULL * indicates that vn_finished_write is not needed down below. */ vn_finished_write(mp); mp = NULL; - error = kern_unlinkat(td, AT_FDCWD, (char *)(intptr_t)cmd.value, - UIO_USERSPACE, 0, (ino_t)cmd.size); + error = kern_funlinkat(td, AT_FDCWD, + (char *)(intptr_t)cmd.value, FD_NONE, UIO_USERSPACE, + 0, (ino_t)cmd.size); break; case FFS_SET_INODE: |