Move ufs_wapbl_rename to ufs_vnops.c next to the old ufs_rename.
This commit is contained in:
parent
b4152f8512
commit
ffbed3d146
@ -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 @@ ufs_whiteout(void *v)
|
||||
* 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)
|
||||
{
|
||||
|
@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: ufs_wapbl.c,v 1.20 2011/07/18 01:14:27 dholland Exp
|
||||
|
||||
#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 @@ static const struct dirtemplate mastertemplate = {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user