svn commit: r203110 - in projects/suj: 6/sbin/fsck_ffs 7/sbin/fsck_ffs 8/sbin/fsck_ffs

Jeff Roberson jeff at FreeBSD.org
Thu Jan 28 09:25:35 UTC 2010


Author: jeff
Date: Thu Jan 28 09:25:34 2010
New Revision: 203110
URL: http://svn.freebsd.org/changeset/base/203110

Log:
   - Merge r203109 from suj/head

Modified:
  projects/suj/6/sbin/fsck_ffs/suj.c
  projects/suj/7/sbin/fsck_ffs/suj.c
  projects/suj/8/sbin/fsck_ffs/suj.c

Modified: projects/suj/6/sbin/fsck_ffs/suj.c
==============================================================================
--- projects/suj/6/sbin/fsck_ffs/suj.c	Thu Jan 28 09:24:10 2010	(r203109)
+++ projects/suj/6/sbin/fsck_ffs/suj.c	Thu Jan 28 09:25:34 2010	(r203110)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009 Jeffrey W. Roberson <jeff at FreeBSD.org>
+ * Copyright 2009, 2010 Jeffrey W. Roberson <jeff at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -48,8 +48,6 @@ __FBSDID("$FreeBSD$");
 
 #include "fsck.h"
 
-static void	ino_decr(ino_t);
-
 #define	DOTDOT_OFFSET	DIRECTSIZ(1)
 #define	SUJ_HASHSIZE	2048
 #define	SUJ_HASHMASK	(SUJ_HASHSIZE - 1)
@@ -64,7 +62,6 @@ struct suj_seg {
 struct suj_rec {
 	TAILQ_ENTRY(suj_rec) sr_next;
 	union jrec	*sr_rec;
-	int		sr_alt;	/* Is alternate address? */
 };
 TAILQ_HEAD(srechd, suj_rec);
 
@@ -75,11 +72,14 @@ struct suj_ino {
 	struct srechd		si_movs;
 	struct jtrncrec		*si_trunc;
 	ino_t			si_ino;
-	int			si_nlinkadj;
-	int			si_skipparent;
-	int			si_linkadj;
-	int			si_hasrecs;
-	int			si_blkadj;
+	char			si_skipparent;
+	char			si_hasrecs;
+	char			si_blkadj;
+	char			si_linkadj;
+	int			si_mode;
+	nlink_t			si_nlinkadj;
+	nlink_t			si_nlink;
+	nlink_t			si_dotlinks;
 };
 LIST_HEAD(inohd, suj_ino);
 
@@ -143,6 +143,8 @@ uint64_t jrecs;
 
 typedef void (*ino_visitor)(ino_t, ufs_lbn_t, ufs2_daddr_t, int);
 static void ino_trunc(ino_t ino, off_t size);
+static void ino_decr(ino_t);
+static void ino_adjust(struct suj_ino *);
 static void ino_build(struct suj_ino *sino);
 
 static void *
@@ -266,7 +268,6 @@ ino_lookup(ino_t ino, int creat)
 	sino = errmalloc(sizeof(*sino));
 	bzero(sino, sizeof(*sino));
 	sino->si_ino = ino;
-	sino->si_nlinkadj = 0;
 	TAILQ_INIT(&sino->si_recs);
 	TAILQ_INIT(&sino->si_newrecs);
 	TAILQ_INIT(&sino->si_movs);
@@ -1095,6 +1096,9 @@ ino_setskip(struct suj_ino *sino, ino_t 
 		sino->si_skipparent = 1;
 }
 
+/*
+ * Free the children of a directory when the directory is discarded.
+ */
 static void
 ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
 {
@@ -1136,11 +1140,21 @@ ino_free_children(ino_t ino, ufs_lbn_t l
 		 * was valid and we have to adjust once more.
 		 */
 		sino = ino_lookup(dp->d_ino, 0);
-		if (sino == NULL || sino->si_linkadj || sino->si_hasrecs == 0) {
-			ino_decr(dp->d_ino);
+		if (sino == NULL || sino->si_hasrecs == 0) {
+			ino_decr(ino);
 			continue;
 		}
 		/*
+		 * Use ino_adjust() so if we lose the last non-dot reference
+		 * to a directory it can be discarded.
+		 */
+		if (sino->si_linkadj) {
+			sino->si_nlink--;
+			if (isparent)
+				sino->si_dotlinks--;
+			ino_adjust(sino);
+		}
+		/*
 		 * Tell any child directories we've already removed their
 		 * parent.  Don't try to adjust our link down again.
 		 */
@@ -1234,12 +1248,39 @@ ino_decr(ino_t ino)
  * free it.
  */
 static void
-ino_adjust(ino_t ino, int lastmode, nlink_t nlink)
+ino_adjust(struct suj_ino *sino)
 {
+	struct jrefrec *rrec;
+	struct suj_rec *srec;
+	struct suj_ino *stmp;
 	union dinode *ip;
+	nlink_t nlink;
 	int reqlink;
 	int mode;
+	ino_t ino;
 
+	nlink = sino->si_nlink;
+	ino = sino->si_ino;
+	/*
+	 * If it's a directory with no real names pointing to it go ahead
+	 * and truncate it.  This will free any children.
+	 */
+	if ((sino->si_mode & IFMT) == IFDIR &&
+	    nlink - sino->si_dotlinks == 0) {
+		sino->si_nlink = nlink = 0;
+		/*
+		 * Mark any .. links so they know not to free this inode
+		 * when they are removed.
+		 */
+		TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
+			rrec = (struct jrefrec *)srec->sr_rec;
+			if (rrec->jr_diroff == DOTDOT_OFFSET) {
+				stmp = ino_lookup(rrec->jr_parent, 0);
+				if (stmp)
+					ino_setskip(stmp, ino);
+			}
+		}
+	}
 	ip = ino_read(ino);
 	mode = DIP(ip, di_mode) & IFMT;
 	if (nlink > LINK_MAX)
@@ -1248,16 +1289,16 @@ ino_adjust(ino_t ino, int lastmode, nlin
 		    ino, nlink, DIP(ip, di_nlink));
 	if (debug)
 		printf("Adjusting ino %d, nlink %d, old link %d lastmode %o\n",
-		    ino, nlink, DIP(ip, di_nlink), lastmode);
+		    ino, nlink, DIP(ip, di_nlink), sino->si_mode);
 	if (mode == 0) {
 		if (debug)
 			printf("ino %d, zero inode freeing bitmap\n", ino);
-		ino_free(ino, lastmode);
+		ino_free(ino, sino->si_mode);
 		return;
 	}
 	/* XXX Should be an assert? */
-	if (mode != lastmode && debug)
-		printf("ino %d, mode %o != %o\n", ino, mode, lastmode);
+	if (mode != sino->si_mode && debug)
+		printf("ino %d, mode %o != %o\n", ino, mode, sino->si_mode);
 	if ((mode & IFMT) == IFDIR)
 		reqlink = 2;
 	else
@@ -1426,10 +1467,12 @@ ino_trunc(ino_t ino, off_t size)
 	 * If we're truncating direct blocks we have to adjust frags
 	 * accordingly.
 	 */
-	if (visitlbn < NDADDR) {
+	if (visitlbn < NDADDR && totalfrags) {
 		long oldspace, newspace;
 
 		bn = DIP(ip, di_db[visitlbn]);
+		if (bn == 0)
+			errx(1, "Bad blk at ino %d lbn %jd\n", ino, visitlbn);
 		oldspace = sblksize(fs, cursize, visitlbn);
 		newspace = sblksize(fs, size, visitlbn);
 		if (oldspace != newspace) {
@@ -1474,7 +1517,6 @@ ino_check(struct suj_ino *sino)
 {
 	struct suj_rec *srec;
 	struct jrefrec *rrec;
-	struct suj_ino *stmp;
 	nlink_t dotlinks;
 	int newlinks;
 	int removes;
@@ -1484,18 +1526,9 @@ ino_check(struct suj_ino *sino)
 	int isat;
 	int mode;
 
-	/*
-	 * Handle truncations that were not complete.  We don't have
-	 * to worry about truncating directory entries as they must have
-	 * been removed for truncate to succeed.
-	 */
-	ino = sino->si_ino;
-	if (sino->si_trunc) {
-		ino_trunc(ino, sino->si_trunc->jt_size);
-		sino->si_trunc = NULL;
-	}
 	if (sino->si_hasrecs == 0)
 		return;
+	ino = sino->si_ino;
 	rrec = (struct jrefrec *)TAILQ_FIRST(&sino->si_recs)->sr_rec;
 	nlink = rrec->jr_nlink;
 	newlinks = 0;
@@ -1528,29 +1561,16 @@ ino_check(struct suj_ino *sino)
 	 * makes no change to the link count but an add increases
 	 * by one.
 	 */
+	if (debug)
+		printf("ino %d nlink %d newlinks %d removes %d dotlinks %d\n",
+		    ino, nlink, newlinks, removes, dotlinks);
 	nlink += newlinks;
 	nlink -= removes;
-	/*
-	 * If it's a directory with no real names pointing to it go ahead
-	 * and truncate it.  This will free any children.
-	 */
-	if ((mode & IFMT) == IFDIR && nlink - dotlinks == 0) {
-		nlink = 0;
-		/*
-		 * Mark any .. links so they know not to free this inode
-		 * when they are removed.
-		 */
-		TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
-			rrec = (struct jrefrec *)srec->sr_rec;
-			if (rrec->jr_diroff == DOTDOT_OFFSET) {
-				stmp = ino_lookup(rrec->jr_parent, 0);
-				if (stmp)
-					ino_setskip(stmp, ino);
-			}
-		}
-	}
 	sino->si_linkadj = 1;
-	ino_adjust(ino, mode, nlink);
+	sino->si_nlink = nlink;
+	sino->si_dotlinks = dotlinks;
+	sino->si_mode = mode;
+	ino_adjust(sino);
 }
 
 /*
@@ -1632,42 +1652,63 @@ cg_build(struct suj_cg *sc)
 }
 
 /*
- * Walk the list of inode and block records for this cg, recovering any
- * changes which were not complete at the time of crash.
+ * Handle inodes requiring truncation.  This must be done prior to
+ * looking up any inodes in directories.
  */
 static void
-cg_check(struct suj_cg *sc)
+cg_trunc(struct suj_cg *sc)
 {
 	struct suj_ino *sino;
-	struct suj_blk *sblk;
 	int i;
 
-	if (debug)
-		printf("Recovering cg %d\n", sc->sc_cgx);
-
 	for (i = 0; i < SUJ_HASHSIZE; i++)
 		LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
-			ino_check(sino);
+			if (sino->si_trunc) {
+				ino_trunc(sino->si_ino,
+				    sino->si_trunc->jt_size);
+				sino->si_trunc = NULL;
+			}
+}
+
+/*
+ * Free any partially allocated blocks and then resolve inode block
+ * counts.
+ */
+static void
+cg_check_blk(struct suj_cg *sc)
+{
+	struct suj_ino *sino;
+	struct suj_blk *sblk;
+	int i;
+
 
 	for (i = 0; i < SUJ_HASHSIZE; i++)
 		LIST_FOREACH(sblk, &sc->sc_blkhash[i], sb_next)
 			blk_check(sblk);
+	/*
+	 * Now that we've freed blocks which are not referenced we
+	 * make a second pass over all inodes to adjust their block
+	 * counts.
+	 */
+	for (i = 0; i < SUJ_HASHSIZE; i++)
+		LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
+			if (sino->si_blkadj)
+				ino_adjblks(sino);
 }
 
 /*
- * Now that we've freed blocks which are not referenced we make a second
- * pass over all inodes to adjust their block counts.
+ * Walk the list of inode records for this cg, recovering any
+ * changes which were not complete at the time of crash.
  */
 static void
-cg_check2(struct suj_cg *sc)
+cg_check_ino(struct suj_cg *sc)
 {
 	struct suj_ino *sino;
 	int i;
 
 	for (i = 0; i < SUJ_HASHSIZE; i++)
 		LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
-			if (sino->si_blkadj)
-				ino_adjblks(sino);
+			ino_check(sino);
 }
 
 /*
@@ -1789,9 +1830,23 @@ ino_unlinked(void)
 static void
 ino_append(union jrec *rec)
 {
+	struct jrefrec *refrec;
+	struct jmvrec *mvrec;
 	struct suj_ino *sino;
 	struct suj_rec *srec;
 
+	mvrec = &rec->rec_jmvrec;
+	refrec = &rec->rec_jrefrec;
+	if (debug && mvrec->jm_op == JOP_MVREF)
+		printf("ino move: ino %d, parent %d, diroff %jd, oldoff %jd\n", 
+		    mvrec->jm_ino, mvrec->jm_parent, mvrec->jm_newoff,
+		    mvrec->jm_oldoff);
+	else if (debug &&
+	    (refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF))
+		printf("ino ref: op %d, ino %d, nlink %d, "
+		    "parent %d, diroff %jd\n", 
+		    refrec->jr_op, refrec->jr_ino, refrec->jr_nlink,
+		    refrec->jr_parent, refrec->jr_diroff);
 	/*
 	 * Lookup the ino and clear truncate if one is found.  Partial
 	 * truncates are always done synchronously so if we discover
@@ -1803,69 +1858,27 @@ ino_append(union jrec *rec)
 	sino->si_hasrecs = 1;
 	srec = errmalloc(sizeof(*srec));
 	srec->sr_rec = rec;
-	srec->sr_alt = 0;
 	TAILQ_INSERT_TAIL(&sino->si_newrecs, srec, sr_next);
 }
 
 /*
- * If we see two ops for the same inode to the same parent at the same
- * offset we could miscount the link with ino_isat() returning twice.
- * Keep only the first record because it has the valid link count but keep
- * the mode from the final op as that should be the correct mode in case
- * it changed.
+ * Add a reference adjustment to the sino list and eliminate dups.  The
+ * primary loop in ino_build_ref() checks for dups but new ones may be
+ * created as a result of offset adjustments.
  */
 static void
-ino_build_ref(struct suj_ino *sino, struct suj_rec *srec)
+ino_add_ref(struct suj_ino *sino, struct suj_rec *srec)
 {
 	struct jrefrec *refrec;
-	struct jmvrec *mvrec;
-	struct suj_rec *srp;
 	struct suj_rec *srn;
 	struct jrefrec *rrn;
 
 	refrec = (struct jrefrec *)srec->sr_rec;
-	if (debug)
-		printf("ino_build: op %d, ino %d, nlink %d, "
-		    "parent %d, diroff %jd\n", 
-		    refrec->jr_op, refrec->jr_ino, refrec->jr_nlink,
-		    refrec->jr_parent, refrec->jr_diroff);
-
-	/*
-	 * Search for a mvrec that matches this offset.  Whether it's an add
-	 * or a remove we can delete the mvref.  It no longer applies to this
-	 * location.
-	 *
-	 * For removes, we have to find the original offset so we can create
-	 * a remove that matches the earlier add so it can be abandoned
-	 * if necessary.  We create an add in the new location so we can
-	 * tolerate the directory block as it existed before or after
-	 * the move.
-	 */
-	if (!TAILQ_EMPTY(&sino->si_movs)) {
-		for (srn = TAILQ_LAST(&sino->si_movs, srechd); srn; srn = srp) {
-			srp = TAILQ_PREV(srn, srechd, sr_next);
-			mvrec = (struct jmvrec *)srn->sr_rec;
-			if (mvrec->jm_parent != refrec->jr_parent ||
-			    mvrec->jm_newoff != refrec->jr_diroff)
-				continue;
-			TAILQ_REMOVE(&sino->si_movs, srn, sr_next);
-			if (refrec->jr_op == JOP_REMREF) {
-				rrn = errmalloc(sizeof(*refrec));
-				*rrn = *refrec;
-				rrn->jr_op = JOP_ADDREF;
-				rrn->jr_diroff = mvrec->jm_oldoff;
-				srn = errmalloc(sizeof(*srec));
-				srn->sr_alt = 1;
-				srn->sr_rec = (union jrec *)rrn;
-				ino_build_ref(sino, srn);
-			}
-		}
-	}
 	/*
-	 * We walk backwards so that adds and removes are evaluated in the
-	 * correct order.  If a primary record conflicts with an alt keep
-	 * the primary and discard the alt.  We must track this to keep
-	 * the correct number of removes in the list.
+	 * We walk backwards so that the oldest link count is preserved.  If
+	 * an add record conflicts with a remove keep the remove.  Redundant
+	 * removes are eliminated in ino_build_ref.  Otherwise we keep the
+	 * oldest record at a given location.
 	 */
 	for (srn = TAILQ_LAST(&sino->si_recs, srechd); srn;
 	    srn = TAILQ_PREV(srn, srechd, sr_next)) {
@@ -1873,91 +1886,135 @@ ino_build_ref(struct suj_ino *sino, stru
 		if (rrn->jr_parent != refrec->jr_parent ||
 		    rrn->jr_diroff != refrec->jr_diroff)
 			continue;
-		if (debug)
-			printf("Discarding dup.\n");
-		if (srn->sr_alt == 0) {
+		if (rrn->jr_op == JOP_REMREF || refrec->jr_op == JOP_ADDREF) {
 			rrn->jr_mode = refrec->jr_mode;
 			return;
 		}
 		/*
+		 * Adding a remove.
+		 *
 		 * Replace the record in place with the old nlink in case
 		 * we replace the head of the list.  Abandon srec as a dup.
 		 */
 		refrec->jr_nlink = rrn->jr_nlink;
 		srn->sr_rec = srec->sr_rec;
-		srn->sr_alt = srec->sr_alt;
 		return;
 	}
 	TAILQ_INSERT_TAIL(&sino->si_recs, srec, sr_next);
 }
 
 /*
- * Apply a move record to an inode.  We must search for adds that preceed us
- * and add duplicates because we won't know which location to search first.
- * Then we add movs to a queue that is maintained until the moved location
- * is removed.  If a single record is moved multiple times we only maintain
- * one copy that contains the original and final diroffs.
+ * Create a duplicate of a reference at a previous location.
  */
 static void
-ino_move_ref(struct suj_ino *sino, struct suj_rec *srec)
+ino_dup_ref(struct suj_ino *sino, struct jrefrec *refrec, off_t diroff)
+{
+	struct jrefrec *rrn;
+	struct suj_rec *srn;
+
+	rrn = errmalloc(sizeof(*refrec));
+	*rrn = *refrec;
+	rrn->jr_op = JOP_ADDREF;
+	rrn->jr_diroff = diroff;
+	srn = errmalloc(sizeof(*srn));
+	srn->sr_rec = (union jrec *)rrn;
+	ino_add_ref(sino, srn);
+}
+
+/*
+ * Add a reference to the list at all known locations.  We follow the offset
+ * changes for a single instance and create duplicate add refs at each so
+ * that we can tolerate any version of the directory block.  Eliminate
+ * removes which collide with adds that are seen in the journal.  They should
+ * not adjust the link count down.
+ */
+static void
+ino_build_ref(struct suj_ino *sino, struct suj_rec *srec)
 {
 	struct jrefrec *refrec;
-	struct jmvrec *mvrn;
+	struct jmvrec *mvrec;
+	struct suj_rec *srp;
 	struct suj_rec *srn;
 	struct jrefrec *rrn;
-	struct jmvrec *mvrec;
+	off_t diroff;
 
-	mvrec = (struct jmvrec *)srec->sr_rec;
-	if (debug)
-		printf("ino_move: ino %d, parent %d, diroff %jd, oldoff %jd\n", 
-		    mvrec->jm_ino, mvrec->jm_parent, mvrec->jm_newoff,
-		    mvrec->jm_oldoff);
+	refrec = (struct jrefrec *)srec->sr_rec;
 	/*
-	 * We walk backwards so we only evaluate the most recent record at
-	 * this offset.
+	 * Search for a mvrec that matches this offset.  Whether it's an add
+	 * or a remove we can delete the mvref after creating a dup record in
+	 * the old location.
 	 */
-	for (srn = TAILQ_LAST(&sino->si_recs, srechd); srn;
-	    srn = TAILQ_PREV(srn, srechd, sr_next)) {
-		rrn = (struct jrefrec *)srn->sr_rec;
-		if (rrn->jr_parent != mvrec->jm_parent ||
-		    rrn->jr_diroff != mvrec->jm_oldoff)
-			continue;
-		/*
-		 * When an entry is moved we don't know whether the write
-		 * to move has completed yet.  To resolve this we create
-		 * a new add dependency in the new location as if it were
-		 * added twice.  Only one will succeed.  Consider the
-		 * new offset the primary location for the inode and the
-		 * old offset the alt.
-		 */
-		srn->sr_alt = 1;
-		refrec = errmalloc(sizeof(*refrec));
-		refrec->jr_op = JOP_ADDREF;
-		refrec->jr_ino = mvrec->jm_ino;
-		refrec->jr_parent = mvrec->jm_parent;
-		refrec->jr_diroff = mvrec->jm_newoff;
-		refrec->jr_mode = rrn->jr_mode;
-		refrec->jr_nlink = rrn->jr_nlink;
-		srn = errmalloc(sizeof(*srn));
-		srn->sr_alt = 0;
-		srn->sr_rec = (union jrec *)refrec;
-		ino_build_ref(sino, srn);
-		break;
+	if (!TAILQ_EMPTY(&sino->si_movs)) {
+		diroff = refrec->jr_diroff;
+		for (srn = TAILQ_LAST(&sino->si_movs, srechd); srn; srn = srp) {
+			srp = TAILQ_PREV(srn, srechd, sr_next);
+			mvrec = (struct jmvrec *)srn->sr_rec;
+			if (mvrec->jm_parent != refrec->jr_parent ||
+			    mvrec->jm_newoff != diroff)
+				continue;
+			diroff = mvrec->jm_oldoff;
+			TAILQ_REMOVE(&sino->si_movs, srn, sr_next);
+			ino_dup_ref(sino, refrec, diroff);
+		}
 	}
 	/*
-	 * Add this mvrec to the queue of pending mvs, possibly collapsing
-	 * it with a prior move for the same inode and offset.
+	 * If a remove wasn't eliminated by an earlier add just append it to
+	 * the list.
 	 */
-	for (srn = TAILQ_LAST(&sino->si_movs, srechd); srn;
-	    srn = TAILQ_PREV(srn, srechd, sr_next)) {
-		mvrn = (struct jmvrec *)srn->sr_rec;
-		if (mvrn->jm_parent != mvrec->jm_parent ||
-		    mvrn->jm_newoff != mvrec->jm_oldoff)
-			continue;
-		mvrn->jm_newoff = mvrec->jm_newoff;
+	if (refrec->jr_op == JOP_REMREF) {
+		ino_add_ref(sino, srec);
 		return;
 	}
-	TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next);
+	/*
+	 * Walk the list of records waiting to be added to the list.  We
+	 * must check for moves that apply to our current offset and remove
+	 * them from the list.  Remove any duplicates to eliminate removes
+	 * with corresponding adds.
+	 */
+	TAILQ_FOREACH_SAFE(srn, &sino->si_newrecs, sr_next, srp) {
+		switch (srn->sr_rec->rec_jrefrec.jr_op) {
+		case JOP_ADDREF:
+			/*
+			 * This should actually be an error we should
+			 * have a remove for every add journaled.
+			 */
+			rrn = (struct jrefrec *)srn->sr_rec;
+			if (rrn->jr_parent != refrec->jr_parent ||
+			    rrn->jr_diroff != refrec->jr_diroff)
+				break;
+			TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next);
+			break;
+		case JOP_REMREF:
+			/*
+			 * Once we remove the current iteration of the
+			 * record at this address we're done.
+			 */
+			rrn = (struct jrefrec *)srn->sr_rec;
+			if (rrn->jr_parent != refrec->jr_parent ||
+			    rrn->jr_diroff != refrec->jr_diroff)
+				break;
+			TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next);
+			ino_add_ref(sino, srec);
+			return;
+		case JOP_MVREF:
+			/*
+			 * Update our diroff based on any moves that match
+			 * and remove the move.
+			 */
+			mvrec = (struct jmvrec *)srn->sr_rec;
+			if (mvrec->jm_parent != refrec->jr_parent ||
+			    mvrec->jm_oldoff != refrec->jr_diroff)
+				break;
+			ino_dup_ref(sino, refrec, mvrec->jm_oldoff);
+			refrec->jr_diroff = mvrec->jm_newoff;
+			TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next);
+			break;
+		default:
+			errx(1, "ino_build_ref: Unknown op %d",
+			    srn->sr_rec->rec_jrefrec.jr_op);
+		}
+	}
+	ino_add_ref(sino, srec);
 }
 
 /*
@@ -1977,7 +2034,10 @@ ino_build(struct suj_ino *sino)
 			ino_build_ref(sino, srec);
 			break;
 		case JOP_MVREF:
-			ino_move_ref(sino, srec);
+			/*
+			 * Add this mvrec to the queue of pending mvs.
+			 */
+			TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next);
 			break;
 		default:
 			errx(1, "ino_build: Unknown op %d",
@@ -2531,8 +2591,9 @@ suj_check(const char *filesys)
 		printf("** Resolving unreferenced inode list.\n");
 		ino_unlinked();
 		printf("** Processing journal entries.\n");
-		cg_apply(cg_check);
-		cg_apply(cg_check2);
+		cg_apply(cg_trunc);
+		cg_apply(cg_check_blk);
+		cg_apply(cg_check_ino);
 	}
 	/*
 	 * To remain idempotent with partial truncations the free bitmaps

Modified: projects/suj/7/sbin/fsck_ffs/suj.c
==============================================================================
--- projects/suj/7/sbin/fsck_ffs/suj.c	Thu Jan 28 09:24:10 2010	(r203109)
+++ projects/suj/7/sbin/fsck_ffs/suj.c	Thu Jan 28 09:25:34 2010	(r203110)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009 Jeffrey W. Roberson <jeff at FreeBSD.org>
+ * Copyright 2009, 2010 Jeffrey W. Roberson <jeff at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -48,8 +48,6 @@ __FBSDID("$FreeBSD$");
 
 #include "fsck.h"
 
-static void	ino_decr(ino_t);
-
 #define	DOTDOT_OFFSET	DIRECTSIZ(1)
 #define	SUJ_HASHSIZE	2048
 #define	SUJ_HASHMASK	(SUJ_HASHSIZE - 1)
@@ -64,7 +62,6 @@ struct suj_seg {
 struct suj_rec {
 	TAILQ_ENTRY(suj_rec) sr_next;
 	union jrec	*sr_rec;
-	int		sr_alt;	/* Is alternate address? */
 };
 TAILQ_HEAD(srechd, suj_rec);
 
@@ -75,11 +72,14 @@ struct suj_ino {
 	struct srechd		si_movs;
 	struct jtrncrec		*si_trunc;
 	ino_t			si_ino;
-	int			si_nlinkadj;
-	int			si_skipparent;
-	int			si_linkadj;
-	int			si_hasrecs;
-	int			si_blkadj;
+	char			si_skipparent;
+	char			si_hasrecs;
+	char			si_blkadj;
+	char			si_linkadj;
+	int			si_mode;
+	nlink_t			si_nlinkadj;
+	nlink_t			si_nlink;
+	nlink_t			si_dotlinks;
 };
 LIST_HEAD(inohd, suj_ino);
 
@@ -143,6 +143,8 @@ uint64_t jrecs;
 
 typedef void (*ino_visitor)(ino_t, ufs_lbn_t, ufs2_daddr_t, int);
 static void ino_trunc(ino_t ino, off_t size);
+static void ino_decr(ino_t);
+static void ino_adjust(struct suj_ino *);
 static void ino_build(struct suj_ino *sino);
 
 static void *
@@ -266,7 +268,6 @@ ino_lookup(ino_t ino, int creat)
 	sino = errmalloc(sizeof(*sino));
 	bzero(sino, sizeof(*sino));
 	sino->si_ino = ino;
-	sino->si_nlinkadj = 0;
 	TAILQ_INIT(&sino->si_recs);
 	TAILQ_INIT(&sino->si_newrecs);
 	TAILQ_INIT(&sino->si_movs);
@@ -1095,6 +1096,9 @@ ino_setskip(struct suj_ino *sino, ino_t 
 		sino->si_skipparent = 1;
 }
 
+/*
+ * Free the children of a directory when the directory is discarded.
+ */
 static void
 ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
 {
@@ -1136,11 +1140,21 @@ ino_free_children(ino_t ino, ufs_lbn_t l
 		 * was valid and we have to adjust once more.
 		 */
 		sino = ino_lookup(dp->d_ino, 0);
-		if (sino == NULL || sino->si_linkadj || sino->si_hasrecs == 0) {
-			ino_decr(dp->d_ino);
+		if (sino == NULL || sino->si_hasrecs == 0) {
+			ino_decr(ino);
 			continue;
 		}
 		/*
+		 * Use ino_adjust() so if we lose the last non-dot reference
+		 * to a directory it can be discarded.
+		 */
+		if (sino->si_linkadj) {
+			sino->si_nlink--;
+			if (isparent)
+				sino->si_dotlinks--;
+			ino_adjust(sino);
+		}
+		/*
 		 * Tell any child directories we've already removed their
 		 * parent.  Don't try to adjust our link down again.
 		 */
@@ -1234,12 +1248,39 @@ ino_decr(ino_t ino)
  * free it.
  */
 static void
-ino_adjust(ino_t ino, int lastmode, nlink_t nlink)
+ino_adjust(struct suj_ino *sino)
 {
+	struct jrefrec *rrec;
+	struct suj_rec *srec;
+	struct suj_ino *stmp;
 	union dinode *ip;
+	nlink_t nlink;
 	int reqlink;
 	int mode;
+	ino_t ino;
 
+	nlink = sino->si_nlink;
+	ino = sino->si_ino;
+	/*
+	 * If it's a directory with no real names pointing to it go ahead
+	 * and truncate it.  This will free any children.
+	 */
+	if ((sino->si_mode & IFMT) == IFDIR &&
+	    nlink - sino->si_dotlinks == 0) {
+		sino->si_nlink = nlink = 0;
+		/*
+		 * Mark any .. links so they know not to free this inode
+		 * when they are removed.
+		 */
+		TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
+			rrec = (struct jrefrec *)srec->sr_rec;
+			if (rrec->jr_diroff == DOTDOT_OFFSET) {
+				stmp = ino_lookup(rrec->jr_parent, 0);
+				if (stmp)
+					ino_setskip(stmp, ino);
+			}
+		}
+	}
 	ip = ino_read(ino);
 	mode = DIP(ip, di_mode) & IFMT;
 	if (nlink > LINK_MAX)
@@ -1248,16 +1289,16 @@ ino_adjust(ino_t ino, int lastmode, nlin
 		    ino, nlink, DIP(ip, di_nlink));
 	if (debug)
 		printf("Adjusting ino %d, nlink %d, old link %d lastmode %o\n",
-		    ino, nlink, DIP(ip, di_nlink), lastmode);
+		    ino, nlink, DIP(ip, di_nlink), sino->si_mode);
 	if (mode == 0) {
 		if (debug)
 			printf("ino %d, zero inode freeing bitmap\n", ino);
-		ino_free(ino, lastmode);
+		ino_free(ino, sino->si_mode);
 		return;
 	}
 	/* XXX Should be an assert? */
-	if (mode != lastmode && debug)
-		printf("ino %d, mode %o != %o\n", ino, mode, lastmode);
+	if (mode != sino->si_mode && debug)
+		printf("ino %d, mode %o != %o\n", ino, mode, sino->si_mode);
 	if ((mode & IFMT) == IFDIR)
 		reqlink = 2;
 	else
@@ -1426,10 +1467,12 @@ ino_trunc(ino_t ino, off_t size)
 	 * If we're truncating direct blocks we have to adjust frags
 	 * accordingly.
 	 */
-	if (visitlbn < NDADDR) {
+	if (visitlbn < NDADDR && totalfrags) {
 		long oldspace, newspace;
 
 		bn = DIP(ip, di_db[visitlbn]);
+		if (bn == 0)
+			errx(1, "Bad blk at ino %d lbn %jd\n", ino, visitlbn);
 		oldspace = sblksize(fs, cursize, visitlbn);
 		newspace = sblksize(fs, size, visitlbn);
 		if (oldspace != newspace) {
@@ -1474,7 +1517,6 @@ ino_check(struct suj_ino *sino)
 {
 	struct suj_rec *srec;
 	struct jrefrec *rrec;
-	struct suj_ino *stmp;
 	nlink_t dotlinks;
 	int newlinks;
 	int removes;
@@ -1484,18 +1526,9 @@ ino_check(struct suj_ino *sino)
 	int isat;
 	int mode;
 
-	/*
-	 * Handle truncations that were not complete.  We don't have
-	 * to worry about truncating directory entries as they must have
-	 * been removed for truncate to succeed.
-	 */
-	ino = sino->si_ino;
-	if (sino->si_trunc) {
-		ino_trunc(ino, sino->si_trunc->jt_size);
-		sino->si_trunc = NULL;
-	}
 	if (sino->si_hasrecs == 0)
 		return;
+	ino = sino->si_ino;
 	rrec = (struct jrefrec *)TAILQ_FIRST(&sino->si_recs)->sr_rec;
 	nlink = rrec->jr_nlink;
 	newlinks = 0;
@@ -1528,29 +1561,16 @@ ino_check(struct suj_ino *sino)
 	 * makes no change to the link count but an add increases
 	 * by one.
 	 */
+	if (debug)
+		printf("ino %d nlink %d newlinks %d removes %d dotlinks %d\n",
+		    ino, nlink, newlinks, removes, dotlinks);
 	nlink += newlinks;
 	nlink -= removes;
-	/*
-	 * If it's a directory with no real names pointing to it go ahead
-	 * and truncate it.  This will free any children.
-	 */
-	if ((mode & IFMT) == IFDIR && nlink - dotlinks == 0) {
-		nlink = 0;
-		/*
-		 * Mark any .. links so they know not to free this inode
-		 * when they are removed.
-		 */
-		TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
-			rrec = (struct jrefrec *)srec->sr_rec;
-			if (rrec->jr_diroff == DOTDOT_OFFSET) {
-				stmp = ino_lookup(rrec->jr_parent, 0);
-				if (stmp)
-					ino_setskip(stmp, ino);
-			}
-		}
-	}
 	sino->si_linkadj = 1;
-	ino_adjust(ino, mode, nlink);
+	sino->si_nlink = nlink;
+	sino->si_dotlinks = dotlinks;
+	sino->si_mode = mode;
+	ino_adjust(sino);
 }
 
 /*
@@ -1632,42 +1652,63 @@ cg_build(struct suj_cg *sc)
 }
 
 /*
- * Walk the list of inode and block records for this cg, recovering any
- * changes which were not complete at the time of crash.
+ * Handle inodes requiring truncation.  This must be done prior to
+ * looking up any inodes in directories.
  */
 static void
-cg_check(struct suj_cg *sc)
+cg_trunc(struct suj_cg *sc)
 {
 	struct suj_ino *sino;
-	struct suj_blk *sblk;
 	int i;
 
-	if (debug)
-		printf("Recovering cg %d\n", sc->sc_cgx);
-
 	for (i = 0; i < SUJ_HASHSIZE; i++)
 		LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
-			ino_check(sino);
+			if (sino->si_trunc) {
+				ino_trunc(sino->si_ino,
+				    sino->si_trunc->jt_size);
+				sino->si_trunc = NULL;
+			}
+}
+
+/*
+ * Free any partially allocated blocks and then resolve inode block
+ * counts.
+ */
+static void
+cg_check_blk(struct suj_cg *sc)
+{
+	struct suj_ino *sino;
+	struct suj_blk *sblk;
+	int i;
+
 
 	for (i = 0; i < SUJ_HASHSIZE; i++)
 		LIST_FOREACH(sblk, &sc->sc_blkhash[i], sb_next)
 			blk_check(sblk);
+	/*
+	 * Now that we've freed blocks which are not referenced we
+	 * make a second pass over all inodes to adjust their block
+	 * counts.
+	 */
+	for (i = 0; i < SUJ_HASHSIZE; i++)
+		LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
+			if (sino->si_blkadj)
+				ino_adjblks(sino);
 }
 
 /*
- * Now that we've freed blocks which are not referenced we make a second
- * pass over all inodes to adjust their block counts.
+ * Walk the list of inode records for this cg, recovering any
+ * changes which were not complete at the time of crash.
  */
 static void
-cg_check2(struct suj_cg *sc)
+cg_check_ino(struct suj_cg *sc)
 {
 	struct suj_ino *sino;
 	int i;
 
 	for (i = 0; i < SUJ_HASHSIZE; i++)
 		LIST_FOREACH(sino, &sc->sc_inohash[i], si_next)
-			if (sino->si_blkadj)
-				ino_adjblks(sino);
+			ino_check(sino);
 }
 
 /*
@@ -1789,9 +1830,23 @@ ino_unlinked(void)
 static void
 ino_append(union jrec *rec)
 {
+	struct jrefrec *refrec;
+	struct jmvrec *mvrec;
 	struct suj_ino *sino;
 	struct suj_rec *srec;
 
+	mvrec = &rec->rec_jmvrec;
+	refrec = &rec->rec_jrefrec;
+	if (debug && mvrec->jm_op == JOP_MVREF)
+		printf("ino move: ino %d, parent %d, diroff %jd, oldoff %jd\n", 
+		    mvrec->jm_ino, mvrec->jm_parent, mvrec->jm_newoff,
+		    mvrec->jm_oldoff);
+	else if (debug &&
+	    (refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF))
+		printf("ino ref: op %d, ino %d, nlink %d, "
+		    "parent %d, diroff %jd\n", 
+		    refrec->jr_op, refrec->jr_ino, refrec->jr_nlink,
+		    refrec->jr_parent, refrec->jr_diroff);
 	/*
 	 * Lookup the ino and clear truncate if one is found.  Partial
 	 * truncates are always done synchronously so if we discover
@@ -1803,69 +1858,27 @@ ino_append(union jrec *rec)
 	sino->si_hasrecs = 1;
 	srec = errmalloc(sizeof(*srec));
 	srec->sr_rec = rec;
-	srec->sr_alt = 0;
 	TAILQ_INSERT_TAIL(&sino->si_newrecs, srec, sr_next);
 }
 
 /*
- * If we see two ops for the same inode to the same parent at the same
- * offset we could miscount the link with ino_isat() returning twice.
- * Keep only the first record because it has the valid link count but keep
- * the mode from the final op as that should be the correct mode in case
- * it changed.
+ * Add a reference adjustment to the sino list and eliminate dups.  The
+ * primary loop in ino_build_ref() checks for dups but new ones may be
+ * created as a result of offset adjustments.
  */
 static void
-ino_build_ref(struct suj_ino *sino, struct suj_rec *srec)
+ino_add_ref(struct suj_ino *sino, struct suj_rec *srec)
 {
 	struct jrefrec *refrec;
-	struct jmvrec *mvrec;
-	struct suj_rec *srp;
 	struct suj_rec *srn;
 	struct jrefrec *rrn;
 

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-projects mailing list