Fix a problem noticed by Reinoud: the fs would try to release an
unlocked vnode when trying to rename a directory. The fix was to shuffle some bits around and #pray. The rename routine actually needs a very very major wide-angle whopping: * it takes locks out-of-order * it deals with references from SAVESTART lookups in interesting ways * I doubt there is any guarantee for correct operation if there are multiple concurrent accesses * the error branches might just as well call panic() directly
This commit is contained in:
parent
276f1a3cde
commit
4a0a4d4f30
|
@ -1,4 +1,4 @@
|
||||||
/* $NetBSD: msdosfs_vnops.c,v 1.42 2007/10/08 18:04:04 ad Exp $ */
|
/* $NetBSD: msdosfs_vnops.c,v 1.43 2007/11/14 19:16:29 pooka Exp $ */
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
|
* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.42 2007/10/08 18:04:04 ad Exp $");
|
__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.43 2007/11/14 19:16:29 pooka Exp $");
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
|
@ -834,6 +834,9 @@ msdosfs_link(v)
|
||||||
* I'm not sure how the memory containing the pathnames pointed at by the
|
* I'm not sure how the memory containing the pathnames pointed at by the
|
||||||
* componentname structures is freed, there may be some memory bleeding
|
* componentname structures is freed, there may be some memory bleeding
|
||||||
* for each rename done.
|
* for each rename done.
|
||||||
|
*
|
||||||
|
* --More-- Notes:
|
||||||
|
* This routine needs help. badly.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
msdosfs_rename(v)
|
msdosfs_rename(v)
|
||||||
|
@ -902,7 +905,11 @@ abortit:
|
||||||
goto abortit;
|
goto abortit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* */
|
/*
|
||||||
|
* XXX: This can deadlock since we hold tdvp/tvp locked.
|
||||||
|
* But I'm not going to fix it now. If lockmgr detected the
|
||||||
|
* deadlock, this might actually work... sorta.
|
||||||
|
*/
|
||||||
if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
|
if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
|
||||||
goto abortit;
|
goto abortit;
|
||||||
dp = VTODE(fdvp);
|
dp = VTODE(fdvp);
|
||||||
|
@ -958,12 +965,23 @@ abortit:
|
||||||
VOP_UNLOCK(fvp, 0);
|
VOP_UNLOCK(fvp, 0);
|
||||||
if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
|
if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
|
||||||
newparent = 1;
|
newparent = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: We can do this here because rename uses SAVEFART and
|
||||||
|
* therefore fdvp has at least two references (one doesn't
|
||||||
|
* belong to us, though, and that's evil). We'll get
|
||||||
|
* another "extra" reference when we do relookup(), so we
|
||||||
|
* need to compensate. We should *NOT* be doing this, but
|
||||||
|
* it works, so whatever.
|
||||||
|
*/
|
||||||
vrele(fdvp);
|
vrele(fdvp);
|
||||||
|
|
||||||
if (doingdirectory && newparent) {
|
if (doingdirectory && newparent) {
|
||||||
if (error) /* write access check above */
|
if (error) /* write access check above */
|
||||||
goto bad;
|
goto tdvpbad;
|
||||||
if (xp != NULL)
|
if (xp != NULL)
|
||||||
vput(tvp);
|
vput(tvp);
|
||||||
|
tvp = NULL;
|
||||||
/*
|
/*
|
||||||
* doscheckpath() vput()'s dp,
|
* doscheckpath() vput()'s dp,
|
||||||
* so we have to do a relookup afterwards
|
* so we have to do a relookup afterwards
|
||||||
|
@ -972,8 +990,15 @@ abortit:
|
||||||
goto out;
|
goto out;
|
||||||
if ((tcnp->cn_flags & SAVESTART) == 0)
|
if ((tcnp->cn_flags & SAVESTART) == 0)
|
||||||
panic("msdosfs_rename: lost to startdir");
|
panic("msdosfs_rename: lost to startdir");
|
||||||
if ((error = relookup(tdvp, &tvp, tcnp)) != 0)
|
vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
|
||||||
|
if ((error = relookup(tdvp, &tvp, tcnp)) != 0) {
|
||||||
|
VOP_UNLOCK(tdvp, 0);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* XXX: SAVESTART causes us to get a reference, but
|
||||||
|
* that's released already above in doscheckpath()
|
||||||
|
*/
|
||||||
dp = VTODE(tdvp);
|
dp = VTODE(tdvp);
|
||||||
xp = tvp ? VTODE(tvp) : NULL;
|
xp = tvp ? VTODE(tvp) : NULL;
|
||||||
}
|
}
|
||||||
|
@ -987,22 +1012,23 @@ abortit:
|
||||||
if (xp->de_Attributes & ATTR_DIRECTORY) {
|
if (xp->de_Attributes & ATTR_DIRECTORY) {
|
||||||
if (!dosdirempty(xp)) {
|
if (!dosdirempty(xp)) {
|
||||||
error = ENOTEMPTY;
|
error = ENOTEMPTY;
|
||||||
goto bad;
|
goto tdvpbad;
|
||||||
}
|
}
|
||||||
if (!doingdirectory) {
|
if (!doingdirectory) {
|
||||||
error = ENOTDIR;
|
error = ENOTDIR;
|
||||||
goto bad;
|
goto tdvpbad;
|
||||||
}
|
}
|
||||||
} else if (doingdirectory) {
|
} else if (doingdirectory) {
|
||||||
error = EISDIR;
|
error = EISDIR;
|
||||||
goto bad;
|
goto tdvpbad;
|
||||||
}
|
}
|
||||||
if ((error = removede(dp, xp)) != 0)
|
if ((error = removede(dp, xp)) != 0)
|
||||||
goto bad;
|
goto tdvpbad;
|
||||||
VN_KNOTE(tdvp, NOTE_WRITE);
|
VN_KNOTE(tdvp, NOTE_WRITE);
|
||||||
VN_KNOTE(tvp, NOTE_DELETE);
|
VN_KNOTE(tvp, NOTE_DELETE);
|
||||||
cache_purge(tvp);
|
cache_purge(tvp);
|
||||||
vput(tvp);
|
vput(tvp);
|
||||||
|
tvp = NULL;
|
||||||
xp = NULL;
|
xp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1022,11 +1048,10 @@ abortit:
|
||||||
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
|
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
|
||||||
if ((fcnp->cn_flags & SAVESTART) == 0)
|
if ((fcnp->cn_flags & SAVESTART) == 0)
|
||||||
panic("msdosfs_rename: lost from startdir");
|
panic("msdosfs_rename: lost from startdir");
|
||||||
if (!newparent)
|
VOP_UNLOCK(tdvp, 0);
|
||||||
VOP_UNLOCK(tdvp, 0);
|
|
||||||
vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
|
vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
|
||||||
if ((error = relookup(fdvp, &fvp, fcnp))) {
|
if ((error = relookup(fdvp, &fvp, fcnp))) {
|
||||||
vput(fdvp);
|
VOP_UNLOCK(fdvp, 0);
|
||||||
vrele(ap->a_fvp);
|
vrele(ap->a_fvp);
|
||||||
vrele(tdvp);
|
vrele(tdvp);
|
||||||
return (error);
|
return (error);
|
||||||
|
@ -1043,6 +1068,7 @@ abortit:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
fdvp_dorele = 1;
|
fdvp_dorele = 1;
|
||||||
|
VOP_UNLOCK(fdvp, 0);
|
||||||
xp = VTODE(fvp);
|
xp = VTODE(fvp);
|
||||||
zp = VTODE(fdvp);
|
zp = VTODE(fdvp);
|
||||||
from_diroffset = zp->de_fndoffset;
|
from_diroffset = zp->de_fndoffset;
|
||||||
|
@ -1060,8 +1086,6 @@ abortit:
|
||||||
panic("rename: lost dir entry");
|
panic("rename: lost dir entry");
|
||||||
vrele(ap->a_fvp);
|
vrele(ap->a_fvp);
|
||||||
VOP_UNLOCK(fvp, 0);
|
VOP_UNLOCK(fvp, 0);
|
||||||
if (newparent)
|
|
||||||
VOP_UNLOCK(fdvp, 0);
|
|
||||||
xp = NULL;
|
xp = NULL;
|
||||||
} else {
|
} else {
|
||||||
vrele(fvp);
|
vrele(fvp);
|
||||||
|
@ -1082,8 +1106,6 @@ abortit:
|
||||||
error = createde(ip, dp, (struct denode **)0, tcnp);
|
error = createde(ip, dp, (struct denode **)0, tcnp);
|
||||||
if (error) {
|
if (error) {
|
||||||
memcpy(ip->de_Name, oldname, 11);
|
memcpy(ip->de_Name, oldname, 11);
|
||||||
if (newparent)
|
|
||||||
VOP_UNLOCK(fdvp, 0);
|
|
||||||
VOP_UNLOCK(fvp, 0);
|
VOP_UNLOCK(fvp, 0);
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
@ -1091,8 +1113,6 @@ abortit:
|
||||||
zp->de_fndoffset = from_diroffset;
|
zp->de_fndoffset = from_diroffset;
|
||||||
if ((error = removede(zp, ip)) != 0) {
|
if ((error = removede(zp, ip)) != 0) {
|
||||||
/* XXX should really panic here, fs is corrupt */
|
/* XXX should really panic here, fs is corrupt */
|
||||||
if (newparent)
|
|
||||||
VOP_UNLOCK(fdvp, 0);
|
|
||||||
VOP_UNLOCK(fvp, 0);
|
VOP_UNLOCK(fvp, 0);
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
@ -1102,8 +1122,6 @@ abortit:
|
||||||
&ip->de_dirclust, 0);
|
&ip->de_dirclust, 0);
|
||||||
if (error) {
|
if (error) {
|
||||||
/* XXX should really panic here, fs is corrupt */
|
/* XXX should really panic here, fs is corrupt */
|
||||||
if (newparent)
|
|
||||||
VOP_UNLOCK(fdvp, 0);
|
|
||||||
VOP_UNLOCK(fvp, 0);
|
VOP_UNLOCK(fvp, 0);
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
@ -1112,8 +1130,6 @@ abortit:
|
||||||
ip->de_diroffset &= pmp->pm_crbomask;
|
ip->de_diroffset &= pmp->pm_crbomask;
|
||||||
}
|
}
|
||||||
reinsert(ip);
|
reinsert(ip);
|
||||||
if (newparent)
|
|
||||||
VOP_UNLOCK(fdvp, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1153,9 +1169,9 @@ abortit:
|
||||||
VN_KNOTE(fvp, NOTE_RENAME);
|
VN_KNOTE(fvp, NOTE_RENAME);
|
||||||
VOP_UNLOCK(fvp, 0);
|
VOP_UNLOCK(fvp, 0);
|
||||||
bad:
|
bad:
|
||||||
if (xp)
|
if (tvp)
|
||||||
vput(tvp);
|
vput(tvp);
|
||||||
vput(tdvp);
|
vrele(tdvp);
|
||||||
out:
|
out:
|
||||||
ip->de_flag &= ~DE_RENAME;
|
ip->de_flag &= ~DE_RENAME;
|
||||||
if (fdvp_dorele)
|
if (fdvp_dorele)
|
||||||
|
@ -1163,6 +1179,10 @@ out:
|
||||||
vrele(fvp);
|
vrele(fvp);
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
|
/* XXX: uuuh */
|
||||||
|
tdvpbad:
|
||||||
|
VOP_UNLOCK(tdvp, 0);
|
||||||
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
|
|
Loading…
Reference in New Issue