Date: Thu, 28 Jan 2010 09:25:35 +0000 (UTC) From: Jeff Roberson <jeff@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r203110 - in projects/suj: 6/sbin/fsck_ffs 7/sbin/fsck_ffs 8/sbin/fsck_ffs Message-ID: <201001280925.o0S9PZ5U093856@svn.freebsd.org>
index | next in thread | raw e-mail
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@FreeBSD.org> + * Copyright 2009, 2010 Jeffrey W. Roberson <jeff@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@FreeBSD.org> + * Copyright 2009, 2010 Jeffrey W. Roberson <jeff@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 ***help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201001280925.o0S9PZ5U093856>
