Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -400,6 +400,7 @@ statfs; cpuset_getdomain; cpuset_setdomain; + unlinkfd; }; FBSDprivate_1.0 { Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ sys/compat/freebsd32/syscalls.master @@ -1123,5 +1123,6 @@ cpuwhich_t which, uint32_t id1, uint32_t id2, \ size_t domainsetsize, domainset_t *mask, \ int policy); } +563 AUE_NULL STD { int unlinkfd(int fd, char *path); } ; vim: syntax=off Index: sys/kern/capabilities.conf =================================================================== --- sys/kern/capabilities.conf +++ sys/kern/capabilities.conf @@ -463,6 +463,7 @@ renameat symlinkat unlinkat +unlinkfd utimensat ## Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -1027,6 +1027,7 @@ cpuwhich_t which, id_t id, \ size_t domainsetsize, domainset_t *mask, \ int policy); } +563 AUE_NULL STD { int unlinkfd(int fd, char *path); } ; 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 @@ -1763,11 +1763,53 @@ return (kern_unlinkat(td, fd, path, UIO_USERSPACE, 0)); } +static int +unlink_vnode(struct thread *td, struct nameidata *nd, struct vnode *vp) +{ + int error; + struct mount *mp; + + /* + * The root of a mounted filesystem cannot be deleted. + * + * XXX: can this only be a VDIR case? + */ + if (vp->v_vflag & VV_ROOT) { + error = EBUSY; + } else { + if (vn_start_write(nd->ni_dvp, &mp, V_NOWAIT) != 0) { + NDFREE(nd, NDF_ONLY_PNBUF); + vput(nd->ni_dvp); + if (vp == nd->ni_dvp) + vrele(vp); + else + vput(vp); + if ((error = vn_start_write(NULL, &mp, + V_XSLEEP | PCATCH)) != 0) + return (error); + return (EINTR); + } +#ifdef MAC + error = mac_vnode_check_unlink(td->td_ucred, nd->ni_dvp, vp, + &nd->ni_cnd); + if (error != 0) + goto out; +#endif + vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); + error = VOP_REMOVE(nd->ni_dvp, vp, &nd->ni_cnd); +#ifdef MAC +out: +#endif + vn_finished_write(mp); + } + + return (error); +} + int kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, ino_t oldinum) { - struct mount *mp; struct vnode *vp; struct nameidata nd; struct stat sb; @@ -1787,41 +1829,14 @@ ((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) && sb.st_ino != oldinum) { error = EIDRM; /* Identifier removed */ - } else { - /* - * The root of a mounted filesystem cannot be deleted. - * - * XXX: can this only be a VDIR case? - */ - if (vp->v_vflag & VV_ROOT) - error = EBUSY; } + if (error == 0) { - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - if (vp == nd.ni_dvp) - vrele(vp); - else - vput(vp); - if ((error = vn_start_write(NULL, &mp, - V_XSLEEP | PCATCH)) != 0) - return (error); + error = unlink_vnode(td, &nd, vp); + if (error == EINTR) goto restart; - } -#ifdef MAC - error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp, - &nd.ni_cnd); - if (error != 0) - goto out; -#endif - vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK); - error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); -#ifdef MAC -out: -#endif - vn_finished_write(mp); } + NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (vp == nd.ni_dvp) @@ -1831,6 +1846,68 @@ return (error); } +#ifndef _SYS_SYSPROTO_H_ +struct unlinkfd_args { + int fd; + char *path; +}; +#endif +int +sys_unlinkfd(struct thread *td, struct unlinkfd_args *uap) +{ + int fd = uap->fd; + char *path = uap->path; + + return (kern_unlinkfd(td, fd, path)); +} + +int +kern_unlinkfd(struct thread *td, int fd, char *path) +{ + struct nameidata nd; + struct file *fp; + struct vnode *vfdp, *vp; + cap_rights_t rights; + int error; + + error = getvnode(td, fd, cap_rights_init(&rights, CAP_UNLINKFD), &fp); + if (error != 0) + return (error); + vfdp = fp->f_vnode; + + if (vfdp->v_type == VDIR) { + error = EPERM; + goto out; + } + +restart: + bwillwrite(); + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, + path, td); + error = namei(&nd); + if (error != 0) { + if (error == EINVAL) + error = EPERM; + goto out; + } + vp = nd.ni_vp; + if (error == 0) { + if (vfdp == vp) { + error = unlink_vnode(td, &nd, vp); + if (error == EINTR) + goto restart; + } else { + error = EINVAL; + } + } + NDFREE(&nd, NDF_ONLY_PNBUF); + vput(nd.ni_dvp); + vput(vp); +out: + fdrop(fp, td); + return (error); +} + /* * Reposition read/write file offset. */ Index: sys/sys/capsicum.h =================================================================== --- sys/sys/capsicum.h +++ sys/sys/capsicum.h @@ -169,6 +169,8 @@ * will be removed. */ #define CAP_UNLINKAT (CAP_LOOKUP | 0x0000000010000000ULL) +/* Allows unlinkfd. */ +#define CAP_UNLINKFD (CAP_LOOKUP | 0x0000080000000000ULL) /* Socket operations. */ /* Allows for accept(2) and accept4(2). */ @@ -213,10 +215,10 @@ CAP_SETSOCKOPT | CAP_SHUTDOWN) /* All used bits for index 0. */ -#define CAP_ALL0 CAPRIGHT(0, 0x000007FFFFFFFFFFULL) +#define CAP_ALL0 CAPRIGHT(0, 0x00000FFFFFFFFFFFULL) /* Available bits for index 0. */ -#define CAP_UNUSED0_44 CAPRIGHT(0, 0x0000080000000000ULL) +#define CAP_UNUSED0_45 CAPRIGHT(0, 0x0000100000000000ULL) /* ... */ #define CAP_UNUSED0_57 CAPRIGHT(0, 0x0100000000000000ULL) Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -285,6 +285,7 @@ off_t length); int kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, ino_t oldinum); +int kern_unlinkfd(struct thread *td, int fd, char *path); 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,