Module Name:    src
Committed By:   dholland
Date:           Mon Jul 18 06:45:28 UTC 2011

Modified Files:
        src/sys/ufs/ufs: ufs_vnops.c ufs_wapbl.c

Log Message:
Move ufs_wapbl_rename to ufs_vnops.c next to the old ufs_rename.


To generate a diff of this commit:
cvs rdiff -u -r1.198 -r1.199 src/sys/ufs/ufs/ufs_vnops.c
cvs rdiff -u -r1.20 -r1.21 src/sys/ufs/ufs/ufs_wapbl.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/ufs/ufs/ufs_vnops.c
diff -u src/sys/ufs/ufs/ufs_vnops.c:1.198 src/sys/ufs/ufs/ufs_vnops.c:1.199
--- src/sys/ufs/ufs/ufs_vnops.c:1.198	Mon Jul 18 02:35:11 2011
+++ src/sys/ufs/ufs/ufs_vnops.c	Mon Jul 18 06:45:27 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_vnops.c,v 1.198 2011/07/18 02:35:11 dholland Exp $	*/
+/*	$NetBSD: ufs_vnops.c,v 1.199 2011/07/18 06:45:27 dholland Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.198 2011/07/18 02:35:11 dholland Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.199 2011/07/18 06:45:27 dholland Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_ffs.h"
@@ -1103,6 +1103,872 @@
  * once.)
  */
 
+/* XXX following lifted from ufs_lookup.c */
+#define	FSFMT(vp)	(((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
+
+/*
+ * Check if either entry referred to by FROM_ULR is within the range
+ * of entries named by TO_ULR.
+ */
+static int
+ulr_overlap(const struct ufs_lookup_results *from_ulr,
+	    const struct ufs_lookup_results *to_ulr)
+{
+	doff_t from_start, from_prevstart;
+	doff_t to_start, to_end;
+
+	/*
+	 * FROM is a DELETE result; offset points to the entry to
+	 * remove and subtracting count gives the previous entry.
+	 */
+	from_start = from_ulr->ulr_offset - from_ulr->ulr_count;
+	from_prevstart = from_ulr->ulr_offset;
+
+	/*
+	 * TO is a RENAME (thus non-DELETE) result; offset points
+	 * to the beginning of a region to write in, and adding
+	 * count gives the end of the region.
+	 */
+	to_start = to_ulr->ulr_offset;
+	to_end = to_ulr->ulr_offset + to_ulr->ulr_count;
+
+	if (from_prevstart >= to_start && from_prevstart < to_end) {
+		return 1;
+	}
+	if (from_start >= to_start && from_start < to_end) {
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Wrapper for relookup that also updates the supplemental results.
+ */
+static int
+do_relookup(struct vnode *dvp, struct ufs_lookup_results *ulr,
+	    struct vnode **vp, struct componentname *cnp)
+{
+	int error;
+
+	error = relookup(dvp, vp, cnp, 0);
+	if (error) {
+		return error;
+	}
+	/* update the supplemental reasults */
+	*ulr = VTOI(dvp)->i_crap;
+	UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
+	return 0;
+}
+
+/*
+ * Lock and relookup a sequence of two directories and two children.
+ *
+ */
+static int
+lock_vnode_sequence(struct vnode *d1, struct ufs_lookup_results *ulr1,
+		    struct vnode **v1_ret, struct componentname *cn1, 
+		    int v1_missing_ok,
+		    int overlap_error,
+		    struct vnode *d2, struct ufs_lookup_results *ulr2,
+		    struct vnode **v2_ret, struct componentname *cn2, 
+		    int v2_missing_ok)
+{
+	struct vnode *v1, *v2;
+	int error;
+
+	KASSERT(d1 != d2);
+
+	vn_lock(d1, LK_EXCLUSIVE | LK_RETRY);
+	if (VTOI(d1)->i_size == 0) {
+		/* d1 has been rmdir'd */
+		VOP_UNLOCK(d1);
+		return ENOENT;
+	}
+	error = do_relookup(d1, ulr1, &v1, cn1);
+	if (v1_missing_ok) {
+		if (error == ENOENT) {
+			/*
+			 * Note: currently if the name doesn't exist,
+			 * relookup succeeds (it intercepts the
+			 * EJUSTRETURN from VOP_LOOKUP) and sets tvp
+			 * to NULL. Therefore, we will never get
+			 * ENOENT and this branch is not needed.
+			 * However, in a saner future the EJUSTRETURN
+			 * garbage will go away, so let's DTRT.
+			 */
+			v1 = NULL;
+			error = 0;
+		}
+	} else {
+		if (error == 0 && v1 == NULL) {
+			/* This is what relookup sets if v1 disappeared. */
+			error = ENOENT;
+		}
+	}
+	if (error) {
+		VOP_UNLOCK(d1);
+		return error;
+	}
+	if (v1 && v1 == d2) {
+		VOP_UNLOCK(d1);
+		VOP_UNLOCK(v1);
+		vrele(v1);
+		return overlap_error;
+	}
+
+	/*
+	 * The right way to do this is to do lookups without locking
+	 * the results, and lock the results afterwards; then at the
+	 * end we can avoid trying to lock v2 if v2 == v1.
+	 *
+	 * However, for the reasons described in the fdvp == tdvp case
+	 * in rename below, we can't do that safely. So, in the case
+	 * where v1 is not a directory, unlock it and lock it again
+	 * afterwards. This is safe in locking order because a
+	 * non-directory can't be above anything else in the tree. If
+	 * v1 *is* a directory, that's not true, but then because d1
+	 * != d2, v1 != v2.
+	 */
+	if (v1 && v1->v_type != VDIR) {
+		VOP_UNLOCK(v1);
+	}
+	vn_lock(d2, LK_EXCLUSIVE | LK_RETRY);
+	if (VTOI(d2)->i_size == 0) {
+		/* d2 has been rmdir'd */
+		VOP_UNLOCK(d2);
+		if (v1 && v1->v_type == VDIR) {
+			VOP_UNLOCK(v1);
+		}
+		VOP_UNLOCK(d1);
+		if (v1) {
+			vrele(v1);
+		}
+		return ENOENT;
+	}
+	error = do_relookup(d2, ulr2, &v2, cn2);
+	if (v2_missing_ok) {
+		if (error == ENOENT) {
+			/* as above */
+			v2 = NULL;
+			error = 0;
+		}
+	} else {
+		if (error == 0 && v2 == NULL) {
+			/* This is what relookup sets if v2 disappeared. */
+			error = ENOENT;
+		}
+	}
+	if (error) {
+		VOP_UNLOCK(d2);
+		if (v1 && v1->v_type == VDIR) {
+			VOP_UNLOCK(v1);
+		}
+		VOP_UNLOCK(d1);
+		if (v1) {
+			vrele(v1);
+		}
+		return error;
+	}
+	if (v1 && v1->v_type != VDIR && v1 != v2) {
+		vn_lock(v1, LK_EXCLUSIVE | LK_RETRY);
+	}
+	*v1_ret = v1;
+	*v2_ret = v2;
+	return 0;
+}
+
+/*
+ * 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		*tvp, *tdvp, *fvp, *fdvp;
+	struct componentname	*tcnp, *fcnp;
+	struct inode		*ip, *txp, *fxp, *tdp, *fdp;
+	struct mount		*mp;
+	struct direct		*newdir;
+	int			doingdirectory, oldparent, newparent, error;
+
+	struct ufs_lookup_results from_ulr, to_ulr;
+
+	tvp = ap->a_tvp;
+	tdvp = ap->a_tdvp;
+	fvp = ap->a_fvp;
+	fdvp = ap->a_fdvp;
+	tcnp = ap->a_tcnp;
+	fcnp = ap->a_fcnp;
+	doingdirectory = oldparent = newparent = error = 0;
+
+	/* save the supplemental lookup results as they currently exist */
+	from_ulr = VTOI(fdvp)->i_crap;
+	to_ulr = VTOI(tdvp)->i_crap;
+	UFS_CHECK_CRAPCOUNTER(VTOI(fdvp));
+	UFS_CHECK_CRAPCOUNTER(VTOI(tdvp));
+
+	/*
+	 * Owing to VFS oddities we are currently called with tdvp/tvp
+	 * locked and not fdvp/fvp. In a sane world we'd be passed
+	 * tdvp and fdvp only, unlocked, and two name strings. Pretend
+	 * we have a sane world and unlock tdvp and tvp.
+	 */
+	VOP_UNLOCK(tdvp);
+	if (tvp && tvp != tdvp) {
+		VOP_UNLOCK(tvp);
+	}
+
+	/* Also pretend we have a sane world and vrele fvp/tvp. */
+	vrele(fvp);
+	fvp = NULL;
+	if (tvp) {
+		vrele(tvp);
+		tvp = NULL;
+	}
+
+	/*
+	 * Check for cross-device rename.
+	 */
+	if (fdvp->v_mount != tdvp->v_mount) {
+		error = EXDEV;
+		goto abort;
+	}
+
+	/*
+	 * Reject "." and ".."
+	 */
+	if ((fcnp->cn_flags & ISDOTDOT) || (tcnp->cn_flags & ISDOTDOT) ||
+	    (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
+	    (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')) {
+		error = EINVAL;
+		goto abort;
+	}
+	    
+	/*
+	 * Get locks.
+	 */
+
+	/* paranoia */
+	fcnp->cn_flags |= LOCKPARENT|LOCKLEAF;
+	tcnp->cn_flags |= LOCKPARENT|LOCKLEAF;
+
+	if (fdvp == tdvp) {
+		/* One directory. Lock it and relookup both children. */
+		vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
+
+		if (VTOI(fdvp)->i_size == 0) {
+			/* directory has been rmdir'd */
+			VOP_UNLOCK(fdvp);
+			error = ENOENT;
+			goto abort;
+		}
+
+		error = do_relookup(fdvp, &from_ulr, &fvp, fcnp);
+		if (error == 0 && fvp == NULL) {
+			/* relookup may produce this if fvp disappears */
+			error = ENOENT;
+		}
+		if (error) {
+			VOP_UNLOCK(fdvp);
+			goto abort;
+		}
+
+		/*
+		 * The right way to do this is to look up both children
+		 * without locking either, and then lock both unless they
+		 * turn out to be the same. However, due to deep-seated
+		 * VFS-level issues all lookups lock the child regardless
+		 * of whether LOCKLEAF is set (if LOCKLEAF is not set,
+		 * the child is locked during lookup and then unlocked)
+		 * so it is not safe to look up tvp while fvp is locked.
+		 *
+		 * Unlocking fvp here temporarily is more or less safe,
+		 * because with the directory locked there's not much
+		 * that can happen to it. However, ideally it wouldn't
+		 * be necessary. XXX.
+		 */
+		VOP_UNLOCK(fvp);
+		/* remember fdvp == tdvp so tdvp is locked */
+		error = do_relookup(tdvp, &to_ulr, &tvp, tcnp);
+		if (error && error != ENOENT) {
+			VOP_UNLOCK(fdvp);
+			goto abort;
+		}
+		if (error == ENOENT) {
+			/*
+			 * Note: currently if the name doesn't exist,
+			 * relookup succeeds (it intercepts the
+			 * EJUSTRETURN from VOP_LOOKUP) and sets tvp
+			 * to NULL. Therefore, we will never get
+			 * ENOENT and this branch is not needed.
+			 * However, in a saner future the EJUSTRETURN
+			 * garbage will go away, so let's DTRT.
+			 */
+			tvp = NULL;
+		}
+
+		/* tvp is locked; lock fvp if necessary */
+		if (!tvp || tvp != fvp) {
+			vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
+		}
+	} else {
+		int found_fdvp;
+		struct vnode *illegal_fvp;
+
+		/*
+		 * The source must not be above the destination. (If
+		 * it were, the rename would detach a section of the
+		 * tree.)
+		 *
+		 * Look up the tree from tdvp to see if we find fdvp,
+		 * and if so, return the immediate child of fdvp we're
+		 * under; that must not turn out to be the same as
+		 * fvp.
+		 *
+		 * The per-volume rename lock guarantees that the
+		 * result of this check remains true until we finish
+		 * looking up and locking.
+		 */
+		error = ufs_parentcheck(fdvp, tdvp, fcnp->cn_cred,
+					&found_fdvp, &illegal_fvp);
+		if (error) {
+			goto abort;
+		}
+
+		/* Must lock in tree order. */
+
+		if (found_fdvp) {
+			/* fdvp -> fvp -> tdvp -> tvp */
+			error = lock_vnode_sequence(fdvp, &from_ulr,
+						    &fvp, fcnp, 0,
+						    EINVAL,
+						    tdvp, &to_ulr,
+						    &tvp, tcnp, 1);
+		} else {
+			/* tdvp -> tvp -> fdvp -> fvp */
+			error = lock_vnode_sequence(tdvp, &to_ulr,
+						    &tvp, tcnp, 1,
+						    ENOTEMPTY,
+						    fdvp, &from_ulr,
+						    &fvp, fcnp, 0);
+		}
+		if (error) {
+			if (illegal_fvp) {
+				vrele(illegal_fvp);
+			}
+			goto abort;
+		}
+		KASSERT(fvp != NULL);
+
+		if (illegal_fvp && fvp == illegal_fvp) {
+			vrele(illegal_fvp);
+			error = EINVAL;
+			goto abort_withlocks;
+		}
+
+		if (illegal_fvp) {
+			vrele(illegal_fvp);
+		}
+	}
+
+	KASSERT(fdvp && VOP_ISLOCKED(fdvp));
+	KASSERT(fvp && VOP_ISLOCKED(fvp));
+	KASSERT(tdvp && VOP_ISLOCKED(tdvp));
+	KASSERT(tvp == NULL || VOP_ISLOCKED(tvp));
+
+	/* --- everything is now locked --- */
+
+	if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||
+	    (VTOI(tdvp)->i_flags & APPEND))) {
+		error = EPERM;
+		goto abort_withlocks;
+	}
+
+	/*
+	 * Check if just deleting a link name.
+	 */
+	if (fvp == tvp) {
+		if (fvp->v_type == VDIR) {
+			error = EINVAL;
+			goto abort_withlocks;
+		}
+
+		/* Release destination completely. Leave fdvp locked. */
+		VOP_ABORTOP(tdvp, tcnp);
+		if (fdvp != tdvp) {
+			VOP_UNLOCK(tdvp);
+		}
+		VOP_UNLOCK(tvp);
+		vrele(tdvp);
+		vrele(tvp);
+
+		/* Delete source. */
+		/* XXX: do we really need to relookup again? */
+
+		/*
+		 * fdvp is still locked, but we just unlocked fvp
+		 * (because fvp == tvp) so just decref fvp
+		 */
+		vrele(fvp);
+		fcnp->cn_flags &= ~(MODMASK);
+		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+		fcnp->cn_nameiop = DELETE;
+		if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
+			vput(fdvp);
+			return (error);
+		}
+		return (VOP_REMOVE(fdvp, fvp, fcnp));
+	}
+	fdp = VTOI(fdvp);
+	ip = VTOI(fvp);
+	if ((nlink_t) ip->i_nlink >= LINK_MAX) {
+		error = EMLINK;
+		goto abort_withlocks;
+	}
+	if ((ip->i_flags & (IMMUTABLE | APPEND)) ||
+		(fdp->i_flags & APPEND)) {
+		error = EPERM;
+		goto abort_withlocks;
+	}
+	if ((ip->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)) {
+			error = EINVAL;
+			goto abort_withlocks;
+		}
+		ip->i_flag |= IN_RENAME;
+		doingdirectory = 1;
+	}
+	oldparent = fdp->i_number;
+	VN_KNOTE(fdvp, NOTE_WRITE);		/* XXXLUKEM/XXX: right place? */
+
+	/*
+	 * Both the directory
+	 * and target vnodes are locked.
+	 */
+	tdp = VTOI(tdvp);
+	txp = NULL;
+	if (tvp)
+		txp = VTOI(tvp);
+
+	mp = fdvp->v_mount;
+	fstrans_start(mp, FSTRANS_SHARED);
+
+	if (oldparent != tdp->i_number)
+		newparent = tdp->i_number;
+
+	/*
+	 * If ".." must be changed (ie the directory gets a new
+	 * parent) the user must have write permission in the source
+	 * so as to be able to change "..".
+	 */
+	if (doingdirectory && newparent) {
+		error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
+		if (error)
+			goto out;
+	}
+
+	KASSERT(fdvp != tvp);
+
+	if (newparent) {
+		/* Check for the rename("foo/foo", "foo") case. */
+		if (fdvp == tvp) {
+			error = doingdirectory ? ENOTEMPTY : EISDIR;
+			goto out;
+		}
+	}
+
+	fxp = VTOI(fvp);
+	fdp = VTOI(fdvp);
+
+	error = UFS_WAPBL_BEGIN(fdvp->v_mount);
+	if (error)
+		goto out2;
+
+	/*
+	 * 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.
+	 */
+	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) {
+		goto bad;
+	}
+
+	/*
+	 * 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 (txp == NULL) {
+		if (tdp->i_dev != ip->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)tdp->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,
+			    UPDATE_DIROP)) != 0) {
+				tdp->i_nlink--;
+				DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
+				tdp->i_flag |= IN_CHANGE;
+				goto bad;
+			}
+		}
+		newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
+		ufs_makedirentry(ip, tcnp, newdir);
+		error = ufs_direnter(tdvp, &to_ulr,
+				     NULL, newdir, tcnp, 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,
+						 UPDATE_WAIT | UPDATE_DIROP);
+			}
+			goto bad;
+		}
+		VN_KNOTE(tdvp, NOTE_WRITE);
+	} else {
+		if (txp->i_dev != tdp->i_dev || txp->i_dev != ip->i_dev)
+			panic("rename: EXDEV");
+		/*
+		 * Short circuit rename(foo, foo).
+		 */
+		if (txp->i_number == ip->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 ((tdp->i_mode & S_ISTXT) &&
+		    kauth_authorize_generic(tcnp->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)) {
+			error = EPERM;
+			goto bad;
+		}
+		/*
+		 * 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 ((txp->i_mode & IFMT) == IFDIR) {
+			if (txp->i_nlink > 2 ||
+			    !ufs_dirempty(txp, tdp->i_number, tcnp->cn_cred)) {
+				error = ENOTEMPTY;
+				goto bad;
+			}
+			if (!doingdirectory) {
+				error = ENOTDIR;
+				goto bad;
+			}
+			cache_purge(tdvp);
+		} else if (doingdirectory) {
+			error = EISDIR;
+			goto bad;
+		}
+		if ((error = ufs_dirrewrite(tdp, to_ulr.ulr_offset,
+		    txp, ip->i_number,
+		    IFTODT(ip->i_mode), doingdirectory && newparent ?
+		    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
+			goto bad;
+		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) {
+				tdp->i_nlink--;
+				DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
+				tdp->i_flag |= IN_CHANGE;
+				UFS_WAPBL_UPDATE(tdvp, 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)))
+				goto bad;
+		}
+		VN_KNOTE(tdvp, NOTE_WRITE);
+		VN_KNOTE(tvp, NOTE_DELETE);
+	}
+
+	/*
+	 * Handle case where the directory entry we need to remove,
+	 * which is/was at from_ulr.ulr_offset, or the one before it,
+	 * which is/was at from_ulr.ulr_offset - from_ulr.ulr_count,
+	 * may have been moved when the directory insertion above
+	 * performed compaction.
+	 */
+	if (tdp->i_number == fdp->i_number &&
+	    ulr_overlap(&from_ulr, &to_ulr)) {
+
+		struct buf *bp;
+		struct direct *ep;
+		struct ufsmount *ump = fdp->i_ump;
+		doff_t curpos;
+		doff_t endsearch;	/* offset to end directory search */
+		uint32_t prev_reclen;
+		int dirblksiz = ump->um_dirblksiz;
+		const int needswap = UFS_MPNEEDSWAP(ump);
+		u_long bmask;
+		int namlen, entryoffsetinblock;
+		char *dirbuf;
+
+		bmask = fdvp->v_mount->mnt_stat.f_iosize - 1;
+
+		/*
+		 * The fcnp entry will be somewhere between the start of
+		 * compaction (to_ulr.ulr_offset) and the original location
+		 * (from_ulr.ulr_offset).
+		 */
+		curpos = to_ulr.ulr_offset;
+		endsearch = from_ulr.ulr_offset + from_ulr.ulr_reclen;
+		entryoffsetinblock = 0;
+
+		/*
+		 * Get the directory block containing the start of
+		 * compaction.
+		 */
+		error = ufs_blkatoff(fdvp, (off_t)to_ulr.ulr_offset, &dirbuf,
+		    &bp, false);
+		if (error)
+			goto bad;
+
+		/*
+		 * Keep existing ulr_count (length of previous record)
+		 * for the case where compaction did not include the
+		 * previous entry but started at the from-entry.
+		 */
+		prev_reclen = from_ulr.ulr_count;
+
+		while (curpos < endsearch) {
+			uint32_t reclen;
+
+			/*
+			 * If necessary, get the next directory block.
+			 *
+			 * dholland 7/13/11 to the best of my understanding
+			 * this should never happen; compaction occurs only
+			 * within single blocks. I think.
+			 */
+			if ((curpos & bmask) == 0) {
+				if (bp != NULL)
+					brelse(bp, 0);
+				error = ufs_blkatoff(fdvp, (off_t)curpos,
+				    &dirbuf, &bp, false);
+				if (error)
+					goto bad;
+				entryoffsetinblock = 0;
+			}
+
+			KASSERT(bp != NULL);
+			ep = (struct direct *)(dirbuf + entryoffsetinblock);
+			reclen = ufs_rw16(ep->d_reclen, needswap);
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+			if (FSFMT(fdvp) && needswap == 0)
+				namlen = ep->d_type;
+			else
+				namlen = ep->d_namlen;
+#else
+			if (FSFMT(fdvp) && 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) {
+				from_ulr.ulr_reclen = reclen;
+				break;
+			}
+			curpos += reclen;
+			entryoffsetinblock += reclen;
+			prev_reclen = reclen;
+		}
+
+		from_ulr.ulr_offset = curpos;
+		from_ulr.ulr_count = prev_reclen;
+
+		KASSERT(curpos <= endsearch);
+
+		/*
+		 * If ulr_offset points to start of a directory block,
+		 * clear ulr_count so ufs_dirremove() doesn't try to
+		 * merge free space over a directory block boundary.
+		 */
+		if ((from_ulr.ulr_offset & (dirblksiz - 1)) == 0)
+			from_ulr.ulr_count = 0;
+
+		brelse(bp, 0);
+	}
+
+	/*
+	 * 3) Unlink the source.
+	 */
+
+#if 0
+	/*
+	 * 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.
+	 */
+#endif
+	KASSERT(fxp == ip);
+
+	/*
+	 * 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(fdp != NULL);
+		ufs_dirrewrite(fxp, mastertemplate.dot_reclen,
+			       fdp, newparent, DT_DIR, 0, IN_CHANGE);
+		cache_purge(fdvp);
+	}
+	error = ufs_dirremove(fdvp, &from_ulr,
+			      fxp, fcnp->cn_flags, 0);
+	fxp->i_flag &= ~IN_RENAME;
+
+	VN_KNOTE(fvp, NOTE_RENAME);
+	goto done;
+
+ out:
+	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);
+ done:
+	UFS_WAPBL_END(fdvp->v_mount);
+ 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;
+
+	VOP_UNLOCK(fdvp);
+	if (tdvp != fdvp) {
+		VOP_UNLOCK(tdvp);
+	}
+	VOP_UNLOCK(fvp);
+	if (tvp && tvp != fvp) {
+		VOP_UNLOCK(tvp);
+	}
+
+	vrele(fdvp);
+	vrele(tdvp);
+	vrele(fvp);
+	if (tvp) {
+		vrele(tvp);
+	}
+
+	fstrans_done(mp);
+	return (error);
+
+ abort_withlocks:
+	VOP_UNLOCK(fdvp);
+	if (tdvp != fdvp) {
+		VOP_UNLOCK(tdvp);
+	}
+	VOP_UNLOCK(fvp);
+	if (tvp && tvp != fvp) {
+		VOP_UNLOCK(tvp);
+	}
+
+ abort:
+	VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
+	VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
+	vrele(tdvp);
+	if (tvp) {
+		vrele(tvp);
+	}
+	vrele(fdvp);
+	if (fvp) {
+		vrele(fvp);
+	}
+	return (error);
+}
+
 int
 ufs_rename(void *v)
 {

Index: src/sys/ufs/ufs/ufs_wapbl.c
diff -u src/sys/ufs/ufs/ufs_wapbl.c:1.20 src/sys/ufs/ufs/ufs_wapbl.c:1.21
--- src/sys/ufs/ufs/ufs_wapbl.c:1.20	Mon Jul 18 01:14:27 2011
+++ src/sys/ufs/ufs/ufs_wapbl.c	Mon Jul 18 06:45:28 2011
@@ -1,4 +1,4 @@
-/*  $NetBSD: ufs_wapbl.c,v 1.20 2011/07/18 01:14:27 dholland Exp $ */
+/*  $NetBSD: ufs_wapbl.c,v 1.21 2011/07/18 06:45:28 dholland Exp $ */
 
 /*-
  * Copyright (c) 2003,2006,2008 The NetBSD Foundation, Inc.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_wapbl.c,v 1.20 2011/07/18 01:14:27 dholland Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_wapbl.c,v 1.21 2011/07/18 06:45:28 dholland Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -101,9 +101,6 @@
 
 #include <uvm/uvm.h>
 
-/* XXX following lifted from ufs_lookup.c */
-#define	FSFMT(vp)	(((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
-
 /*
  * A virgin directory (no blushing please).
  */
@@ -112,869 +109,6 @@
 	0,	DIRBLKSIZ - 12,	DT_DIR,	2,	".."
 };
 
-/*
- * Check if either entry referred to by FROM_ULR is within the range
- * of entries named by TO_ULR.
- */
-static int
-ulr_overlap(const struct ufs_lookup_results *from_ulr,
-	    const struct ufs_lookup_results *to_ulr)
-{
-	doff_t from_start, from_prevstart;
-	doff_t to_start, to_end;
-
-	/*
-	 * FROM is a DELETE result; offset points to the entry to
-	 * remove and subtracting count gives the previous entry.
-	 */
-	from_start = from_ulr->ulr_offset - from_ulr->ulr_count;
-	from_prevstart = from_ulr->ulr_offset;
-
-	/*
-	 * TO is a RENAME (thus non-DELETE) result; offset points
-	 * to the beginning of a region to write in, and adding
-	 * count gives the end of the region.
-	 */
-	to_start = to_ulr->ulr_offset;
-	to_end = to_ulr->ulr_offset + to_ulr->ulr_count;
-
-	if (from_prevstart >= to_start && from_prevstart < to_end) {
-		return 1;
-	}
-	if (from_start >= to_start && from_start < to_end) {
-		return 1;
-	}
-	return 0;
-}
-
-/*
- * Wrapper for relookup that also updates the supplemental results.
- */
-static int
-do_relookup(struct vnode *dvp, struct ufs_lookup_results *ulr,
-	    struct vnode **vp, struct componentname *cnp)
-{
-	int error;
-
-	error = relookup(dvp, vp, cnp, 0);
-	if (error) {
-		return error;
-	}
-	/* update the supplemental reasults */
-	*ulr = VTOI(dvp)->i_crap;
-	UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
-	return 0;
-}
-
-/*
- * Lock and relookup a sequence of two directories and two children.
- *
- */
-static int
-lock_vnode_sequence(struct vnode *d1, struct ufs_lookup_results *ulr1,
-		    struct vnode **v1_ret, struct componentname *cn1, 
-		    int v1_missing_ok,
-		    int overlap_error,
-		    struct vnode *d2, struct ufs_lookup_results *ulr2,
-		    struct vnode **v2_ret, struct componentname *cn2, 
-		    int v2_missing_ok)
-{
-	struct vnode *v1, *v2;
-	int error;
-
-	KASSERT(d1 != d2);
-
-	vn_lock(d1, LK_EXCLUSIVE | LK_RETRY);
-	if (VTOI(d1)->i_size == 0) {
-		/* d1 has been rmdir'd */
-		VOP_UNLOCK(d1);
-		return ENOENT;
-	}
-	error = do_relookup(d1, ulr1, &v1, cn1);
-	if (v1_missing_ok) {
-		if (error == ENOENT) {
-			/*
-			 * Note: currently if the name doesn't exist,
-			 * relookup succeeds (it intercepts the
-			 * EJUSTRETURN from VOP_LOOKUP) and sets tvp
-			 * to NULL. Therefore, we will never get
-			 * ENOENT and this branch is not needed.
-			 * However, in a saner future the EJUSTRETURN
-			 * garbage will go away, so let's DTRT.
-			 */
-			v1 = NULL;
-			error = 0;
-		}
-	} else {
-		if (error == 0 && v1 == NULL) {
-			/* This is what relookup sets if v1 disappeared. */
-			error = ENOENT;
-		}
-	}
-	if (error) {
-		VOP_UNLOCK(d1);
-		return error;
-	}
-	if (v1 && v1 == d2) {
-		VOP_UNLOCK(d1);
-		VOP_UNLOCK(v1);
-		vrele(v1);
-		return overlap_error;
-	}
-
-	/*
-	 * The right way to do this is to do lookups without locking
-	 * the results, and lock the results afterwards; then at the
-	 * end we can avoid trying to lock v2 if v2 == v1.
-	 *
-	 * However, for the reasons described in the fdvp == tdvp case
-	 * in rename below, we can't do that safely. So, in the case
-	 * where v1 is not a directory, unlock it and lock it again
-	 * afterwards. This is safe in locking order because a
-	 * non-directory can't be above anything else in the tree. If
-	 * v1 *is* a directory, that's not true, but then because d1
-	 * != d2, v1 != v2.
-	 */
-	if (v1 && v1->v_type != VDIR) {
-		VOP_UNLOCK(v1);
-	}
-	vn_lock(d2, LK_EXCLUSIVE | LK_RETRY);
-	if (VTOI(d2)->i_size == 0) {
-		/* d2 has been rmdir'd */
-		VOP_UNLOCK(d2);
-		if (v1 && v1->v_type == VDIR) {
-			VOP_UNLOCK(v1);
-		}
-		VOP_UNLOCK(d1);
-		if (v1) {
-			vrele(v1);
-		}
-		return ENOENT;
-	}
-	error = do_relookup(d2, ulr2, &v2, cn2);
-	if (v2_missing_ok) {
-		if (error == ENOENT) {
-			/* as above */
-			v2 = NULL;
-			error = 0;
-		}
-	} else {
-		if (error == 0 && v2 == NULL) {
-			/* This is what relookup sets if v2 disappeared. */
-			error = ENOENT;
-		}
-	}
-	if (error) {
-		VOP_UNLOCK(d2);
-		if (v1 && v1->v_type == VDIR) {
-			VOP_UNLOCK(v1);
-		}
-		VOP_UNLOCK(d1);
-		if (v1) {
-			vrele(v1);
-		}
-		return error;
-	}
-	if (v1 && v1->v_type != VDIR && v1 != v2) {
-		vn_lock(v1, LK_EXCLUSIVE | LK_RETRY);
-	}
-	*v1_ret = v1;
-	*v2_ret = v2;
-	return 0;
-}
-
-/*
- * 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		*tvp, *tdvp, *fvp, *fdvp;
-	struct componentname	*tcnp, *fcnp;
-	struct inode		*ip, *txp, *fxp, *tdp, *fdp;
-	struct mount		*mp;
-	struct direct		*newdir;
-	int			doingdirectory, oldparent, newparent, error;
-
-	struct ufs_lookup_results from_ulr, to_ulr;
-
-	tvp = ap->a_tvp;
-	tdvp = ap->a_tdvp;
-	fvp = ap->a_fvp;
-	fdvp = ap->a_fdvp;
-	tcnp = ap->a_tcnp;
-	fcnp = ap->a_fcnp;
-	doingdirectory = oldparent = newparent = error = 0;
-
-	/* save the supplemental lookup results as they currently exist */
-	from_ulr = VTOI(fdvp)->i_crap;
-	to_ulr = VTOI(tdvp)->i_crap;
-	UFS_CHECK_CRAPCOUNTER(VTOI(fdvp));
-	UFS_CHECK_CRAPCOUNTER(VTOI(tdvp));
-
-	/*
-	 * Owing to VFS oddities we are currently called with tdvp/tvp
-	 * locked and not fdvp/fvp. In a sane world we'd be passed
-	 * tdvp and fdvp only, unlocked, and two name strings. Pretend
-	 * we have a sane world and unlock tdvp and tvp.
-	 */
-	VOP_UNLOCK(tdvp);
-	if (tvp && tvp != tdvp) {
-		VOP_UNLOCK(tvp);
-	}
-
-	/* Also pretend we have a sane world and vrele fvp/tvp. */
-	vrele(fvp);
-	fvp = NULL;
-	if (tvp) {
-		vrele(tvp);
-		tvp = NULL;
-	}
-
-	/*
-	 * Check for cross-device rename.
-	 */
-	if (fdvp->v_mount != tdvp->v_mount) {
-		error = EXDEV;
-		goto abort;
-	}
-
-	/*
-	 * Reject "." and ".."
-	 */
-	if ((fcnp->cn_flags & ISDOTDOT) || (tcnp->cn_flags & ISDOTDOT) ||
-	    (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
-	    (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')) {
-		error = EINVAL;
-		goto abort;
-	}
-	    
-	/*
-	 * Get locks.
-	 */
-
-	/* paranoia */
-	fcnp->cn_flags |= LOCKPARENT|LOCKLEAF;
-	tcnp->cn_flags |= LOCKPARENT|LOCKLEAF;
-
-	if (fdvp == tdvp) {
-		/* One directory. Lock it and relookup both children. */
-		vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
-
-		if (VTOI(fdvp)->i_size == 0) {
-			/* directory has been rmdir'd */
-			VOP_UNLOCK(fdvp);
-			error = ENOENT;
-			goto abort;
-		}
-
-		error = do_relookup(fdvp, &from_ulr, &fvp, fcnp);
-		if (error == 0 && fvp == NULL) {
-			/* relookup may produce this if fvp disappears */
-			error = ENOENT;
-		}
-		if (error) {
-			VOP_UNLOCK(fdvp);
-			goto abort;
-		}
-
-		/*
-		 * The right way to do this is to look up both children
-		 * without locking either, and then lock both unless they
-		 * turn out to be the same. However, due to deep-seated
-		 * VFS-level issues all lookups lock the child regardless
-		 * of whether LOCKLEAF is set (if LOCKLEAF is not set,
-		 * the child is locked during lookup and then unlocked)
-		 * so it is not safe to look up tvp while fvp is locked.
-		 *
-		 * Unlocking fvp here temporarily is more or less safe,
-		 * because with the directory locked there's not much
-		 * that can happen to it. However, ideally it wouldn't
-		 * be necessary. XXX.
-		 */
-		VOP_UNLOCK(fvp);
-		/* remember fdvp == tdvp so tdvp is locked */
-		error = do_relookup(tdvp, &to_ulr, &tvp, tcnp);
-		if (error && error != ENOENT) {
-			VOP_UNLOCK(fdvp);
-			goto abort;
-		}
-		if (error == ENOENT) {
-			/*
-			 * Note: currently if the name doesn't exist,
-			 * relookup succeeds (it intercepts the
-			 * EJUSTRETURN from VOP_LOOKUP) and sets tvp
-			 * to NULL. Therefore, we will never get
-			 * ENOENT and this branch is not needed.
-			 * However, in a saner future the EJUSTRETURN
-			 * garbage will go away, so let's DTRT.
-			 */
-			tvp = NULL;
-		}
-
-		/* tvp is locked; lock fvp if necessary */
-		if (!tvp || tvp != fvp) {
-			vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
-		}
-	} else {
-		int found_fdvp;
-		struct vnode *illegal_fvp;
-
-		/*
-		 * The source must not be above the destination. (If
-		 * it were, the rename would detach a section of the
-		 * tree.)
-		 *
-		 * Look up the tree from tdvp to see if we find fdvp,
-		 * and if so, return the immediate child of fdvp we're
-		 * under; that must not turn out to be the same as
-		 * fvp.
-		 *
-		 * The per-volume rename lock guarantees that the
-		 * result of this check remains true until we finish
-		 * looking up and locking.
-		 */
-		error = ufs_parentcheck(fdvp, tdvp, fcnp->cn_cred,
-					&found_fdvp, &illegal_fvp);
-		if (error) {
-			goto abort;
-		}
-
-		/* Must lock in tree order. */
-
-		if (found_fdvp) {
-			/* fdvp -> fvp -> tdvp -> tvp */
-			error = lock_vnode_sequence(fdvp, &from_ulr,
-						    &fvp, fcnp, 0,
-						    EINVAL,
-						    tdvp, &to_ulr,
-						    &tvp, tcnp, 1);
-		} else {
-			/* tdvp -> tvp -> fdvp -> fvp */
-			error = lock_vnode_sequence(tdvp, &to_ulr,
-						    &tvp, tcnp, 1,
-						    ENOTEMPTY,
-						    fdvp, &from_ulr,
-						    &fvp, fcnp, 0);
-		}
-		if (error) {
-			if (illegal_fvp) {
-				vrele(illegal_fvp);
-			}
-			goto abort;
-		}
-		KASSERT(fvp != NULL);
-
-		if (illegal_fvp && fvp == illegal_fvp) {
-			vrele(illegal_fvp);
-			error = EINVAL;
-			goto abort_withlocks;
-		}
-
-		if (illegal_fvp) {
-			vrele(illegal_fvp);
-		}
-	}
-
-	KASSERT(fdvp && VOP_ISLOCKED(fdvp));
-	KASSERT(fvp && VOP_ISLOCKED(fvp));
-	KASSERT(tdvp && VOP_ISLOCKED(tdvp));
-	KASSERT(tvp == NULL || VOP_ISLOCKED(tvp));
-
-	/* --- everything is now locked --- */
-
-	if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||
-	    (VTOI(tdvp)->i_flags & APPEND))) {
-		error = EPERM;
-		goto abort_withlocks;
-	}
-
-	/*
-	 * Check if just deleting a link name.
-	 */
-	if (fvp == tvp) {
-		if (fvp->v_type == VDIR) {
-			error = EINVAL;
-			goto abort_withlocks;
-		}
-
-		/* Release destination completely. Leave fdvp locked. */
-		VOP_ABORTOP(tdvp, tcnp);
-		if (fdvp != tdvp) {
-			VOP_UNLOCK(tdvp);
-		}
-		VOP_UNLOCK(tvp);
-		vrele(tdvp);
-		vrele(tvp);
-
-		/* Delete source. */
-		/* XXX: do we really need to relookup again? */
-
-		/*
-		 * fdvp is still locked, but we just unlocked fvp
-		 * (because fvp == tvp) so just decref fvp
-		 */
-		vrele(fvp);
-		fcnp->cn_flags &= ~(MODMASK);
-		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
-		fcnp->cn_nameiop = DELETE;
-		if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
-			vput(fdvp);
-			return (error);
-		}
-		return (VOP_REMOVE(fdvp, fvp, fcnp));
-	}
-	fdp = VTOI(fdvp);
-	ip = VTOI(fvp);
-	if ((nlink_t) ip->i_nlink >= LINK_MAX) {
-		error = EMLINK;
-		goto abort_withlocks;
-	}
-	if ((ip->i_flags & (IMMUTABLE | APPEND)) ||
-		(fdp->i_flags & APPEND)) {
-		error = EPERM;
-		goto abort_withlocks;
-	}
-	if ((ip->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)) {
-			error = EINVAL;
-			goto abort_withlocks;
-		}
-		ip->i_flag |= IN_RENAME;
-		doingdirectory = 1;
-	}
-	oldparent = fdp->i_number;
-	VN_KNOTE(fdvp, NOTE_WRITE);		/* XXXLUKEM/XXX: right place? */
-
-	/*
-	 * Both the directory
-	 * and target vnodes are locked.
-	 */
-	tdp = VTOI(tdvp);
-	txp = NULL;
-	if (tvp)
-		txp = VTOI(tvp);
-
-	mp = fdvp->v_mount;
-	fstrans_start(mp, FSTRANS_SHARED);
-
-	if (oldparent != tdp->i_number)
-		newparent = tdp->i_number;
-
-	/*
-	 * If ".." must be changed (ie the directory gets a new
-	 * parent) the user must have write permission in the source
-	 * so as to be able to change "..".
-	 */
-	if (doingdirectory && newparent) {
-		error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
-		if (error)
-			goto out;
-	}
-
-	KASSERT(fdvp != tvp);
-
-	if (newparent) {
-		/* Check for the rename("foo/foo", "foo") case. */
-		if (fdvp == tvp) {
-			error = doingdirectory ? ENOTEMPTY : EISDIR;
-			goto out;
-		}
-	}
-
-	fxp = VTOI(fvp);
-	fdp = VTOI(fdvp);
-
-	error = UFS_WAPBL_BEGIN(fdvp->v_mount);
-	if (error)
-		goto out2;
-
-	/*
-	 * 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.
-	 */
-	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) {
-		goto bad;
-	}
-
-	/*
-	 * 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 (txp == NULL) {
-		if (tdp->i_dev != ip->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)tdp->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,
-			    UPDATE_DIROP)) != 0) {
-				tdp->i_nlink--;
-				DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
-				tdp->i_flag |= IN_CHANGE;
-				goto bad;
-			}
-		}
-		newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
-		ufs_makedirentry(ip, tcnp, newdir);
-		error = ufs_direnter(tdvp, &to_ulr,
-				     NULL, newdir, tcnp, 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,
-						 UPDATE_WAIT | UPDATE_DIROP);
-			}
-			goto bad;
-		}
-		VN_KNOTE(tdvp, NOTE_WRITE);
-	} else {
-		if (txp->i_dev != tdp->i_dev || txp->i_dev != ip->i_dev)
-			panic("rename: EXDEV");
-		/*
-		 * Short circuit rename(foo, foo).
-		 */
-		if (txp->i_number == ip->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 ((tdp->i_mode & S_ISTXT) &&
-		    kauth_authorize_generic(tcnp->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)) {
-			error = EPERM;
-			goto bad;
-		}
-		/*
-		 * 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 ((txp->i_mode & IFMT) == IFDIR) {
-			if (txp->i_nlink > 2 ||
-			    !ufs_dirempty(txp, tdp->i_number, tcnp->cn_cred)) {
-				error = ENOTEMPTY;
-				goto bad;
-			}
-			if (!doingdirectory) {
-				error = ENOTDIR;
-				goto bad;
-			}
-			cache_purge(tdvp);
-		} else if (doingdirectory) {
-			error = EISDIR;
-			goto bad;
-		}
-		if ((error = ufs_dirrewrite(tdp, to_ulr.ulr_offset,
-		    txp, ip->i_number,
-		    IFTODT(ip->i_mode), doingdirectory && newparent ?
-		    newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
-			goto bad;
-		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) {
-				tdp->i_nlink--;
-				DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
-				tdp->i_flag |= IN_CHANGE;
-				UFS_WAPBL_UPDATE(tdvp, 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)))
-				goto bad;
-		}
-		VN_KNOTE(tdvp, NOTE_WRITE);
-		VN_KNOTE(tvp, NOTE_DELETE);
-	}
-
-	/*
-	 * Handle case where the directory entry we need to remove,
-	 * which is/was at from_ulr.ulr_offset, or the one before it,
-	 * which is/was at from_ulr.ulr_offset - from_ulr.ulr_count,
-	 * may have been moved when the directory insertion above
-	 * performed compaction.
-	 */
-	if (tdp->i_number == fdp->i_number &&
-	    ulr_overlap(&from_ulr, &to_ulr)) {
-
-		struct buf *bp;
-		struct direct *ep;
-		struct ufsmount *ump = fdp->i_ump;
-		doff_t curpos;
-		doff_t endsearch;	/* offset to end directory search */
-		uint32_t prev_reclen;
-		int dirblksiz = ump->um_dirblksiz;
-		const int needswap = UFS_MPNEEDSWAP(ump);
-		u_long bmask;
-		int namlen, entryoffsetinblock;
-		char *dirbuf;
-
-		bmask = fdvp->v_mount->mnt_stat.f_iosize - 1;
-
-		/*
-		 * The fcnp entry will be somewhere between the start of
-		 * compaction (to_ulr.ulr_offset) and the original location
-		 * (from_ulr.ulr_offset).
-		 */
-		curpos = to_ulr.ulr_offset;
-		endsearch = from_ulr.ulr_offset + from_ulr.ulr_reclen;
-		entryoffsetinblock = 0;
-
-		/*
-		 * Get the directory block containing the start of
-		 * compaction.
-		 */
-		error = ufs_blkatoff(fdvp, (off_t)to_ulr.ulr_offset, &dirbuf,
-		    &bp, false);
-		if (error)
-			goto bad;
-
-		/*
-		 * Keep existing ulr_count (length of previous record)
-		 * for the case where compaction did not include the
-		 * previous entry but started at the from-entry.
-		 */
-		prev_reclen = from_ulr.ulr_count;
-
-		while (curpos < endsearch) {
-			uint32_t reclen;
-
-			/*
-			 * If necessary, get the next directory block.
-			 *
-			 * dholland 7/13/11 to the best of my understanding
-			 * this should never happen; compaction occurs only
-			 * within single blocks. I think.
-			 */
-			if ((curpos & bmask) == 0) {
-				if (bp != NULL)
-					brelse(bp, 0);
-				error = ufs_blkatoff(fdvp, (off_t)curpos,
-				    &dirbuf, &bp, false);
-				if (error)
-					goto bad;
-				entryoffsetinblock = 0;
-			}
-
-			KASSERT(bp != NULL);
-			ep = (struct direct *)(dirbuf + entryoffsetinblock);
-			reclen = ufs_rw16(ep->d_reclen, needswap);
-
-#if (BYTE_ORDER == LITTLE_ENDIAN)
-			if (FSFMT(fdvp) && needswap == 0)
-				namlen = ep->d_type;
-			else
-				namlen = ep->d_namlen;
-#else
-			if (FSFMT(fdvp) && 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) {
-				from_ulr.ulr_reclen = reclen;
-				break;
-			}
-			curpos += reclen;
-			entryoffsetinblock += reclen;
-			prev_reclen = reclen;
-		}
-
-		from_ulr.ulr_offset = curpos;
-		from_ulr.ulr_count = prev_reclen;
-
-		KASSERT(curpos <= endsearch);
-
-		/*
-		 * If ulr_offset points to start of a directory block,
-		 * clear ulr_count so ufs_dirremove() doesn't try to
-		 * merge free space over a directory block boundary.
-		 */
-		if ((from_ulr.ulr_offset & (dirblksiz - 1)) == 0)
-			from_ulr.ulr_count = 0;
-
-		brelse(bp, 0);
-	}
-
-	/*
-	 * 3) Unlink the source.
-	 */
-
-#if 0
-	/*
-	 * 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.
-	 */
-#endif
-	KASSERT(fxp == ip);
-
-	/*
-	 * 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(fdp != NULL);
-		ufs_dirrewrite(fxp, mastertemplate.dot_reclen,
-			       fdp, newparent, DT_DIR, 0, IN_CHANGE);
-		cache_purge(fdvp);
-	}
-	error = ufs_dirremove(fdvp, &from_ulr,
-			      fxp, fcnp->cn_flags, 0);
-	fxp->i_flag &= ~IN_RENAME;
-
-	VN_KNOTE(fvp, NOTE_RENAME);
-	goto done;
-
- out:
-	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);
- done:
-	UFS_WAPBL_END(fdvp->v_mount);
- 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;
-
-	VOP_UNLOCK(fdvp);
-	if (tdvp != fdvp) {
-		VOP_UNLOCK(tdvp);
-	}
-	VOP_UNLOCK(fvp);
-	if (tvp && tvp != fvp) {
-		VOP_UNLOCK(tvp);
-	}
-
-	vrele(fdvp);
-	vrele(tdvp);
-	vrele(fvp);
-	if (tvp) {
-		vrele(tvp);
-	}
-
-	fstrans_done(mp);
-	return (error);
-
- abort_withlocks:
-	VOP_UNLOCK(fdvp);
-	if (tdvp != fdvp) {
-		VOP_UNLOCK(tdvp);
-	}
-	VOP_UNLOCK(fvp);
-	if (tvp && tvp != fvp) {
-		VOP_UNLOCK(tvp);
-	}
-
- abort:
-	VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
-	VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
-	vrele(tdvp);
-	if (tvp) {
-		vrele(tvp);
-	}
-	vrele(fdvp);
-	if (fvp) {
-		vrele(fvp);
-	}
-	return (error);
-}
-
 #ifdef WAPBL_DEBUG_INODES
 #error WAPBL_DEBUG_INODES: not functional before ufs_wapbl.c is updated
 void

Reply via email to