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

Reply via email to