tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

locking patches for ufs_rename



The attached patches correct the locking in ufs_rename and
ufs_wapbl_rename. They have been tested lightly and work in the simple
cases; I will beat on them harder before committing.

The first patch is naming reform to abolish the alphabet soup of tdvp,
tvp, tdp, ip, xp, and so forth. This has been checked to make only
very minor changes to the generated .o files.

The second patch tidies up some formatting problems left behind by the
longer symbol names in the first patch; this makes slightly less minor
changes to the generated .o files.

The third patch rototills ufs_rename and ufs_wapbl_rename to behave
sensibly with respect to locking, or at least as sensibly as is
currently possible without reworking the VOP_LOOKUP locking model.

Since I've pretty much rewritten the whole thing the diffs may not be
the easiest to read, so I've also included copies of the updated
functions, plus the new ufs_parentcheck() that replaces ufs_checkpath().

Any testing or auditing/reviewing would be appreciated.

-- 
David A. Holland
dholland%netbsd.org@localhost
Name reform inside ufs_rename (and the wapbl copy thereof).

   vanilla  wapbl     new

   fdvp     fdvp  ->  fromparent_vnode
   fvp      fvp   ->  fromchild_vnode
   tdvp     tdvp  ->  toparent_vnode
   tvp      tvp   ->  tochild_vnode

   fcnp     fcnp  ->  from_name
   tcnp     tcnp  ->  to_name

   dp       fdp   ->  fromparent_i
   dp       tdp   ->  toparent_i
   ip       ip    ->  fromchild_i
   xp       fxp   ->  fromchild_relookup_i
   xp       txp   ->  tochild_i

(The last set are called _i rather than _inode because they're easier
to visually distinguish from _vnode that way.)

Object file diffs have been scrutinized. They're not quite empty
unfortunately because some of these symbol names are stringized.

This changeset creates long lines that I'll rectify in the next
commit; enough line numbers appear to be compiled into the files to
make it desirable to examine the object diffs separately.

diff -r 5c163770c43e sys/ufs/ufs/ufs_vnops.c
--- a/sys/ufs/ufs/ufs_vnops.c   Sun Sep 27 13:30:45 2009 -0400
+++ b/sys/ufs/ufs/ufs_vnops.c   Sun Sep 27 23:15:37 2009 -0400
@@ -964,9 +964,9 @@
                struct vnode            *a_tvp;
                struct componentname    *a_tcnp;
        } */ *ap = v;
-       struct vnode            *tvp, *tdvp, *fvp, *fdvp;
-       struct componentname    *tcnp, *fcnp;
-       struct inode            *ip, *xp, *dp;
+       struct vnode            *tochild_vnode, *toparent_vnode, 
*fromchild_vnode, *fromparent_vnode;
+       struct componentname    *to_name, *from_name;
+       struct inode            *fromchild_i, *fromchild_relookup_i, 
*tochild_i, *toparent_i, *fromparent_i;
        struct mount            *mp;
        struct direct           *newdir;
        int                     doingdirectory, oldparent, newparent, error;
@@ -976,114 +976,114 @@
                return wapbl_ufs_rename(v);
 #endif
 
-       tvp = ap->a_tvp;
-       tdvp = ap->a_tdvp;
-       fvp = ap->a_fvp;
-       fdvp = ap->a_fdvp;
-       tcnp = ap->a_tcnp;
-       fcnp = ap->a_fcnp;
+       tochild_vnode = ap->a_tvp;
+       toparent_vnode = ap->a_tdvp;
+       fromchild_vnode = ap->a_fvp;
+       fromparent_vnode = ap->a_fdvp;
+       to_name = ap->a_tcnp;
+       from_name = ap->a_fcnp;
        doingdirectory = oldparent = newparent = error = 0;
 
 #ifdef DIAGNOSTIC
-       if ((tcnp->cn_flags & HASBUF) == 0 ||
-           (fcnp->cn_flags & HASBUF) == 0)
+       if ((to_name->cn_flags & HASBUF) == 0 ||
+           (from_name->cn_flags & HASBUF) == 0)
                panic("ufs_rename: no name");
 #endif
        /*
         * Check for cross-device rename.
         */
-       if ((fvp->v_mount != tdvp->v_mount) ||
-           (tvp && (fvp->v_mount != tvp->v_mount))) {
+       if ((fromchild_vnode->v_mount != toparent_vnode->v_mount) ||
+           (tochild_vnode && (fromchild_vnode->v_mount != 
tochild_vnode->v_mount))) {
                error = EXDEV;
  abortit:
-               VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
-               if (tdvp == tvp)
-                       vrele(tdvp);
+               VOP_ABORTOP(toparent_vnode, to_name); /* XXX, why not in NFS? */
+               if (toparent_vnode == tochild_vnode)
+                       vrele(toparent_vnode);
                else
-                       vput(tdvp);
-               if (tvp)
-                       vput(tvp);
-               VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
-               vrele(fdvp);
-               vrele(fvp);
+                       vput(toparent_vnode);
+               if (tochild_vnode)
+                       vput(tochild_vnode);
+               VOP_ABORTOP(fromparent_vnode, from_name); /* XXX, why not in 
NFS? */
+               vrele(fromparent_vnode);
+               vrele(fromchild_vnode);
                return (error);
        }
 
        /*
         * Check if just deleting a link name.
         */
-       if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||
-           (VTOI(tdvp)->i_flags & APPEND))) {
+       if (tochild_vnode && ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | 
APPEND)) ||
+           (VTOI(toparent_vnode)->i_flags & APPEND))) {
                error = EPERM;
                goto abortit;
        }
-       if (fvp == tvp) {
-               if (fvp->v_type == VDIR) {
+       if (fromchild_vnode == tochild_vnode) {
+               if (fromchild_vnode->v_type == VDIR) {
                        error = EINVAL;
                        goto abortit;
                }
 
                /* Release destination completely. */
-               VOP_ABORTOP(tdvp, tcnp);
-               vput(tdvp);
-               vput(tvp);
+               VOP_ABORTOP(toparent_vnode, to_name);
+               vput(toparent_vnode);
+               vput(tochild_vnode);
 
                /* Delete source. */
-               vrele(fvp);
-               fcnp->cn_flags &= ~(MODMASK | SAVESTART);
-               fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
-               fcnp->cn_nameiop = DELETE;
-               vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fdvp, &fvp, fcnp))) {
-                       vput(fdvp);
+               vrele(fromchild_vnode);
+               from_name->cn_flags &= ~(MODMASK | SAVESTART);
+               from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
+               from_name->cn_nameiop = DELETE;
+               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               if ((error = relookup(fromparent_vnode, &fromchild_vnode, 
from_name))) {
+                       vput(fromparent_vnode);
                        return (error);
                }
-               return (VOP_REMOVE(fdvp, fvp, fcnp));
+               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode, 
from_name));
        }
-       if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
+       if ((error = vn_lock(fromchild_vnode, LK_EXCLUSIVE)) != 0)
                goto abortit;
-       dp = VTOI(fdvp);
-       ip = VTOI(fvp);
-       if ((nlink_t) ip->i_nlink >= LINK_MAX) {
-               VOP_UNLOCK(fvp, 0);
+       fromparent_i = VTOI(fromparent_vnode);
+       fromchild_i = VTOI(fromchild_vnode);
+       if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
+               VOP_UNLOCK(fromchild_vnode, 0);
                error = EMLINK;
                goto abortit;
        }
-       if ((ip->i_flags & (IMMUTABLE | APPEND)) ||
-               (dp->i_flags & APPEND)) {
-               VOP_UNLOCK(fvp, 0);
+       if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) ||
+               (fromparent_i->i_flags & APPEND)) {
+               VOP_UNLOCK(fromchild_vnode, 0);
                error = EPERM;
                goto abortit;
        }
-       if ((ip->i_mode & IFMT) == IFDIR) {
+       if ((fromchild_i->i_mode & IFMT) == IFDIR) {
                /*
                 * Avoid ".", "..", and aliases of "." for obvious reasons.
                 */
-               if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
-                   dp == ip ||
-                   (fcnp->cn_flags & ISDOTDOT) ||
-                   (tcnp->cn_flags & ISDOTDOT) ||
-                   (ip->i_flag & IN_RENAME)) {
-                       VOP_UNLOCK(fvp, 0);
+               if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == 
'.') ||
+                   fromparent_i == fromchild_i ||
+                   (from_name->cn_flags & ISDOTDOT) ||
+                   (to_name->cn_flags & ISDOTDOT) ||
+                   (fromchild_i->i_flag & IN_RENAME)) {
+                       VOP_UNLOCK(fromchild_vnode, 0);
                        error = EINVAL;
                        goto abortit;
                }
-               ip->i_flag |= IN_RENAME;
-               oldparent = dp->i_number;
+               fromchild_i->i_flag |= IN_RENAME;
+               oldparent = fromparent_i->i_number;
                doingdirectory = 1;
        }
-       VN_KNOTE(fdvp, NOTE_WRITE);             /* XXXLUKEM/XXX: right place? */
+       VN_KNOTE(fromparent_vnode, NOTE_WRITE);         /* XXXLUKEM/XXX: right 
place? */
 
        /*
         * When the target exists, both the directory
         * and target vnodes are returned locked.
         */
-       dp = VTOI(tdvp);
-       xp = NULL;
-       if (tvp)
-               xp = VTOI(tvp);
+       toparent_i = VTOI(toparent_vnode);
+       tochild_i = NULL;
+       if (tochild_vnode)
+               tochild_i = VTOI(tochild_vnode);
 
-       mp = fdvp->v_mount;
+       mp = fromparent_vnode->v_mount;
        fstrans_start(mp, FSTRANS_SHARED);
 
        /*
@@ -1092,11 +1092,11 @@
         *    completing our work, the link count
         *    may be wrong, but correctable.
         */
-       ip->i_nlink++;
-       DIP_ASSIGN(ip, nlink, ip->i_nlink);
-       ip->i_flag |= IN_CHANGE;
-       if ((error = UFS_UPDATE(fvp, NULL, NULL, UPDATE_DIROP)) != 0) {
-               VOP_UNLOCK(fvp, 0);
+       fromchild_i->i_nlink++;
+       DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
+       fromchild_i->i_flag |= IN_CHANGE;
+       if ((error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP)) != 
0) {
+               VOP_UNLOCK(fromchild_vnode, 0);
                goto bad;
        }
 
@@ -1110,31 +1110,31 @@
         * to namei, as the parent directory is unlocked by the
         * call to checkpath().
         */
-       error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
-       VOP_UNLOCK(fvp, 0);
-       if (oldparent != dp->i_number)
-               newparent = dp->i_number;
+       error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
+       VOP_UNLOCK(fromchild_vnode, 0);
+       if (oldparent != toparent_i->i_number)
+               newparent = toparent_i->i_number;
        if (doingdirectory && newparent) {
                if (error)      /* write access check above */
                        goto bad;
-               if (xp != NULL)
-                       vput(tvp);
-               vref(tdvp);     /* compensate for the ref checkpath loses */
-               if ((error = ufs_checkpath(ip, dp, tcnp->cn_cred)) != 0) {
-                       vrele(tdvp);
+               if (tochild_i != NULL)
+                       vput(tochild_vnode);
+               vref(toparent_vnode);   /* compensate for the ref checkpath 
loses */
+               if ((error = ufs_checkpath(fromchild_i, toparent_i, 
to_name->cn_cred)) != 0) {
+                       vrele(toparent_vnode);
                        goto out;
                }
-               tcnp->cn_flags &= ~SAVESTART;
-               vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
-               error = relookup(tdvp, &tvp, tcnp);
+               to_name->cn_flags &= ~SAVESTART;
+               vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               error = relookup(toparent_vnode, &tochild_vnode, to_name);
                if (error != 0) {
-                       vput(tdvp);
+                       vput(toparent_vnode);
                        goto out;
                }
-               dp = VTOI(tdvp);
-               xp = NULL;
-               if (tvp)
-                       xp = VTOI(tvp);
+               toparent_i = VTOI(toparent_vnode);
+               tochild_i = NULL;
+               if (tochild_vnode)
+                       tochild_i = VTOI(tochild_vnode);
        }
        /*
         * 2) If target doesn't exist, link the target
@@ -1143,8 +1143,8 @@
         *    entry to reference the source inode and
         *    expunge the original entry's existence.
         */
-       if (xp == NULL) {
-               if (dp->i_dev != ip->i_dev)
+       if (tochild_i == NULL) {
+               if (toparent_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Account for ".." in new directory.
@@ -1152,44 +1152,44 @@
                 * parent we don't fool with the link count.
                 */
                if (doingdirectory && newparent) {
-                       if ((nlink_t)dp->i_nlink >= LINK_MAX) {
+                       if ((nlink_t)toparent_i->i_nlink >= LINK_MAX) {
                                error = EMLINK;
                                goto bad;
                        }
-                       dp->i_nlink++;
-                       DIP_ASSIGN(dp, nlink, dp->i_nlink);
-                       dp->i_flag |= IN_CHANGE;
-                       if ((error = UFS_UPDATE(tdvp, NULL, NULL,
+                       toparent_i->i_nlink++;
+                       DIP_ASSIGN(toparent_i, nlink, toparent_i->i_nlink);
+                       toparent_i->i_flag |= IN_CHANGE;
+                       if ((error = UFS_UPDATE(toparent_vnode, NULL, NULL,
                            UPDATE_DIROP)) != 0) {
-                               dp->i_nlink--;
-                               DIP_ASSIGN(dp, nlink, dp->i_nlink);
-                               dp->i_flag |= IN_CHANGE;
+                               toparent_i->i_nlink--;
+                               DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
+                               toparent_i->i_flag |= IN_CHANGE;
                                goto bad;
                        }
                }
                newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
-               ufs_makedirentry(ip, tcnp, newdir);
-               error = ufs_direnter(tdvp, NULL, newdir, tcnp, NULL);
+               ufs_makedirentry(fromchild_i, to_name, newdir);
+               error = ufs_direnter(toparent_vnode, NULL, newdir, to_name, 
NULL);
                pool_cache_put(ufs_direct_cache, newdir);
                if (error != 0) {
                        if (doingdirectory && newparent) {
-                               dp->i_nlink--;
-                               DIP_ASSIGN(dp, nlink, dp->i_nlink);
-                               dp->i_flag |= IN_CHANGE;
-                               (void)UFS_UPDATE(tdvp, NULL, NULL,
+                               toparent_i->i_nlink--;
+                               DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
+                               toparent_i->i_flag |= IN_CHANGE;
+                               (void)UFS_UPDATE(toparent_vnode, NULL, NULL,
                                                 UPDATE_WAIT|UPDATE_DIROP);
                        }
                        goto bad;
                }
-               VN_KNOTE(tdvp, NOTE_WRITE);
-               vput(tdvp);
+               VN_KNOTE(toparent_vnode, NOTE_WRITE);
+               vput(toparent_vnode);
        } else {
-               if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
+               if (tochild_i->i_dev != toparent_i->i_dev || tochild_i->i_dev 
!= fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Short circuit rename(foo, foo).
                 */
-               if (xp->i_number == ip->i_number)
+               if (tochild_i->i_number == fromchild_i->i_number)
                        panic("rename: same file");
                /*
                 * If the parent directory is "sticky", then the user must
@@ -1197,11 +1197,11 @@
                 * otherwise the destination may not be changed (except by
                 * root). This implements append-only directories.
                 */
-               if ((dp->i_mode & S_ISTXT) &&
-                   kauth_authorize_generic(tcnp->cn_cred,
+               if ((toparent_i->i_mode & S_ISTXT) &&
+                   kauth_authorize_generic(to_name->cn_cred,
                     KAUTH_GENERIC_ISSUSER, NULL) != 0 &&
-                   kauth_cred_geteuid(tcnp->cn_cred) != dp->i_uid &&
-                   xp->i_uid != kauth_cred_geteuid(tcnp->cn_cred)) {
+                   kauth_cred_geteuid(to_name->cn_cred) != toparent_i->i_uid &&
+                   tochild_i->i_uid != kauth_cred_geteuid(to_name->cn_cred)) {
                        error = EPERM;
                        goto bad;
                }
@@ -1210,9 +1210,9 @@
                 * to it. Also, ensure source and target are compatible
                 * (both directories, or both not directories).
                 */
-               if ((xp->i_mode & IFMT) == IFDIR) {
-                       if (xp->i_nlink > 2 ||
-                           !ufs_dirempty(xp, dp->i_number, tcnp->cn_cred)) {
+               if ((tochild_i->i_mode & IFMT) == IFDIR) {
+                       if (tochild_i->i_nlink > 2 ||
+                           !ufs_dirempty(tochild_i, toparent_i->i_number, 
to_name->cn_cred)) {
                                error = ENOTEMPTY;
                                goto bad;
                        }
@@ -1220,13 +1220,13 @@
                                error = ENOTDIR;
                                goto bad;
                        }
-                       cache_purge(tdvp);
+                       cache_purge(toparent_vnode);
                } else if (doingdirectory) {
                        error = EISDIR;
                        goto bad;
                }
-               if ((error = ufs_dirrewrite(dp, xp, ip->i_number,
-                   IFTODT(ip->i_mode), doingdirectory && newparent ?
+               if ((error = ufs_dirrewrite(toparent_i, tochild_i, 
fromchild_i->i_number,
+                   IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
                        goto bad;
                if (doingdirectory) {
@@ -1238,38 +1238,38 @@
                         * but there may be other hard links.
                         */
                        if (!newparent) {
-                               dp->i_nlink--;
-                               DIP_ASSIGN(dp, nlink, dp->i_nlink);
-                               dp->i_flag |= IN_CHANGE;
+                               toparent_i->i_nlink--;
+                               DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
+                               toparent_i->i_flag |= IN_CHANGE;
                        }
-                       xp->i_nlink--;
-                       DIP_ASSIGN(xp, nlink, xp->i_nlink);
-                       xp->i_flag |= IN_CHANGE;
-                       if ((error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC,
-                           tcnp->cn_cred)))
+                       tochild_i->i_nlink--;
+                       DIP_ASSIGN(tochild_i, nlink, tochild_i->i_nlink);
+                       tochild_i->i_flag |= IN_CHANGE;
+                       if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0, 
IO_SYNC,
+                           to_name->cn_cred)))
                                goto bad;
                }
-               VN_KNOTE(tdvp, NOTE_WRITE);
-               vput(tdvp);
-               VN_KNOTE(tvp, NOTE_DELETE);
-               vput(tvp);
-               xp = NULL;
+               VN_KNOTE(toparent_vnode, NOTE_WRITE);
+               vput(toparent_vnode);
+               VN_KNOTE(tochild_vnode, NOTE_DELETE);
+               vput(tochild_vnode);
+               tochild_i = NULL;
        }
 
        /*
         * 3) Unlink the source.
         */
-       fcnp->cn_flags &= ~(MODMASK | SAVESTART);
-       fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
-       vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
-       if ((error = relookup(fdvp, &fvp, fcnp))) {
-               vput(fdvp);
+       from_name->cn_flags &= ~(MODMASK | SAVESTART);
+       from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
+       vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+       if ((error = relookup(fromparent_vnode, &fromchild_vnode, from_name))) {
+               vput(fromparent_vnode);
                vrele(ap->a_fvp);
                goto out2;
        }
-       if (fvp != NULL) {
-               xp = VTOI(fvp);
-               dp = VTOI(fdvp);
+       if (fromchild_vnode != NULL) {
+               fromchild_relookup_i = VTOI(fromchild_vnode);
+               toparent_i = VTOI(fromparent_vnode);
        } else {
                /*
                 * From name has disappeared.
@@ -1289,7 +1289,7 @@
         * flag ensures that it cannot be moved by another rename or removed
         * by a rmdir.
         */
-       if (xp != ip) {
+       if (fromchild_relookup_i != fromchild_i) {
                if (doingdirectory)
                        panic("rename: lost dir entry");
        } else {
@@ -1300,39 +1300,39 @@
                 * and ".." set to point to the new parent.
                 */
                if (doingdirectory && newparent) {
-                       KASSERT(dp != NULL);
-                       xp->i_offset = mastertemplate.dot_reclen;
-                       ufs_dirrewrite(xp, dp, newparent, DT_DIR, 0, IN_CHANGE);
-                       cache_purge(fdvp);
+                       KASSERT(toparent_i != NULL);
+                       fromchild_relookup_i->i_offset = 
mastertemplate.dot_reclen;
+                       ufs_dirrewrite(fromchild_relookup_i, toparent_i, 
newparent, DT_DIR, 0, IN_CHANGE);
+                       cache_purge(fromparent_vnode);
                }
-               error = ufs_dirremove(fdvp, xp, fcnp->cn_flags, 0);
-               xp->i_flag &= ~IN_RENAME;
+               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i, 
from_name->cn_flags, 0);
+               fromchild_relookup_i->i_flag &= ~IN_RENAME;
        }
-       VN_KNOTE(fvp, NOTE_RENAME);
-       if (dp)
-               vput(fdvp);
-       if (xp)
-               vput(fvp);
+       VN_KNOTE(fromchild_vnode, NOTE_RENAME);
+       if (toparent_i)
+               vput(fromparent_vnode);
+       if (fromchild_relookup_i)
+               vput(fromchild_vnode);
        vrele(ap->a_fvp);
        goto out2;
 
        /* exit routines from steps 1 & 2 */
  bad:
-       if (xp)
-               vput(ITOV(xp));
-       vput(ITOV(dp));
+       if (tochild_i)
+               vput(ITOV(tochild_i));
+       vput(ITOV(toparent_i));
  out:
        if (doingdirectory)
-               ip->i_flag &= ~IN_RENAME;
-       if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
-               ip->i_nlink--;
-               DIP_ASSIGN(ip, nlink, ip->i_nlink);
-               ip->i_flag |= IN_CHANGE;
-               ip->i_flag &= ~IN_RENAME;
-               vput(fvp);
+               fromchild_i->i_flag &= ~IN_RENAME;
+       if (vn_lock(fromchild_vnode, LK_EXCLUSIVE) == 0) {
+               fromchild_i->i_nlink--;
+               DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
+               fromchild_i->i_flag |= IN_CHANGE;
+               fromchild_i->i_flag &= ~IN_RENAME;
+               vput(fromchild_vnode);
        } else
-               vrele(fvp);
-       vrele(fdvp);
+               vrele(fromchild_vnode);
+       vrele(fromparent_vnode);
 
        /* exit routines from step 3 */
  out2:
diff -r 5c163770c43e sys/ufs/ufs/ufs_wapbl.c
--- a/sys/ufs/ufs/ufs_wapbl.c   Sun Sep 27 13:30:45 2009 -0400
+++ b/sys/ufs/ufs/ufs_wapbl.c   Sun Sep 27 23:15:37 2009 -0400
@@ -158,9 +158,9 @@
                struct vnode            *a_tvp;
                struct componentname    *a_tcnp;
        } */ *ap = v;
-       struct vnode            *tvp, *tdvp, *fvp, *fdvp;
-       struct componentname    *tcnp, *fcnp;
-       struct inode            *ip, *txp, *fxp, *tdp, *fdp;
+       struct vnode            *tochild_vnode, *toparent_vnode, 
*fromchild_vnode, *fromparent_vnode;
+       struct componentname    *to_name, *from_name;
+       struct inode            *fromchild_i, *tochild_i, 
*fromchild_relookup_i, *toparent_i, *fromparent_i;
        struct mount            *mp;
        struct direct           *newdir;
        int                     doingdirectory, oldparent, newparent, error;
@@ -175,114 +175,114 @@
        doff_t    saved_t_offset;
        u_int32_t saved_t_reclen;
 
-       tvp = ap->a_tvp;
-       tdvp = ap->a_tdvp;
-       fvp = ap->a_fvp;
-       fdvp = ap->a_fdvp;
-       tcnp = ap->a_tcnp;
-       fcnp = ap->a_fcnp;
+       tochild_vnode = ap->a_tvp;
+       toparent_vnode = ap->a_tdvp;
+       fromchild_vnode = ap->a_fvp;
+       fromparent_vnode = ap->a_fdvp;
+       to_name = ap->a_tcnp;
+       from_name = ap->a_fcnp;
        doingdirectory = oldparent = newparent = error = 0;
 
 #ifdef DIAGNOSTIC
-       if ((tcnp->cn_flags & HASBUF) == 0 ||
-           (fcnp->cn_flags & HASBUF) == 0)
+       if ((to_name->cn_flags & HASBUF) == 0 ||
+           (from_name->cn_flags & HASBUF) == 0)
                panic("ufs_rename: no name");
 #endif
        /*
         * Check for cross-device rename.
         */
-       if ((fvp->v_mount != tdvp->v_mount) ||
-           (tvp && (fvp->v_mount != tvp->v_mount))) {
+       if ((fromchild_vnode->v_mount != toparent_vnode->v_mount) ||
+           (tochild_vnode && (fromchild_vnode->v_mount != 
tochild_vnode->v_mount))) {
                error = EXDEV;
  abortit:
-               VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
-               if (tdvp == tvp)
-                       vrele(tdvp);
+               VOP_ABORTOP(toparent_vnode, to_name); /* XXX, why not in NFS? */
+               if (toparent_vnode == tochild_vnode)
+                       vrele(toparent_vnode);
                else
-                       vput(tdvp);
-               if (tvp)
-                       vput(tvp);
-               VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
-               vrele(fdvp);
-               vrele(fvp);
+                       vput(toparent_vnode);
+               if (tochild_vnode)
+                       vput(tochild_vnode);
+               VOP_ABORTOP(fromparent_vnode, from_name); /* XXX, why not in 
NFS? */
+               vrele(fromparent_vnode);
+               vrele(fromchild_vnode);
                return (error);
        }
 
        /*
         * Check if just deleting a link name.
         */
-       if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||
-           (VTOI(tdvp)->i_flags & APPEND))) {
+       if (tochild_vnode && ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | 
APPEND)) ||
+           (VTOI(toparent_vnode)->i_flags & APPEND))) {
                error = EPERM;
                goto abortit;
        }
-       if (fvp == tvp) {
-               if (fvp->v_type == VDIR) {
+       if (fromchild_vnode == tochild_vnode) {
+               if (fromchild_vnode->v_type == VDIR) {
                        error = EINVAL;
                        goto abortit;
                }
 
                /* Release destination completely. */
-               VOP_ABORTOP(tdvp, tcnp);
-               vput(tdvp);
-               vput(tvp);
+               VOP_ABORTOP(toparent_vnode, to_name);
+               vput(toparent_vnode);
+               vput(tochild_vnode);
 
                /* Delete source. */
-               vrele(fvp);
-               fcnp->cn_flags &= ~(MODMASK | SAVESTART);
-               fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
-               fcnp->cn_nameiop = DELETE;
-               vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fdvp, &fvp, fcnp))) {
-                       vput(fdvp);
+               vrele(fromchild_vnode);
+               from_name->cn_flags &= ~(MODMASK | SAVESTART);
+               from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
+               from_name->cn_nameiop = DELETE;
+               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               if ((error = relookup(fromparent_vnode, &fromchild_vnode, 
from_name))) {
+                       vput(fromparent_vnode);
                        return (error);
                }
-               return (VOP_REMOVE(fdvp, fvp, fcnp));
+               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode, 
from_name));
        }
-       if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
+       if ((error = vn_lock(fromchild_vnode, LK_EXCLUSIVE)) != 0)
                goto abortit;
-       fdp = VTOI(fdvp);
-       ip = VTOI(fvp);
-       if ((nlink_t) ip->i_nlink >= LINK_MAX) {
-               VOP_UNLOCK(fvp, 0);
+       fromparent_i = VTOI(fromparent_vnode);
+       fromchild_i = VTOI(fromchild_vnode);
+       if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
+               VOP_UNLOCK(fromchild_vnode, 0);
                error = EMLINK;
                goto abortit;
        }
-       if ((ip->i_flags & (IMMUTABLE | APPEND)) ||
-               (fdp->i_flags & APPEND)) {
-               VOP_UNLOCK(fvp, 0);
+       if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) ||
+               (fromparent_i->i_flags & APPEND)) {
+               VOP_UNLOCK(fromchild_vnode, 0);
                error = EPERM;
                goto abortit;
        }
-       if ((ip->i_mode & IFMT) == IFDIR) {
+       if ((fromchild_i->i_mode & IFMT) == IFDIR) {
                /*
                 * Avoid ".", "..", and aliases of "." for obvious reasons.
                 */
-               if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
-                   fdp == ip ||
-                   (fcnp->cn_flags & ISDOTDOT) ||
-                   (tcnp->cn_flags & ISDOTDOT) ||
-                   (ip->i_flag & IN_RENAME)) {
-                       VOP_UNLOCK(fvp, 0);
+               if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == 
'.') ||
+                   fromparent_i == fromchild_i ||
+                   (from_name->cn_flags & ISDOTDOT) ||
+                   (to_name->cn_flags & ISDOTDOT) ||
+                   (fromchild_i->i_flag & IN_RENAME)) {
+                       VOP_UNLOCK(fromchild_vnode, 0);
                        error = EINVAL;
                        goto abortit;
                }
-               ip->i_flag |= IN_RENAME;
+               fromchild_i->i_flag |= IN_RENAME;
                doingdirectory = 1;
        }
-       oldparent = fdp->i_number;
-       VN_KNOTE(fdvp, NOTE_WRITE);             /* XXXLUKEM/XXX: right place? */
+       oldparent = fromparent_i->i_number;
+       VN_KNOTE(fromparent_vnode, NOTE_WRITE);         /* XXXLUKEM/XXX: right 
place? */
 
        /*
         * When the target exists, both the directory
         * and target vnodes are returned locked.
         */
-       tdp = VTOI(tdvp);
-       txp = NULL;
-       if (tvp)
-               txp = VTOI(tvp);
+       toparent_i = VTOI(toparent_vnode);
+       tochild_i = NULL;
+       if (tochild_vnode)
+               tochild_i = VTOI(tochild_vnode);
 
-       mp = fdvp->v_mount;
+       mp = fromparent_vnode->v_mount;
        fstrans_start(mp, FSTRANS_SHARED);
 
        /*
@@ -295,77 +295,77 @@
         * to namei, as the parent directory is unlocked by the
         * call to checkpath().
         */
-       error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
-       VOP_UNLOCK(fvp, 0);
-       if (oldparent != tdp->i_number)
-               newparent = tdp->i_number;
+       error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
+       VOP_UNLOCK(fromchild_vnode, 0);
+       if (oldparent != toparent_i->i_number)
+               newparent = toparent_i->i_number;
        if (doingdirectory && newparent) {
                if (error)      /* write access check above */
                        goto out;
-               if (txp != NULL)
-                       vput(tvp);
-               txp = NULL;
-               vref(tdvp);     /* compensate for the ref checkpath loses */
-               if ((error = ufs_checkpath(ip, tdp, tcnp->cn_cred)) != 0) {
-                       vrele(tdvp);
-                       tdp = NULL;
+               if (tochild_i != NULL)
+                       vput(tochild_vnode);
+               tochild_i = NULL;
+               vref(toparent_vnode);   /* compensate for the ref checkpath 
loses */
+               if ((error = ufs_checkpath(fromchild_i, toparent_i, 
to_name->cn_cred)) != 0) {
+                       vrele(toparent_vnode);
+                       toparent_i = NULL;
                        goto out;
                }
-               tcnp->cn_flags &= ~SAVESTART;
-               tdp = NULL;
-               vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
-               error = relookup(tdvp, &tvp, tcnp);
+               to_name->cn_flags &= ~SAVESTART;
+               toparent_i = NULL;
+               vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               error = relookup(toparent_vnode, &tochild_vnode, to_name);
                if (error != 0) {
-                       vput(tdvp);
+                       vput(toparent_vnode);
                        goto out;
                }
-               tdp = VTOI(tdvp);
-               if (tvp)
-                       txp = VTOI(tvp);
+               toparent_i = VTOI(toparent_vnode);
+               if (tochild_vnode)
+                       tochild_i = VTOI(tochild_vnode);
        }
 
        /*
-        * XXX handle case where fdvp is parent of tdvp,
-        * by unlocking tdvp and regrabbing it with vget after?
+        * XXX handle case where fromparent_vnode is parent of toparent_vnode,
+        * by unlocking toparent_vnode and regrabbing it with vget after?
         */
 
-       /* save directory lookup information in case tdvp == fdvp */
-       saved_t_count  = tdp->i_count;
-       saved_t_endoff = tdp->i_endoff;
-       saved_t_diroff = tdp->i_diroff;
-       saved_t_offset = tdp->i_offset;
-       saved_t_reclen = tdp->i_reclen;
+       /* save directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       saved_t_count  = toparent_i->i_count;
+       saved_t_endoff = toparent_i->i_endoff;
+       saved_t_diroff = toparent_i->i_diroff;
+       saved_t_offset = toparent_i->i_offset;
+       saved_t_reclen = toparent_i->i_reclen;
 
        /*
         * This was moved up to before the journal lock to
         * avoid potential deadlock
         */
-       fcnp->cn_flags &= ~(MODMASK | SAVESTART);
-       fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+       from_name->cn_flags &= ~(MODMASK | SAVESTART);
+       from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
        if (newparent) {
                /* Check for the rename("foo/foo", "foo") case. */
-               if (fdvp == tvp) {
+               if (fromparent_vnode == tochild_vnode) {
                        error = doingdirectory ? ENOTEMPTY : EISDIR;
                        goto out;
                }
-               vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fdvp, &fvp, fcnp))) {
-                       vput(fdvp);
+               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               if ((error = relookup(fromparent_vnode, &fromchild_vnode, 
from_name))) {
+                       vput(fromparent_vnode);
                        vrele(ap->a_fvp);
                        goto out2;
                }
        } else {
-               error = VOP_LOOKUP(fdvp, &fvp, fcnp);
+               error = VOP_LOOKUP(fromparent_vnode, &fromchild_vnode, 
from_name);
                if (error && (error != EJUSTRETURN)) {
-                       vput(fdvp);
+                       vput(fromparent_vnode);
                        vrele(ap->a_fvp);
                        goto out2;
                }
                error = 0;
        }
-       if (fvp != NULL) {
-               fxp = VTOI(fvp);
-               fdp = VTOI(fdvp);
+       if (fromchild_vnode != NULL) {
+               fromchild_relookup_i = VTOI(fromchild_vnode);
+               fromparent_i = VTOI(fromparent_vnode);
        } else {
                /*
                 * From name has disappeared.
@@ -378,20 +378,20 @@
        }
        vrele(ap->a_fvp);
 
-       /* save directory lookup information in case tdvp == fdvp */
-       saved_f_count  = fdp->i_count;
-       saved_f_diroff = fdp->i_diroff;
-       saved_f_offset = fdp->i_offset;
-       saved_f_reclen = fdp->i_reclen;
+       /* save directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       saved_f_count  = fromparent_i->i_count;
+       saved_f_diroff = fromparent_i->i_diroff;
+       saved_f_offset = fromparent_i->i_offset;
+       saved_f_reclen = fromparent_i->i_reclen;
 
-       /* restore directory lookup information in case tdvp == fdvp */
-       tdp->i_offset = saved_t_offset;
-       tdp->i_reclen = saved_t_reclen;
-       tdp->i_count  = saved_t_count;
-       tdp->i_endoff = saved_t_endoff;
-       tdp->i_diroff = saved_t_diroff;
+       /* restore directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       toparent_i->i_offset = saved_t_offset;
+       toparent_i->i_reclen = saved_t_reclen;
+       toparent_i->i_count  = saved_t_count;
+       toparent_i->i_endoff = saved_t_endoff;
+       toparent_i->i_diroff = saved_t_diroff;
 
-       error = UFS_WAPBL_BEGIN(fdvp->v_mount);
+       error = UFS_WAPBL_BEGIN(fromparent_vnode->v_mount);
        if (error)
                goto out2;
 
@@ -401,10 +401,10 @@
         *    completing our work, the link count
         *    may be wrong, but correctable.
         */
-       ip->i_nlink++;
-       DIP_ASSIGN(ip, nlink, ip->i_nlink);
-       ip->i_flag |= IN_CHANGE;
-       if ((error = UFS_UPDATE(fvp, NULL, NULL, UPDATE_DIROP)) != 0) {
+       fromchild_i->i_nlink++;
+       DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
+       fromchild_i->i_flag |= IN_CHANGE;
+       if ((error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP)) != 
0) {
                goto bad;
        }
 
@@ -415,8 +415,8 @@
         *    entry to reference the source inode and
         *    expunge the original entry's existence.
         */
-       if (txp == NULL) {
-               if (tdp->i_dev != ip->i_dev)
+       if (tochild_i == NULL) {
+               if (toparent_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Account for ".." in new directory.
@@ -424,43 +424,43 @@
                 * parent we don't fool with the link count.
                 */
                if (doingdirectory && newparent) {
-                       if ((nlink_t)tdp->i_nlink >= LINK_MAX) {
+                       if ((nlink_t)toparent_i->i_nlink >= LINK_MAX) {
                                error = EMLINK;
                                goto bad;
                        }
-                       tdp->i_nlink++;
-                       DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
-                       tdp->i_flag |= IN_CHANGE;
-                       if ((error = UFS_UPDATE(tdvp, NULL, NULL,
+                       toparent_i->i_nlink++;
+                       DIP_ASSIGN(toparent_i, nlink, toparent_i->i_nlink);
+                       toparent_i->i_flag |= IN_CHANGE;
+                       if ((error = UFS_UPDATE(toparent_vnode, NULL, NULL,
                            UPDATE_DIROP)) != 0) {
-                               tdp->i_nlink--;
-                               DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
-                               tdp->i_flag |= IN_CHANGE;
+                               toparent_i->i_nlink--;
+                               DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
+                               toparent_i->i_flag |= IN_CHANGE;
                                goto bad;
                        }
                }
                newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
-               ufs_makedirentry(ip, tcnp, newdir);
-               error = ufs_direnter(tdvp, NULL, newdir, tcnp, NULL);
+               ufs_makedirentry(fromchild_i, to_name, newdir);
+               error = ufs_direnter(toparent_vnode, NULL, newdir, to_name, 
NULL);
                pool_cache_put(ufs_direct_cache, newdir);
                if (error != 0) {
                        if (doingdirectory && newparent) {
-                               tdp->i_nlink--;
-                               DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
-                               tdp->i_flag |= IN_CHANGE;
-                               (void)UFS_UPDATE(tdvp, NULL, NULL,
+                               toparent_i->i_nlink--;
+                               DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
+                               toparent_i->i_flag |= IN_CHANGE;
+                               (void)UFS_UPDATE(toparent_vnode, NULL, NULL,
                                                 UPDATE_WAIT | UPDATE_DIROP);
                        }
                        goto bad;
                }
-               VN_KNOTE(tdvp, NOTE_WRITE);
+               VN_KNOTE(toparent_vnode, NOTE_WRITE);
        } else {
-               if (txp->i_dev != tdp->i_dev || txp->i_dev != ip->i_dev)
+               if (tochild_i->i_dev != toparent_i->i_dev || tochild_i->i_dev 
!= fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Short circuit rename(foo, foo).
                 */
-               if (txp->i_number == ip->i_number)
+               if (tochild_i->i_number == fromchild_i->i_number)
                        panic("rename: same file");
                /*
                 * If the parent directory is "sticky", then the user must
@@ -468,11 +468,11 @@
                 * otherwise the destination may not be changed (except by
                 * root). This implements append-only directories.
                 */
-               if ((tdp->i_mode & S_ISTXT) &&
-                   kauth_authorize_generic(tcnp->cn_cred,
+               if ((toparent_i->i_mode & S_ISTXT) &&
+                   kauth_authorize_generic(to_name->cn_cred,
                     KAUTH_GENERIC_ISSUSER, NULL) != 0 &&
-                   kauth_cred_geteuid(tcnp->cn_cred) != tdp->i_uid &&
-                   txp->i_uid != kauth_cred_geteuid(tcnp->cn_cred)) {
+                   kauth_cred_geteuid(to_name->cn_cred) != toparent_i->i_uid &&
+                   tochild_i->i_uid != kauth_cred_geteuid(to_name->cn_cred)) {
                        error = EPERM;
                        goto bad;
                }
@@ -481,9 +481,9 @@
                 * to it. Also, ensure source and target are compatible
                 * (both directories, or both not directories).
                 */
-               if ((txp->i_mode & IFMT) == IFDIR) {
-                       if (txp->i_nlink > 2 ||
-                           !ufs_dirempty(txp, tdp->i_number, tcnp->cn_cred)) {
+               if ((tochild_i->i_mode & IFMT) == IFDIR) {
+                       if (tochild_i->i_nlink > 2 ||
+                           !ufs_dirempty(tochild_i, toparent_i->i_number, 
to_name->cn_cred)) {
                                error = ENOTEMPTY;
                                goto bad;
                        }
@@ -491,13 +491,13 @@
                                error = ENOTDIR;
                                goto bad;
                        }
-                       cache_purge(tdvp);
+                       cache_purge(toparent_vnode);
                } else if (doingdirectory) {
                        error = EISDIR;
                        goto bad;
                }
-               if ((error = ufs_dirrewrite(tdp, txp, ip->i_number,
-                   IFTODT(ip->i_mode), doingdirectory && newparent ?
+               if ((error = ufs_dirrewrite(toparent_i, tochild_i, 
fromchild_i->i_number,
+                   IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
                        goto bad;
                if (doingdirectory) {
@@ -509,34 +509,34 @@
                         * but there may be other hard links.
                         */
                        if (!newparent) {
-                               tdp->i_nlink--;
-                               DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
-                               tdp->i_flag |= IN_CHANGE;
-                               UFS_WAPBL_UPDATE(tdvp, NULL, NULL, 0);
+                               toparent_i->i_nlink--;
+                               DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
+                               toparent_i->i_flag |= IN_CHANGE;
+                               UFS_WAPBL_UPDATE(toparent_vnode, NULL, NULL, 0);
                        }
-                       txp->i_nlink--;
-                       DIP_ASSIGN(txp, nlink, txp->i_nlink);
-                       txp->i_flag |= IN_CHANGE;
-                       if ((error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC,
-                           tcnp->cn_cred)))
+                       tochild_i->i_nlink--;
+                       DIP_ASSIGN(tochild_i, nlink, tochild_i->i_nlink);
+                       tochild_i->i_flag |= IN_CHANGE;
+                       if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0, 
IO_SYNC,
+                           to_name->cn_cred)))
                                goto bad;
                }
-               VN_KNOTE(tdvp, NOTE_WRITE);
-               VN_KNOTE(tvp, NOTE_DELETE);
+               VN_KNOTE(toparent_vnode, NOTE_WRITE);
+               VN_KNOTE(tochild_vnode, NOTE_DELETE);
        }
 
-       /* restore directory lookup information in case tdvp == fdvp */
-       fdp->i_offset = saved_f_offset;
-       fdp->i_reclen = saved_f_reclen;
-       fdp->i_count  = saved_f_count;
-       fdp->i_diroff = saved_f_diroff;
+       /* restore directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       fromparent_i->i_offset = saved_f_offset;
+       fromparent_i->i_reclen = saved_f_reclen;
+       fromparent_i->i_count  = saved_f_count;
+       fromparent_i->i_diroff = saved_f_diroff;
 
        /*
         * Handle case where the directory we need to remove may have
         * been moved when the directory insertion above performed compaction.
         * or when i_count may be wrong due to insertion before this entry.
         */
-       if ((tdp->i_number == fdp->i_number) &&
+       if ((toparent_i->i_number == fromparent_i->i_number) &&
                (((saved_f_offset >= saved_t_offset) &&
                        (saved_f_offset < saved_t_offset + saved_t_count)) ||
                ((saved_f_offset - saved_f_count >= saved_t_offset) &&
@@ -544,7 +544,7 @@
                         saved_t_offset + saved_t_count)))) {
                struct buf *bp;
                struct direct *ep;
-               struct ufsmount *ump = fdp->i_ump;
+               struct ufsmount *ump = fromparent_i->i_ump;
                doff_t endsearch;       /* offset to end directory search */
                int dirblksiz = ump->um_dirblksiz;
                const int needswap = UFS_MPNEEDSWAP(ump);
@@ -552,34 +552,34 @@
                int namlen, entryoffsetinblock;
                char *dirbuf;
 
-               bmask = fdvp->v_mount->mnt_stat.f_iosize - 1;
+               bmask = fromparent_vnode->v_mount->mnt_stat.f_iosize - 1;
 
                /*
-                * the fcnp entry will be somewhere between the start of
+                * the from_name entry will be somewhere between the start of
                 * compaction and the original location.
                 */
-               fdp->i_offset = saved_t_offset;
-               error = ufs_blkatoff(fdvp, (off_t)fdp->i_offset, &dirbuf, &bp,
+               fromparent_i->i_offset = saved_t_offset;
+               error = ufs_blkatoff(fromparent_vnode, 
(off_t)fromparent_i->i_offset, &dirbuf, &bp,
                    false);
                if (error)
                        goto bad;
 
                /*
-                * keep existing fdp->i_count in case
-                * compaction started at the same location as the fcnp entry.
+                * keep existing fromparent_i->i_count in case
+                * compaction started at the same location as the from_name 
entry.
                 */
                endsearch = saved_f_offset + saved_f_reclen;
                entryoffsetinblock = 0;
-               while (fdp->i_offset < endsearch) {
+               while (fromparent_i->i_offset < endsearch) {
                        int reclen;
 
                        /*
                         * If necessary, get the next directory block.
                         */
-                       if ((fdp->i_offset & bmask) == 0) {
+                       if ((fromparent_i->i_offset & bmask) == 0) {
                                if (bp != NULL)
                                        brelse(bp, 0);
-                               error = ufs_blkatoff(fdvp, (off_t)fdp->i_offset,
+                               error = ufs_blkatoff(fromparent_vnode, 
(off_t)fromparent_i->i_offset,
                                    &dirbuf, &bp, false);
                                if (error)
                                        goto bad;
@@ -591,37 +591,37 @@
                        reclen = ufs_rw16(ep->d_reclen, needswap);
 
 #if (BYTE_ORDER == LITTLE_ENDIAN)
-                       if (FSFMT(fdvp) && needswap == 0)
+                       if (FSFMT(fromparent_vnode) && needswap == 0)
                                namlen = ep->d_type;
                        else
                                namlen = ep->d_namlen;
 #else
-                       if (FSFMT(fdvp) && needswap != 0)
+                       if (FSFMT(fromparent_vnode) && needswap != 0)
                                namlen = ep->d_type;
                        else
                                namlen = ep->d_namlen;
 #endif
                        if ((ep->d_ino != 0) &&
                            (ufs_rw32(ep->d_ino, needswap) != WINO) &&
-                           (namlen == fcnp->cn_namelen) &&
-                           memcmp(ep->d_name, fcnp->cn_nameptr, namlen) == 0) {
-                               fdp->i_reclen = reclen;
+                           (namlen == from_name->cn_namelen) &&
+                           memcmp(ep->d_name, from_name->cn_nameptr, namlen) 
== 0) {
+                               fromparent_i->i_reclen = reclen;
                                break;
                        }
-                       fdp->i_offset += reclen;
-                       fdp->i_count = reclen;
+                       fromparent_i->i_offset += reclen;
+                       fromparent_i->i_count = reclen;
                        entryoffsetinblock += reclen;
                }
 
-               KASSERT(fdp->i_offset <= endsearch);
+               KASSERT(fromparent_i->i_offset <= endsearch);
 
                /*
-                * If fdp->i_offset points to start of a directory block,
-                * set fdp->i_count so ufs_dirremove() doesn't compact over
+                * If fromparent_i->i_offset points to start of a directory 
block,
+                * set fromparent_i->i_count so ufs_dirremove() doesn't compact 
over
                 * a directory block boundary.
                 */
-               if ((fdp->i_offset & (dirblksiz - 1)) == 0)
-                       fdp->i_count = 0;
+               if ((fromparent_i->i_offset & (dirblksiz - 1)) == 0)
+                       fromparent_i->i_count = 0;
 
                brelse(bp, 0);
        }
@@ -638,7 +638,7 @@
         * flag ensures that it cannot be moved by another rename or removed
         * by a rmdir.
         */
-       if (fxp != ip) {
+       if (fromchild_relookup_i != fromchild_i) {
                if (doingdirectory)
                        panic("rename: lost dir entry");
        } else {
@@ -649,50 +649,50 @@
                 * and ".." set to point to the new parent.
                 */
                if (doingdirectory && newparent) {
-                       KASSERT(fdp != NULL);
-                       fxp->i_offset = mastertemplate.dot_reclen;
-                       ufs_dirrewrite(fxp, fdp, newparent, DT_DIR, 0, 
IN_CHANGE);
-                       cache_purge(fdvp);
+                       KASSERT(toparent_i != NULL);
+                       fromchild_relookup_i->i_offset = 
mastertemplate.dot_reclen;
+                       ufs_dirrewrite(fromchild_relookup_i, toparent_i, 
newparent, DT_DIR, 0, IN_CHANGE);
+                       cache_purge(fromparent_vnode);
                }
-               error = ufs_dirremove(fdvp, fxp, fcnp->cn_flags, 0);
-               fxp->i_flag &= ~IN_RENAME;
+               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i, 
from_name->cn_flags, 0);
+               fromchild_relookup_i->i_flag &= ~IN_RENAME;
        }
-       VN_KNOTE(fvp, NOTE_RENAME);
+       VN_KNOTE(fromchild_vnode, NOTE_RENAME);
        goto done;
 
  out:
-       vrele(fvp);
-       vrele(fdvp);
+       vrele(fromchild_vnode);
+       vrele(fromparent_vnode);
        goto out2;
 
        /* exit routines from steps 1 & 2 */
  bad:
        if (doingdirectory)
-               ip->i_flag &= ~IN_RENAME;
-       ip->i_nlink--;
-       DIP_ASSIGN(ip, nlink, ip->i_nlink);
-       ip->i_flag |= IN_CHANGE;
-       ip->i_flag &= ~IN_RENAME;
-       UFS_WAPBL_UPDATE(fvp, NULL, NULL, 0);
+               fromchild_i->i_flag &= ~IN_RENAME;
+       fromchild_i->i_nlink--;
+       DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
+       fromchild_i->i_flag |= IN_CHANGE;
+       fromchild_i->i_flag &= ~IN_RENAME;
+       UFS_WAPBL_UPDATE(fromchild_vnode, NULL, NULL, 0);
  done:
-       UFS_WAPBL_END(fdvp->v_mount);
-       vput(fdvp);
-       vput(fvp);
+       UFS_WAPBL_END(fromparent_vnode->v_mount);
+       vput(fromparent_vnode);
+       vput(fromchild_vnode);
  out2:
        /*
         * clear IN_RENAME - some exit paths happen too early to go
         * through the cleanup done in the "bad" case above, so we
         * always do this mini-cleanup here.
         */
-       ip->i_flag &= ~IN_RENAME;
+       fromchild_i->i_flag &= ~IN_RENAME;
 
-       if (txp)
-               vput(ITOV(txp));
-       if (tdp) {
+       if (tochild_i)
+               vput(ITOV(tochild_i));
+       if (toparent_i) {
                if (newparent)
-                       vput(ITOV(tdp));
+                       vput(ITOV(toparent_i));
                else
-                       vrele(ITOV(tdp));
+                       vrele(ITOV(toparent_i));
        }
 
        fstrans_done(mp);
Fix up (most of the) long lines caused by longer symbol names in
previous commit.

diff -r 0be35579a3be sys/ufs/ufs/ufs_vnops.c
--- a/sys/ufs/ufs/ufs_vnops.c   Sun Sep 27 23:15:37 2009 -0400
+++ b/sys/ufs/ufs/ufs_vnops.c   Sun Sep 27 23:16:49 2009 -0400
@@ -964,9 +964,18 @@
                struct vnode            *a_tvp;
                struct componentname    *a_tcnp;
        } */ *ap = v;
-       struct vnode            *tochild_vnode, *toparent_vnode, 
*fromchild_vnode, *fromparent_vnode;
-       struct componentname    *to_name, *from_name;
-       struct inode            *fromchild_i, *fromchild_relookup_i, 
*tochild_i, *toparent_i, *fromparent_i;
+       struct vnode            *fromparent_vnode;
+       struct inode            *fromparent_i;
+       struct componentname    *from_name;
+       struct vnode            *fromchild_vnode;
+       struct inode            *fromchild_i, *fromchild_relookup_i;
+
+       struct vnode            *toparent_vnode;
+       struct inode            *toparent_i;
+       struct componentname    *to_name;
+       struct vnode            *tochild_vnode;
+       struct inode            *tochild_i;
+
        struct mount            *mp;
        struct direct           *newdir;
        int                     doingdirectory, oldparent, newparent, error;
@@ -993,7 +1002,8 @@
         * Check for cross-device rename.
         */
        if ((fromchild_vnode->v_mount != toparent_vnode->v_mount) ||
-           (tochild_vnode && (fromchild_vnode->v_mount != 
tochild_vnode->v_mount))) {
+           (tochild_vnode &&
+            (fromchild_vnode->v_mount != tochild_vnode->v_mount))) {
                error = EXDEV;
  abortit:
                VOP_ABORTOP(toparent_vnode, to_name); /* XXX, why not in NFS? */
@@ -1012,8 +1022,9 @@
        /*
         * Check if just deleting a link name.
         */
-       if (tochild_vnode && ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | 
APPEND)) ||
-           (VTOI(toparent_vnode)->i_flags & APPEND))) {
+       if (tochild_vnode &&
+           ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | APPEND)) ||
+            (VTOI(toparent_vnode)->i_flags & APPEND))) {
                error = EPERM;
                goto abortit;
        }
@@ -1034,11 +1045,13 @@
                from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
                from_name->cn_nameiop = DELETE;
                vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fromparent_vnode, &fromchild_vnode, 
from_name))) {
+               if ((error = relookup(fromparent_vnode, &fromchild_vnode,
+                                     from_name))) {
                        vput(fromparent_vnode);
                        return (error);
                }
-               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode, 
from_name));
+               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode,
+                                  from_name));
        }
        if ((error = vn_lock(fromchild_vnode, LK_EXCLUSIVE)) != 0)
                goto abortit;
@@ -1059,7 +1072,8 @@
                /*
                 * Avoid ".", "..", and aliases of "." for obvious reasons.
                 */
-               if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == 
'.') ||
+               if ((from_name->cn_namelen == 1 &&
+                    from_name->cn_nameptr[0] == '.') ||
                    fromparent_i == fromchild_i ||
                    (from_name->cn_flags & ISDOTDOT) ||
                    (to_name->cn_flags & ISDOTDOT) ||
@@ -1119,8 +1133,10 @@
                        goto bad;
                if (tochild_i != NULL)
                        vput(tochild_vnode);
-               vref(toparent_vnode);   /* compensate for the ref checkpath 
loses */
-               if ((error = ufs_checkpath(fromchild_i, toparent_i, 
to_name->cn_cred)) != 0) {
+               /* compensate for the ref checkpath loses */
+               vref(toparent_vnode);
+               if ((error = ufs_checkpath(fromchild_i, toparent_i,
+                                          to_name->cn_cred)) != 0) {
                        vrele(toparent_vnode);
                        goto out;
                }
@@ -1184,7 +1200,8 @@
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
                vput(toparent_vnode);
        } else {
-               if (tochild_i->i_dev != toparent_i->i_dev || tochild_i->i_dev 
!= fromchild_i->i_dev)
+               if (tochild_i->i_dev != toparent_i->i_dev ||
+                   tochild_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Short circuit rename(foo, foo).
@@ -1212,7 +1229,8 @@
                 */
                if ((tochild_i->i_mode & IFMT) == IFDIR) {
                        if (tochild_i->i_nlink > 2 ||
-                           !ufs_dirempty(tochild_i, toparent_i->i_number, 
to_name->cn_cred)) {
+                           !ufs_dirempty(tochild_i, toparent_i->i_number,
+                                         to_name->cn_cred)) {
                                error = ENOTEMPTY;
                                goto bad;
                        }
@@ -1225,7 +1243,8 @@
                        error = EISDIR;
                        goto bad;
                }
-               if ((error = ufs_dirrewrite(toparent_i, tochild_i, 
fromchild_i->i_number,
+               if ((error = ufs_dirrewrite(toparent_i, tochild_i,
+                   fromchild_i->i_number,
                    IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
                        goto bad;
@@ -1302,10 +1321,12 @@
                if (doingdirectory && newparent) {
                        KASSERT(toparent_i != NULL);
                        fromchild_relookup_i->i_offset = 
mastertemplate.dot_reclen;
-                       ufs_dirrewrite(fromchild_relookup_i, toparent_i, 
newparent, DT_DIR, 0, IN_CHANGE);
+                       ufs_dirrewrite(fromchild_relookup_i, toparent_i,
+                           newparent, DT_DIR, 0, IN_CHANGE);
                        cache_purge(fromparent_vnode);
                }
-               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i, 
from_name->cn_flags, 0);
+               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i,
+                   from_name->cn_flags, 0);
                fromchild_relookup_i->i_flag &= ~IN_RENAME;
        }
        VN_KNOTE(fromchild_vnode, NOTE_RENAME);
diff -r 0be35579a3be sys/ufs/ufs/ufs_wapbl.c
--- a/sys/ufs/ufs/ufs_wapbl.c   Sun Sep 27 23:15:37 2009 -0400
+++ b/sys/ufs/ufs/ufs_wapbl.c   Sun Sep 27 23:16:49 2009 -0400
@@ -158,9 +158,19 @@
                struct vnode            *a_tvp;
                struct componentname    *a_tcnp;
        } */ *ap = v;
-       struct vnode            *tochild_vnode, *toparent_vnode, 
*fromchild_vnode, *fromparent_vnode;
-       struct componentname    *to_name, *from_name;
-       struct inode            *fromchild_i, *tochild_i, 
*fromchild_relookup_i, *toparent_i, *fromparent_i;
+       struct vnode            *fromparent_vnode;
+       struct inode            *fromparent_i;
+       struct componentname    *from_name;
+       struct vnode            *fromchild_vnode;
+       struct inode            *fromchild_i;
+       struct inode            *fromchild_relookup_i;
+
+       struct vnode            *toparent_vnode;
+       struct inode            *toparent_i;
+       struct componentname    *to_name;
+       struct vnode            *tochild_vnode;
+       struct inode            *tochild_i;
+
        struct mount            *mp;
        struct direct           *newdir;
        int                     doingdirectory, oldparent, newparent, error;
@@ -192,7 +202,8 @@
         * Check for cross-device rename.
         */
        if ((fromchild_vnode->v_mount != toparent_vnode->v_mount) ||
-           (tochild_vnode && (fromchild_vnode->v_mount != 
tochild_vnode->v_mount))) {
+           (tochild_vnode &&
+            (fromchild_vnode->v_mount != tochild_vnode->v_mount))) {
                error = EXDEV;
  abortit:
                VOP_ABORTOP(toparent_vnode, to_name); /* XXX, why not in NFS? */
@@ -211,8 +222,9 @@
        /*
         * Check if just deleting a link name.
         */
-       if (tochild_vnode && ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | 
APPEND)) ||
-           (VTOI(toparent_vnode)->i_flags & APPEND))) {
+       if (tochild_vnode && 
+           ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | APPEND)) ||
+            (VTOI(toparent_vnode)->i_flags & APPEND))) {
                error = EPERM;
                goto abortit;
        }
@@ -233,11 +245,13 @@
                from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
                from_name->cn_nameiop = DELETE;
                vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fromparent_vnode, &fromchild_vnode, 
from_name))) {
+               if ((error = relookup(fromparent_vnode, &fromchild_vnode,
+                                     from_name))) {
                        vput(fromparent_vnode);
                        return (error);
                }
-               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode, 
from_name));
+               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode,
+                                  from_name));
        }
        if ((error = vn_lock(fromchild_vnode, LK_EXCLUSIVE)) != 0)
                goto abortit;
@@ -258,7 +272,8 @@
                /*
                 * Avoid ".", "..", and aliases of "." for obvious reasons.
                 */
-               if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == 
'.') ||
+               if ((from_name->cn_namelen == 1 &&
+                    from_name->cn_nameptr[0] == '.') ||
                    fromparent_i == fromchild_i ||
                    (from_name->cn_flags & ISDOTDOT) ||
                    (to_name->cn_flags & ISDOTDOT) ||
@@ -305,8 +320,10 @@
                if (tochild_i != NULL)
                        vput(tochild_vnode);
                tochild_i = NULL;
-               vref(toparent_vnode);   /* compensate for the ref checkpath 
loses */
-               if ((error = ufs_checkpath(fromchild_i, toparent_i, 
to_name->cn_cred)) != 0) {
+               /* compensate for the ref checkpath loses */
+               vref(toparent_vnode);
+               if ((error = ufs_checkpath(fromchild_i, toparent_i,
+                                          to_name->cn_cred)) != 0) {
                        vrele(toparent_vnode);
                        toparent_i = NULL;
                        goto out;
@@ -329,7 +346,10 @@
         * by unlocking toparent_vnode and regrabbing it with vget after?
         */
 
-       /* save directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       /*
+        * save directory lookup information in case toparent_vnode ==
+        * fromparent_vnode
+        */
        saved_t_count  = toparent_i->i_count;
        saved_t_endoff = toparent_i->i_endoff;
        saved_t_diroff = toparent_i->i_diroff;
@@ -349,7 +369,8 @@
                        goto out;
                }
                vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fromparent_vnode, &fromchild_vnode, 
from_name))) {
+               if ((error = relookup(fromparent_vnode, &fromchild_vnode,
+                                     from_name))) {
                        vput(fromparent_vnode);
                        vrele(ap->a_fvp);
                        goto out2;
@@ -378,13 +399,19 @@
        }
        vrele(ap->a_fvp);
 
-       /* save directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       /*
+        * save directory lookup information in case toparent_vnode ==
+        * fromparent_vnode
+        */
        saved_f_count  = fromparent_i->i_count;
        saved_f_diroff = fromparent_i->i_diroff;
        saved_f_offset = fromparent_i->i_offset;
        saved_f_reclen = fromparent_i->i_reclen;
 
-       /* restore directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       /*
+        * restore directory lookup information in case toparent_vnode
+        * == fromparent_vnode
+        */
        toparent_i->i_offset = saved_t_offset;
        toparent_i->i_reclen = saved_t_reclen;
        toparent_i->i_count  = saved_t_count;
@@ -455,7 +482,8 @@
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
        } else {
-               if (tochild_i->i_dev != toparent_i->i_dev || tochild_i->i_dev 
!= fromchild_i->i_dev)
+               if (tochild_i->i_dev != toparent_i->i_dev ||
+                   tochild_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Short circuit rename(foo, foo).
@@ -483,7 +511,8 @@
                 */
                if ((tochild_i->i_mode & IFMT) == IFDIR) {
                        if (tochild_i->i_nlink > 2 ||
-                           !ufs_dirempty(tochild_i, toparent_i->i_number, 
to_name->cn_cred)) {
+                           !ufs_dirempty(tochild_i, toparent_i->i_number,
+                                         to_name->cn_cred)) {
                                error = ENOTEMPTY;
                                goto bad;
                        }
@@ -496,7 +525,8 @@
                        error = EISDIR;
                        goto bad;
                }
-               if ((error = ufs_dirrewrite(toparent_i, tochild_i, 
fromchild_i->i_number,
+               if ((error = ufs_dirrewrite(toparent_i, tochild_i,
+                   fromchild_i->i_number,
                    IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
                        goto bad;
@@ -517,15 +547,18 @@
                        tochild_i->i_nlink--;
                        DIP_ASSIGN(tochild_i, nlink, tochild_i->i_nlink);
                        tochild_i->i_flag |= IN_CHANGE;
-                       if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0, 
IO_SYNC,
-                           to_name->cn_cred)))
+                       if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0,
+                           IO_SYNC, to_name->cn_cred)))
                                goto bad;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
                VN_KNOTE(tochild_vnode, NOTE_DELETE);
        }
 
-       /* restore directory lookup information in case toparent_vnode == 
fromparent_vnode */
+       /*
+        * restore directory lookup information in case toparent_vnode
+        * == fromparent_vnode
+        */
        fromparent_i->i_offset = saved_f_offset;
        fromparent_i->i_reclen = saved_f_reclen;
        fromparent_i->i_count  = saved_f_count;
@@ -559,8 +592,8 @@
                 * compaction and the original location.
                 */
                fromparent_i->i_offset = saved_t_offset;
-               error = ufs_blkatoff(fromparent_vnode, 
(off_t)fromparent_i->i_offset, &dirbuf, &bp,
-                   false);
+               error = ufs_blkatoff(fromparent_vnode,
+                   (off_t)fromparent_i->i_offset, &dirbuf, &bp, false);
                if (error)
                        goto bad;
 
@@ -579,8 +612,9 @@
                        if ((fromparent_i->i_offset & bmask) == 0) {
                                if (bp != NULL)
                                        brelse(bp, 0);
-                               error = ufs_blkatoff(fromparent_vnode, 
(off_t)fromparent_i->i_offset,
-                                   &dirbuf, &bp, false);
+                               error = ufs_blkatoff(fromparent_vnode,
+                                   (off_t)fromparent_i->i_offset, &dirbuf, &bp,
+                                   false);
                                if (error)
                                        goto bad;
                                entryoffsetinblock = 0;
@@ -616,9 +650,10 @@
                KASSERT(fromparent_i->i_offset <= endsearch);
 
                /*
-                * If fromparent_i->i_offset points to start of a directory 
block,
-                * set fromparent_i->i_count so ufs_dirremove() doesn't compact 
over
-                * a directory block boundary.
+                * If fromparent_i->i_offset points to start of a
+                * directory block, set fromparent_i->i_count so
+                * ufs_dirremove() doesn't compact over a directory
+                * block boundary.
                 */
                if ((fromparent_i->i_offset & (dirblksiz - 1)) == 0)
                        fromparent_i->i_count = 0;
@@ -651,10 +686,12 @@
                if (doingdirectory && newparent) {
                        KASSERT(toparent_i != NULL);
                        fromchild_relookup_i->i_offset = 
mastertemplate.dot_reclen;
-                       ufs_dirrewrite(fromchild_relookup_i, toparent_i, 
newparent, DT_DIR, 0, IN_CHANGE);
+                       ufs_dirrewrite(fromchild_relookup_i, toparent_i,
+                           newparent, DT_DIR, 0, IN_CHANGE);
                        cache_purge(fromparent_vnode);
                }
-               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i, 
from_name->cn_flags, 0);
+               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i,
+                   from_name->cn_flags, 0);
                fromchild_relookup_i->i_flag &= ~IN_RENAME;
        }
        VN_KNOTE(fromchild_vnode, NOTE_RENAME);
Apply a large sledgehammer to ufs_rename and ufs_wapbl_rename.

This should fix (nearly) all the abundant deadlocks and race
conditions it had. One last set of race conditions needs nontrivial
namei cleanup first, and there's quite a bit of ugliness that namei
cleanup can make go away.

ufs_wapbl_rename is now almost completely in sync with ufs_rename,
since the major difference had to do with rearranging the bodgy lock
handling to avoid deadlocking on the wapbl journal lock. I'll leave it
to the wapbl guys to finish the merge though.

I mostly left the core rename logic alone; it could stand further
tidying. Many of the error checks could be moved earlier, and since
the locking is now sane there's no difficulty with doing so.

XXX: I have my doubts as to whether all the permission checks are
XXX: being made properly. What's here is what was here before, modulo
XXX: any gross blunders I managed to make ... hopefully none.

PRs: kern/8491 kern/24887 kern/41417 and probably others.

diff -r 5339016a36e5 sys/ufs/ufs/ufs_extern.h
--- a/sys/ufs/ufs/ufs_extern.h  Mon Sep 28 00:48:56 2009 -0400
+++ b/sys/ufs/ufs/ufs_extern.h  Mon Sep 28 00:56:22 2009 -0400
@@ -129,7 +129,8 @@
 int    ufs_dirremove(struct vnode *, struct inode *, int, int);
 int    ufs_dirrewrite(struct inode *, struct inode *, ino_t, int, int, int);
 int    ufs_dirempty(struct inode *, ino_t, kauth_cred_t);
-int    ufs_checkpath(struct inode *, struct inode *, kauth_cred_t);
+int    ufs_parentcheck(struct vnode *, struct vnode *, kauth_cred_t,
+                       int *, struct vnode **);
 int    ufs_blkatoff(struct vnode *, off_t, char **, struct buf **, bool);
 
 /* ufs_quota.c */
diff -r 5339016a36e5 sys/ufs/ufs/ufs_lookup.c
--- a/sys/ufs/ufs/ufs_lookup.c  Mon Sep 28 00:48:56 2009 -0400
+++ b/sys/ufs/ufs/ufs_lookup.c  Mon Sep 28 00:56:22 2009 -0400
@@ -1168,6 +1168,7 @@
        return (1);
 }
 
+#if 0 /* XXX remove ufs_checkpath() before commit */
 /*
  * Check if source directory is in the path of the target directory.
  * Target is supplied locked, source is unlocked.
@@ -1240,6 +1241,127 @@
                vput(vp);
        return (error);
 }
+#endif
+
+/*
+ * Extract the inode number of ".." from a directory.
+ * Helper for ufs_parentcheck.
+ */
+static int
+ufs_readdotdot(struct vnode *vp, int needswap, kauth_cred_t cred, ino_t 
*result)
+{
+       struct dirtemplate dirbuf;
+       int namlen, error;
+
+       error = vn_rdwr(UIO_READ, vp, &dirbuf,
+                   sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
+                   IO_NODELOCKED, cred, NULL, NULL);
+       if (error) {
+               return error;
+       }
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+       if (FSFMT(vp) && needswap == 0)
+               namlen = dirbuf.dotdot_type;
+       else
+               namlen = dirbuf.dotdot_namlen;
+#else
+       if (FSFMT(vp) && needswap != 0)
+               namlen = dirbuf.dotdot_type;
+       else
+               namlen = dirbuf.dotdot_namlen;
+#endif
+       if (namlen != 2 ||
+           dirbuf.dotdot_name[0] != '.' ||
+           dirbuf.dotdot_name[1] != '.') {
+               printf("ufs_readdotdot: directory %llu contains "
+                      "garbage instead of ..\n",
+                      (unsigned long long) VTOI(vp)->i_number);
+               return ENOTDIR;
+       }
+       *result = ufs_rw32(dirbuf.dotdot_ino, needswap);
+       return 0;
+}
+
+/*
+ * Check if LOWER is a descendent of UPPER. If we find UPPER, return
+ * nonzero in FOUND and return a reference to the immediate descendent
+ * of UPPER in UPPERCHILD. If we don't find UPPER (that is, if we
+ * reach the volume root and that isn't UPPER), return zero in FOUND
+ * and null in UPPERCHILD.
+ *
+ * Neither UPPER nor LOWER should be locked.
+ *
+ * On error (such as a permissions error checking up the directory
+ * tree) fail entirely.
+ */
+int
+ufs_parentcheck(struct vnode *upper, struct vnode *lower, kauth_cred_t cred,
+               int *found_ret, struct vnode **upperchild_ret)
+{
+       const int needswap = UFS_MPNEEDSWAP(target->i_ump);
+       ino_t upper_ino, found_ino;
+       struct vnode *current, *next;
+       int error;
+
+       if (upper == lower) {
+               VREF(upper);
+               *found_ret = 1;
+               *upperchild_ret = upper;
+               return 0;
+       }
+       if (VTOI(lower)->i_number == ROOTINO) {
+               *found_ret = 0;
+               *upperchild_ret = NULL;
+               return 0;
+       }
+
+       upper_ino = VTOI(upper)->i_number;
+
+       current = lower;
+       VREF(current);
+       vn_lock(current, LK_EXCLUSIVE | LK_RETRY);
+
+       for (;;) {
+               error = ufs_readdotdot(current, needswap, cred, &found_ino);
+               if (error) {
+                       vput(current);
+                       return error;
+               }
+               if (found_ino == upper_ino) {
+                       VOP_UNLOCK(current, 0);
+                       *found_ret = 1;
+                       *upperchild_ret = current;
+                       return 0;
+               }
+               if (found_ino == ROOTINO) {
+                       vput(current);
+                       *found_ret = 0;
+                       *upperchild_ret = NULL;
+                       return 0;
+               }
+               VOP_UNLOCK(current, 0);
+               error = VFS_VGET(current->v_mount, found_ino, &next);
+               if (error) {
+                       vrele(current);
+                       return error;
+               }
+               KASSERT(VOP_ISLOCKED(next));
+               if (next->v_type != VDIR) {
+                       printf("ufs_parentcheck: inode %llu reached via .. of "
+                              "inode %llu is not a directory\n",
+                           (unsigned long long)VTOI(next)->i_number,
+                           (unsigned long long)VTOI(current)->i_number);
+                       vput(next);
+                       vrele(current);
+                       return ENOTDIR;
+               }
+               vrele(current);
+               current = next;
+       }
+
+       return 0;
+}
 
 #define        UFS_DIRRABLKS 0
 int ufs_dirrablks = UFS_DIRRABLKS;
diff -r 5339016a36e5 sys/ufs/ufs/ufs_vnops.c
--- a/sys/ufs/ufs/ufs_vnops.c   Mon Sep 28 00:48:56 2009 -0400
+++ b/sys/ufs/ufs/ufs_vnops.c   Mon Sep 28 00:56:22 2009 -0400
@@ -968,7 +968,7 @@
        struct inode            *fromparent_i;
        struct componentname    *from_name;
        struct vnode            *fromchild_vnode;
-       struct inode            *fromchild_i, *fromchild_relookup_i;
+       struct inode            *fromchild_i;
 
        struct vnode            *toparent_vnode;
        struct inode            *toparent_i;
@@ -976,126 +976,473 @@
        struct vnode            *tochild_vnode;
        struct inode            *tochild_i;
 
+       struct vnode            *illegal_vnode;
        struct mount            *mp;
        struct direct           *newdir;
-       int                     doingdirectory, oldparent, newparent, error;
+       int                     error;
+       int                     oldparent, newparent;
+       int                     doingdirectory;
+       int                     foundfromparent;
+       int                     do_race_condition;
+       int                     noabort = 0;
 
 #ifdef WAPBL
        if (ap->a_tdvp->v_mount->mnt_wapbl)
                return wapbl_ufs_rename(v);
 #endif
 
-       tochild_vnode = ap->a_tvp;
+       /*
+        * Due to oddities in namei and in the fs-independent code,
+        * currently we are called with references to all four vnodes,
+        * but with only toparent_vnode locked.
+        *
+        * In a sane world we'd be passed just the two parent vnodes,
+        * both unlocked, and two (string) names.
+        *
+        * For now we will begin by setting up to mimic the sane world.
+        */
+
+       fromparent_vnode = ap->a_fdvp;
+       fromparent_i = VTOI(fromparent_vnode);
+
+       from_name = ap->a_fcnp;
+       fromchild_vnode = NULL;
+
        toparent_vnode = ap->a_tdvp;
-       fromchild_vnode = ap->a_fvp;
-       fromparent_vnode = ap->a_fdvp;
+       toparent_i = VTOI(toparent_vnode);
+
        to_name = ap->a_tcnp;
-       from_name = ap->a_fcnp;
-       doingdirectory = oldparent = newparent = error = 0;
+       tochild_vnode = NULL;
+
+       VOP_UNLOCK(toparent_vnode, 0);
+
+       illegal_vnode = NULL;
+       do_race_condition = 0;
+
+       /*
+        * Part 1: check for broken args and illegal operations.
+        */
 
 #ifdef DIAGNOSTIC
+       /* We expect SAVESTART to have been set. */
        if ((to_name->cn_flags & HASBUF) == 0 ||
            (from_name->cn_flags & HASBUF) == 0)
                panic("ufs_rename: no name");
 #endif
-       /*
-        * Check for cross-device rename.
-        */
-       if ((fromchild_vnode->v_mount != toparent_vnode->v_mount) ||
-           (tochild_vnode &&
-            (fromchild_vnode->v_mount != tochild_vnode->v_mount))) {
+
+       /* Cross-device rename is prohibited. */
+       if (fromparent_vnode->v_mount != toparent_vnode->v_mount) {
                error = EXDEV;
- abortit:
-               VOP_ABORTOP(toparent_vnode, to_name); /* XXX, why not in NFS? */
-               if (toparent_vnode == tochild_vnode)
-                       vrele(toparent_vnode);
-               else
-                       vput(toparent_vnode);
-               if (tochild_vnode)
-                       vput(tochild_vnode);
-               VOP_ABORTOP(fromparent_vnode, from_name); /* XXX, why not in 
NFS? */
-               vrele(fromparent_vnode);
-               vrele(fromchild_vnode);
-               return (error);
+               goto fail_early;
+       }
+
+       /* If either parent isn't a directory, just give up now. */
+       if (fromparent_vnode->v_type != VDIR ||
+           toparent_vnode->v_type != VDIR) {
+               error = ENOTDIR;
+               goto fail_early;
+       }
+
+       /* The "." and ".." entries may not be renamed. */
+       if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == '.') ||
+           (from_name->cn_flags & ISDOTDOT) ||
+           (to_name->cn_flags & ISDOTDOT)) {
+               error = EINVAL;
+               goto fail_early;
        }
 
        /*
-        * Check if just deleting a link name.
+        * Part 2: get locks.
+        *
+        * We lock parent vnodes before child vnodes. This means in
+        * particular that if A is above B in the directory tree then
+        * A must be locked before B. (This is true regardless of how
+        * many steps appear in between, because an arbitrary number
+        * of other processes could lock parent/child in between and
+        * establish a lock cycle and deadlock.)
+        *
+        * Therefore, if TOPARENT is above FROMPARENT we must lock
+        * TOPARENT first; if FROMPARENT is above TOPARENT we must
+        * lock FROMPARENT first; and if they're incommensurate it
+        * doesn't matter. (But, we rely on the fact that there's a
+        * whole-volume rename lock to prevent deadlock among groups
+        * of renames upon overlapping sets of incommensurate vnodes.)
+        *
+        * In addition to establishing lock ordering the parent check
+        * also serves to rule out cases where someone tries to move a
+        * directory underneath itself, e.g. rename("a/b", "a/b/c").
+        * If allowed to proceed such renames would detach portions of
+        * the directory tree and make fsck very unhappy.
+        *
+        * Note that it is an error for *FROMCHILD* to be above
+        * TOPARENT; however, *FROMPARENT* can be above TOPARENT, as
+        * in rename("a/b", "a/c/d").
+        *
+        * The parent check searches up the tree from TOPARENT until
+        * it either finds FROMPARENT or the root of the volume. It
+        * also returns the vnode it saw immediately before TOPARENT,
+        * if any. Later on (after looking up FROMCHILD) we will check
+        * to see if this *is* FROMCHILD and if so fail.
+        *
+        * If the parent check finds FROMPARENT, it means FROMPARENT
+        * is above TOPARENT, so we lock FROMPARENT first and then
+        * TOPARENT. Otherwise, either TOPARENT is above FROMPARENT or
+        * they're incommensurate and we lock TOPARENT first.
+        *
+        * In either case each of the child vnodes has to be looked up
+        * and locked immediately after its parent. The two cases
+        *
+        *       FROMPARENT/FROMCHILD/TOPARENT/TOCHILD
+        *       TOPARENT/TOCHILD/FROMPARENT/FROMCHILD
+        *
+        * can cause deadlock otherwise. (Note that both of these are
+        * error cases; the first fails the parent check and the
+        * second fails because TOCHILD isn't empty. The parent check
+        * case can be handled without attempting to lock FROMCHILD,
+        * (XXX or at least it could be in a saner world of namei),
+        * but the nonempty case requires locking TOCHILD to test.
+        *
+        * Therefore the procedure is either
+        *
+        *   lock FROMPARENT
+        *   lookup FROMCHILD
+        *   lock FROMCHILD
+        *   lock TOPARENT
+        *   lookup TOCHILD
+        *   lock TOCHILD
+        *
+        * or
+        *
+        *   lock TOPARENT
+        *   lookup TOCHILD
+        *   lock TOCHILD
+        *   lock FROMPARENT
+        *   lookup FROMCHILD
+        *   lock FROMCHILD
+        *
+        * In a saner namei world we could simplify this (some) by always
+        * handling FROMCHILD last, but that isn't currently possible.
+        *
+        * Note that for now we aren't doing lookup so much as relookup()
+        * and checking what we find against what was passed down from the
+        * fs-independent code.
+        *
+        * On top of all the above, just to make everything more
+        * exciting, any two of the vnodes might end up being the same.
+        *
+        * FROMPARENT == FROMCHILD      mv a/. foo      is an error.
+        * FROMPARENT == TOPARENT       mv a/b a/c      is ok.
+        * FROMPARENT == TOCHILD        mv a/b/c a/b    will give ENOTEMPTY.
+        * FROMCHILD == TOPARENT        mv a/b a/b/c    fails the parent check.
+        * FROMCHILD == TOCHILD         mv a/b a/b      is ok.
+        * TOPARENT == TOCHILD          mv foo a/.      is an error.
+        *
+        * This introduces more cases in the locking, because each
+        * distinct vnode must be locked exactly once.
+        *
+        * When FROMPARENT == TOPARENT and FROMCHILD != TOCHILD we
+        * assume it doesn't matter what order the children are locked
+        * in, because the per-volume rename lock excludes other
+        * renames and no other operation locks two files in the same
+        * directory at once. (Note: if it turns out that link() does,
+        * link() is wrong.)
         */
-       if (tochild_vnode &&
-           ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | APPEND)) ||
-            (VTOI(toparent_vnode)->i_flags & APPEND))) {
+
+       /*
+        * XXX until such time as we can do lookups without the namei
+        * and lookup machinery "helpfully" locking the result vnode
+        * for us, we can't avoid tripping on cases where FROMCHILD ==
+        * TOCHILD. The right way to do this is to check after looking
+        * the second one up and only lock it if it's different. Easy,
+        * but requires having the right underlying abstractions.
+        *
+        * Instead, we'll have to check the child vnode passed down in
+        * the argument block. For the branch where we look up
+        * FROMCHILD first we do this:
+        *
+        *     - check if FROMCHILD is the same as the TOCHILD passed
+        *       in (ap->a_tvp)
+        *     - if so, unlock FROMCHILD before looking up TOCHILD
+        *       and set do_race_condition
+        *     - afterward if they aren't the same lock FROMCHILD again.
+        *
+        * Eww.
+        *
+        * On the plus side, this method at least shouldn't be able to
+        * deadlock when relocking FROMCHILD.
+        *
+        * Needless to say this is a race condition and should be
+        * fixed up once namei has been rendered sufficiently sane.
+        */
+
+       // XXX do we need this?
+       //from_name->cn_flags &= ~SAVESTART;
+       //to_name->cn_flags &= ~SAVESTART;
+       // XXX or this form instead?
+       //from_name->cn_flags &= ~(MODMASK | SAVESTART);
+       //from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
+
+       if (fromparent_vnode == toparent_vnode) {
+               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               error = relookup(fromparent_vnode, &fromchild_vnode,
+                                from_name);
+               if (error) {
+                       VOP_UNLOCK(fromparent_vnode, 0);
+                       goto fail_nolocks;
+               }
+               /* XXX see note above */
+               if (fromchild_vnode == ap->a_tvp) {
+                       VOP_UNLOCK(fromchild_vnode, 0);
+                       do_race_condition = 1;
+               }
+               error = relookup(toparent_vnode, &tochild_vnode,
+                                to_name);
+               if (error && error != ENOENT) {
+                       /*VOP_UNLOCK(toparent_vnode); -- same as fromparent */
+                       VOP_UNLOCK(fromchild_vnode, 0);
+                       VOP_UNLOCK(fromparent_vnode, 0);
+                       goto fail_nolocks;
+               }
+               if (error == ENOENT) {
+                       /*
+                        * Note: currently in this case relookup() succeeds
+                        * and sets tochild_vnode = NULL, but this piece of
+                        * logic will be wanted in the (saner) future.
+                        * (XXX: remove this comment when appropriate)
+                        */
+                       tochild_vnode = NULL;
+               }
+               if (do_race_condition) {
+                       if (tochild_vnode != fromchild_vnode) {
+                               vn_lock(fromchild_vnode,
+                                       LK_EXCLUSIVE | LK_RETRY);
+                       }
+               }
+       }
+       else {
+               error = ufs_parentcheck(fromparent_vnode, toparent_vnode,
+                           from_name->cn_cred,
+                           &foundfromparent, &illegal_vnode);
+               if (error) {
+                       goto fail_nolocks;
+               }
+               if (foundfromparent) {
+                       vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       error = relookup(fromparent_vnode, &fromchild_vnode,
+                                        from_name);
+                       if (error) {
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (fromchild_vnode == toparent_vnode) {
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               VOP_UNLOCK(fromchild_vnode, 0);
+                               error = EINVAL;
+                               goto fail_nolocks;
+                       }
+                       vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       /* XXX see note above */
+                       if (fromchild_vnode == ap->a_tvp) {
+                               VOP_UNLOCK(fromchild_vnode, 0);
+                               do_race_condition = 1;
+                       }
+                       error = relookup(toparent_vnode, &tochild_vnode,
+                                        to_name);
+                       if (error && error != ENOENT) {
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               VOP_UNLOCK(fromchild_vnode, 0);
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (error == ENOENT) {
+                               tochild_vnode = NULL;
+                       }
+                       if (do_race_condition) {
+                               if (tochild_vnode != fromchild_vnode) {
+                                       vn_lock(fromchild_vnode,
+                                               LK_EXCLUSIVE | LK_RETRY);
+                               }
+                       }
+               }
+               else {
+                       vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       error = relookup(toparent_vnode, &tochild_vnode,
+                                        to_name);
+                       if (error && error != ENOENT) {
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (error == ENOENT) {
+                               tochild_vnode = NULL;
+                       }
+                       if (fromparent_vnode == tochild_vnode) {
+                               VOP_UNLOCK(tochild_vnode, 0);
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               error = ENOTEMPTY;
+                               goto fail_nolocks;
+                       }
+                       vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       /* XXX see note above */
+                       if (tochild_vnode && tochild_vnode == ap->a_fvp) {
+                               VOP_UNLOCK(tochild_vnode, 0);
+                               do_race_condition = 1;
+                       }
+                       error = relookup(fromparent_vnode, &fromchild_vnode,
+                                        from_name);
+                       if (error) {
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               VOP_UNLOCK(tochild_vnode, 0);
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (do_race_condition) {
+                               if (fromchild_vnode != tochild_vnode) {
+                                       vn_lock(tochild_vnode,
+                                               LK_EXCLUSIVE | LK_RETRY);
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Note: after relookup to get fromchild_vnode and
+        * tochild_vnode, we have extra references to (what may or may
+        * not be the same) vnodes in ap->a_fvp and ap->a_tvp. These
+        * are cleaned up at function exit below.
+        */
+
+       /* Now that we have fromchild, make sure it passes the parent check */
+       if (fromchild_vnode == illegal_vnode) {
+               vrele(illegal_vnode);
+               error = EINVAL;
+               goto fail_withlocks;
+       }
+       vrele(illegal_vnode);
+       illegal_vnode = NULL;
+
+       /*
+        * All four vnodes are now locked, and we can proceed to do
+        * the real work.
+        */
+
+       KASSERT(VOP_ISLOCKED(fromparent_vnode));
+       KASSERT(VOP_ISLOCKED(fromchild_vnode));
+       KASSERT(VOP_ISLOCKED(toparent_vnode));
+       KASSERT(tochild_vnode == NULL || VOP_ISLOCKED(tochild_vnode));
+
+       /* Get the rest of the inodes. */
+       fromchild_i = VTOI(fromchild_vnode);
+       tochild_i = tochild_vnode ? VTOI(tochild_vnode) : NULL;
+
+       KASSERT(fromparent_i != fromchild_i);
+       KASSERT(tochild_i == NULL || toparent_i != tochild_i);
+
+       /*
+        * Part 3. More checks.
+        */
+
+       /* More cross-directory cases. */
+       if (fromparent_vnode->v_mount != fromchild_vnode->v_mount) {
+               error = EXDEV;
+               goto fail_withlocks;
+       }
+       if (tochild_vnode != NULL &&
+           toparent_vnode->v_mount != tochild_vnode->v_mount) {
+               error = EXDEV;
+               goto fail_withlocks;
+       }
+
+       /* Check flags. */
+       if ((fromparent_i->i_flags & APPEND) != 0) {
                error = EPERM;
-               goto abortit;
+               goto fail_withlocks;
        }
+       if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+       if ((toparent_i->i_flags & APPEND) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+       if (tochild_vnode != NULL &&
+           (tochild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+
+       /* Moving a directory over itself is not allowed. */
+       if (fromchild_vnode == tochild_vnode &&
+           fromchild_vnode->v_type == VDIR) {
+               error = EINVAL;
+               goto fail_withlocks;
+       }
+
+       /* Make sure bumping the target's link count won't overflow. */
+       if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
+               error = EMLINK;
+               goto fail_withlocks;
+       }
+
+       /* This flag is now useless and should be removed. */
+       if (fromchild_i->i_flag & IN_RENAME) {
+               error = EINVAL;
+               goto fail_withlocks;
+       }
+
+       /*
+        * Part 4. Rename logic.
+        */
+
+       /* If moving a file over itself, just unlink the from end. */
        if (fromchild_vnode == tochild_vnode) {
-               if (fromchild_vnode->v_type == VDIR) {
-                       error = EINVAL;
-                       goto abortit;
+               /* This case was ruled out above. */
+               KASSERT(fromchild_vnode->v_type != VDIR);
+
+               /* Release to-side completely. */
+               VOP_ABORTOP(toparent_vnode, to_name);
+               if (toparent_vnode != fromparent_vnode) {
+                       VOP_UNLOCK(toparent_vnode, 0);
+               }
+               vrele(toparent_vnode);
+               vrele(tochild_vnode);
+
+               /* Clean up a_fvp/a_tvp. */
+               vrele(ap->a_fvp);
+               if (ap->a_tvp != NULL) {
+                       vrele(ap->a_tvp);
                }
 
-               /* Release destination completely. */
-               VOP_ABORTOP(toparent_vnode, to_name);
-               vput(toparent_vnode);
-               vput(tochild_vnode);
-
-               /* Delete source. */
-               vrele(fromchild_vnode);
+               /* Now unlink the from-side. */
                from_name->cn_flags &= ~(MODMASK | SAVESTART);
                from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
                from_name->cn_nameiop = DELETE;
-               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fromparent_vnode, &fromchild_vnode,
-                                     from_name))) {
-                       vput(fromparent_vnode);
-                       return (error);
-               }
-               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode,
-                                  from_name));
+               return VOP_REMOVE(fromparent_vnode, fromchild_vnode, from_name);
        }
-       if ((error = vn_lock(fromchild_vnode, LK_EXCLUSIVE)) != 0)
-               goto abortit;
-       fromparent_i = VTOI(fromparent_vnode);
-       fromchild_i = VTOI(fromchild_vnode);
-       if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
-               VOP_UNLOCK(fromchild_vnode, 0);
-               error = EMLINK;
-               goto abortit;
+
+       oldparent = fromparent_i->i_number;
+       newparent = toparent_i->i_number;
+       doingdirectory = (fromchild_i->i_mode & IFMT) == IFDIR;
+
+       /* XXX update the logic below to not test "newparent!=0" */
+       if (oldparent == newparent) {
+               newparent = 0;
        }
-       if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) ||
-               (fromparent_i->i_flags & APPEND)) {
-               VOP_UNLOCK(fromchild_vnode, 0);
-               error = EPERM;
-               goto abortit;
+
+       /* Check permissions. */
+       error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
+       if (error) {
+               goto fail_withlocks;
        }
-       if ((fromchild_i->i_mode & IFMT) == IFDIR) {
-               /*
-                * Avoid ".", "..", and aliases of "." for obvious reasons.
-                */
-               if ((from_name->cn_namelen == 1 &&
-                    from_name->cn_nameptr[0] == '.') ||
-                   fromparent_i == fromchild_i ||
-                   (from_name->cn_flags & ISDOTDOT) ||
-                   (to_name->cn_flags & ISDOTDOT) ||
-                   (fromchild_i->i_flag & IN_RENAME)) {
-                       VOP_UNLOCK(fromchild_vnode, 0);
-                       error = EINVAL;
-                       goto abortit;
-               }
+
+       noabort = 1;
+
+       if (doingdirectory) {
                fromchild_i->i_flag |= IN_RENAME;
-               oldparent = fromparent_i->i_number;
-               doingdirectory = 1;
        }
-       VN_KNOTE(fromparent_vnode, NOTE_WRITE);         /* XXXLUKEM/XXX: right 
place? */
 
-       /*
-        * When the target exists, both the directory
-        * and target vnodes are returned locked.
-        */
-       toparent_i = VTOI(toparent_vnode);
-       tochild_i = NULL;
-       if (tochild_vnode)
-               tochild_i = VTOI(tochild_vnode);
+       /* XXXLUKEM/XXX: right place? */
+       VN_KNOTE(fromparent_vnode, NOTE_WRITE);
 
        mp = fromparent_vnode->v_mount;
        fstrans_start(mp, FSTRANS_SHARED);
@@ -1109,56 +1456,19 @@
        fromchild_i->i_nlink++;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
-       if ((error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP)) != 
0) {
-               VOP_UNLOCK(fromchild_vnode, 0);
-               goto bad;
+       error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP);
+       if (error) {
+               goto fail_withfstrans;
        }
 
        /*
-        * If ".." must be changed (ie the directory gets a new
-        * parent) then the source directory must not be in the
-        * directory hierarchy above the target, as this would
-        * orphan everything below the source directory. Also
-        * the user must have write permission in the source so
-        * as to be able to change "..". We must repeat the call
-        * to namei, as the parent directory is unlocked by the
-        * call to checkpath().
-        */
-       error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
-       VOP_UNLOCK(fromchild_vnode, 0);
-       if (oldparent != toparent_i->i_number)
-               newparent = toparent_i->i_number;
-       if (doingdirectory && newparent) {
-               if (error)      /* write access check above */
-                       goto bad;
-               if (tochild_i != NULL)
-                       vput(tochild_vnode);
-               /* compensate for the ref checkpath loses */
-               vref(toparent_vnode);
-               if ((error = ufs_checkpath(fromchild_i, toparent_i,
-                                          to_name->cn_cred)) != 0) {
-                       vrele(toparent_vnode);
-                       goto out;
-               }
-               to_name->cn_flags &= ~SAVESTART;
-               vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               error = relookup(toparent_vnode, &tochild_vnode, to_name);
-               if (error != 0) {
-                       vput(toparent_vnode);
-                       goto out;
-               }
-               toparent_i = VTOI(toparent_vnode);
-               tochild_i = NULL;
-               if (tochild_vnode)
-                       tochild_i = VTOI(tochild_vnode);
-       }
-       /*
         * 2) If target doesn't exist, link the target
         *    to the source and unlink the source.
         *    Otherwise, rewrite the target directory
         *    entry to reference the source inode and
         *    expunge the original entry's existence.
         */
+
        if (tochild_i == NULL) {
                if (toparent_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
@@ -1170,7 +1480,7 @@
                if (doingdirectory && newparent) {
                        if ((nlink_t)toparent_i->i_nlink >= LINK_MAX) {
                                error = EMLINK;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                        toparent_i->i_nlink++;
                        DIP_ASSIGN(toparent_i, nlink, toparent_i->i_nlink);
@@ -1180,7 +1490,7 @@
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                }
                newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
@@ -1193,12 +1503,11 @@
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                                (void)UFS_UPDATE(toparent_vnode, NULL, NULL,
-                                                UPDATE_WAIT|UPDATE_DIROP);
+                                                UPDATE_WAIT | UPDATE_DIROP);
                        }
-                       goto bad;
+                       goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
-               vput(toparent_vnode);
        } else {
                if (tochild_i->i_dev != toparent_i->i_dev ||
                    tochild_i->i_dev != fromchild_i->i_dev)
@@ -1220,7 +1529,7 @@
                    kauth_cred_geteuid(to_name->cn_cred) != toparent_i->i_uid &&
                    tochild_i->i_uid != kauth_cred_geteuid(to_name->cn_cred)) {
                        error = EPERM;
-                       goto bad;
+                       goto fail_withlinkcount;
                }
                /*
                 * Target must be empty if a directory and have no links
@@ -1232,22 +1541,22 @@
                            !ufs_dirempty(tochild_i, toparent_i->i_number,
                                          to_name->cn_cred)) {
                                error = ENOTEMPTY;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                        if (!doingdirectory) {
                                error = ENOTDIR;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                        cache_purge(toparent_vnode);
                } else if (doingdirectory) {
                        error = EISDIR;
-                       goto bad;
+                       goto fail_withlinkcount;
                }
                if ((error = ufs_dirrewrite(toparent_i, tochild_i,
                    fromchild_i->i_number,
                    IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
-                       goto bad;
+                       goto fail_withlinkcount;
                if (doingdirectory) {
                        /*
                         * Truncate inode. The only stuff left in the directory
@@ -1264,41 +1573,17 @@
                        tochild_i->i_nlink--;
                        DIP_ASSIGN(tochild_i, nlink, tochild_i->i_nlink);
                        tochild_i->i_flag |= IN_CHANGE;
-                       if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0, 
IO_SYNC,
-                           to_name->cn_cred)))
-                               goto bad;
+                       if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0,
+                           IO_SYNC, to_name->cn_cred)))
+                               goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
-               vput(toparent_vnode);
                VN_KNOTE(tochild_vnode, NOTE_DELETE);
-               vput(tochild_vnode);
-               tochild_i = NULL;
        }
 
        /*
         * 3) Unlink the source.
         */
-       from_name->cn_flags &= ~(MODMASK | SAVESTART);
-       from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
-       vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-       if ((error = relookup(fromparent_vnode, &fromchild_vnode, from_name))) {
-               vput(fromparent_vnode);
-               vrele(ap->a_fvp);
-               goto out2;
-       }
-       if (fromchild_vnode != NULL) {
-               fromchild_relookup_i = VTOI(fromchild_vnode);
-               toparent_i = VTOI(fromparent_vnode);
-       } else {
-               /*
-                * From name has disappeared.
-                */
-               if (doingdirectory)
-                       panic("rename: lost dir entry");
-               vrele(ap->a_fvp);
-               error = 0;
-               goto out2;
-       }
        /*
         * Ensure that the directory entry still exists and has not
         * changed while the new name has been entered. If the source is
@@ -1308,57 +1593,85 @@
         * flag ensures that it cannot be moved by another rename or removed
         * by a rmdir.
         */
-       if (fromchild_relookup_i != fromchild_i) {
-               if (doingdirectory)
-                       panic("rename: lost dir entry");
+
+       /*
+        * If the source is a directory with a
+        * new parent, the link count of the old
+        * parent directory must be decremented
+        * and ".." set to point to the new parent.
+        */
+       if (doingdirectory && newparent) {
+               KASSERT(toparent_i != NULL);
+               fromchild_i->i_offset = mastertemplate.dot_reclen;
+               ufs_dirrewrite(fromchild_i, toparent_i,
+                   newparent, DT_DIR, 0, IN_CHANGE);
+               cache_purge(fromparent_vnode);
+       }
+       error = ufs_dirremove(fromparent_vnode, fromchild_i,
+           from_name->cn_flags, 0);
+       VN_KNOTE(fromchild_vnode, NOTE_RENAME);
+
+       if (error) {
+               goto fail_withfstrans;
+       }
+       goto succeed;
+
+ fail_withlinkcount:   
+       fromchild_i->i_nlink--;
+       DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
+       fromchild_i->i_flag |= IN_CHANGE;
+       UFS_WAPBL_UPDATE(fromchild_vnode, NULL, NULL, 0);
+
+ fail_withfstrans:
+ succeed:
+       fstrans_done(mp);
+       fromchild_i->i_flag &= ~IN_RENAME;
+
+ fail_withlocks:
+       /* See comment up top about which of these can legally be the same. */
+       if (fromparent_vnode == toparent_vnode) {
+               VOP_UNLOCK(fromparent_vnode, 0);
        } else {
-               /*
-                * If the source is a directory with a
-                * new parent, the link count of the old
-                * parent directory must be decremented
-                * and ".." set to point to the new parent.
-                */
-               if (doingdirectory && newparent) {
-                       KASSERT(toparent_i != NULL);
-                       fromchild_relookup_i->i_offset = 
mastertemplate.dot_reclen;
-                       ufs_dirrewrite(fromchild_relookup_i, toparent_i,
-                           newparent, DT_DIR, 0, IN_CHANGE);
-                       cache_purge(fromparent_vnode);
+               VOP_UNLOCK(fromparent_vnode, 0);
+               VOP_UNLOCK(toparent_vnode, 0);
+       }
+       if (fromchild_vnode == tochild_vnode) {
+               VOP_UNLOCK(fromchild_vnode, 0);
+       } else {
+               VOP_UNLOCK(fromchild_vnode, 0);
+               if (tochild_vnode) {
+                       VOP_UNLOCK(tochild_vnode, 0);
                }
-               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i,
-                   from_name->cn_flags, 0);
-               fromchild_relookup_i->i_flag &= ~IN_RENAME;
        }
-       VN_KNOTE(fromchild_vnode, NOTE_RENAME);
-       if (toparent_i)
-               vput(fromparent_vnode);
-       if (fromchild_relookup_i)
-               vput(fromchild_vnode);
+
+ fail_nolocks:
+       if (fromchild_vnode != NULL) {
+               vrele(fromchild_vnode);
+               fromchild_vnode = NULL;
+       }
+       if (tochild_vnode != NULL) {
+               vrele(tochild_vnode);
+               tochild_vnode = NULL;
+       }
+
+ fail_early:
+       if (!noabort) {
+               /* XXX, why not in NFS? */
+               VOP_ABORTOP(fromparent_vnode, from_name);
+               VOP_ABORTOP(toparent_vnode, to_name);
+       }
+
+       vrele(fromparent_vnode);
+       vrele(toparent_vnode);
+
+       KASSERT(fromchild_vnode == NULL);
+       KASSERT(tochild_vnode == NULL);
+
        vrele(ap->a_fvp);
-       goto out2;
+       if (ap->a_tvp != NULL)
+               vrele(ap->a_tvp);
 
-       /* exit routines from steps 1 & 2 */
- bad:
-       if (tochild_i)
-               vput(ITOV(tochild_i));
-       vput(ITOV(toparent_i));
- out:
-       if (doingdirectory)
-               fromchild_i->i_flag &= ~IN_RENAME;
-       if (vn_lock(fromchild_vnode, LK_EXCLUSIVE) == 0) {
-               fromchild_i->i_nlink--;
-               DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
-               fromchild_i->i_flag |= IN_CHANGE;
-               fromchild_i->i_flag &= ~IN_RENAME;
-               vput(fromchild_vnode);
-       } else
-               vrele(fromchild_vnode);
-       vrele(fromparent_vnode);
-
-       /* exit routines from step 3 */
- out2:
-       fstrans_done(mp);
-       return (error);
+       return error;
 }
 
 int
diff -r 5339016a36e5 sys/ufs/ufs/ufs_wapbl.c
--- a/sys/ufs/ufs/ufs_wapbl.c   Mon Sep 28 00:48:56 2009 -0400
+++ b/sys/ufs/ufs/ufs_wapbl.c   Mon Sep 28 00:56:22 2009 -0400
@@ -163,7 +163,6 @@
        struct componentname    *from_name;
        struct vnode            *fromchild_vnode;
        struct inode            *fromchild_i;
-       struct inode            *fromchild_relookup_i;
 
        struct vnode            *toparent_vnode;
        struct inode            *toparent_i;
@@ -171,9 +170,15 @@
        struct vnode            *tochild_vnode;
        struct inode            *tochild_i;
 
+       struct vnode            *illegal_vnode;
        struct mount            *mp;
        struct direct           *newdir;
-       int                     doingdirectory, oldparent, newparent, error;
+       int                     error;
+       int                     oldparent, newparent;
+       int                     doingdirectory;
+       int                     foundfromparent;
+       int                     do_race_condition;
+       int                     noabort = 0;
 
        int32_t   saved_f_count;
        doff_t    saved_f_diroff;
@@ -185,242 +190,506 @@
        doff_t    saved_t_offset;
        u_int32_t saved_t_reclen;
 
-       tochild_vnode = ap->a_tvp;
+       /*
+        * Due to oddities in namei and in the fs-independent code,
+        * currently we are called with references to all four vnodes,
+        * but with only toparent_vnode locked.
+        *
+        * In a sane world we'd be passed just the two parent vnodes,
+        * both unlocked, and two (string) names.
+        *
+        * For now we will begin by setting up to mimic the sane world.
+        */
+
+       fromparent_vnode = ap->a_fdvp;
+       fromparent_i = VTOI(fromparent_vnode);
+
+       from_name = ap->a_fcnp;
+       fromchild_vnode = NULL;
+
        toparent_vnode = ap->a_tdvp;
-       fromchild_vnode = ap->a_fvp;
-       fromparent_vnode = ap->a_fdvp;
+       toparent_i = VTOI(toparent_vnode);
+
        to_name = ap->a_tcnp;
-       from_name = ap->a_fcnp;
-       doingdirectory = oldparent = newparent = error = 0;
+       tochild_vnode = NULL;
+
+       VOP_UNLOCK(toparent_vnode, 0);
+
+       illegal_vnode = NULL;
+       do_race_condition = 0;
+
+       /* Because gcc 4.1 is not being real smart, zero all this */
+       saved_f_count = 0;
+       saved_f_diroff = 0;
+       saved_f_offset = 0;
+       saved_f_reclen = 0;
+       saved_t_count = 0;
+       saved_t_endoff = 0;
+       saved_t_diroff = 0;
+       saved_t_offset = 0;
+       saved_t_reclen = 0;
+
+       /*
+        * Part 1: check for broken args and illegal operations.
+        */
 
 #ifdef DIAGNOSTIC
+       /* We expect SAVESTART to have been set. */
        if ((to_name->cn_flags & HASBUF) == 0 ||
            (from_name->cn_flags & HASBUF) == 0)
                panic("ufs_rename: no name");
 #endif
-       /*
-        * Check for cross-device rename.
-        */
-       if ((fromchild_vnode->v_mount != toparent_vnode->v_mount) ||
-           (tochild_vnode &&
-            (fromchild_vnode->v_mount != tochild_vnode->v_mount))) {
+
+       /* Cross-device rename is prohibited. */
+       if (fromparent_vnode->v_mount != toparent_vnode->v_mount) {
                error = EXDEV;
- abortit:
-               VOP_ABORTOP(toparent_vnode, to_name); /* XXX, why not in NFS? */
-               if (toparent_vnode == tochild_vnode)
-                       vrele(toparent_vnode);
-               else
-                       vput(toparent_vnode);
-               if (tochild_vnode)
-                       vput(tochild_vnode);
-               VOP_ABORTOP(fromparent_vnode, from_name); /* XXX, why not in 
NFS? */
-               vrele(fromparent_vnode);
-               vrele(fromchild_vnode);
-               return (error);
+               goto fail_early;
+       }
+
+       /* If either parent isn't a directory, just give up now. */
+       if (fromparent_vnode->v_type != VDIR ||
+           toparent_vnode->v_type != VDIR) {
+               error = ENOTDIR;
+               goto fail_early;
+       }
+
+       /* The "." and ".." entries may not be renamed. */
+       if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == '.') ||
+           (from_name->cn_flags & ISDOTDOT) ||
+           (to_name->cn_flags & ISDOTDOT)) {
+               error = EINVAL;
+               goto fail_early;
        }
 
        /*
-        * Check if just deleting a link name.
+        * Part 2: get locks.
+        *
+        * We lock parent vnodes before child vnodes. This means in
+        * particular that if A is above B in the directory tree then
+        * A must be locked before B. (This is true regardless of how
+        * many steps appear in between, because an arbitrary number
+        * of other processes could lock parent/child in between and
+        * establish a lock cycle and deadlock.)
+        *
+        * Therefore, if TOPARENT is above FROMPARENT we must lock
+        * TOPARENT first; if FROMPARENT is above TOPARENT we must
+        * lock FROMPARENT first; and if they're incommensurate it
+        * doesn't matter. (But, we rely on the fact that there's a
+        * whole-volume rename lock to prevent deadlock among groups
+        * of renames upon overlapping sets of incommensurate vnodes.)
+        *
+        * In addition to establishing lock ordering the parent check
+        * also serves to rule out cases where someone tries to move a
+        * directory underneath itself, e.g. rename("a/b", "a/b/c").
+        * If allowed to proceed such renames would detach portions of
+        * the directory tree and make fsck very unhappy.
+        *
+        * Note that it is an error for *FROMCHILD* to be above
+        * TOPARENT; however, *FROMPARENT* can be above TOPARENT, as
+        * in rename("a/b", "a/c/d").
+        *
+        * The parent check searches up the tree from TOPARENT until
+        * it either finds FROMPARENT or the root of the volume. It
+        * also returns the vnode it saw immediately before TOPARENT,
+        * if any. Later on (after looking up FROMCHILD) we will check
+        * to see if this *is* FROMCHILD and if so fail.
+        *
+        * If the parent check finds FROMPARENT, it means FROMPARENT
+        * is above TOPARENT, so we lock FROMPARENT first and then
+        * TOPARENT. Otherwise, either TOPARENT is above FROMPARENT or
+        * they're incommensurate and we lock TOPARENT first.
+        *
+        * In either case each of the child vnodes has to be looked up
+        * and locked immediately after its parent. The two cases
+        *
+        *       FROMPARENT/FROMCHILD/TOPARENT/TOCHILD
+        *       TOPARENT/TOCHILD/FROMPARENT/FROMCHILD
+        *
+        * can cause deadlock otherwise. (Note that both of these are
+        * error cases; the first fails the parent check and the
+        * second fails because TOCHILD isn't empty. The parent check
+        * case can be handled without attempting to lock FROMCHILD,
+        * (XXX or at least it could be in a saner world of namei),
+        * but the nonempty case requires locking TOCHILD to test.
+        *
+        * Therefore the procedure is either
+        *
+        *   lock FROMPARENT
+        *   lookup FROMCHILD
+        *   lock FROMCHILD
+        *   lock TOPARENT
+        *   lookup TOCHILD
+        *   lock TOCHILD
+        *
+        * or
+        *
+        *   lock TOPARENT
+        *   lookup TOCHILD
+        *   lock TOCHILD
+        *   lock FROMPARENT
+        *   lookup FROMCHILD
+        *   lock FROMCHILD
+        *
+        * In a saner namei world we could simplify this (some) by always
+        * handling FROMCHILD last, but that isn't currently possible.
+        *
+        * Note that for now we aren't doing lookup so much as relookup()
+        * and checking what we find against what was passed down from the
+        * fs-independent code.
+        *
+        * On top of all the above, just to make everything more
+        * exciting, any two of the vnodes might end up being the same.
+        *
+        * FROMPARENT == FROMCHILD      mv a/. foo      is an error.
+        * FROMPARENT == TOPARENT       mv a/b a/c      is ok.
+        * FROMPARENT == TOCHILD        mv a/b/c a/b    will give ENOTEMPTY.
+        * FROMCHILD == TOPARENT        mv a/b a/b/c    fails the parent check.
+        * FROMCHILD == TOCHILD         mv a/b a/b      is ok.
+        * TOPARENT == TOCHILD          mv foo a/.      is an error.
+        *
+        * This introduces more cases in the locking, because each
+        * distinct vnode must be locked exactly once.
+        *
+        * When FROMPARENT == TOPARENT and FROMCHILD != TOCHILD we
+        * assume it doesn't matter what order the children are locked
+        * in, because the per-volume rename lock excludes other
+        * renames and no other operation locks two files in the same
+        * directory at once. (Note: if it turns out that link() does,
+        * link() is wrong.)
         */
-       if (tochild_vnode && 
-           ((VTOI(tochild_vnode)->i_flags & (IMMUTABLE | APPEND)) ||
-            (VTOI(toparent_vnode)->i_flags & APPEND))) {
-               error = EPERM;
-               goto abortit;
-       }
-       if (fromchild_vnode == tochild_vnode) {
-               if (fromchild_vnode->v_type == VDIR) {
-                       error = EINVAL;
-                       goto abortit;
+
+       /*
+        * XXX until such time as we can do lookups without the namei
+        * and lookup machinery "helpfully" locking the result vnode
+        * for us, we can't avoid tripping on cases where FROMCHILD ==
+        * TOCHILD. The right way to do this is to check after looking
+        * the second one up and only lock it if it's different. Easy,
+        * but requires having the right underlying abstractions.
+        *
+        * Instead, we'll have to check the child vnode passed down in
+        * the argument block. For the branch where we look up
+        * FROMCHILD first we do this:
+        *
+        *     - check if FROMCHILD is the same as the TOCHILD passed
+        *       in (ap->a_tvp)
+        *     - if so, unlock FROMCHILD before looking up TOCHILD
+        *       and set do_race_condition
+        *     - afterward if they aren't the same lock FROMCHILD again.
+        *
+        * Eww.
+        *
+        * On the plus side, this method at least shouldn't be able to
+        * deadlock when relocking FROMCHILD.
+        *
+        * Needless to say this is a race condition and should be
+        * fixed up once namei has been rendered sufficiently sane.
+        */
+
+       // XXX do we need this?
+       //from_name->cn_flags &= ~SAVESTART;
+       //to_name->cn_flags &= ~SAVESTART;
+       // XXX or this form instead?
+       //from_name->cn_flags &= ~(MODMASK | SAVESTART);
+       //from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
+
+       if (fromparent_vnode == toparent_vnode) {
+               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+               error = relookup(fromparent_vnode, &fromchild_vnode,
+                                from_name);
+               if (error) {
+                       VOP_UNLOCK(fromparent_vnode, 0);
+                       goto fail_nolocks;
                }
 
-               /* Release destination completely. */
+               /*
+                * save directory lookup information
+                */
+               saved_f_count  = fromparent_i->i_count;
+               saved_f_diroff = fromparent_i->i_diroff;
+               saved_f_offset = fromparent_i->i_offset;
+               saved_f_reclen = fromparent_i->i_reclen;
+
+               /* XXX see note above */
+               if (fromchild_vnode == ap->a_tvp) {
+                       VOP_UNLOCK(fromchild_vnode, 0);
+                       do_race_condition = 1;
+               }
+               error = relookup(toparent_vnode, &tochild_vnode,
+                                to_name);
+               if (error && error != ENOENT) {
+                       /*VOP_UNLOCK(toparent_vnode); -- same as fromparent */
+                       VOP_UNLOCK(fromchild_vnode, 0);
+                       VOP_UNLOCK(fromparent_vnode, 0);
+                       goto fail_nolocks;
+               }
+               if (error == ENOENT) {
+                       /*
+                        * Note: currently in this case relookup() succeeds
+                        * and sets tochild_vnode = NULL, but this piece of
+                        * logic will be wanted in the (saner) future.
+                        * (XXX: remove this comment when appropriate)
+                        */
+                       tochild_vnode = NULL;
+               }
+               if (do_race_condition) {
+                       if (tochild_vnode != fromchild_vnode) {
+                               vn_lock(fromchild_vnode,
+                                       LK_EXCLUSIVE | LK_RETRY);
+                       }
+               }
+
+               /*
+                * save directory lookup information
+                */
+               saved_t_count  = toparent_i->i_count;
+               saved_t_endoff = toparent_i->i_endoff;
+               saved_t_diroff = toparent_i->i_diroff;
+               saved_t_offset = toparent_i->i_offset;
+               saved_t_reclen = toparent_i->i_reclen;
+       }
+       else {
+               error = ufs_parentcheck(fromparent_vnode, toparent_vnode,
+                           from_name->cn_cred,
+                           &foundfromparent, &illegal_vnode);
+               if (error) {
+                       goto fail_nolocks;
+               }
+               if (foundfromparent) {
+                       vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       error = relookup(fromparent_vnode, &fromchild_vnode,
+                                        from_name);
+                       if (error) {
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (fromchild_vnode == toparent_vnode) {
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               VOP_UNLOCK(fromchild_vnode, 0);
+                               error = EINVAL;
+                               goto fail_nolocks;
+                       }
+                       vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       /* XXX see note above */
+                       if (fromchild_vnode == ap->a_tvp) {
+                               VOP_UNLOCK(fromchild_vnode, 0);
+                               do_race_condition = 1;
+                       }
+                       error = relookup(toparent_vnode, &tochild_vnode,
+                                        to_name);
+                       if (error && error != ENOENT) {
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               VOP_UNLOCK(fromchild_vnode, 0);
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (error == ENOENT) {
+                               tochild_vnode = NULL;
+                       }
+                       if (do_race_condition) {
+                               if (tochild_vnode != fromchild_vnode) {
+                                       vn_lock(fromchild_vnode,
+                                               LK_EXCLUSIVE | LK_RETRY);
+                               }
+                       }
+               }
+               else {
+                       vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       error = relookup(toparent_vnode, &tochild_vnode,
+                                        to_name);
+                       if (error && error != ENOENT) {
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (error == ENOENT) {
+                               tochild_vnode = NULL;
+                       }
+                       if (fromparent_vnode == tochild_vnode) {
+                               VOP_UNLOCK(tochild_vnode, 0);
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               error = ENOTEMPTY;
+                               goto fail_nolocks;
+                       }
+                       vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
+                       /* XXX see note above */
+                       if (tochild_vnode && tochild_vnode == ap->a_fvp) {
+                               VOP_UNLOCK(tochild_vnode, 0);
+                               do_race_condition = 1;
+                       }
+                       error = relookup(fromparent_vnode, &fromchild_vnode,
+                                        from_name);
+                       if (error) {
+                               VOP_UNLOCK(fromparent_vnode, 0);
+                               VOP_UNLOCK(tochild_vnode, 0);
+                               VOP_UNLOCK(toparent_vnode, 0);
+                               goto fail_nolocks;
+                       }
+                       if (do_race_condition) {
+                               if (fromchild_vnode != tochild_vnode) {
+                                       vn_lock(tochild_vnode,
+                                               LK_EXCLUSIVE | LK_RETRY);
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Note: after relookup to get fromchild_vnode and
+        * tochild_vnode, we have extra references to (what may or may
+        * not be the same) vnodes in ap->a_fvp and ap->a_tvp. These
+        * are cleaned up at function exit below.
+        */
+
+       /* Now that we have fromchild, make sure it passes the parent check */
+       if (fromchild_vnode == illegal_vnode) {
+               vrele(illegal_vnode);
+               error = EINVAL;
+               goto fail_withlocks;
+       }
+       vrele(illegal_vnode);
+       illegal_vnode = NULL;
+
+       /*
+        * All four vnodes are now locked, and we can proceed to do
+        * the real work.
+        */
+
+       KASSERT(VOP_ISLOCKED(fromparent_vnode));
+       KASSERT(VOP_ISLOCKED(fromchild_vnode));
+       KASSERT(VOP_ISLOCKED(toparent_vnode));
+       KASSERT(tochild_vnode == NULL || VOP_ISLOCKED(tochild_vnode));
+
+       /* Get the rest of the inodes. */
+       fromchild_i = VTOI(fromchild_vnode);
+       tochild_i = tochild_vnode ? VTOI(tochild_vnode) : NULL;
+
+       KASSERT(fromparent_i != fromchild_i);
+       KASSERT(tochild_i == NULL || toparent_i != tochild_i);
+
+       /*
+        * Part 3. More checks.
+        */
+
+       /* More cross-directory cases. */
+       if (fromparent_vnode->v_mount != fromchild_vnode->v_mount) {
+               error = EXDEV;
+               goto fail_withlocks;
+       }
+       if (tochild_vnode != NULL &&
+           toparent_vnode->v_mount != tochild_vnode->v_mount) {
+               error = EXDEV;
+               goto fail_withlocks;
+       }
+
+       /* Check flags. */
+       if ((fromparent_i->i_flags & APPEND) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+       if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+       if ((toparent_i->i_flags & APPEND) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+       if (tochild_vnode != NULL &&
+           (tochild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
+               error = EPERM;
+               goto fail_withlocks;
+       }
+
+       /* Moving a directory over itself is not allowed. */
+       if (fromchild_vnode == tochild_vnode &&
+           fromchild_vnode->v_type == VDIR) {
+               error = EINVAL;
+               goto fail_withlocks;
+       }
+
+       /* Make sure bumping the target's link count won't overflow. */
+       if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
+               error = EMLINK;
+               goto fail_withlocks;
+       }
+
+       /* This flag is now useless and should be removed. */
+       if (fromchild_i->i_flag & IN_RENAME) {
+               error = EINVAL;
+               goto fail_withlocks;
+       }
+
+       /*
+        * Part 4. Rename logic.
+        */
+
+       /* If moving a file over itself, just unlink the from end. */
+       if (fromchild_vnode == tochild_vnode) {
+               /* This case was ruled out above. */
+               KASSERT(fromchild_vnode->v_type != VDIR);
+
+               /* Release to-side completely. */
                VOP_ABORTOP(toparent_vnode, to_name);
-               vput(toparent_vnode);
-               vput(tochild_vnode);
+               if (toparent_vnode != fromparent_vnode) {
+                       VOP_UNLOCK(toparent_vnode, 0);
+               }
+               vrele(toparent_vnode);
+               vrele(tochild_vnode);
 
-               /* Delete source. */
-               vrele(fromchild_vnode);
+               /* Clean up a_fvp/a_tvp. */
+               vrele(ap->a_fvp);
+               if (ap->a_tvp != NULL) {
+                       vrele(ap->a_tvp);
+               }
+
+               /* Now unlink the from-side. */
                from_name->cn_flags &= ~(MODMASK | SAVESTART);
                from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
                from_name->cn_nameiop = DELETE;
-               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fromparent_vnode, &fromchild_vnode,
-                                     from_name))) {
-                       vput(fromparent_vnode);
-                       return (error);
-               }
-               return (VOP_REMOVE(fromparent_vnode, fromchild_vnode,
-                                  from_name));
+               return VOP_REMOVE(fromparent_vnode, fromchild_vnode, from_name);
        }
-       if ((error = vn_lock(fromchild_vnode, LK_EXCLUSIVE)) != 0)
-               goto abortit;
-       fromparent_i = VTOI(fromparent_vnode);
-       fromchild_i = VTOI(fromchild_vnode);
-       if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
-               VOP_UNLOCK(fromchild_vnode, 0);
-               error = EMLINK;
-               goto abortit;
+
+       oldparent = fromparent_i->i_number;
+       newparent = toparent_i->i_number;
+       doingdirectory = (fromchild_i->i_mode & IFMT) == IFDIR;
+
+       /* XXX update the logic below to not test "newparent!=0" */
+       if (oldparent == newparent) {
+               newparent = 0;
        }
-       if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) ||
-               (fromparent_i->i_flags & APPEND)) {
-               VOP_UNLOCK(fromchild_vnode, 0);
-               error = EPERM;
-               goto abortit;
+
+       /* Check permissions. */
+       error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
+       if (error) {
+               goto fail_withlocks;
        }
-       if ((fromchild_i->i_mode & IFMT) == IFDIR) {
-               /*
-                * Avoid ".", "..", and aliases of "." for obvious reasons.
-                */
-               if ((from_name->cn_namelen == 1 &&
-                    from_name->cn_nameptr[0] == '.') ||
-                   fromparent_i == fromchild_i ||
-                   (from_name->cn_flags & ISDOTDOT) ||
-                   (to_name->cn_flags & ISDOTDOT) ||
-                   (fromchild_i->i_flag & IN_RENAME)) {
-                       VOP_UNLOCK(fromchild_vnode, 0);
-                       error = EINVAL;
-                       goto abortit;
-               }
+
+       noabort = 1;
+
+       if (doingdirectory) {
                fromchild_i->i_flag |= IN_RENAME;
-               doingdirectory = 1;
        }
-       oldparent = fromparent_i->i_number;
-       VN_KNOTE(fromparent_vnode, NOTE_WRITE);         /* XXXLUKEM/XXX: right 
place? */
 
-       /*
-        * When the target exists, both the directory
-        * and target vnodes are returned locked.
-        */
-       toparent_i = VTOI(toparent_vnode);
-       tochild_i = NULL;
-       if (tochild_vnode)
-               tochild_i = VTOI(tochild_vnode);
+       /* XXXLUKEM/XXX: right place? */
+       VN_KNOTE(fromparent_vnode, NOTE_WRITE);
 
        mp = fromparent_vnode->v_mount;
        fstrans_start(mp, FSTRANS_SHARED);
 
-       /*
-        * If ".." must be changed (ie the directory gets a new
-        * parent) then the source directory must not be in the
-        * directory hierarchy above the target, as this would
-        * orphan everything below the source directory. Also
-        * the user must have write permission in the source so
-        * as to be able to change "..". We must repeat the call 
-        * to namei, as the parent directory is unlocked by the
-        * call to checkpath().
-        */
-       error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
-       VOP_UNLOCK(fromchild_vnode, 0);
-       if (oldparent != toparent_i->i_number)
-               newparent = toparent_i->i_number;
-       if (doingdirectory && newparent) {
-               if (error)      /* write access check above */
-                       goto out;
-               if (tochild_i != NULL)
-                       vput(tochild_vnode);
-               tochild_i = NULL;
-               /* compensate for the ref checkpath loses */
-               vref(toparent_vnode);
-               if ((error = ufs_checkpath(fromchild_i, toparent_i,
-                                          to_name->cn_cred)) != 0) {
-                       vrele(toparent_vnode);
-                       toparent_i = NULL;
-                       goto out;
-               }
-               to_name->cn_flags &= ~SAVESTART;
-               toparent_i = NULL;
-               vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               error = relookup(toparent_vnode, &tochild_vnode, to_name);
-               if (error != 0) {
-                       vput(toparent_vnode);
-                       goto out;
-               }
-               toparent_i = VTOI(toparent_vnode);
-               if (tochild_vnode)
-                       tochild_i = VTOI(tochild_vnode);
-       }
-
-       /*
-        * XXX handle case where fromparent_vnode is parent of toparent_vnode,
-        * by unlocking toparent_vnode and regrabbing it with vget after?
-        */
-
-       /*
-        * save directory lookup information in case toparent_vnode ==
-        * fromparent_vnode
-        */
-       saved_t_count  = toparent_i->i_count;
-       saved_t_endoff = toparent_i->i_endoff;
-       saved_t_diroff = toparent_i->i_diroff;
-       saved_t_offset = toparent_i->i_offset;
-       saved_t_reclen = toparent_i->i_reclen;
-
-       /*
-        * This was moved up to before the journal lock to
-        * avoid potential deadlock
-        */
-       from_name->cn_flags &= ~(MODMASK | SAVESTART);
-       from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
-       if (newparent) {
-               /* Check for the rename("foo/foo", "foo") case. */
-               if (fromparent_vnode == tochild_vnode) {
-                       error = doingdirectory ? ENOTEMPTY : EISDIR;
-                       goto out;
-               }
-               vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
-               if ((error = relookup(fromparent_vnode, &fromchild_vnode,
-                                     from_name))) {
-                       vput(fromparent_vnode);
-                       vrele(ap->a_fvp);
-                       goto out2;
-               }
-       } else {
-               error = VOP_LOOKUP(fromparent_vnode, &fromchild_vnode, 
from_name);
-               if (error && (error != EJUSTRETURN)) {
-                       vput(fromparent_vnode);
-                       vrele(ap->a_fvp);
-                       goto out2;
-               }
-               error = 0;
-       }
-       if (fromchild_vnode != NULL) {
-               fromchild_relookup_i = VTOI(fromchild_vnode);
-               fromparent_i = VTOI(fromparent_vnode);
-       } else {
-               /*
-                * From name has disappeared.
-                */
-               if (doingdirectory)
-                       panic("rename: lost dir entry");
-               vrele(ap->a_fvp);
-               error = ENOENT; /* XXX ufs_rename sets "0" here */
-               goto out2;
-       }
-       vrele(ap->a_fvp);
-
-       /*
-        * save directory lookup information in case toparent_vnode ==
-        * fromparent_vnode
-        */
-       saved_f_count  = fromparent_i->i_count;
-       saved_f_diroff = fromparent_i->i_diroff;
-       saved_f_offset = fromparent_i->i_offset;
-       saved_f_reclen = fromparent_i->i_reclen;
+       error = UFS_WAPBL_BEGIN(fromparent_vnode->v_mount);
+       if (error)
+               goto fail_withfstrans;
 
        /*
         * restore directory lookup information in case toparent_vnode
         * == fromparent_vnode
         */
-       toparent_i->i_offset = saved_t_offset;
-       toparent_i->i_reclen = saved_t_reclen;
-       toparent_i->i_count  = saved_t_count;
-       toparent_i->i_endoff = saved_t_endoff;
-       toparent_i->i_diroff = saved_t_diroff;
-
-       error = UFS_WAPBL_BEGIN(fromparent_vnode->v_mount);
-       if (error)
-               goto out2;
+       if (fromparent_vnode == toparent_vnode) {
+               toparent_i->i_offset = saved_t_offset;
+               toparent_i->i_reclen = saved_t_reclen;
+               toparent_i->i_count  = saved_t_count;
+               toparent_i->i_endoff = saved_t_endoff;
+               toparent_i->i_diroff = saved_t_diroff;
+       }
 
        /*
         * 1) Bump link count while we're moving stuff
@@ -431,8 +700,9 @@
        fromchild_i->i_nlink++;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
-       if ((error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP)) != 
0) {
-               goto bad;
+       error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP);
+       if (error) {
+               goto fail_withwapbl;
        }
 
        /*
@@ -442,6 +712,7 @@
         *    entry to reference the source inode and
         *    expunge the original entry's existence.
         */
+
        if (tochild_i == NULL) {
                if (toparent_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
@@ -453,7 +724,7 @@
                if (doingdirectory && newparent) {
                        if ((nlink_t)toparent_i->i_nlink >= LINK_MAX) {
                                error = EMLINK;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                        toparent_i->i_nlink++;
                        DIP_ASSIGN(toparent_i, nlink, toparent_i->i_nlink);
@@ -463,7 +734,7 @@
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                }
                newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
@@ -478,7 +749,7 @@
                                (void)UFS_UPDATE(toparent_vnode, NULL, NULL,
                                                 UPDATE_WAIT | UPDATE_DIROP);
                        }
-                       goto bad;
+                       goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
        } else {
@@ -502,7 +773,7 @@
                    kauth_cred_geteuid(to_name->cn_cred) != toparent_i->i_uid &&
                    tochild_i->i_uid != kauth_cred_geteuid(to_name->cn_cred)) {
                        error = EPERM;
-                       goto bad;
+                       goto fail_withlinkcount;
                }
                /*
                 * Target must be empty if a directory and have no links
@@ -514,22 +785,22 @@
                            !ufs_dirempty(tochild_i, toparent_i->i_number,
                                          to_name->cn_cred)) {
                                error = ENOTEMPTY;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                        if (!doingdirectory) {
                                error = ENOTDIR;
-                               goto bad;
+                               goto fail_withlinkcount;
                        }
                        cache_purge(toparent_vnode);
                } else if (doingdirectory) {
                        error = EISDIR;
-                       goto bad;
+                       goto fail_withlinkcount;
                }
                if ((error = ufs_dirrewrite(toparent_i, tochild_i,
                    fromchild_i->i_number,
                    IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
-                       goto bad;
+                       goto fail_withlinkcount;
                if (doingdirectory) {
                        /*
                         * Truncate inode. The only stuff left in the directory
@@ -549,7 +820,7 @@
                        tochild_i->i_flag |= IN_CHANGE;
                        if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0,
                            IO_SYNC, to_name->cn_cred)))
-                               goto bad;
+                               goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
                VN_KNOTE(tochild_vnode, NOTE_DELETE);
@@ -559,10 +830,12 @@
         * restore directory lookup information in case toparent_vnode
         * == fromparent_vnode
         */
-       fromparent_i->i_offset = saved_f_offset;
-       fromparent_i->i_reclen = saved_f_reclen;
-       fromparent_i->i_count  = saved_f_count;
-       fromparent_i->i_diroff = saved_f_diroff;
+       if (fromparent_vnode == toparent_vnode) {
+               fromparent_i->i_offset = saved_f_offset;
+               fromparent_i->i_reclen = saved_f_reclen;
+               fromparent_i->i_count  = saved_f_count;
+               fromparent_i->i_diroff = saved_f_diroff;
+       }
 
        /*
         * Handle case where the directory we need to remove may have
@@ -595,7 +868,7 @@
                error = ufs_blkatoff(fromparent_vnode,
                    (off_t)fromparent_i->i_offset, &dirbuf, &bp, false);
                if (error)
-                       goto bad;
+                       goto fail_withlinkcount;
 
                /*
                 * keep existing fromparent_i->i_count in case
@@ -616,7 +889,7 @@
                                    (off_t)fromparent_i->i_offset, &dirbuf, &bp,
                                    false);
                                if (error)
-                                       goto bad;
+                                       goto fail_withlinkcount;
                                entryoffsetinblock = 0;
                        }
 
@@ -673,67 +946,88 @@
         * flag ensures that it cannot be moved by another rename or removed
         * by a rmdir.
         */
-       if (fromchild_relookup_i != fromchild_i) {
-               if (doingdirectory)
-                       panic("rename: lost dir entry");
-       } else {
-               /*
-                * If the source is a directory with a
-                * new parent, the link count of the old
-                * parent directory must be decremented
-                * and ".." set to point to the new parent.
-                */
-               if (doingdirectory && newparent) {
-                       KASSERT(toparent_i != NULL);
-                       fromchild_relookup_i->i_offset = 
mastertemplate.dot_reclen;
-                       ufs_dirrewrite(fromchild_relookup_i, toparent_i,
-                           newparent, DT_DIR, 0, IN_CHANGE);
-                       cache_purge(fromparent_vnode);
-               }
-               error = ufs_dirremove(fromparent_vnode, fromchild_relookup_i,
-                   from_name->cn_flags, 0);
-               fromchild_relookup_i->i_flag &= ~IN_RENAME;
+
+       /*
+        * If the source is a directory with a
+        * new parent, the link count of the old
+        * parent directory must be decremented
+        * and ".." set to point to the new parent.
+        */
+       if (doingdirectory && newparent) {
+               KASSERT(toparent_i != NULL);
+               fromchild_i->i_offset = mastertemplate.dot_reclen;
+               ufs_dirrewrite(fromchild_i, toparent_i,
+                   newparent, DT_DIR, 0, IN_CHANGE);
+               cache_purge(fromparent_vnode);
        }
+       error = ufs_dirremove(fromparent_vnode, fromchild_i,
+           from_name->cn_flags, 0);
        VN_KNOTE(fromchild_vnode, NOTE_RENAME);
-       goto done;
 
- out:
-       vrele(fromchild_vnode);
-       vrele(fromparent_vnode);
-       goto out2;
+       if (error) {
+               goto fail_withwapbl;
+       }
+       goto succeed;
 
-       /* exit routines from steps 1 & 2 */
- bad:
-       if (doingdirectory)
-               fromchild_i->i_flag &= ~IN_RENAME;
+ fail_withlinkcount:   
        fromchild_i->i_nlink--;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
-       fromchild_i->i_flag &= ~IN_RENAME;
        UFS_WAPBL_UPDATE(fromchild_vnode, NULL, NULL, 0);
- done:
+
+ fail_withwapbl:
+ succeed:
        UFS_WAPBL_END(fromparent_vnode->v_mount);
-       vput(fromparent_vnode);
-       vput(fromchild_vnode);
- out2:
-       /*
-        * clear IN_RENAME - some exit paths happen too early to go
-        * through the cleanup done in the "bad" case above, so we
-        * always do this mini-cleanup here.
-        */
+
+ fail_withfstrans:
+       fstrans_done(mp);
        fromchild_i->i_flag &= ~IN_RENAME;
 
-       if (tochild_i)
-               vput(ITOV(tochild_i));
-       if (toparent_i) {
-               if (newparent)
-                       vput(ITOV(toparent_i));
-               else
-                       vrele(ITOV(toparent_i));
+ fail_withlocks:
+       /* See comment up top about which of these can legally be the same. */
+       if (fromparent_vnode == toparent_vnode) {
+               VOP_UNLOCK(fromparent_vnode, 0);
+       } else {
+               VOP_UNLOCK(fromparent_vnode, 0);
+               VOP_UNLOCK(toparent_vnode, 0);
+       }
+       if (fromchild_vnode == tochild_vnode) {
+               VOP_UNLOCK(fromchild_vnode, 0);
+       } else {
+               VOP_UNLOCK(fromchild_vnode, 0);
+               if (tochild_vnode) {
+                       VOP_UNLOCK(tochild_vnode, 0);
+               }
        }
 
-       fstrans_done(mp);
-       return (error);
+ fail_nolocks:
+       if (fromchild_vnode != NULL) {
+               vrele(fromchild_vnode);
+               fromchild_vnode = NULL;
+       }
+       if (tochild_vnode != NULL) {
+               vrele(tochild_vnode);
+               tochild_vnode = NULL;
+       }
+
+ fail_early:
+       if (!noabort) {
+               /* XXX, why not in NFS? */
+               VOP_ABORTOP(fromparent_vnode, from_name);
+               VOP_ABORTOP(toparent_vnode, to_name);
+       }
+
+       vrele(fromparent_vnode);
+       vrele(toparent_vnode);
+
+       KASSERT(fromchild_vnode == NULL);
+       KASSERT(tochild_vnode == NULL);
+
+       vrele(ap->a_fvp);
+       if (ap->a_tvp != NULL)
+               vrele(ap->a_tvp);
+
+       return error;
 }
 
 #ifdef WAPBL_DEBUG_INODES
(This is the new non-wapbl rename.)

/*
 * Rename vnode operation
 *      rename("foo", "bar");
 * is essentially
 *      unlink("bar");
 *      link("foo", "bar");
 *      unlink("foo");
 * but ``atomically''.  Can't do full commit without saving state in the
 * inode on disk which isn't feasible at this time.  Best we can do is
 * always guarantee the target exists.
 *
 * Basic algorithm is:
 *
 * 1) Bump link count on source while we're linking it to the
 *    target.  This also ensure the inode won't be deleted out
 *    from underneath us while we work (it may be truncated by
 *    a concurrent `trunc' or `open' for creation).
 * 2) Link source to destination.  If destination already exists,
 *    delete it first.
 * 3) Unlink source reference to inode if still around. If a
 *    directory was moved and the parent of the destination
 *    is different from the source, patch the ".." entry in the
 *    directory.
 */
int
ufs_rename(void *v)
{
        struct vop_rename_args  /* {
                struct vnode            *a_fdvp;
                struct vnode            *a_fvp;
                struct componentname    *a_fcnp;
                struct vnode            *a_tdvp;
                struct vnode            *a_tvp;
                struct componentname    *a_tcnp;
        } */ *ap = v;
        struct vnode            *fromparent_vnode;
        struct inode            *fromparent_i;
        struct componentname    *from_name;
        struct vnode            *fromchild_vnode;
        struct inode            *fromchild_i;

        struct vnode            *toparent_vnode;
        struct inode            *toparent_i;
        struct componentname    *to_name;
        struct vnode            *tochild_vnode;
        struct inode            *tochild_i;

        struct vnode            *illegal_vnode;
        struct mount            *mp;
        struct direct           *newdir;
        int                     error;
        int                     oldparent, newparent;
        int                     doingdirectory;
        int                     foundfromparent;
        int                     do_race_condition;
        int                     noabort = 0;

#ifdef WAPBL
        if (ap->a_tdvp->v_mount->mnt_wapbl)
                return wapbl_ufs_rename(v);
#endif

        /*
         * Due to oddities in namei and in the fs-independent code,
         * currently we are called with references to all four vnodes,
         * but with only toparent_vnode locked.
         *
         * In a sane world we'd be passed just the two parent vnodes,
         * both unlocked, and two (string) names.
         *
         * For now we will begin by setting up to mimic the sane world.
         */

        fromparent_vnode = ap->a_fdvp;
        fromparent_i = VTOI(fromparent_vnode);

        from_name = ap->a_fcnp;
        fromchild_vnode = NULL;

        toparent_vnode = ap->a_tdvp;
        toparent_i = VTOI(toparent_vnode);

        to_name = ap->a_tcnp;
        tochild_vnode = NULL;

        VOP_UNLOCK(toparent_vnode, 0);

        illegal_vnode = NULL;
        do_race_condition = 0;

        /*
         * Part 1: check for broken args and illegal operations.
         */

#ifdef DIAGNOSTIC
        /* We expect SAVESTART to have been set. */
        if ((to_name->cn_flags & HASBUF) == 0 ||
            (from_name->cn_flags & HASBUF) == 0)
                panic("ufs_rename: no name");
#endif

        /* Cross-device rename is prohibited. */
        if (fromparent_vnode->v_mount != toparent_vnode->v_mount) {
                error = EXDEV;
                goto fail_early;
        }

        /* If either parent isn't a directory, just give up now. */
        if (fromparent_vnode->v_type != VDIR ||
            toparent_vnode->v_type != VDIR) {
                error = ENOTDIR;
                goto fail_early;
        }

        /* The "." and ".." entries may not be renamed. */
        if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == '.') ||
            (from_name->cn_flags & ISDOTDOT) ||
            (to_name->cn_flags & ISDOTDOT)) {
                error = EINVAL;
                goto fail_early;
        }

        /*
         * Part 2: get locks.
         *
         * We lock parent vnodes before child vnodes. This means in
         * particular that if A is above B in the directory tree then
         * A must be locked before B. (This is true regardless of how
         * many steps appear in between, because an arbitrary number
         * of other processes could lock parent/child in between and
         * establish a lock cycle and deadlock.)
         *
         * Therefore, if TOPARENT is above FROMPARENT we must lock
         * TOPARENT first; if FROMPARENT is above TOPARENT we must
         * lock FROMPARENT first; and if they're incommensurate it
         * doesn't matter. (But, we rely on the fact that there's a
         * whole-volume rename lock to prevent deadlock among groups
         * of renames upon overlapping sets of incommensurate vnodes.)
         *
         * In addition to establishing lock ordering the parent check
         * also serves to rule out cases where someone tries to move a
         * directory underneath itself, e.g. rename("a/b", "a/b/c").
         * If allowed to proceed such renames would detach portions of
         * the directory tree and make fsck very unhappy.
         *
         * Note that it is an error for *FROMCHILD* to be above
         * TOPARENT; however, *FROMPARENT* can be above TOPARENT, as
         * in rename("a/b", "a/c/d").
         *
         * The parent check searches up the tree from TOPARENT until
         * it either finds FROMPARENT or the root of the volume. It
         * also returns the vnode it saw immediately before TOPARENT,
         * if any. Later on (after looking up FROMCHILD) we will check
         * to see if this *is* FROMCHILD and if so fail.
         *
         * If the parent check finds FROMPARENT, it means FROMPARENT
         * is above TOPARENT, so we lock FROMPARENT first and then
         * TOPARENT. Otherwise, either TOPARENT is above FROMPARENT or
         * they're incommensurate and we lock TOPARENT first.
         *
         * In either case each of the child vnodes has to be looked up
         * and locked immediately after its parent. The two cases
         *
         *       FROMPARENT/FROMCHILD/TOPARENT/TOCHILD
         *       TOPARENT/TOCHILD/FROMPARENT/FROMCHILD
         *
         * can cause deadlock otherwise. (Note that both of these are
         * error cases; the first fails the parent check and the
         * second fails because TOCHILD isn't empty. The parent check
         * case can be handled without attempting to lock FROMCHILD,
         * (XXX or at least it could be in a saner world of namei),
         * but the nonempty case requires locking TOCHILD to test.
         *
         * Therefore the procedure is either
         *
         *   lock FROMPARENT
         *   lookup FROMCHILD
         *   lock FROMCHILD
         *   lock TOPARENT
         *   lookup TOCHILD
         *   lock TOCHILD
         *
         * or
         *
         *   lock TOPARENT
         *   lookup TOCHILD
         *   lock TOCHILD
         *   lock FROMPARENT
         *   lookup FROMCHILD
         *   lock FROMCHILD
         *
         * In a saner namei world we could simplify this (some) by always
         * handling FROMCHILD last, but that isn't currently possible.
         *
         * Note that for now we aren't doing lookup so much as relookup()
         * and checking what we find against what was passed down from the
         * fs-independent code.
         *
         * On top of all the above, just to make everything more
         * exciting, any two of the vnodes might end up being the same.
         *
         * FROMPARENT == FROMCHILD      mv a/. foo      is an error.
         * FROMPARENT == TOPARENT       mv a/b a/c      is ok.
         * FROMPARENT == TOCHILD        mv a/b/c a/b    will give ENOTEMPTY.
         * FROMCHILD == TOPARENT        mv a/b a/b/c    fails the parent check.
         * FROMCHILD == TOCHILD         mv a/b a/b      is ok.
         * TOPARENT == TOCHILD          mv foo a/.      is an error.
         *
         * This introduces more cases in the locking, because each
         * distinct vnode must be locked exactly once.
         *
         * When FROMPARENT == TOPARENT and FROMCHILD != TOCHILD we
         * assume it doesn't matter what order the children are locked
         * in, because the per-volume rename lock excludes other
         * renames and no other operation locks two files in the same
         * directory at once. (Note: if it turns out that link() does,
         * link() is wrong.)
         */

        /*
         * XXX until such time as we can do lookups without the namei
         * and lookup machinery "helpfully" locking the result vnode
         * for us, we can't avoid tripping on cases where FROMCHILD ==
         * TOCHILD. The right way to do this is to check after looking
         * the second one up and only lock it if it's different. Easy,
         * but requires having the right underlying abstractions.
         *
         * Instead, we'll have to check the child vnode passed down in
         * the argument block. For the branch where we look up
         * FROMCHILD first we do this:
         *
         *     - check if FROMCHILD is the same as the TOCHILD passed
         *       in (ap->a_tvp)
         *     - if so, unlock FROMCHILD before looking up TOCHILD
         *       and set do_race_condition
         *     - afterward if they aren't the same lock FROMCHILD again.
         *
         * Eww.
         *
         * On the plus side, this method at least shouldn't be able to
         * deadlock when relocking FROMCHILD.
         *
         * Needless to say this is a race condition and should be
         * fixed up once namei has been rendered sufficiently sane.
         */

        // XXX do we need this?
        //from_name->cn_flags &= ~SAVESTART;
        //to_name->cn_flags &= ~SAVESTART;
        // XXX or this form instead?
        //from_name->cn_flags &= ~(MODMASK | SAVESTART);
        //from_name->cn_flags |= LOCKPARENT | LOCKLEAF;

        if (fromparent_vnode == toparent_vnode) {
                vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                error = relookup(fromparent_vnode, &fromchild_vnode,
                                 from_name);
                if (error) {
                        VOP_UNLOCK(fromparent_vnode, 0);
                        goto fail_nolocks;
                }
                /* XXX see note above */
                if (fromchild_vnode == ap->a_tvp) {
                        VOP_UNLOCK(fromchild_vnode, 0);
                        do_race_condition = 1;
                }
                error = relookup(toparent_vnode, &tochild_vnode,
                                 to_name);
                if (error && error != ENOENT) {
                        /*VOP_UNLOCK(toparent_vnode); -- same as fromparent */
                        VOP_UNLOCK(fromchild_vnode, 0);
                        VOP_UNLOCK(fromparent_vnode, 0);
                        goto fail_nolocks;
                }
                if (error == ENOENT) {
                        /*
                         * Note: currently in this case relookup() succeeds
                         * and sets tochild_vnode = NULL, but this piece of
                         * logic will be wanted in the (saner) future.
                         * (XXX: remove this comment when appropriate)
                         */
                        tochild_vnode = NULL;
                }
                if (do_race_condition) {
                        if (tochild_vnode != fromchild_vnode) {
                                vn_lock(fromchild_vnode,
                                        LK_EXCLUSIVE | LK_RETRY);
                        }
                }
        }
        else {
                error = ufs_parentcheck(fromparent_vnode, toparent_vnode,
                            from_name->cn_cred,
                            &foundfromparent, &illegal_vnode);
                if (error) {
                        goto fail_nolocks;
                }
                if (foundfromparent) {
                        vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        error = relookup(fromparent_vnode, &fromchild_vnode,
                                         from_name);
                        if (error) {
                                VOP_UNLOCK(fromparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (fromchild_vnode == toparent_vnode) {
                                VOP_UNLOCK(fromparent_vnode, 0);
                                VOP_UNLOCK(fromchild_vnode, 0);
                                error = EINVAL;
                                goto fail_nolocks;
                        }
                        vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        /* XXX see note above */
                        if (fromchild_vnode == ap->a_tvp) {
                                VOP_UNLOCK(fromchild_vnode, 0);
                                do_race_condition = 1;
                        }
                        error = relookup(toparent_vnode, &tochild_vnode,
                                         to_name);
                        if (error && error != ENOENT) {
                                VOP_UNLOCK(toparent_vnode, 0);
                                VOP_UNLOCK(fromchild_vnode, 0);
                                VOP_UNLOCK(fromparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (error == ENOENT) {
                                tochild_vnode = NULL;
                        }
                        if (do_race_condition) {
                                if (tochild_vnode != fromchild_vnode) {
                                        vn_lock(fromchild_vnode,
                                                LK_EXCLUSIVE | LK_RETRY);
                                }
                        }
                }
                else {
                        vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        error = relookup(toparent_vnode, &tochild_vnode,
                                         to_name);
                        if (error && error != ENOENT) {
                                VOP_UNLOCK(toparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (error == ENOENT) {
                                tochild_vnode = NULL;
                        }
                        if (fromparent_vnode == tochild_vnode) {
                                VOP_UNLOCK(tochild_vnode, 0);
                                VOP_UNLOCK(toparent_vnode, 0);
                                error = ENOTEMPTY;
                                goto fail_nolocks;
                        }
                        vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        /* XXX see note above */
                        if (tochild_vnode && tochild_vnode == ap->a_fvp) {
                                VOP_UNLOCK(tochild_vnode, 0);
                                do_race_condition = 1;
                        }
                        error = relookup(fromparent_vnode, &fromchild_vnode,
                                         from_name);
                        if (error) {
                                VOP_UNLOCK(fromparent_vnode, 0);
                                VOP_UNLOCK(tochild_vnode, 0);
                                VOP_UNLOCK(toparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (do_race_condition) {
                                if (fromchild_vnode != tochild_vnode) {
                                        vn_lock(tochild_vnode,
                                                LK_EXCLUSIVE | LK_RETRY);
                                }
                        }
                }
        }

        /*
         * Note: after relookup to get fromchild_vnode and
         * tochild_vnode, we have extra references to (what may or may
         * not be the same) vnodes in ap->a_fvp and ap->a_tvp. These
         * are cleaned up at function exit below.
         */

        /* Now that we have fromchild, make sure it passes the parent check */
        if (fromchild_vnode == illegal_vnode) {
                vrele(illegal_vnode);
                error = EINVAL;
                goto fail_withlocks;
        }
        vrele(illegal_vnode);
        illegal_vnode = NULL;

        /*
         * All four vnodes are now locked, and we can proceed to do
         * the real work.
         */

        KASSERT(VOP_ISLOCKED(fromparent_vnode));
        KASSERT(VOP_ISLOCKED(fromchild_vnode));
        KASSERT(VOP_ISLOCKED(toparent_vnode));
        KASSERT(tochild_vnode == NULL || VOP_ISLOCKED(tochild_vnode));

        /* Get the rest of the inodes. */
        fromchild_i = VTOI(fromchild_vnode);
        tochild_i = tochild_vnode ? VTOI(tochild_vnode) : NULL;

        KASSERT(fromparent_i != fromchild_i);
        KASSERT(tochild_i == NULL || toparent_i != tochild_i);

        /*
         * Part 3. More checks.
         */

        /* More cross-directory cases. */
        if (fromparent_vnode->v_mount != fromchild_vnode->v_mount) {
                error = EXDEV;
                goto fail_withlocks;
        }
        if (tochild_vnode != NULL &&
            toparent_vnode->v_mount != tochild_vnode->v_mount) {
                error = EXDEV;
                goto fail_withlocks;
        }

        /* Check flags. */
        if ((fromparent_i->i_flags & APPEND) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }
        if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }
        if ((toparent_i->i_flags & APPEND) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }
        if (tochild_vnode != NULL &&
            (tochild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }

        /* Moving a directory over itself is not allowed. */
        if (fromchild_vnode == tochild_vnode &&
            fromchild_vnode->v_type == VDIR) {
                error = EINVAL;
                goto fail_withlocks;
        }

        /* Make sure bumping the target's link count won't overflow. */
        if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
                error = EMLINK;
                goto fail_withlocks;
        }

        /* This flag is now useless and should be removed. */
        if (fromchild_i->i_flag & IN_RENAME) {
                error = EINVAL;
                goto fail_withlocks;
        }

        /*
         * Part 4. Rename logic.
         */

        /* If moving a file over itself, just unlink the from end. */
        if (fromchild_vnode == tochild_vnode) {
                /* This case was ruled out above. */
                KASSERT(fromchild_vnode->v_type != VDIR);

                /* Release to-side completely. */
                VOP_ABORTOP(toparent_vnode, to_name);
                if (toparent_vnode != fromparent_vnode) {
                        VOP_UNLOCK(toparent_vnode, 0);
                }
                vrele(toparent_vnode);
                vrele(tochild_vnode);

                /* Clean up a_fvp/a_tvp. */
                vrele(ap->a_fvp);
                if (ap->a_tvp != NULL) {
                        vrele(ap->a_tvp);
                }

                /* Now unlink the from-side. */
                from_name->cn_flags &= ~(MODMASK | SAVESTART);
                from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
                from_name->cn_nameiop = DELETE;
                return VOP_REMOVE(fromparent_vnode, fromchild_vnode, from_name);
        }

        oldparent = fromparent_i->i_number;
        newparent = toparent_i->i_number;
        doingdirectory = (fromchild_i->i_mode & IFMT) == IFDIR;

        /* XXX update the logic below to not test "newparent!=0" */
        if (oldparent == newparent) {
                newparent = 0;
        }

        /* Check permissions. */
        error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
        if (error) {
                goto fail_withlocks;
        }

        noabort = 1;

        if (doingdirectory) {
                fromchild_i->i_flag |= IN_RENAME;
        }

        /* XXXLUKEM/XXX: right place? */
        VN_KNOTE(fromparent_vnode, NOTE_WRITE);

        mp = fromparent_vnode->v_mount;
        fstrans_start(mp, FSTRANS_SHARED);

        /*
         * 1) Bump link count while we're moving stuff
         *    around.  If we crash somewhere before
         *    completing our work, the link count
         *    may be wrong, but correctable.
         */
        fromchild_i->i_nlink++;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
        error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP);
        if (error) {
                goto fail_withfstrans;
        }

        /*
         * 2) If target doesn't exist, link the target
         *    to the source and unlink the source.
         *    Otherwise, rewrite the target directory
         *    entry to reference the source inode and
         *    expunge the original entry's existence.
         */

        if (tochild_i == NULL) {
                if (toparent_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Account for ".." in new directory.
                 * When source and destination have the same
                 * parent we don't fool with the link count.
                 */
                if (doingdirectory && newparent) {
                        if ((nlink_t)toparent_i->i_nlink >= LINK_MAX) {
                                error = EMLINK;
                                goto fail_withlinkcount;
                        }
                        toparent_i->i_nlink++;
                        DIP_ASSIGN(toparent_i, nlink, toparent_i->i_nlink);
                        toparent_i->i_flag |= IN_CHANGE;
                        if ((error = UFS_UPDATE(toparent_vnode, NULL, NULL,
                            UPDATE_DIROP)) != 0) {
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                                goto fail_withlinkcount;
                        }
                }
                newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
                ufs_makedirentry(fromchild_i, to_name, newdir);
                error = ufs_direnter(toparent_vnode, NULL, newdir, to_name, 
NULL);
                pool_cache_put(ufs_direct_cache, newdir);
                if (error != 0) {
                        if (doingdirectory && newparent) {
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                                (void)UFS_UPDATE(toparent_vnode, NULL, NULL,
                                                 UPDATE_WAIT | UPDATE_DIROP);
                        }
                        goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
        } else {
                if (tochild_i->i_dev != toparent_i->i_dev ||
                    tochild_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Short circuit rename(foo, foo).
                 */
                if (tochild_i->i_number == fromchild_i->i_number)
                        panic("rename: same file");
                /*
                 * If the parent directory is "sticky", then the user must
                 * own the parent directory, or the destination of the rename,
                 * otherwise the destination may not be changed (except by
                 * root). This implements append-only directories.
                 */
                if ((toparent_i->i_mode & S_ISTXT) &&
                    kauth_authorize_generic(to_name->cn_cred,
                     KAUTH_GENERIC_ISSUSER, NULL) != 0 &&
                    kauth_cred_geteuid(to_name->cn_cred) != toparent_i->i_uid &&
                    tochild_i->i_uid != kauth_cred_geteuid(to_name->cn_cred)) {
                        error = EPERM;
                        goto fail_withlinkcount;
                }
                /*
                 * Target must be empty if a directory and have no links
                 * to it. Also, ensure source and target are compatible
                 * (both directories, or both not directories).
                 */
                if ((tochild_i->i_mode & IFMT) == IFDIR) {
                        if (tochild_i->i_nlink > 2 ||
                            !ufs_dirempty(tochild_i, toparent_i->i_number,
                                          to_name->cn_cred)) {
                                error = ENOTEMPTY;
                                goto fail_withlinkcount;
                        }
                        if (!doingdirectory) {
                                error = ENOTDIR;
                                goto fail_withlinkcount;
                        }
                        cache_purge(toparent_vnode);
                } else if (doingdirectory) {
                        error = EISDIR;
                        goto fail_withlinkcount;
                }
                if ((error = ufs_dirrewrite(toparent_i, tochild_i,
                    fromchild_i->i_number,
                    IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
                        goto fail_withlinkcount;
                if (doingdirectory) {
                        /*
                         * Truncate inode. The only stuff left in the directory
                         * is "." and "..". The "." reference is inconsequential
                         * since we are quashing it. We have removed the "."
                         * reference and the reference in the parent directory,
                         * but there may be other hard links.
                         */
                        if (!newparent) {
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                        }
                        tochild_i->i_nlink--;
                        DIP_ASSIGN(tochild_i, nlink, tochild_i->i_nlink);
                        tochild_i->i_flag |= IN_CHANGE;
                        if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0,
                            IO_SYNC, to_name->cn_cred)))
                                goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
                VN_KNOTE(tochild_vnode, NOTE_DELETE);
        }

        /*
         * 3) Unlink the source.
         */
        /*
         * Ensure that the directory entry still exists and has not
         * changed while the new name has been entered. If the source is
         * a file then the entry may have been unlinked or renamed. In
         * either case there is no further work to be done. If the source
         * is a directory then it cannot have been rmdir'ed; The IRENAME
         * flag ensures that it cannot be moved by another rename or removed
         * by a rmdir.
         */

        /*
         * If the source is a directory with a
         * new parent, the link count of the old
         * parent directory must be decremented
         * and ".." set to point to the new parent.
         */
        if (doingdirectory && newparent) {
                KASSERT(toparent_i != NULL);
                fromchild_i->i_offset = mastertemplate.dot_reclen;
                ufs_dirrewrite(fromchild_i, toparent_i,
                    newparent, DT_DIR, 0, IN_CHANGE);
                cache_purge(fromparent_vnode);
        }
        error = ufs_dirremove(fromparent_vnode, fromchild_i,
            from_name->cn_flags, 0);
        VN_KNOTE(fromchild_vnode, NOTE_RENAME);

        if (error) {
                goto fail_withfstrans;
        }
        goto succeed;

 fail_withlinkcount:    
        fromchild_i->i_nlink--;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
        UFS_WAPBL_UPDATE(fromchild_vnode, NULL, NULL, 0);

 fail_withfstrans:
 succeed:
        fstrans_done(mp);
        fromchild_i->i_flag &= ~IN_RENAME;

 fail_withlocks:
        /* See comment up top about which of these can legally be the same. */
        if (fromparent_vnode == toparent_vnode) {
                VOP_UNLOCK(fromparent_vnode, 0);
        } else {
                VOP_UNLOCK(fromparent_vnode, 0);
                VOP_UNLOCK(toparent_vnode, 0);
        }
        if (fromchild_vnode == tochild_vnode) {
                VOP_UNLOCK(fromchild_vnode, 0);
        } else {
                VOP_UNLOCK(fromchild_vnode, 0);
                if (tochild_vnode) {
                        VOP_UNLOCK(tochild_vnode, 0);
                }
        }

 fail_nolocks:
        if (fromchild_vnode != NULL) {
                vrele(fromchild_vnode);
                fromchild_vnode = NULL;
        }
        if (tochild_vnode != NULL) {
                vrele(tochild_vnode);
                tochild_vnode = NULL;
        }

 fail_early:
        if (!noabort) {
                /* XXX, why not in NFS? */
                VOP_ABORTOP(fromparent_vnode, from_name);
                VOP_ABORTOP(toparent_vnode, to_name);
        }

        vrele(fromparent_vnode);
        vrele(toparent_vnode);

        KASSERT(fromchild_vnode == NULL);
        KASSERT(tochild_vnode == NULL);

        vrele(ap->a_fvp);
        if (ap->a_tvp != NULL)
                vrele(ap->a_tvp);

        return error;
}
/*
 * Rename vnode operation
 *      rename("foo", "bar");
 * is essentially
 *      unlink("bar");
 *      link("foo", "bar");
 *      unlink("foo");
 * but ``atomically''.  Can't do full commit without saving state in the
 * inode on disk which isn't feasible at this time.  Best we can do is
 * always guarantee the target exists.
 *
 * Basic algorithm is:
 *
 * 1) Bump link count on source while we're linking it to the
 *    target.  This also ensure the inode won't be deleted out
 *    from underneath us while we work (it may be truncated by
 *    a concurrent `trunc' or `open' for creation).
 * 2) Link source to destination.  If destination already exists,
 *    delete it first.
 * 3) Unlink source reference to inode if still around. If a
 *    directory was moved and the parent of the destination
 *    is different from the source, patch the ".." entry in the
 *    directory.
 *
 * WAPBL NOTE: wapbl_ufs_rename derived from ufs_rename in ufs_vnops.c
 * ufs_vnops.c netbsd cvs revision 1.108
 * which has the berkeley copyright above
 * changes introduced to ufs_rename since netbsd cvs revision 1.164
 * will need to be ported into wapbl_ufs_rename
 */
int
wapbl_ufs_rename(void *v)
{
        struct vop_rename_args  /* {
                struct vnode            *a_fdvp;
                struct vnode            *a_fvp;
                struct componentname    *a_fcnp;
                struct vnode            *a_tdvp;
                struct vnode            *a_tvp;
                struct componentname    *a_tcnp;
        } */ *ap = v;
        struct vnode            *fromparent_vnode;
        struct inode            *fromparent_i;
        struct componentname    *from_name;
        struct vnode            *fromchild_vnode;
        struct inode            *fromchild_i;

        struct vnode            *toparent_vnode;
        struct inode            *toparent_i;
        struct componentname    *to_name;
        struct vnode            *tochild_vnode;
        struct inode            *tochild_i;

        struct vnode            *illegal_vnode;
        struct mount            *mp;
        struct direct           *newdir;
        int                     error;
        int                     oldparent, newparent;
        int                     doingdirectory;
        int                     foundfromparent;
        int                     do_race_condition;
        int                     noabort = 0;

        int32_t   saved_f_count;
        doff_t    saved_f_diroff;
        doff_t    saved_f_offset;
        u_int32_t saved_f_reclen;
        int32_t   saved_t_count;
        doff_t    saved_t_endoff;
        doff_t    saved_t_diroff;
        doff_t    saved_t_offset;
        u_int32_t saved_t_reclen;

        /*
         * Due to oddities in namei and in the fs-independent code,
         * currently we are called with references to all four vnodes,
         * but with only toparent_vnode locked.
         *
         * In a sane world we'd be passed just the two parent vnodes,
         * both unlocked, and two (string) names.
         *
         * For now we will begin by setting up to mimic the sane world.
         */

        fromparent_vnode = ap->a_fdvp;
        fromparent_i = VTOI(fromparent_vnode);

        from_name = ap->a_fcnp;
        fromchild_vnode = NULL;

        toparent_vnode = ap->a_tdvp;
        toparent_i = VTOI(toparent_vnode);

        to_name = ap->a_tcnp;
        tochild_vnode = NULL;

        VOP_UNLOCK(toparent_vnode, 0);

        illegal_vnode = NULL;
        do_race_condition = 0;

        /* Because gcc 4.1 is not being real smart, zero all this */
        saved_f_count = 0;
        saved_f_diroff = 0;
        saved_f_offset = 0;
        saved_f_reclen = 0;
        saved_t_count = 0;
        saved_t_endoff = 0;
        saved_t_diroff = 0;
        saved_t_offset = 0;
        saved_t_reclen = 0;

        /*
         * Part 1: check for broken args and illegal operations.
         */

#ifdef DIAGNOSTIC
        /* We expect SAVESTART to have been set. */
        if ((to_name->cn_flags & HASBUF) == 0 ||
            (from_name->cn_flags & HASBUF) == 0)
                panic("ufs_rename: no name");
#endif

        /* Cross-device rename is prohibited. */
        if (fromparent_vnode->v_mount != toparent_vnode->v_mount) {
                error = EXDEV;
                goto fail_early;
        }

        /* If either parent isn't a directory, just give up now. */
        if (fromparent_vnode->v_type != VDIR ||
            toparent_vnode->v_type != VDIR) {
                error = ENOTDIR;
                goto fail_early;
        }

        /* The "." and ".." entries may not be renamed. */
        if ((from_name->cn_namelen == 1 && from_name->cn_nameptr[0] == '.') ||
            (from_name->cn_flags & ISDOTDOT) ||
            (to_name->cn_flags & ISDOTDOT)) {
                error = EINVAL;
                goto fail_early;
        }

        /*
         * Part 2: get locks.
         *
         * We lock parent vnodes before child vnodes. This means in
         * particular that if A is above B in the directory tree then
         * A must be locked before B. (This is true regardless of how
         * many steps appear in between, because an arbitrary number
         * of other processes could lock parent/child in between and
         * establish a lock cycle and deadlock.)
         *
         * Therefore, if TOPARENT is above FROMPARENT we must lock
         * TOPARENT first; if FROMPARENT is above TOPARENT we must
         * lock FROMPARENT first; and if they're incommensurate it
         * doesn't matter. (But, we rely on the fact that there's a
         * whole-volume rename lock to prevent deadlock among groups
         * of renames upon overlapping sets of incommensurate vnodes.)
         *
         * In addition to establishing lock ordering the parent check
         * also serves to rule out cases where someone tries to move a
         * directory underneath itself, e.g. rename("a/b", "a/b/c").
         * If allowed to proceed such renames would detach portions of
         * the directory tree and make fsck very unhappy.
         *
         * Note that it is an error for *FROMCHILD* to be above
         * TOPARENT; however, *FROMPARENT* can be above TOPARENT, as
         * in rename("a/b", "a/c/d").
         *
         * The parent check searches up the tree from TOPARENT until
         * it either finds FROMPARENT or the root of the volume. It
         * also returns the vnode it saw immediately before TOPARENT,
         * if any. Later on (after looking up FROMCHILD) we will check
         * to see if this *is* FROMCHILD and if so fail.
         *
         * If the parent check finds FROMPARENT, it means FROMPARENT
         * is above TOPARENT, so we lock FROMPARENT first and then
         * TOPARENT. Otherwise, either TOPARENT is above FROMPARENT or
         * they're incommensurate and we lock TOPARENT first.
         *
         * In either case each of the child vnodes has to be looked up
         * and locked immediately after its parent. The two cases
         *
         *       FROMPARENT/FROMCHILD/TOPARENT/TOCHILD
         *       TOPARENT/TOCHILD/FROMPARENT/FROMCHILD
         *
         * can cause deadlock otherwise. (Note that both of these are
         * error cases; the first fails the parent check and the
         * second fails because TOCHILD isn't empty. The parent check
         * case can be handled without attempting to lock FROMCHILD,
         * (XXX or at least it could be in a saner world of namei),
         * but the nonempty case requires locking TOCHILD to test.
         *
         * Therefore the procedure is either
         *
         *   lock FROMPARENT
         *   lookup FROMCHILD
         *   lock FROMCHILD
         *   lock TOPARENT
         *   lookup TOCHILD
         *   lock TOCHILD
         *
         * or
         *
         *   lock TOPARENT
         *   lookup TOCHILD
         *   lock TOCHILD
         *   lock FROMPARENT
         *   lookup FROMCHILD
         *   lock FROMCHILD
         *
         * In a saner namei world we could simplify this (some) by always
         * handling FROMCHILD last, but that isn't currently possible.
         *
         * Note that for now we aren't doing lookup so much as relookup()
         * and checking what we find against what was passed down from the
         * fs-independent code.
         *
         * On top of all the above, just to make everything more
         * exciting, any two of the vnodes might end up being the same.
         *
         * FROMPARENT == FROMCHILD      mv a/. foo      is an error.
         * FROMPARENT == TOPARENT       mv a/b a/c      is ok.
         * FROMPARENT == TOCHILD        mv a/b/c a/b    will give ENOTEMPTY.
         * FROMCHILD == TOPARENT        mv a/b a/b/c    fails the parent check.
         * FROMCHILD == TOCHILD         mv a/b a/b      is ok.
         * TOPARENT == TOCHILD          mv foo a/.      is an error.
         *
         * This introduces more cases in the locking, because each
         * distinct vnode must be locked exactly once.
         *
         * When FROMPARENT == TOPARENT and FROMCHILD != TOCHILD we
         * assume it doesn't matter what order the children are locked
         * in, because the per-volume rename lock excludes other
         * renames and no other operation locks two files in the same
         * directory at once. (Note: if it turns out that link() does,
         * link() is wrong.)
         */

        /*
         * XXX until such time as we can do lookups without the namei
         * and lookup machinery "helpfully" locking the result vnode
         * for us, we can't avoid tripping on cases where FROMCHILD ==
         * TOCHILD. The right way to do this is to check after looking
         * the second one up and only lock it if it's different. Easy,
         * but requires having the right underlying abstractions.
         *
         * Instead, we'll have to check the child vnode passed down in
         * the argument block. For the branch where we look up
         * FROMCHILD first we do this:
         *
         *     - check if FROMCHILD is the same as the TOCHILD passed
         *       in (ap->a_tvp)
         *     - if so, unlock FROMCHILD before looking up TOCHILD
         *       and set do_race_condition
         *     - afterward if they aren't the same lock FROMCHILD again.
         *
         * Eww.
         *
         * On the plus side, this method at least shouldn't be able to
         * deadlock when relocking FROMCHILD.
         *
         * Needless to say this is a race condition and should be
         * fixed up once namei has been rendered sufficiently sane.
         */

        // XXX do we need this?
        //from_name->cn_flags &= ~SAVESTART;
        //to_name->cn_flags &= ~SAVESTART;
        // XXX or this form instead?
        //from_name->cn_flags &= ~(MODMASK | SAVESTART);
        //from_name->cn_flags |= LOCKPARENT | LOCKLEAF;

        if (fromparent_vnode == toparent_vnode) {
                vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                error = relookup(fromparent_vnode, &fromchild_vnode,
                                 from_name);
                if (error) {
                        VOP_UNLOCK(fromparent_vnode, 0);
                        goto fail_nolocks;
                }

                /*
                 * save directory lookup information
                 */
                saved_f_count  = fromparent_i->i_count;
                saved_f_diroff = fromparent_i->i_diroff;
                saved_f_offset = fromparent_i->i_offset;
                saved_f_reclen = fromparent_i->i_reclen;

                /* XXX see note above */
                if (fromchild_vnode == ap->a_tvp) {
                        VOP_UNLOCK(fromchild_vnode, 0);
                        do_race_condition = 1;
                }
                error = relookup(toparent_vnode, &tochild_vnode,
                                 to_name);
                if (error && error != ENOENT) {
                        /*VOP_UNLOCK(toparent_vnode); -- same as fromparent */
                        VOP_UNLOCK(fromchild_vnode, 0);
                        VOP_UNLOCK(fromparent_vnode, 0);
                        goto fail_nolocks;
                }
                if (error == ENOENT) {
                        /*
                         * Note: currently in this case relookup() succeeds
                         * and sets tochild_vnode = NULL, but this piece of
                         * logic will be wanted in the (saner) future.
                         * (XXX: remove this comment when appropriate)
                         */
                        tochild_vnode = NULL;
                }
                if (do_race_condition) {
                        if (tochild_vnode != fromchild_vnode) {
                                vn_lock(fromchild_vnode,
                                        LK_EXCLUSIVE | LK_RETRY);
                        }
                }

                /*
                 * save directory lookup information
                 */
                saved_t_count  = toparent_i->i_count;
                saved_t_endoff = toparent_i->i_endoff;
                saved_t_diroff = toparent_i->i_diroff;
                saved_t_offset = toparent_i->i_offset;
                saved_t_reclen = toparent_i->i_reclen;
        }
        else {
                error = ufs_parentcheck(fromparent_vnode, toparent_vnode,
                            from_name->cn_cred,
                            &foundfromparent, &illegal_vnode);
                if (error) {
                        goto fail_nolocks;
                }
                if (foundfromparent) {
                        vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        error = relookup(fromparent_vnode, &fromchild_vnode,
                                         from_name);
                        if (error) {
                                VOP_UNLOCK(fromparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (fromchild_vnode == toparent_vnode) {
                                VOP_UNLOCK(fromparent_vnode, 0);
                                VOP_UNLOCK(fromchild_vnode, 0);
                                error = EINVAL;
                                goto fail_nolocks;
                        }
                        vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        /* XXX see note above */
                        if (fromchild_vnode == ap->a_tvp) {
                                VOP_UNLOCK(fromchild_vnode, 0);
                                do_race_condition = 1;
                        }
                        error = relookup(toparent_vnode, &tochild_vnode,
                                         to_name);
                        if (error && error != ENOENT) {
                                VOP_UNLOCK(toparent_vnode, 0);
                                VOP_UNLOCK(fromchild_vnode, 0);
                                VOP_UNLOCK(fromparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (error == ENOENT) {
                                tochild_vnode = NULL;
                        }
                        if (do_race_condition) {
                                if (tochild_vnode != fromchild_vnode) {
                                        vn_lock(fromchild_vnode,
                                                LK_EXCLUSIVE | LK_RETRY);
                                }
                        }
                }
                else {
                        vn_lock(toparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        error = relookup(toparent_vnode, &tochild_vnode,
                                         to_name);
                        if (error && error != ENOENT) {
                                VOP_UNLOCK(toparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (error == ENOENT) {
                                tochild_vnode = NULL;
                        }
                        if (fromparent_vnode == tochild_vnode) {
                                VOP_UNLOCK(tochild_vnode, 0);
                                VOP_UNLOCK(toparent_vnode, 0);
                                error = ENOTEMPTY;
                                goto fail_nolocks;
                        }
                        vn_lock(fromparent_vnode, LK_EXCLUSIVE | LK_RETRY);
                        /* XXX see note above */
                        if (tochild_vnode && tochild_vnode == ap->a_fvp) {
                                VOP_UNLOCK(tochild_vnode, 0);
                                do_race_condition = 1;
                        }
                        error = relookup(fromparent_vnode, &fromchild_vnode,
                                         from_name);
                        if (error) {
                                VOP_UNLOCK(fromparent_vnode, 0);
                                VOP_UNLOCK(tochild_vnode, 0);
                                VOP_UNLOCK(toparent_vnode, 0);
                                goto fail_nolocks;
                        }
                        if (do_race_condition) {
                                if (fromchild_vnode != tochild_vnode) {
                                        vn_lock(tochild_vnode,
                                                LK_EXCLUSIVE | LK_RETRY);
                                }
                        }
                }
        }

        /*
         * Note: after relookup to get fromchild_vnode and
         * tochild_vnode, we have extra references to (what may or may
         * not be the same) vnodes in ap->a_fvp and ap->a_tvp. These
         * are cleaned up at function exit below.
         */

        /* Now that we have fromchild, make sure it passes the parent check */
        if (fromchild_vnode == illegal_vnode) {
                vrele(illegal_vnode);
                error = EINVAL;
                goto fail_withlocks;
        }
        vrele(illegal_vnode);
        illegal_vnode = NULL;

        /*
         * All four vnodes are now locked, and we can proceed to do
         * the real work.
         */

        KASSERT(VOP_ISLOCKED(fromparent_vnode));
        KASSERT(VOP_ISLOCKED(fromchild_vnode));
        KASSERT(VOP_ISLOCKED(toparent_vnode));
        KASSERT(tochild_vnode == NULL || VOP_ISLOCKED(tochild_vnode));

        /* Get the rest of the inodes. */
        fromchild_i = VTOI(fromchild_vnode);
        tochild_i = tochild_vnode ? VTOI(tochild_vnode) : NULL;

        KASSERT(fromparent_i != fromchild_i);
        KASSERT(tochild_i == NULL || toparent_i != tochild_i);

        /*
         * Part 3. More checks.
         */

        /* More cross-directory cases. */
        if (fromparent_vnode->v_mount != fromchild_vnode->v_mount) {
                error = EXDEV;
                goto fail_withlocks;
        }
        if (tochild_vnode != NULL &&
            toparent_vnode->v_mount != tochild_vnode->v_mount) {
                error = EXDEV;
                goto fail_withlocks;
        }

        /* Check flags. */
        if ((fromparent_i->i_flags & APPEND) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }
        if ((fromchild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }
        if ((toparent_i->i_flags & APPEND) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }
        if (tochild_vnode != NULL &&
            (tochild_i->i_flags & (IMMUTABLE | APPEND)) != 0) {
                error = EPERM;
                goto fail_withlocks;
        }

        /* Moving a directory over itself is not allowed. */
        if (fromchild_vnode == tochild_vnode &&
            fromchild_vnode->v_type == VDIR) {
                error = EINVAL;
                goto fail_withlocks;
        }

        /* Make sure bumping the target's link count won't overflow. */
        if ((nlink_t) fromchild_i->i_nlink >= LINK_MAX) {
                error = EMLINK;
                goto fail_withlocks;
        }

        /* This flag is now useless and should be removed. */
        if (fromchild_i->i_flag & IN_RENAME) {
                error = EINVAL;
                goto fail_withlocks;
        }

        /*
         * Part 4. Rename logic.
         */

        /* If moving a file over itself, just unlink the from end. */
        if (fromchild_vnode == tochild_vnode) {
                /* This case was ruled out above. */
                KASSERT(fromchild_vnode->v_type != VDIR);

                /* Release to-side completely. */
                VOP_ABORTOP(toparent_vnode, to_name);
                if (toparent_vnode != fromparent_vnode) {
                        VOP_UNLOCK(toparent_vnode, 0);
                }
                vrele(toparent_vnode);
                vrele(tochild_vnode);

                /* Clean up a_fvp/a_tvp. */
                vrele(ap->a_fvp);
                if (ap->a_tvp != NULL) {
                        vrele(ap->a_tvp);
                }

                /* Now unlink the from-side. */
                from_name->cn_flags &= ~(MODMASK | SAVESTART);
                from_name->cn_flags |= LOCKPARENT | LOCKLEAF;
                from_name->cn_nameiop = DELETE;
                return VOP_REMOVE(fromparent_vnode, fromchild_vnode, from_name);
        }

        oldparent = fromparent_i->i_number;
        newparent = toparent_i->i_number;
        doingdirectory = (fromchild_i->i_mode & IFMT) == IFDIR;

        /* XXX update the logic below to not test "newparent!=0" */
        if (oldparent == newparent) {
                newparent = 0;
        }

        /* Check permissions. */
        error = VOP_ACCESS(fromchild_vnode, VWRITE, to_name->cn_cred);
        if (error) {
                goto fail_withlocks;
        }

        noabort = 1;

        if (doingdirectory) {
                fromchild_i->i_flag |= IN_RENAME;
        }

        /* XXXLUKEM/XXX: right place? */
        VN_KNOTE(fromparent_vnode, NOTE_WRITE);

        mp = fromparent_vnode->v_mount;
        fstrans_start(mp, FSTRANS_SHARED);

        error = UFS_WAPBL_BEGIN(fromparent_vnode->v_mount);
        if (error)
                goto fail_withfstrans;

        /*
         * restore directory lookup information in case toparent_vnode
         * == fromparent_vnode
         */
        if (fromparent_vnode == toparent_vnode) {
                toparent_i->i_offset = saved_t_offset;
                toparent_i->i_reclen = saved_t_reclen;
                toparent_i->i_count  = saved_t_count;
                toparent_i->i_endoff = saved_t_endoff;
                toparent_i->i_diroff = saved_t_diroff;
        }

        /*
         * 1) Bump link count while we're moving stuff
         *    around.  If we crash somewhere before
         *    completing our work, the link count
         *    may be wrong, but correctable.
         */
        fromchild_i->i_nlink++;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
        error = UFS_UPDATE(fromchild_vnode, NULL, NULL, UPDATE_DIROP);
        if (error) {
                goto fail_withwapbl;
        }

        /*
         * 2) If target doesn't exist, link the target
         *    to the source and unlink the source.
         *    Otherwise, rewrite the target directory
         *    entry to reference the source inode and
         *    expunge the original entry's existence.
         */

        if (tochild_i == NULL) {
                if (toparent_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Account for ".." in new directory.
                 * When source and destination have the same
                 * parent we don't fool with the link count.
                 */
                if (doingdirectory && newparent) {
                        if ((nlink_t)toparent_i->i_nlink >= LINK_MAX) {
                                error = EMLINK;
                                goto fail_withlinkcount;
                        }
                        toparent_i->i_nlink++;
                        DIP_ASSIGN(toparent_i, nlink, toparent_i->i_nlink);
                        toparent_i->i_flag |= IN_CHANGE;
                        if ((error = UFS_UPDATE(toparent_vnode, NULL, NULL,
                            UPDATE_DIROP)) != 0) {
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                                goto fail_withlinkcount;
                        }
                }
                newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
                ufs_makedirentry(fromchild_i, to_name, newdir);
                error = ufs_direnter(toparent_vnode, NULL, newdir, to_name, 
NULL);
                pool_cache_put(ufs_direct_cache, newdir);
                if (error != 0) {
                        if (doingdirectory && newparent) {
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                                (void)UFS_UPDATE(toparent_vnode, NULL, NULL,
                                                 UPDATE_WAIT | UPDATE_DIROP);
                        }
                        goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
        } else {
                if (tochild_i->i_dev != toparent_i->i_dev ||
                    tochild_i->i_dev != fromchild_i->i_dev)
                        panic("rename: EXDEV");
                /*
                 * Short circuit rename(foo, foo).
                 */
                if (tochild_i->i_number == fromchild_i->i_number)
                        panic("rename: same file");
                /*
                 * If the parent directory is "sticky", then the user must
                 * own the parent directory, or the destination of the rename,
                 * otherwise the destination may not be changed (except by
                 * root). This implements append-only directories.
                 */
                if ((toparent_i->i_mode & S_ISTXT) &&
                    kauth_authorize_generic(to_name->cn_cred,
                     KAUTH_GENERIC_ISSUSER, NULL) != 0 &&
                    kauth_cred_geteuid(to_name->cn_cred) != toparent_i->i_uid &&
                    tochild_i->i_uid != kauth_cred_geteuid(to_name->cn_cred)) {
                        error = EPERM;
                        goto fail_withlinkcount;
                }
                /*
                 * Target must be empty if a directory and have no links
                 * to it. Also, ensure source and target are compatible
                 * (both directories, or both not directories).
                 */
                if ((tochild_i->i_mode & IFMT) == IFDIR) {
                        if (tochild_i->i_nlink > 2 ||
                            !ufs_dirempty(tochild_i, toparent_i->i_number,
                                          to_name->cn_cred)) {
                                error = ENOTEMPTY;
                                goto fail_withlinkcount;
                        }
                        if (!doingdirectory) {
                                error = ENOTDIR;
                                goto fail_withlinkcount;
                        }
                        cache_purge(toparent_vnode);
                } else if (doingdirectory) {
                        error = EISDIR;
                        goto fail_withlinkcount;
                }
                if ((error = ufs_dirrewrite(toparent_i, tochild_i,
                    fromchild_i->i_number,
                    IFTODT(fromchild_i->i_mode), doingdirectory && newparent ?
                    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
                        goto fail_withlinkcount;
                if (doingdirectory) {
                        /*
                         * Truncate inode. The only stuff left in the directory
                         * is "." and "..". The "." reference is inconsequential
                         * since we are quashing it. We have removed the "."
                         * reference and the reference in the parent directory,
                         * but there may be other hard links.
                         */
                        if (!newparent) {
                                toparent_i->i_nlink--;
                                DIP_ASSIGN(toparent_i, nlink, 
toparent_i->i_nlink);
                                toparent_i->i_flag |= IN_CHANGE;
                                UFS_WAPBL_UPDATE(toparent_vnode, NULL, NULL, 0);
                        }
                        tochild_i->i_nlink--;
                        DIP_ASSIGN(tochild_i, nlink, tochild_i->i_nlink);
                        tochild_i->i_flag |= IN_CHANGE;
                        if ((error = UFS_TRUNCATE(tochild_vnode, (off_t)0,
                            IO_SYNC, to_name->cn_cred)))
                                goto fail_withlinkcount;
                }
                VN_KNOTE(toparent_vnode, NOTE_WRITE);
                VN_KNOTE(tochild_vnode, NOTE_DELETE);
        }

        /*
         * restore directory lookup information in case toparent_vnode
         * == fromparent_vnode
         */
        if (fromparent_vnode == toparent_vnode) {
                fromparent_i->i_offset = saved_f_offset;
                fromparent_i->i_reclen = saved_f_reclen;
                fromparent_i->i_count  = saved_f_count;
                fromparent_i->i_diroff = saved_f_diroff;
        }

        /*
         * Handle case where the directory we need to remove may have
         * been moved when the directory insertion above performed compaction.
         * or when i_count may be wrong due to insertion before this entry.
         */
        if ((toparent_i->i_number == fromparent_i->i_number) &&
                (((saved_f_offset >= saved_t_offset) &&
                        (saved_f_offset < saved_t_offset + saved_t_count)) ||
                ((saved_f_offset - saved_f_count >= saved_t_offset) &&
                        (saved_f_offset - saved_f_count <
                         saved_t_offset + saved_t_count)))) {
                struct buf *bp;
                struct direct *ep;
                struct ufsmount *ump = fromparent_i->i_ump;
                doff_t endsearch;       /* offset to end directory search */
                int dirblksiz = ump->um_dirblksiz;
                const int needswap = UFS_MPNEEDSWAP(ump);
                u_long bmask;
                int namlen, entryoffsetinblock;
                char *dirbuf;

                bmask = fromparent_vnode->v_mount->mnt_stat.f_iosize - 1;

                /*
                 * the from_name entry will be somewhere between the start of
                 * compaction and the original location.
                 */
                fromparent_i->i_offset = saved_t_offset;
                error = ufs_blkatoff(fromparent_vnode,
                    (off_t)fromparent_i->i_offset, &dirbuf, &bp, false);
                if (error)
                        goto fail_withlinkcount;

                /*
                 * keep existing fromparent_i->i_count in case
                 * compaction started at the same location as the from_name 
entry.
                 */
                endsearch = saved_f_offset + saved_f_reclen;
                entryoffsetinblock = 0;
                while (fromparent_i->i_offset < endsearch) {
                        int reclen;

                        /*
                         * If necessary, get the next directory block.
                         */
                        if ((fromparent_i->i_offset & bmask) == 0) {
                                if (bp != NULL)
                                        brelse(bp, 0);
                                error = ufs_blkatoff(fromparent_vnode,
                                    (off_t)fromparent_i->i_offset, &dirbuf, &bp,
                                    false);
                                if (error)
                                        goto fail_withlinkcount;
                                entryoffsetinblock = 0;
                        }

                        KASSERT(bp != NULL);
                        ep = (struct direct *)(dirbuf + entryoffsetinblock);
                        reclen = ufs_rw16(ep->d_reclen, needswap);

#if (BYTE_ORDER == LITTLE_ENDIAN)
                        if (FSFMT(fromparent_vnode) && needswap == 0)
                                namlen = ep->d_type;
                        else
                                namlen = ep->d_namlen;
#else
                        if (FSFMT(fromparent_vnode) && needswap != 0)
                                namlen = ep->d_type;
                        else
                                namlen = ep->d_namlen;
#endif
                        if ((ep->d_ino != 0) &&
                            (ufs_rw32(ep->d_ino, needswap) != WINO) &&
                            (namlen == from_name->cn_namelen) &&
                            memcmp(ep->d_name, from_name->cn_nameptr, namlen) 
== 0) {
                                fromparent_i->i_reclen = reclen;
                                break;
                        }
                        fromparent_i->i_offset += reclen;
                        fromparent_i->i_count = reclen;
                        entryoffsetinblock += reclen;
                }

                KASSERT(fromparent_i->i_offset <= endsearch);

                /*
                 * If fromparent_i->i_offset points to start of a
                 * directory block, set fromparent_i->i_count so
                 * ufs_dirremove() doesn't compact over a directory
                 * block boundary.
                 */
                if ((fromparent_i->i_offset & (dirblksiz - 1)) == 0)
                        fromparent_i->i_count = 0;

                brelse(bp, 0);
        }

        /*
         * 3) Unlink the source.
         */
        /*
         * Ensure that the directory entry still exists and has not
         * changed while the new name has been entered. If the source is
         * a file then the entry may have been unlinked or renamed. In
         * either case there is no further work to be done. If the source
         * is a directory then it cannot have been rmdir'ed; The IRENAME
         * flag ensures that it cannot be moved by another rename or removed
         * by a rmdir.
         */

        /*
         * If the source is a directory with a
         * new parent, the link count of the old
         * parent directory must be decremented
         * and ".." set to point to the new parent.
         */
        if (doingdirectory && newparent) {
                KASSERT(toparent_i != NULL);
                fromchild_i->i_offset = mastertemplate.dot_reclen;
                ufs_dirrewrite(fromchild_i, toparent_i,
                    newparent, DT_DIR, 0, IN_CHANGE);
                cache_purge(fromparent_vnode);
        }
        error = ufs_dirremove(fromparent_vnode, fromchild_i,
            from_name->cn_flags, 0);
        VN_KNOTE(fromchild_vnode, NOTE_RENAME);

        if (error) {
                goto fail_withwapbl;
        }
        goto succeed;

 fail_withlinkcount:    
        fromchild_i->i_nlink--;
        DIP_ASSIGN(fromchild_i, nlink, fromchild_i->i_nlink);
        fromchild_i->i_flag |= IN_CHANGE;
        UFS_WAPBL_UPDATE(fromchild_vnode, NULL, NULL, 0);

 fail_withwapbl:
 succeed:
        UFS_WAPBL_END(fromparent_vnode->v_mount);

 fail_withfstrans:
        fstrans_done(mp);
        fromchild_i->i_flag &= ~IN_RENAME;

 fail_withlocks:
        /* See comment up top about which of these can legally be the same. */
        if (fromparent_vnode == toparent_vnode) {
                VOP_UNLOCK(fromparent_vnode, 0);
        } else {
                VOP_UNLOCK(fromparent_vnode, 0);
                VOP_UNLOCK(toparent_vnode, 0);
        }
        if (fromchild_vnode == tochild_vnode) {
                VOP_UNLOCK(fromchild_vnode, 0);
        } else {
                VOP_UNLOCK(fromchild_vnode, 0);
                if (tochild_vnode) {
                        VOP_UNLOCK(tochild_vnode, 0);
                }
        }

 fail_nolocks:
        if (fromchild_vnode != NULL) {
                vrele(fromchild_vnode);
                fromchild_vnode = NULL;
        }
        if (tochild_vnode != NULL) {
                vrele(tochild_vnode);
                tochild_vnode = NULL;
        }

 fail_early:
        if (!noabort) {
                /* XXX, why not in NFS? */
                VOP_ABORTOP(fromparent_vnode, from_name);
                VOP_ABORTOP(toparent_vnode, to_name);
        }

        vrele(fromparent_vnode);
        vrele(toparent_vnode);

        KASSERT(fromchild_vnode == NULL);
        KASSERT(tochild_vnode == NULL);

        vrele(ap->a_fvp);
        if (ap->a_tvp != NULL)
                vrele(ap->a_tvp);

        return error;
}
/*
 * Extract the inode number of ".." from a directory.
 * Helper for ufs_parentcheck.
 */
static int
ufs_readdotdot(struct vnode *vp, int needswap, kauth_cred_t cred, ino_t *result)
{
        struct dirtemplate dirbuf;
        int namlen, error;

        error = vn_rdwr(UIO_READ, vp, &dirbuf,
                    sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
                    IO_NODELOCKED, cred, NULL, NULL);
        if (error) {
                return error;
        }

#if (BYTE_ORDER == LITTLE_ENDIAN)
        if (FSFMT(vp) && needswap == 0)
                namlen = dirbuf.dotdot_type;
        else
                namlen = dirbuf.dotdot_namlen;
#else
        if (FSFMT(vp) && needswap != 0)
                namlen = dirbuf.dotdot_type;
        else
                namlen = dirbuf.dotdot_namlen;
#endif
        if (namlen != 2 ||
            dirbuf.dotdot_name[0] != '.' ||
            dirbuf.dotdot_name[1] != '.') {
                printf("ufs_readdotdot: directory %llu contains "
                       "garbage instead of ..\n",
                       (unsigned long long) VTOI(vp)->i_number);
                return ENOTDIR;
        }
        *result = ufs_rw32(dirbuf.dotdot_ino, needswap);
        return 0;
}

/*
 * Check if LOWER is a descendent of UPPER. If we find UPPER, return
 * nonzero in FOUND and return a reference to the immediate descendent
 * of UPPER in UPPERCHILD. If we don't find UPPER (that is, if we
 * reach the volume root and that isn't UPPER), return zero in FOUND
 * and null in UPPERCHILD.
 *
 * Neither UPPER nor LOWER should be locked.
 *
 * On error (such as a permissions error checking up the directory
 * tree) fail entirely.
 */
int
ufs_parentcheck(struct vnode *upper, struct vnode *lower, kauth_cred_t cred,
                int *found_ret, struct vnode **upperchild_ret)
{
        const int needswap = UFS_MPNEEDSWAP(target->i_ump);
        ino_t upper_ino, found_ino;
        struct vnode *current, *next;
        int error;

        if (upper == lower) {
                VREF(upper);
                *found_ret = 1;
                *upperchild_ret = upper;
                return 0;
        }
        if (VTOI(lower)->i_number == ROOTINO) {
                *found_ret = 0;
                *upperchild_ret = NULL;
                return 0;
        }

        upper_ino = VTOI(upper)->i_number;

        current = lower;
        VREF(current);
        vn_lock(current, LK_EXCLUSIVE | LK_RETRY);

        for (;;) {
                error = ufs_readdotdot(current, needswap, cred, &found_ino);
                if (error) {
                        vput(current);
                        return error;
                }
                if (found_ino == upper_ino) {
                        VOP_UNLOCK(current, 0);
                        *found_ret = 1;
                        *upperchild_ret = current;
                        return 0;
                }
                if (found_ino == ROOTINO) {
                        vput(current);
                        *found_ret = 0;
                        *upperchild_ret = NULL;
                        return 0;
                }
                VOP_UNLOCK(current, 0);
                error = VFS_VGET(current->v_mount, found_ino, &next);
                if (error) {
                        vrele(current);
                        return error;
                }
                KASSERT(VOP_ISLOCKED(next));
                if (next->v_type != VDIR) {
                        printf("ufs_parentcheck: inode %llu reached via .. of "
                               "inode %llu is not a directory\n",
                            (unsigned long long)VTOI(next)->i_number,
                            (unsigned long long)VTOI(current)->i_number);
                        vput(next);
                        vrele(current);
                        return ENOTDIR;
                }
                vrele(current);
                current = next;
        }

        return 0;
}


Home | Main Index | Thread Index | Old Index