From owner-svn-src-projects@FreeBSD.ORG Wed Jul 14 08:48:54 2010 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 356181065672; Wed, 14 Jul 2010 08:48:54 +0000 (UTC) (envelope-from jeff@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 231278FC20; Wed, 14 Jul 2010 08:48:54 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o6E8msFD011460; Wed, 14 Jul 2010 08:48:54 GMT (envelope-from jeff@svn.freebsd.org) Received: (from jeff@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o6E8ms9h011452; Wed, 14 Jul 2010 08:48:54 GMT (envelope-from jeff@svn.freebsd.org) Message-Id: <201007140848.o6E8ms9h011452@svn.freebsd.org> From: Jeff Roberson Date: Wed, 14 Jul 2010 08:48:54 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r210049 - in projects/suj/6: sbin/dumpfs sbin/fsck_ffs sbin/tunefs sys/kern sys/sys sys/ufs/ffs sys/ufs/ufs X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 14 Jul 2010 08:48:54 -0000 Author: jeff Date: Wed Jul 14 08:48:53 2010 New Revision: 210049 URL: http://svn.freebsd.org/changeset/base/210049 Log: - Bring in suj fixes from head to suj/6. Modified: projects/suj/6/sbin/dumpfs/dumpfs.c projects/suj/6/sbin/fsck_ffs/main.c projects/suj/6/sbin/fsck_ffs/pass1.c projects/suj/6/sbin/fsck_ffs/pass2.c projects/suj/6/sbin/fsck_ffs/pass4.c projects/suj/6/sbin/fsck_ffs/suj.c projects/suj/6/sbin/tunefs/tunefs.8 projects/suj/6/sbin/tunefs/tunefs.c projects/suj/6/sys/kern/vfs_vnops.c projects/suj/6/sys/sys/buf.h projects/suj/6/sys/sys/mount.h projects/suj/6/sys/ufs/ffs/ffs_alloc.c projects/suj/6/sys/ufs/ffs/ffs_extern.h projects/suj/6/sys/ufs/ffs/ffs_inode.c projects/suj/6/sys/ufs/ffs/ffs_snapshot.c projects/suj/6/sys/ufs/ffs/ffs_softdep.c projects/suj/6/sys/ufs/ffs/ffs_vfsops.c projects/suj/6/sys/ufs/ffs/ffs_vnops.c projects/suj/6/sys/ufs/ffs/fs.h projects/suj/6/sys/ufs/ffs/softdep.h projects/suj/6/sys/ufs/ufs/inode.h projects/suj/6/sys/ufs/ufs/ufs_inode.c projects/suj/6/sys/ufs/ufs/ufs_lookup.c projects/suj/6/sys/ufs/ufs/ufs_vnops.c Modified: projects/suj/6/sbin/dumpfs/dumpfs.c ============================================================================== --- projects/suj/6/sbin/dumpfs/dumpfs.c Wed Jul 14 08:47:19 2010 (r210048) +++ projects/suj/6/sbin/dumpfs/dumpfs.c Wed Jul 14 08:48:53 2010 (r210049) @@ -219,7 +219,7 @@ dumpfs(const char *name) if (fsflags & FS_UNCLEAN) printf("unclean "); if (fsflags & FS_DOSOFTDEP) - printf("soft-updates "); + printf("soft-updates%s ", (fsflags & FS_SUJ) ? "+journal" : ""); if (fsflags & FS_NEEDSFSCK) printf("needs fsck run "); if (fsflags & FS_INDEXDIRS) @@ -231,7 +231,7 @@ dumpfs(const char *name) if (fsflags & FS_FLAGS_UPDATED) printf("fs_flags expanded "); fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_INDEXDIRS | - FS_ACLS | FS_MULTILABEL | FS_FLAGS_UPDATED); + FS_ACLS | FS_MULTILABEL | FS_FLAGS_UPDATED | FS_SUJ); if (fsflags != 0) printf("unknown flags (%#x)", fsflags); putchar('\n'); Modified: projects/suj/6/sbin/fsck_ffs/main.c ============================================================================== --- projects/suj/6/sbin/fsck_ffs/main.c Wed Jul 14 08:47:19 2010 (r210048) +++ projects/suj/6/sbin/fsck_ffs/main.c Wed Jul 14 08:48:53 2010 (r210049) @@ -242,26 +242,6 @@ checkfilesys(char *filesys) exit(7); /* Filesystem clean, report it now */ exit(0); } - if (ckclean && skipclean) { - /* - * If file system is su+j, check it here. - */ - if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0) - exit(3); /* Cannot read superblock */ - close(fsreadfd); -#if 0 - if ((sblock.fs_flags & FS_SUJ) != 0) { - if (sblock.fs_clean == 1) { - pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); - exit(0); - } - suj_check(filesys); - if (chkdoreload(mntp) == 0) - exit(0); - exit(4); - } -#endif - } /* * If we are to do a background check: * Get the mount point information of the file system @@ -364,13 +344,17 @@ checkfilesys(char *filesys) /* * Determine if we can and should do journal recovery. */ - if ((sblock.fs_flags & (FS_SUJ | FS_NEEDSFSCK)) == FS_SUJ) { - if (preen || reply("USE JOURNAL?")) { - if (suj_check(filesys) == 0) - goto out; - /* suj_check failed, fall through. */ + if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) { + if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK && skipclean) { + if (preen || reply("USE JOURNAL?")) { + if (suj_check(filesys) == 0) { ++ printf("\n***** FILE SYSTEM MARKED CLEAN *****\n"); + goto out; + } + /* suj_check failed, fall through. */ + } + printf("** Skipping journal, falling through to full fsck\n"); } - printf("** Skipping journal, falling through to full fsck\n"); /* * Write the superblock so we don't try to recover the * journal on another pass. @@ -400,7 +384,10 @@ checkfilesys(char *filesys) */ if (duplist) { if (preen || usedsoftdep) - pfatal("INTERNAL ERROR: dups with -p"); + pfatal("INTERNAL ERROR: dups with %s%s%s", + preen ? "-p" : "", + (preen && usedsoftdep) ? " and " : "", + usedsoftdep ? "softupdates" : ""); printf("** Phase 1b - Rescan For More DUPS\n"); pass1b(); } Modified: projects/suj/6/sbin/fsck_ffs/pass1.c ============================================================================== --- projects/suj/6/sbin/fsck_ffs/pass1.c Wed Jul 14 08:47:19 2010 (r210048) +++ projects/suj/6/sbin/fsck_ffs/pass1.c Wed Jul 14 08:48:53 2010 (r210049) @@ -95,10 +95,16 @@ pass1(void) getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize); if (sblock.fs_magic == FS_UFS2_MAGIC) { inosused = cgrp.cg_initediblk; - if (inosused > sblock.fs_ipg) + if (inosused > sblock.fs_ipg) { + pfatal("%s (%d > %d) %s %d\nReset to %d\n", + "Too many initialized inodes", inosused, + sblock.fs_ipg, "in cylinder group", c, + sblock.fs_ipg); inosused = sblock.fs_ipg; - } else + } + } else { inosused = sblock.fs_ipg; + } if (got_siginfo) { printf("%s: phase 1: cyl group %d of %d (%d%%)\n", cdevname, c, sblock.fs_ncg, Modified: projects/suj/6/sbin/fsck_ffs/pass2.c ============================================================================== --- projects/suj/6/sbin/fsck_ffs/pass2.c Wed Jul 14 08:47:19 2010 (r210048) +++ projects/suj/6/sbin/fsck_ffs/pass2.c Wed Jul 14 08:48:53 2010 (r210049) @@ -36,12 +36,14 @@ static const char sccsid[] = "@(#)pass2. __FBSDID("$FreeBSD$"); #include +#include #include #include #include #include +#include #include #include @@ -49,6 +51,8 @@ __FBSDID("$FreeBSD$"); #define MINDIRSIZE (sizeof (struct dirtemplate)) +static int fix_extraneous(struct inoinfo *, struct inodesc *); +static int deleteentry(struct inodesc *); static int blksort(const void *, const void *); static int pass2check(struct inodesc *); @@ -212,9 +216,48 @@ pass2(void) inoinfo(inp->i_parent)->ino_linkcnt--; continue; } - fileerror(inp->i_parent, inp->i_number, - "BAD INODE NUMBER FOR '..'"); - if (reply("FIX") == 0) + /* + * Here we have: + * inp->i_number is directory with bad ".." in it. + * inp->i_dotdot is current value of "..". + * inp->i_parent is directory to which ".." should point. + */ + getpathname(pathbuf, inp->i_parent, inp->i_number); + printf("BAD INODE NUMBER FOR '..' in DIR I=%d (%s)\n", + inp->i_number, pathbuf); + getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot); + printf("CURRENTLY POINTS TO I=%d (%s), ", inp->i_dotdot, + pathbuf); + getpathname(pathbuf, inp->i_parent, inp->i_parent); + printf("SHOULD POINT TO I=%d (%s)", inp->i_parent, pathbuf); + if (cursnapshot != 0) { + /* + * We need to: + * setcwd(inp->i_number); + * setdotdot(inp->i_dotdot, inp->i_parent); + */ + cmd.value = inp->i_number; + if (sysctlbyname("vfs.ffs.setcwd", 0, 0, + &cmd, sizeof cmd) == -1) { + /* kernel lacks support for these functions */ + printf(" (IGNORED)\n"); + continue; + } + cmd.value = inp->i_dotdot; /* verify same value */ + cmd.size = inp->i_parent; /* new parent */ + if (sysctlbyname("vfs.ffs.setdotdot", 0, 0, + &cmd, sizeof cmd) == -1) { + printf(" (FIX FAILED: %s)\n", strerror(errno)); + continue; + } + printf(" (FIXED)\n"); + inoinfo(inp->i_parent)->ino_linkcnt--; + inp->i_dotdot = inp->i_parent; + continue; + } + if (preen) + printf(" (FIXED)\n"); + else if (reply("FIX") == 0) continue; inoinfo(inp->i_dotdot)->ino_linkcnt++; inoinfo(inp->i_parent)->ino_linkcnt--; @@ -231,13 +274,12 @@ static int pass2check(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; + char dirname[MAXPATHLEN + 1]; struct inoinfo *inp; int n, entrysize, ret = 0; union dinode *dp; const char *errmsg; struct direct proto; - char namebuf[MAXPATHLEN + 1]; - char pathbuf[MAXPATHLEN + 1]; /* * check for "." @@ -393,9 +435,37 @@ again: errmsg = "DUP/BAD"; else if (!preen && !usedsoftdep) errmsg = "ZERO LENGTH DIRECTORY"; - else { + else if (cursnapshot == 0) { n = 1; break; + } else { + getpathname(dirname, idesc->id_number, + dirp->d_ino); + pwarn("ZERO LENGTH DIRECTORY %s I=%d", + dirname, dirp->d_ino); + /* + * We need to: + * setcwd(idesc->id_parent); + * rmdir(dirp->d_name); + */ + cmd.value = idesc->id_number; + if (sysctlbyname("vfs.ffs.setcwd", 0, 0, + &cmd, sizeof cmd) == -1) { + /* kernel lacks support */ + printf(" (IGNORED)\n"); + n = 1; + break; + } + if (rmdir(dirp->d_name) == -1) { + printf(" (REMOVAL FAILED: %s)\n", + strerror(errno)); + n = 1; + break; + } + /* ".." reference to parent is removed */ + inoinfo(idesc->id_number)->ino_linkcnt--; + printf(" (REMOVED)\n"); + break; } fileerror(idesc->id_number, dirp->d_ino, errmsg); if ((n = reply("REMOVE")) == 1) @@ -414,27 +484,12 @@ again: case DFOUND: inp = getinoinfo(dirp->d_ino); - if (inp->i_parent != 0 && idesc->id_entryno > 2) { - getpathname(pathbuf, idesc->id_number, - idesc->id_number); - getpathname(namebuf, dirp->d_ino, dirp->d_ino); - pwarn("%s%s%s %s %s\n", pathbuf, - (strcmp(pathbuf, "/") == 0 ? "" : "/"), - dirp->d_name, - "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", - namebuf); - if (cursnapshot != 0) - break; - if (preen) { - printf(" (REMOVED)\n"); - n = 1; - break; - } - if ((n = reply("REMOVE")) == 1) + if (idesc->id_entryno > 2) { + if (inp->i_parent == 0) + inp->i_parent = idesc->id_number; + else if ((n = fix_extraneous(inp, idesc)) == 1) break; } - if (idesc->id_entryno > 2) - inp->i_parent = idesc->id_number; /* FALLTHROUGH */ case FSTATE: @@ -460,6 +515,143 @@ again: return (ret|KEEPON|ALTERED); } +static int +fix_extraneous(struct inoinfo *inp, struct inodesc *idesc) +{ + char *cp; + struct inodesc dotdesc; + char oldname[MAXPATHLEN + 1]; + char newname[MAXPATHLEN + 1]; + + /* + * If we have not yet found "..", look it up now so we know + * which inode the directory itself believes is its parent. + */ + if (inp->i_dotdot == 0) { + memset(&dotdesc, 0, sizeof(struct inodesc)); + dotdesc.id_type = DATA; + dotdesc.id_number = idesc->id_dirp->d_ino; + dotdesc.id_func = findino; + dotdesc.id_name = strdup(".."); + if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND)) + inp->i_dotdot = dotdesc.id_parent; + } + /* + * We have the previously found old name (inp->i_parent) and the + * just found new name (idesc->id_number). We have five cases: + * 1) ".." is missing - can remove either name, choose to delete + * new one and let fsck create ".." pointing to old name. + * 2) Both new and old are in same directory, choose to delete + * the new name and let fsck fix ".." if it is wrong. + * 3) ".." does not point to the new name, so delete it and let + * fsck fix ".." to point to the old one if it is wrong. + * 4) ".." points to the old name only, so delete the new one. + * 5) ".." points to the new name only, so delete the old one. + * + * For cases 1-4 we eliminate the new name; + * for case 5 we eliminate the old name. + */ + if (inp->i_dotdot == 0 || /* Case 1 */ + idesc->id_number == inp->i_parent || /* Case 2 */ + inp->i_dotdot != idesc->id_number || /* Case 3 */ + inp->i_dotdot == inp->i_parent) { /* Case 4 */ + getpathname(newname, idesc->id_number, idesc->id_number); + if (strcmp(newname, "/") != 0) + strcat (newname, "/"); + strcat(newname, idesc->id_dirp->d_name); + getpathname(oldname, inp->i_number, inp->i_number); + pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", + newname, oldname); + if (cursnapshot != 0) { + /* + * We need to + * setcwd(idesc->id_number); + * unlink(idesc->id_dirp->d_name); + */ + cmd.value = idesc->id_number; + if (sysctlbyname("vfs.ffs.setcwd", 0, 0, + &cmd, sizeof cmd) == -1) { + printf(" (IGNORED)\n"); + return (0); + } + cmd.value = (intptr_t)idesc->id_dirp->d_name; + cmd.size = inp->i_number; /* verify same name */ + if (sysctlbyname("vfs.ffs.unlink", 0, 0, + &cmd, sizeof cmd) == -1) { + printf(" (UNLINK FAILED: %s)\n", + strerror(errno)); + return (0); + } + printf(" (REMOVED)\n"); + return (0); + } + if (preen) { + printf(" (REMOVED)\n"); + return (1); + } + return (reply("REMOVE")); + } + /* + * None of the first four cases above, so must be case (5). + * Eliminate the old name and make the new the name the parent. + */ + getpathname(oldname, inp->i_parent, inp->i_number); + getpathname(newname, inp->i_number, inp->i_number); + pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname, + newname); + if (cursnapshot != 0) { + /* + * We need to + * setcwd(inp->i_parent); + * unlink(last component of oldname pathname); + */ + cmd.value = inp->i_parent; + if (sysctlbyname("vfs.ffs.setcwd", 0, 0, + &cmd, sizeof cmd) == -1) { + printf(" (IGNORED)\n"); + return (0); + } + if ((cp = rindex(oldname, '/')) == NULL) { + printf(" (IGNORED)\n"); + return (0); + } + cmd.value = (intptr_t)(cp + 1); + cmd.size = inp->i_number; /* verify same name */ + if (sysctlbyname("vfs.ffs.unlink", 0, 0, + &cmd, sizeof cmd) == -1) { + printf(" (UNLINK FAILED: %s)\n", + strerror(errno)); + return (0); + } + printf(" (REMOVED)\n"); + inp->i_parent = idesc->id_number; /* reparent to correct dir */ + return (0); + } + if (!preen && !reply("REMOVE")) + return (0); + memset(&dotdesc, 0, sizeof(struct inodesc)); + dotdesc.id_type = DATA; + dotdesc.id_number = inp->i_parent; /* directory in which name appears */ + dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */ + dotdesc.id_func = deleteentry; + if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen) + printf(" (REMOVED)\n"); + inp->i_parent = idesc->id_number; /* reparent to correct directory */ + inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */ + return (0); +} + +static int +deleteentry(struct inodesc *idesc) +{ + struct direct *dirp = idesc->id_dirp; + + if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent) + return (KEEPON); + dirp->d_ino = 0; + return (ALTERED|STOP|FOUND); +} + /* * Routine to sort disk blocks. */ Modified: projects/suj/6/sbin/fsck_ffs/pass4.c ============================================================================== --- projects/suj/6/sbin/fsck_ffs/pass4.c Wed Jul 14 08:47:19 2010 (r210048) +++ projects/suj/6/sbin/fsck_ffs/pass4.c Wed Jul 14 08:48:53 2010 (r210049) @@ -97,6 +97,9 @@ pass4(void) break; case DCLEAR: + /* if on snapshot, already cleared */ + if (cursnapshot != 0) + break; dp = ginode(inumber); if (DIP(dp, di_size) == 0) { clri(&idesc, "ZERO LENGTH", 1); Modified: projects/suj/6/sbin/fsck_ffs/suj.c ============================================================================== --- projects/suj/6/sbin/fsck_ffs/suj.c Wed Jul 14 08:47:19 2010 (r210048) +++ projects/suj/6/sbin/fsck_ffs/suj.c Wed Jul 14 08:48:53 2010 (r210049) @@ -37,12 +37,15 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -141,7 +144,10 @@ uint64_t freedir; uint64_t jbytes; uint64_t jrecs; +static jmp_buf jmpbuf; + typedef void (*ino_visitor)(ino_t, ufs_lbn_t, ufs2_daddr_t, int); +static void err_suj(const char *, ...) __dead2; static void ino_trunc(ino_t, off_t); static void ino_decr(ino_t); static void ino_adjust(struct suj_ino *); @@ -155,11 +161,30 @@ errmalloc(size_t n) a = malloc(n); if (a == NULL) - errx(1, "malloc(%zu)", n); + err(EX_OSERR, "malloc(%zu)", n); return (a); } /* + * When hit a fatal error in journalling check, print out + * the error and then offer to fallback to normal fsck. + */ +static void +err_suj(const char * restrict fmt, ...) +{ + va_list ap; + + if (preen) + (void)fprintf(stdout, "%s: ", cdevname); + + va_start(ap, fmt); + (void)vfprintf(stdout, fmt, ap); + va_end(ap); + + longjmp(jmpbuf, -1); +} + +/* * Open the given provider, load superblock. */ static void @@ -169,9 +194,9 @@ opendisk(const char *devnam) return; disk = malloc(sizeof(*disk)); if (disk == NULL) - errx(1, "malloc(%zu)", sizeof(*disk)); + err(EX_OSERR, "malloc(%zu)", sizeof(*disk)); if (ufs_disk_fillout(disk, devnam) == -1) { - err(1, "ufs_disk_fillout(%s) failed: %s", devnam, + err(EX_OSERR, "ufs_disk_fillout(%s) failed: %s", devnam, disk->d_error); } fs = &disk->d_fs; @@ -203,9 +228,9 @@ closedisk(const char *devnam) fs->fs_time = time(NULL); fs->fs_mtime = time(NULL); if (sbwrite(disk, 0) == -1) - err(1, "sbwrite(%s)", devnam); + err(EX_OSERR, "sbwrite(%s)", devnam); if (ufs_disk_close(disk) == -1) - err(1, "ufs_disk_close(%s)", devnam); + err(EX_OSERR, "ufs_disk_close(%s)", devnam); free(disk); disk = NULL; fs = NULL; @@ -221,10 +246,8 @@ cg_lookup(int cgx) struct cghd *hd; struct suj_cg *sc; - if (cgx < 0 || cgx >= fs->fs_ncg) { - abort(); - errx(1, "Bad cg number %d", cgx); - } + if (cgx < 0 || cgx >= fs->fs_ncg) + err_suj("Bad cg number %d\n", cgx); if (lastcg && lastcg->sc_cgx == cgx) return (lastcg); hd = &cghash[SUJ_HASH(cgx)]; @@ -241,7 +264,7 @@ cg_lookup(int cgx) LIST_INSERT_HEAD(hd, sc, sc_next); if (bread(disk, fsbtodb(fs, cgtod(fs, sc->sc_cgx)), sc->sc_cgbuf, fs->fs_bsize) == -1) - err(1, "Unable to read cylinder group %d", sc->sc_cgx); + err_suj("Unable to read cylinder group %d\n", sc->sc_cgx); return (sc); } @@ -344,7 +367,7 @@ dblk_read(ufs2_daddr_t blk, int size) dblk->db_buf = errmalloc(size); dblk->db_size = size; if (bread(disk, fsbtodb(fs, blk), dblk->db_buf, size) == -1) - err(1, "Failed to read data block %jd", blk); + err_suj("Failed to read data block %jd\n", blk); } return (dblk->db_buf); } @@ -370,7 +393,7 @@ dblk_write(void) continue; if (bwrite(disk, fsbtodb(fs, dblk->db_blk), dblk->db_buf, dblk->db_size) == -1) - err(1, "Unable to write block %jd", + err_suj("Unable to write block %jd\n", dblk->db_blk); } } @@ -403,7 +426,7 @@ ino_read(ino_t ino) iblk->ib_blk = blk; LIST_INSERT_HEAD(hd, iblk, ib_next); if (bread(disk, fsbtodb(fs, blk), iblk->ib_buf, fs->fs_bsize) == -1) - err(1, "Failed to read inode block %jd", blk); + err_suj("Failed to read inode block %jd\n", blk); found: sc->sc_lastiblk = iblk; off = ino_to_fsbo(fs, ino); @@ -447,7 +470,7 @@ iblk_write(struct ino_blk *iblk) return; if (bwrite(disk, fsbtodb(fs, iblk->ib_blk), iblk->ib_buf, fs->fs_bsize) == -1) - err(1, "Failed to write inode block %jd", iblk->ib_blk); + err_suj("Failed to write inode block %jd\n", iblk->ib_blk); } static int @@ -679,9 +702,9 @@ indir_blkatoff(ufs2_daddr_t blk, ino_t i return (0); level = lbn_level(cur); if (level == -1) - errx(1, "Invalid indir lbn %jd", lbn); + err_suj("Invalid indir lbn %jd\n", lbn); if (level == 0 && lbn < 0) - errx(1, "Invalid lbn %jd", lbn); + err_suj("Invalid lbn %jd\n", lbn); bap2 = (void *)dblk_read(blk, fs->fs_bsize); bap1 = (void *)bap2; lbnadd = 1; @@ -693,7 +716,7 @@ indir_blkatoff(ufs2_daddr_t blk, ino_t i else i = (-lbn - base) / lbnadd; if (i < 0 || i >= NINDIR(fs)) - errx(1, "Invalid indirect index %d produced by lbn %jd", + err_suj("Invalid indirect index %d produced by lbn %jd\n", i, lbn); if (level == 0) cur = base + (i * lbnadd); @@ -705,10 +728,8 @@ indir_blkatoff(ufs2_daddr_t blk, ino_t i blk = bap2[i]; if (cur == lbn) return (blk); - if (level == 0) { - abort(); - errx(1, "Invalid lbn %jd at level 0", lbn); - } + if (level == 0) + err_suj("Invalid lbn %jd at level 0\n", lbn); return indir_blkatoff(blk, ino, cur, lbn); } @@ -762,7 +783,8 @@ ino_blkatoff(union dinode *ip, ino_t ino continue; return indir_blkatoff(DIP(ip, di_ib[i]), ino, -cur - i, lbn); } - errx(1, "lbn %jd not in ino", lbn); + err_suj("lbn %jd not in ino\n", lbn); + /* NOTREACHED */ } /* @@ -786,6 +808,44 @@ blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_ } /* + * Clear the directory entry at diroff that should point to child. Minimal + * checking is done and it is assumed that this path was verified with isat. + */ +static void +ino_clrat(ino_t parent, off_t diroff, ino_t child) +{ + union dinode *dip; + struct direct *dp; + ufs2_daddr_t blk; + uint8_t *block; + ufs_lbn_t lbn; + int blksize; + int frags; + int doff; + + if (debug) + printf("Clearing inode %d from parent %d at offset %jd\n", + child, parent, diroff); + + lbn = lblkno(fs, diroff); + doff = blkoff(fs, diroff); + dip = ino_read(parent); + blk = ino_blkatoff(dip, parent, lbn, &frags); + blksize = sblksize(fs, DIP(dip, di_size), lbn); + block = dblk_read(blk, blksize); + dp = (struct direct *)&block[doff]; + if (dp->d_ino != child) + errx(1, "Inode %d does not exist in %d at %jd", + child, parent, diroff); + dp->d_ino = 0; + dblk_dirty(blk); + /* + * The actual .. reference count will already have been removed + * from the parent by the .. remref record. + */ +} + +/* * Determines whether a pointer to an inode exists within a directory * at a specified offset. Returns the mode of the found entry. */ @@ -851,7 +911,7 @@ ino_isat(ino_t parent, off_t diroff, ino dpoff += dp->d_reclen; } while (dpoff <= doff); if (dpoff > fs->fs_bsize) - errx(1, "Corrupt directory block in dir ino %d", parent); + err_suj("Corrupt directory block in dir ino %d\n", parent); /* Not found. */ if (dpoff != doff) { if (debug) @@ -907,7 +967,7 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, uf return; level = lbn_level(lbn); if (level == -1) - errx(1, "Invalid level for lbn %jd", lbn); + err_suj("Invalid level for lbn %jd\n", lbn); if ((flags & VISIT_ROOT) == 0 && blk_isindir(blk, ino, lbn) == 0) { if (debug) printf("blk %jd ino %d lbn %jd(%d) is not indir.\n", @@ -1055,7 +1115,6 @@ ino_adjblks(struct suj_ino *sino) if (visitlbn >= NDADDR) { isize = DIP(ip, di_size); size = lblktosize(fs, visitlbn + 1); - printf("ino %d isize %jd size %jd\n", ino, isize, size); if (isize > size) isize = size; /* Always truncate to free any unpopulated indirects. */ @@ -1113,6 +1172,57 @@ ino_setskip(struct suj_ino *sino, ino_t sino->si_skipparent = 1; } +static void +ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot) +{ + struct suj_ino *sino; + struct suj_rec *srec; + struct jrefrec *rrec; + + /* + * Lookup this inode to see if we have a record for it. + */ + sino = ino_lookup(child, 0); + /* + * Tell any child directories we've already removed their + * parent link cnt. Don't try to adjust our link down again. + */ + if (sino != NULL && isdotdot == 0) + ino_setskip(sino, parent); + /* + * No valid record for this inode. Just drop the on-disk + * link by one. + */ + if (sino == NULL || sino->si_hasrecs == 0) { + ino_decr(child); + return; + } + /* + * Use ino_adjust() if ino_check() has already processed this + * child. If we lose the last non-dot reference to a + * directory it will be discarded. + */ + if (sino->si_linkadj) { + sino->si_nlink--; + if (isdotdot) + sino->si_dotlinks--; + ino_adjust(sino); + return; + } + /* + * If we haven't yet processed this inode we need to make + * sure we will successfully discover the lost path. If not + * use nlinkadj to remember. + */ + TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { + rrec = (struct jrefrec *)srec->sr_rec; + if (rrec->jr_parent == parent && + rrec->jr_diroff == diroff) + return; + } + sino->si_nlinkadj++; +} + /* * Free the children of a directory when the directory is discarded. */ @@ -1120,13 +1230,11 @@ static void ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) { struct suj_ino *sino; - struct suj_rec *srec; - struct jrefrec *rrec; struct direct *dp; off_t diroff; uint8_t *block; int skipparent; - int isparent; + int isdotdot; int dpoff; int size; @@ -1144,53 +1252,15 @@ ino_free_children(ino_t ino, ufs_lbn_t l continue; if (dp->d_namlen == 1 && dp->d_name[0] == '.') continue; - isparent = dp->d_namlen == 2 && dp->d_name[0] == '.' && + isdotdot = dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.'; - if (isparent && skipparent == 1) + if (isdotdot && skipparent == 1) continue; if (debug) printf("Directory %d removing ino %d name %s\n", ino, dp->d_ino, dp->d_name); - /* - * Lookup this inode to see if we have a record for it. - * If not, we've already adjusted it assuming this path - * was valid and we have to adjust once more. - */ - sino = ino_lookup(dp->d_ino, 0); - 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. - */ - if (isparent == 0) - ino_setskip(sino, ino); - /* - * If we haven't yet processed this inode we need to make - * sure we will successfully discover the lost path. If not - * use nlinkadj to remember. - */ diroff = lblktosize(fs, lbn) + dpoff; - TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { - rrec = (struct jrefrec *)srec->sr_rec; - if (rrec->jr_parent == ino && - rrec->jr_diroff == diroff) - break; - } - if (srec == NULL) - sino->si_nlinkadj++; + ino_remref(ino, dp->d_ino, diroff, isdotdot); } } @@ -1204,7 +1274,7 @@ ino_reclaim(union dinode *ip, ino_t ino, uint32_t gen; if (ino == ROOTINO) - errx(1, "Attempting to free ROOTINO"); + err_suj("Attempting to free ROOTINO\n"); if (debug) printf("Truncating and freeing ino %d, nlink %d, mode %o\n", ino, DIP(ip, di_nlink), DIP(ip, di_mode)); @@ -1241,9 +1311,9 @@ ino_decr(ino_t ino) nlink = DIP(ip, di_nlink); mode = DIP(ip, di_mode); if (nlink < 1) - errx(1, "Inode %d link count %d invalid", ino, nlink); + err_suj("Inode %d link count %d invalid\n", ino, nlink); if (mode == 0) - errx(1, "Inode %d has a link of %d with 0 mode.", ino, nlink); + err_suj("Inode %d has a link of %d with 0 mode\n", ino, nlink); nlink--; if ((mode & IFMT) == IFDIR) reqlink = 2; @@ -1272,18 +1342,38 @@ ino_adjust(struct suj_ino *sino) struct suj_ino *stmp; union dinode *ip; nlink_t nlink; + int recmode; int reqlink; + int isdot; int mode; ino_t ino; nlink = sino->si_nlink; ino = sino->si_ino; + mode = sino->si_mode & IFMT; + /* + * If it's a directory with no dot links, it was truncated before + * the name was cleared. We need to clear the dirent that + * points at it. + */ + if (mode == IFDIR && nlink == 1 && sino->si_dotlinks == 0) { + sino->si_nlink = nlink = 0; + TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { + rrec = (struct jrefrec *)srec->sr_rec; + if (ino_isat(rrec->jr_parent, rrec->jr_diroff, ino, + &recmode, &isdot) == 0) + continue; + ino_clrat(rrec->jr_parent, rrec->jr_diroff, ino); + break; + } + if (srec == NULL) + errx(1, "Directory %d name not found", 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) { + if (mode == IFDIR && nlink - sino->si_dotlinks == 0) { sino->si_nlink = nlink = 0; /* * Mark any .. links so they know not to free this inode @@ -1301,8 +1391,8 @@ ino_adjust(struct suj_ino *sino) ip = ino_read(ino); mode = DIP(ip, di_mode) & IFMT; if (nlink > LINK_MAX) - errx(1, - "ino %d nlink manipulation error, new link %d, old link %d", + err_suj( + "ino %d nlink manipulation error, new link %d, old link %d\n", ino, nlink, DIP(ip, di_nlink)); if (debug) printf("Adjusting ino %d, nlink %d, old link %d lastmode %o\n", @@ -1360,7 +1450,7 @@ indir_trunc(ino_t ino, ufs_lbn_t lbn, uf dirty = 0; level = lbn_level(lbn); if (level == -1) - errx(1, "Invalid level for lbn %jd", lbn); + err_suj("Invalid level for lbn %jd\n", lbn); lbnadd = 1; for (i = level; i > 0; i--) lbnadd *= NINDIR(fs); @@ -1489,7 +1579,7 @@ ino_trunc(ino_t ino, off_t size) bn = DIP(ip, di_db[visitlbn]); if (bn == 0) - errx(1, "Bad blk at ino %d lbn %jd\n", ino, visitlbn); + err_suj("Bad blk at ino %d lbn %jd\n", ino, visitlbn); oldspace = sblksize(fs, cursize, visitlbn); newspace = sblksize(fs, size, visitlbn); if (oldspace != newspace) { @@ -1513,7 +1603,7 @@ ino_trunc(ino_t ino, off_t size) bn = ino_blkatoff(ip, ino, visitlbn, &frags); if (bn == 0) - errx(1, "Block missing from ino %d at lbn %jd\n", + err_suj("Block missing from ino %d at lbn %jd\n", ino, visitlbn); clrsize = frags * fs->fs_fsize; buf = dblk_read(bn, clrsize); @@ -1556,7 +1646,7 @@ ino_check(struct suj_ino *sino) isat = ino_isat(rrec->jr_parent, rrec->jr_diroff, rrec->jr_ino, &mode, &isdot); if (isat && (mode & IFMT) != (rrec->jr_mode & IFMT)) - errx(1, "Inode mode/directory type mismatch %o != %o", + err_suj("Inode mode/directory type mismatch %o != %o\n", mode, rrec->jr_mode); if (debug) printf("jrefrec: op %d ino %d, nlink %d, parent %d, " @@ -1779,7 +1869,7 @@ cg_write(struct suj_cg *sc) fs->fs_cs(fs, sc->sc_cgx) = cgp->cg_cs; if (bwrite(disk, fsbtodb(fs, cgtod(fs, sc->sc_cgx)), sc->sc_cgbuf, fs->fs_bsize) == -1) - err(1, "Unable to write cylinder group %d", sc->sc_cgx); + err_suj("Unable to write cylinder group %d\n", sc->sc_cgx); } /* @@ -1971,6 +2061,7 @@ ino_build_ref(struct suj_ino *sino, stru continue; diroff = mvrec->jm_oldoff; TAILQ_REMOVE(&sino->si_movs, srn, sr_next); + free(srn); ino_dup_ref(sino, refrec, diroff); } } @@ -2027,7 +2118,7 @@ ino_build_ref(struct suj_ino *sino, stru TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); break; default: - errx(1, "ino_build_ref: Unknown op %d", + err_suj("ino_build_ref: Unknown op %d\n", srn->sr_rec->rec_jrefrec.jr_op); } } @@ -2057,7 +2148,7 @@ ino_build(struct suj_ino *sino) TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next); break; default: - errx(1, "ino_build: Unknown op %d", + err_suj("ino_build: Unknown op %d\n", srec->sr_rec->rec_jrefrec.jr_op); } } @@ -2108,7 +2199,7 @@ blk_build(struct jblkrec *blkrec) blkrec->jb_blkno -= frag; blkrec->jb_oldfrags = frag; if (blkrec->jb_oldfrags + blkrec->jb_frags > fs->fs_frag) - errx(1, "Invalid fragment count %d oldfrags %d", + err_suj("Invalid fragment count %d oldfrags %d\n", blkrec->jb_frags, frag); /* * Detect dups. If we detect a dup we always discard the oldest @@ -2186,7 +2277,7 @@ suj_build(void) ino_build_trunc((struct jtrncrec *)rec); break; default: - errx(1, "Unknown journal operation %d (%d)", + err_suj("Unknown journal operation %d (%d)\n", rec->rec_jrefrec.jr_op, off); } i++; @@ -2234,9 +2325,10 @@ suj_prune(void) newseq = seg->ss_rec.jsr_seq; } - if (newseq != oldseq) - errx(1, "Journal file sequence mismatch %jd != %jd", + if (newseq != oldseq) { + err_suj("Journal file sequence mismatch %jd != %jd\n", newseq, oldseq); + } /* * The kernel may asynchronously write segments which can create * gaps in the sequence space. Throw away any segments after the @@ -2464,9 +2556,10 @@ restart: /* * Read 1MB at a time and scan for records within this block. */ - if (bread(disk, blk, &block, size) == -1) - err(1, "Error reading journal block %jd", + if (bread(disk, blk, &block, size) == -1) { + err_suj("Error reading journal block %jd\n", (intmax_t)blk); + } for (rec = (void *)block; size; size -= recsize, *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***