I missed one flag: AT_REMOVEDIR. This makes unlinkat() behave the same as rmdir(), and the diff below changes sys_rmdir() to call dounlinkat() using it.
Here's how to read this diff and be convinced it's correct: 1. First read dounlinkat() by itself, and convince yourself that if flag == 0, the behavior is the same as it was already. (The new control flow is a little awkward, but the semantics are the same.) 2. Next compare the resulting code with the code in sys_rmdir(), and notice that if flag == AT_REMOVEDIR, then they're the same. ok? Index: sys/fcntl.h =================================================================== RCS file: /home/mdempsky/anoncvs/cvs/src/sys/sys/fcntl.h,v retrieving revision 1.14 diff -u -p -r1.14 fcntl.h --- sys/fcntl.h 8 Jul 2011 04:23:24 -0000 1.14 +++ sys/fcntl.h 8 Jul 2011 10:05:05 -0000 @@ -194,6 +194,7 @@ struct flock { #define AT_EACCESS 0x01 #define AT_SYMLINK_NOFOLLOW 0x02 #define AT_SYMLINK_FOLLOW 0x04 +#define AT_REMOVEDIR 0x08 #endif #ifndef _KERNEL Index: kern/vfs_syscalls.c =================================================================== RCS file: /home/mdempsky/anoncvs/cvs/src/sys/kern/vfs_syscalls.c,v retrieving revision 1.170 diff -u -p -r1.170 vfs_syscalls.c --- kern/vfs_syscalls.c 8 Jul 2011 04:23:24 -0000 1.170 +++ kern/vfs_syscalls.c 8 Jul 2011 10:02:06 -0000 @@ -1484,9 +1484,8 @@ dounlinkat(struct proc *p, int fd, const int error; struct nameidata nd; - /* XXX: Support AT_REMOVEDIR. */ - if (flag != 0) - return (ENOTSUP); + if (flag & ~AT_REMOVEDIR) + return (EINVAL); NDINITAT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, fd, path, p); @@ -1494,24 +1493,41 @@ dounlinkat(struct proc *p, int fd, const return (error); vp = nd.ni_vp; + if (flag & AT_REMOVEDIR) { + if (vp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + /* + * No rmdir "." please. + */ + if (nd.ni_dvp == vp) { + error = EBUSY; + goto out; + } + } + /* * The root of a mounted filesystem cannot be deleted. */ - if (vp->v_flag & VROOT) { + if (vp->v_flag & VROOT) + error = EBUSY; +out: + if (!error) { + if (flag & AT_REMOVEDIR) { + error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + } else { + (void)uvm_vnp_uncache(vp); + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + } + } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); - error = EBUSY; - goto out; } - - (void)uvm_vnp_uncache(vp); - - error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); -out: return (error); } @@ -2614,43 +2630,9 @@ sys_rmdir(struct proc *p, void *v, regis struct sys_rmdir_args /* { syscallarg(const char *) path; } */ *uap = v; - struct vnode *vp; - int error; - struct nameidata nd; - NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, - SCARG(uap, path), p); - if ((error = namei(&nd)) != 0) - return (error); - vp = nd.ni_vp; - if (vp->v_type != VDIR) { - error = ENOTDIR; - goto out; - } - /* - * No rmdir "." please. - */ - if (nd.ni_dvp == vp) { - error = EBUSY; - goto out; - } - /* - * The root of a mounted filesystem cannot be deleted. - */ - if (vp->v_flag & VROOT) - error = EBUSY; -out: - if (!error) { - error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vput(vp); - } - return (error); + return (dounlinkat(p, AT_FDCWD, SCARG(uap, path), AT_REMOVEDIR, + retval)); } /*