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));
}
/*