summaryrefslogtreecommitdiffstats
path: root/sys/fs/ext2fs
diff options
context:
space:
mode:
authorpfg <pfg@FreeBSD.org>2012-05-16 15:53:38 +0000
committerpfg <pfg@FreeBSD.org>2012-05-16 15:53:38 +0000
commitb227d4379e3f747272635a63519b7b82bc3ddbf8 (patch)
treec9418b3914997e9de0427abee9b4df88bd2e77b5 /sys/fs/ext2fs
parent3288f283ffb15bb0d83572220243e790cffe0209 (diff)
downloadFreeBSD-src-b227d4379e3f747272635a63519b7b82bc3ddbf8.zip
FreeBSD-src-b227d4379e3f747272635a63519b7b82bc3ddbf8.tar.gz
Fix a couple of issues that appear to be inherited from the old
8.x code: - If the lock cannot be acquired immediately unlocks 'bar' vnode and then locks both vnodes in order. - wrong vnode type panics from cache_enter_time after calls by ext2_lookup. The fix merges the fixes from ufs/ufs_lookup.c. Submitted by: Mateusz Guzik Approved by: jhb@ (mentor) Reviewed by: kib@ MFC after: 1 week
Diffstat (limited to 'sys/fs/ext2fs')
-rw-r--r--sys/fs/ext2fs/ext2_lookup.c58
-rw-r--r--sys/fs/ext2fs/ext2_vnops.c6
2 files changed, 52 insertions, 12 deletions
diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c
index 35ab631..c279998 100644
--- a/sys/fs/ext2fs/ext2_lookup.c
+++ b/sys/fs/ext2fs/ext2_lookup.c
@@ -115,6 +115,8 @@ static u_char dt_to_ext2_ft[] = {
static int ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
int entryoffsetinblock);
+static int ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp,
+ struct componentname *cnp, ino_t *dd_ino);
/*
* Vnode op for reading directories.
@@ -285,7 +287,14 @@ ext2_lookup(ap)
struct componentname *a_cnp;
} */ *ap;
{
- struct vnode *vdp; /* vnode for directory being searched */
+
+ return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL));
+}
+
+static int
+ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
+ ino_t *dd_ino)
+{
struct inode *dp; /* inode for directory being searched */
struct buf *bp; /* a buffer of directory entries */
struct ext2fs_direct_2 *ep; /* the current directory entry */
@@ -305,22 +314,22 @@ ext2_lookup(ap)
doff_t enduseful; /* pointer past last used dir slot */
u_long bmask; /* block offset mask */
int namlen, error;
- struct vnode **vpp = ap->a_vpp;
- struct componentname *cnp = ap->a_cnp;
struct ucred *cred = cnp->cn_cred;
int flags = cnp->cn_flags;
int nameiop = cnp->cn_nameiop;
- ino_t ino;
+ ino_t ino, ino1;
int ltype;
- int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize;
+ int DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize;
+
+ if (vpp != NULL)
+ *vpp = NULL;
- bp = NULL;
- slotoffset = -1;
- *vpp = NULL;
- vdp = ap->a_dvp;
dp = VTOI(vdp);
bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
+restart:
+ bp = NULL;
+ slotoffset = -1;
/*
* We now have a segment name to search for, and a directory to search.
@@ -536,10 +545,12 @@ searchloop:
* Insert name into cache (as non-existent) if appropriate.
*/
if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
- cache_enter(vdp, *vpp, cnp);
+ cache_enter(vdp, NULL, cnp);
return (ENOENT);
found:
+ if (dd_ino != NULL)
+ *dd_ino = ino;
if (numdirpasses == 2)
nchstats.ncs_pass2++;
/*
@@ -582,6 +593,8 @@ found:
dp->i_count = 0;
else
dp->i_count = dp->i_offset - prevoff;
+ if (dd_ino != NULL)
+ return (0);
if (dp->i_number == ino) {
VREF(vdp);
*vpp = vdp;
@@ -622,6 +635,8 @@ found:
*/
if (dp->i_number == ino)
return (EISDIR);
+ if (dd_ino != NULL)
+ return (0);
if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
&tdp)) != 0)
return (error);
@@ -629,6 +644,8 @@ found:
cnp->cn_flags |= SAVENAME;
return (0);
}
+ if (dd_ino != NULL)
+ return (0);
/*
* Step through the translation in the name. We do not `vput' the
@@ -655,8 +672,27 @@ found:
VOP_UNLOCK(pdp, 0); /* race to get the inode */
error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp);
vn_lock(pdp, ltype | LK_RETRY);
- if (error != 0)
+ if (pdp->v_iflag & VI_DOOMED) {
+ if (error == 0)
+ vput(tdp);
+ error = ENOENT;
+ }
+ if (error)
return (error);
+ /*
+ * Recheck that ".." entry in the vdp directory points
+ * to the inode we looked up before vdp lock was
+ * dropped.
+ */
+ error = ext2_lookup_ino(pdp, NULL, cnp, &ino1);
+ if (error) {
+ vput(tdp);
+ return (error);
+ }
+ if (ino1 != ino) {
+ vput(tdp);
+ goto restart;
+ }
*vpp = tdp;
} else if (dp->i_number == ino) {
VREF(vdp); /* we want ourself, ie "." */
diff --git a/sys/fs/ext2fs/ext2_vnops.c b/sys/fs/ext2fs/ext2_vnops.c
index 21b7687..4f8f6a9 100644
--- a/sys/fs/ext2fs/ext2_vnops.c
+++ b/sys/fs/ext2fs/ext2_vnops.c
@@ -1336,7 +1336,11 @@ ext2_rmdir(ap)
error = ext2_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
cnp->cn_thread);
cache_purge(ITOV(ip));
- vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+ if (vn_lock(dvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
+ VOP_UNLOCK(vp, 0);
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ }
out:
return (error);
}
OpenPOWER on IntegriCloud