diff options
Diffstat (limited to 'sbin/fsck_ffs')
-rw-r--r-- | sbin/fsck_ffs/Makefile | 4 | ||||
-rw-r--r-- | sbin/fsck_ffs/Makefile.depend | 2 | ||||
-rw-r--r-- | sbin/fsck_ffs/dir.c | 195 | ||||
-rw-r--r-- | sbin/fsck_ffs/ea.c | 12 | ||||
-rw-r--r-- | sbin/fsck_ffs/fsck.h | 89 | ||||
-rw-r--r-- | sbin/fsck_ffs/fsck_ffs.8 | 6 | ||||
-rw-r--r-- | sbin/fsck_ffs/fsutil.c | 427 | ||||
-rw-r--r-- | sbin/fsck_ffs/gjournal.c | 21 | ||||
-rw-r--r-- | sbin/fsck_ffs/globs.c | 46 | ||||
-rw-r--r-- | sbin/fsck_ffs/inode.c | 578 | ||||
-rw-r--r-- | sbin/fsck_ffs/main.c | 178 | ||||
-rw-r--r-- | sbin/fsck_ffs/pass1.c | 129 | ||||
-rw-r--r-- | sbin/fsck_ffs/pass1b.c | 10 | ||||
-rw-r--r-- | sbin/fsck_ffs/pass2.c | 169 | ||||
-rw-r--r-- | sbin/fsck_ffs/pass3.c | 16 | ||||
-rw-r--r-- | sbin/fsck_ffs/pass4.c | 8 | ||||
-rw-r--r-- | sbin/fsck_ffs/pass5.c | 63 | ||||
-rw-r--r-- | sbin/fsck_ffs/setup.c | 329 | ||||
-rw-r--r-- | sbin/fsck_ffs/suj.c | 210 | ||||
-rw-r--r-- | sbin/fsck_ffs/utilities.c | 8 |
20 files changed, 1752 insertions, 748 deletions
diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile index ba8a39a87e1f..59805dbbf3d6 100644 --- a/sbin/fsck_ffs/Makefile +++ b/sbin/fsck_ffs/Makefile @@ -1,7 +1,5 @@ -# $FreeBSD$ -# @(#)Makefile 8.2 (Berkeley) 4/27/95 -PACKAGE=runtime +PACKAGE=ufs PROG= fsck_ffs LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_ufs LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_4.2bsd diff --git a/sbin/fsck_ffs/Makefile.depend b/sbin/fsck_ffs/Makefile.depend index 735bf03b75b6..910688a678fa 100644 --- a/sbin/fsck_ffs/Makefile.depend +++ b/sbin/fsck_ffs/Makefile.depend @@ -1,8 +1,6 @@ -# $FreeBSD$ # Autogenerated - do NOT edit! DIRDEPS = \ - gnu/lib/csu \ include \ include/xlocale \ lib/${CSU_DIR} \ diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c index 42ecf4112253..3eb0b63c0988 100644 --- a/sbin/fsck_ffs/dir.c +++ b/sbin/fsck_ffs/dir.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/time.h> #include <sys/types.h> @@ -63,7 +55,6 @@ static struct dirtemplate dirhead = { static int chgino(struct inodesc *); static int dircheck(struct inodesc *, struct bufarea *, struct direct *); static int expanddir(struct inode *ip, char *name); -static void freedir(ino_t ino, ino_t parent); static struct direct *fsck_readdir(struct inodesc *); static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); static int lftempname(char *bufp, ino_t ino); @@ -89,6 +80,7 @@ propagate(void) if (inoinfo(inp->i_parent)->ino_state == DFOUND && INO_IS_DUNFOUND(inp->i_number)) { inoinfo(inp->i_number)->ino_state = DFOUND; + check_dirdepth(inp); change++; } } @@ -96,6 +88,105 @@ propagate(void) } /* + * Check that the recorded depth of the directory is correct. + */ +void +check_dirdepth(struct inoinfo *inp) +{ + struct inoinfo *parentinp; + struct inode ip; + union dinode *dp; + int saveresolved; + size_t size; + static int updateasked, dirdepthupdate; + + if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { + pfatal("check_dirdepth: UNKNOWN PARENT DIR"); + return; + } + /* + * If depth is correct, nothing to do. + */ + if (parentinp->i_depth + 1 == inp->i_depth) + return; + /* + * Only the root inode should have depth of 0, so if any other + * directory has a depth of 0 then this is an old filesystem + * that has not been tracking directory depth. Ask just once + * whether it should start tracking directory depth. + */ + if (inp->i_depth == 0 && updateasked == 0) { + updateasked = 1; + if (preen) { + pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n"); + dirdepthupdate = 1; + } else { + /* + * The file system can be marked clean even if + * a directory does not have the right depth. + * Hence, resolved should not be cleared when + * the filesystem does not update directory depths. + */ + saveresolved = resolved; + dirdepthupdate = + reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH"); + resolved = saveresolved; + } + } + /* + * If we are not converting or we are running in no-write mode + * there is nothing more to do. + */ + if ((inp->i_depth == 0 && dirdepthupdate == 0) || + (fswritefd < 0 && bkgrdflag == 0)) + return; + /* + * Individual directory at wrong depth. Report it and correct if + * in preen mode or ask if in interactive mode. Note that if a + * directory is renamed to a new location that is at a different + * level in the tree, its depth will be recalculated, but none of + * the directories that it contains will be updated. Thus it is + * not unexpected to find directories with incorrect depths. No + * operational harm will come from this though new directory + * placement in the subtree may not be as optimal until the depths + * of the affected directories are corrected. + * + * To avoid much spurious output on otherwise clean filesystems + * we only generate detailed output when the debug flag is given. + */ + ginode(inp->i_number, &ip); + dp = ip.i_dp; + if (inp->i_depth != 0 && debug) { + pwarn("DIRECTORY"); + prtinode(&ip); + printf(" DEPTH %d SHOULD BE %d", inp->i_depth, + parentinp->i_depth + 1); + if (preen == 0 && reply("ADJUST") == 0) { + irelse(&ip); + return; + } + if (preen) + printf(" (ADJUSTED)\n"); + } + inp->i_depth = parentinp->i_depth + 1; + if (bkgrdflag == 0) { + DIP_SET(dp, di_dirdepth, inp->i_depth); + inodirty(&ip); + } else { + cmd.value = inp->i_number; + cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth); + if (debug) + printf("adjdepth ino %ld amt %jd\n", (long)cmd.value, + (intmax_t)cmd.size); + size = MIBSIZE; + if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 || + sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) + rwerror("ADJUST INODE DEPTH", cmd.value); + } + irelse(&ip); +} + +/* * Scan each entry in a directory block. */ int @@ -161,7 +252,7 @@ fsck_readdir(struct inodesc *idesc) dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); /* * Only need to check current entry if it is the first in the - * the block, as later entries will have been checked in the + * block, as later entries will have been checked in the * previous call to this function. */ if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { @@ -338,7 +429,7 @@ fileerror(ino_t cwd, ino_t ino, const char *errmesg) char pathbuf[MAXPATHLEN + 1]; pwarn("%s ", errmesg); - if (ino < UFS_ROOTINO || ino > maxino) { + if (ino < UFS_ROOTINO || ino >= maxino) { pfatal("out-of-range inode number %ju", (uintmax_t)ino); return; } @@ -422,7 +513,8 @@ adjust(struct inodesc *idesc, int lcnt) (long long)cmd.size); if (sysctl(adjrefcnt, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) - rwerror("ADJUST INODE", cmd.value); + rwerror("ADJUST INODE LINK COUNT", + cmd.value); } } } @@ -472,8 +564,9 @@ linkup(ino_t orphan, ino_t parentdir, char *name) { struct inode ip; union dinode *dp; - int lostdir; + int lostdir, depth; ino_t oldlfdir; + struct inoinfo *inp; struct inodesc idesc; char tempname[BUFSIZ]; @@ -516,7 +609,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) if (preen) printf(" (CREATED)\n"); } else { - freedir(lfdir, UFS_ROOTINO); + freedirino(lfdir, UFS_ROOTINO); lfdir = 0; if (preen) printf("\n"); @@ -525,6 +618,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) } } irelse(&ip); + free(idesc.id_name); if (lfdir == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); printf("\n\n"); @@ -545,7 +639,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) irelse(&ip); return (0); } - if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) { + if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); irelse(&ip); return (0); @@ -574,23 +668,27 @@ linkup(ino_t orphan, ino_t parentdir, char *name) } inoinfo(orphan)->ino_linkcnt--; if (lostdir) { - if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && + depth = DIP(dp, di_dirdepth) + 1; + if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 && parentdir != (ino_t)-1) (void)makeentry(orphan, lfdir, ".."); DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); inodirty(&ip); inoinfo(lfdir)->ino_linkcnt++; pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); - if (parentdir != (ino_t)-1) { + inp = getinoinfo(parentdir); + if (parentdir != (ino_t)-1 && inp != NULL) { printf("PARENT WAS I=%lu\n", (u_long)parentdir); /* - * The parent directory, because of the ordering + * If the parent directory did not have to + * be replaced then because of the ordering * guarantees, has had the link count incremented * for the child, but no entry was made. This * fixes the parent link count so that fsck does * not need to be rerun. */ - inoinfo(parentdir)->ino_linkcnt++; + if ((inp->i_flags & INFO_NEW) != 0) + inoinfo(parentdir)->ino_linkcnt++; } if (preen == 0) printf("\n"); @@ -603,7 +701,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) * fix an entry in a directory. */ int -changeino(ino_t dir, const char *name, ino_t newnum) +changeino(ino_t dir, const char *name, ino_t newnum, int depth) { struct inodesc idesc; struct inode ip; @@ -617,7 +715,12 @@ changeino(ino_t dir, const char *name, ino_t newnum) idesc.id_name = strdup(name); idesc.id_parent = newnum; /* new value for name */ ginode(dir, &ip); - error = ckinode(ip.i_dp, &idesc); + if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) { + DIP_SET(ip.i_dp, di_dirdepth, depth); + inodirty(&ip); + getinoinfo(dir)->i_depth = depth; + } + free(idesc.id_name); irelse(&ip); return (error); } @@ -652,15 +755,18 @@ makeentry(ino_t parent, ino_t ino, const char *name) } if ((ckinode(dp, &idesc) & ALTERED) != 0) { irelse(&ip); + free(idesc.id_name); return (1); } getpathname(pathbuf, parent, parent); if (expanddir(&ip, pathbuf) == 0) { irelse(&ip); + free(idesc.id_name); return (0); } retval = ckinode(dp, &idesc) & ALTERED; irelse(&ip); + free(idesc.id_name); return (retval); } @@ -675,14 +781,17 @@ expanddir(struct inode *ip, char *name) struct bufarea *bp, *nbp; struct inodesc idesc; union dinode *dp; - int indiralloced; + long cg, indiralloced; char *cp; nbp = NULL; indiralloced = newblk = indirblk = 0; + memset(&idesc, 0, sizeof(struct inodesc)); + idesc.id_type = ADDR; pwarn("NO SPACE LEFT IN %s", name); if (!preen && reply("EXPAND") == 0) return (0); + cg = ino_to_cg(&sblock, ip->i_number); dp = ip->i_dp; filesize = DIP(dp, di_size); lastlbn = lblkno(&sblock, filesize); @@ -701,7 +810,8 @@ expanddir(struct inode *ip, char *name) bp = getdirblk(oldblk, lastlbnsize); if (bp->b_errs) goto bad; - if ((newblk = allocblk(sblock.fs_frag)) == 0) + newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail); + if (newblk == 0) goto bad; nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA); if (nbp->b_errs) @@ -720,6 +830,7 @@ expanddir(struct inode *ip, char *name) memmove(cp, &emptydir, sizeof emptydir); dirty(nbp); brelse(nbp); + binval(bp); idesc.id_blkno = oldblk; idesc.id_numfrags = numfrags(&sblock, lastlbnsize); (void)freeblock(&idesc); @@ -727,7 +838,7 @@ expanddir(struct inode *ip, char *name) printf(" (EXPANDED)\n"); return (1); } - if ((newblk = allocblk(sblock.fs_frag)) == 0) + if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0) goto bad; bp = getdirblk(newblk, sblock.fs_bsize); if (bp->b_errs) @@ -745,8 +856,12 @@ expanddir(struct inode *ip, char *name) * Allocate indirect block if needed. */ if ((indirblk = DIP(dp, di_ib[0])) == 0) { - if ((indirblk = allocblk(sblock.fs_frag)) == 0) + indirblk = allocblk(cg, sblock.fs_frag, + std_checkblkavail); + if (indirblk == 0) { + binval(bp); goto bad; + } indiralloced = 1; } nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1); @@ -757,6 +872,7 @@ expanddir(struct inode *ip, char *name) DIP_SET(dp, di_ib[0], indirblk); DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); + inodirty(ip); } IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk); dirty(nbp); @@ -770,8 +886,10 @@ expanddir(struct inode *ip, char *name) return (1); bad: pfatal(" (EXPANSION FAILED)\n"); - if (nbp != NULL) + if (nbp != NULL) { + binval(bp); brelse(nbp); + } if (newblk != 0) { idesc.id_blkno = newblk; idesc.id_numfrags = sblock.fs_frag; @@ -796,8 +914,8 @@ allocdir(ino_t parent, ino_t request, int mode) struct inode ip; union dinode *dp; struct bufarea *bp; - struct inoinfo *inp; struct dirtemplate *dirp; + struct inoinfo *inp, *parentinp; ino = allocino(request, IFDIR|mode); if (ino == 0) @@ -822,8 +940,12 @@ allocdir(ino_t parent, ino_t request, int mode) DIP_SET(dp, di_nlink, 2); inodirty(&ip); if (ino == UFS_ROOTINO) { + inp = cacheino(dp, ino); + inp->i_parent = parent; + inp->i_dotdot = parent; + inp->i_flags |= INFO_NEW; + inoinfo(ino)->ino_type = DT_DIR; inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); - cacheino(dp, ino); irelse(&ip); return(ino); } @@ -832,10 +954,18 @@ allocdir(ino_t parent, ino_t request, int mode) irelse(&ip); return (0); } - cacheino(dp, ino); - inp = getinoinfo(ino); + inp = cacheino(dp, ino); inp->i_parent = parent; inp->i_dotdot = parent; + inp->i_flags |= INFO_NEW; + if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { + pfatal("allocdir: UNKNOWN PARENT DIR"); + } else { + inp->i_depth = parentinp->i_depth + 1; + DIP_SET(dp, di_dirdepth, inp->i_depth); + inodirty(&ip); + } + inoinfo(ino)->ino_type = DT_DIR; inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; if (inoinfo(ino)->ino_state == DSTATE) { inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); @@ -853,8 +983,8 @@ allocdir(ino_t parent, ino_t request, int mode) /* * free a directory inode */ -static void -freedir(ino_t ino, ino_t parent) +void +freedirino(ino_t ino, ino_t parent) { struct inode ip; union dinode *dp; @@ -866,6 +996,7 @@ freedir(ino_t ino, ino_t parent) inodirty(&ip); irelse(&ip); } + removecachedino(ino); freeino(ino); } diff --git a/sbin/fsck_ffs/ea.c b/sbin/fsck_ffs/ea.c index 54353005bdc0..f6ebc5c072fc 100644 --- a/sbin/fsck_ffs/ea.c +++ b/sbin/fsck_ffs/ea.c @@ -1,5 +1,5 @@ /*- - * SPDX-License-Identifier: BSD-3-Clause-FreeBSD + * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Poul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. @@ -35,9 +35,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/time.h> #include <sys/stdint.h> @@ -61,10 +58,9 @@ eascan(struct inodesc *idesc, struct ufs2_dinode *dp) return (0); #else struct bufarea *bp; - u_int dsize, n; + u_int n; u_char *cp; long blksiz; - char dbuf[DIRBLKSIZ]; printf("Inode %ju extsize %ju\n", (intmax_t)idesc->id_number, (uintmax_t)dp->di_extsize); @@ -74,8 +70,10 @@ eascan(struct inodesc *idesc, struct ufs2_dinode *dp) blksiz = sblock.fs_fsize; else blksiz = sblock.fs_bsize; - printf("blksiz = %ju\n", (intmax_t)blksiz); bp = getdatablk(dp->di_extb[0], blksiz, BT_EXTATTR); + if (bp->b_errs) + return (STOP); + printf("blksiz = %ju\n", (intmax_t)blksiz); cp = (u_char *)bp->b_un.b_buf; for (n = 0; n < blksiz; n++) { printf("%02x", cp[n]); diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 1fb0df0c5124..312142eab9a7 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -1,5 +1,5 @@ /*- - * SPDX-License-Identifier: BSD-3-Clause and BSD-2-Clause-FreeBSD + * SPDX-License-Identifier: BSD-3-Clause and BSD-2-Clause * * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. @@ -57,9 +57,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)fsck.h 8.4 (Berkeley) 5/9/95 - * $FreeBSD$ */ #ifndef _FSCK_H_ @@ -68,6 +65,7 @@ #include <unistd.h> #include <stdlib.h> #include <stdio.h> +#include <libufs.h> #include <sys/queue.h> @@ -112,7 +110,7 @@ struct inostat { #define FSTATE 0x2 /* inode is file */ #define FZLINK 0x3 /* inode is file with a link count of zero */ #define DSTATE 0x4 /* inode is directory */ -#define DZLINK 0x5 /* inode is directory with a zero link count */ +#define DZLINK 0x5 /* inode is directory with a zero link count */ #define DFOUND 0x6 /* directory found during descent */ /* 0x7 UNUSED - see S_IS_DVALID() definition */ #define DCLEAR 0x8 /* directory is to be cleared */ @@ -200,8 +198,7 @@ struct bufarea { #define BT_INODES 7 /* Buffer holds inodes */ #define BT_DIRDATA 8 /* Buffer holds directory data */ #define BT_DATA 9 /* Buffer holds user data */ -#define BT_EMPTY 10 /* Buffer allocated but not filled */ -#define BT_NUMBUFTYPES 11 +#define BT_NUMBUFTYPES 10 #define BT_NAMES { \ "unknown", \ "Superblock", \ @@ -212,8 +209,7 @@ struct bufarea { "External Attribute", \ "Inode Block", \ "Directory Contents", \ - "User Data", \ - "Allocated but not filled" } + "User Data" } extern char *buftype[]; #define BT_BUFTYPE(type) \ type < BT_NUMBUFTYPES ? buftype[type] : buftype[BT_UNKNOWN] @@ -234,7 +230,7 @@ extern struct bufarea *pdirbp; /* current directory contents */ (bp)->b_flags |= B_DIRTY; \ } while (0) #define initbarea(bp, type) do { \ - (bp)->b_bno = (ufs2_daddr_t)-1; \ + (bp)->b_bno = (ufs2_daddr_t)-4; \ (bp)->b_size = 0; \ (bp)->b_errs = 0; \ (bp)->b_flags = 0; \ @@ -299,21 +295,30 @@ struct dups { struct dups *next; ufs2_daddr_t dup; }; -extern struct dups *duplist; /* head of dup list */ -extern struct dups *muldup; /* end of unique duplicate dup block numbers */ +extern struct dups *duplist; /* head of dup list */ +extern struct dups *muldup; /* end of unique duplicate dup block numbers */ /* * Inode cache data structures. */ -extern struct inoinfo { - struct inoinfo *i_nexthash; /* next entry in hash chain */ +struct inoinfo { + SLIST_ENTRY(inoinfo) i_hash; /* hash list */ ino_t i_number; /* inode number of this entry */ ino_t i_parent; /* inode number of parent */ ino_t i_dotdot; /* inode number of `..' */ size_t i_isize; /* size of inode */ + u_int i_depth; /* depth of directory from root */ + u_int i_flags; /* flags, see below */ u_int i_numblks; /* size of block array in bytes */ ufs2_daddr_t i_blks[1]; /* actually longer */ -} **inphead, **inpsort; +}; +extern SLIST_HEAD(inohash, inoinfo) *inphash; +extern struct inoinfo **inpsort; +/* + * flags for struct inoinfo + */ +#define INFO_NEW 0x0000001 /* replaced broken directory */ + extern long dirhash, inplast; extern unsigned long numdirs, listmax; extern long countdirs; /* number of directories we actually found */ @@ -326,6 +331,7 @@ extern int adjnbfree[MIBSIZE]; /* MIB cmd to adjust number of free blocks */ extern int adjnifree[MIBSIZE]; /* MIB cmd to adjust number of free inodes */ extern int adjnffree[MIBSIZE]; /* MIB cmd to adjust number of free frags */ extern int adjnumclusters[MIBSIZE]; /* MIB cmd adjust number of free clusters */ +extern int adjdepth[MIBSIZE]; /* MIB cmd to adjust directory depth count */ extern int freefiles[MIBSIZE]; /* MIB cmd to free a set of files */ extern int freedirs[MIBSIZE]; /* MIB cmd to free a set of directories */ extern int freeblks[MIBSIZE]; /* MIB cmd to free a set of data blocks */ @@ -339,8 +345,10 @@ extern off_t bflag; /* location of alternate super block */ extern int bkgrdflag; /* use a snapshot to run on an active system */ extern char *blockmap; /* ptr to primary blk allocation map */ extern char *cdevname; /* name of device being checked */ +extern int cgheader_corrupt; /* one or more CG headers are corrupt */ extern char ckclean; /* only do work if not cleanly unmounted */ extern int ckhashadd; /* check hashes to be added */ +extern char *copybuf; /* buffer to copy snapshot blocks */ extern int cvtlevel; /* convert to newer file system format */ extern long dev_bsize; /* computed value of DEV_BSIZE */ extern u_int real_dev_bsize; /* actual disk sector size, not overridden */ @@ -363,10 +371,10 @@ extern char preen; /* just fix normal inconsistencies */ extern char rerun; /* rerun fsck. Only used in non-preen mode */ extern char resolved; /* cleared if unresolved changes => not clean */ extern int returntosingle; /* 1 => return to single user mode on exit */ -extern int sbhashfailed; /* when reading superblock check hash failed */ extern long secsize; /* actual disk sector size */ extern char skipclean; /* skip clean file systems if preening */ -extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ +extern int snapcnt; /* number of active snapshots */ +extern struct inode snaplist[FSMAXSNAP + 1]; /* list of active snapshots */ extern int sujrecovery; /* 1 => doing check using the journal */ extern int surrender; /* Give up if reads fail */ extern char usedsoftdep; /* just fix soft dependency inconsistencies */ @@ -415,6 +423,20 @@ Malloc(size_t size) break; return (retval); } +/* + * Allocate a block of memory to be used as an I/O buffer. + * Ensure that the buffer is aligned to the I/O subsystem requirements. + */ +static inline void* +Balloc(size_t size) +{ + void *retval; + + while ((retval = aligned_alloc(LIBUFS_BUFALIGN, size)) == NULL) + if (flushentry() == 0) + break; + return (retval); +} /* * Wrapper for calloc() that flushes the cylinder group cache to try @@ -436,9 +458,11 @@ struct fstab; void adjust(struct inodesc *, int lcnt); void alarmhandler(int sig); -ufs2_daddr_t allocblk(long frags); +ufs2_daddr_t allocblk(long cg, long frags, ufs2_daddr_t (*checkblkavail) + (ufs2_daddr_t blkno, long frags)); ino_t allocdir(ino_t parent, ino_t request, int mode); ino_t allocino(ino_t request, int type); +void binval(struct bufarea *); void blkerror(ino_t ino, const char *type, ufs2_daddr_t blk); char *blockcheck(char *name); int blread(int fd, char *buf, ufs2_daddr_t blk, long size); @@ -447,18 +471,24 @@ void blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size); void blerase(int fd, ufs2_daddr_t blk, long size); void blzero(int fd, ufs2_daddr_t blk, long size); void brelse(struct bufarea *); -void cacheino(union dinode *dp, ino_t inumber); +struct inoinfo *cacheino(union dinode *dp, ino_t inumber); void catch(int); void catchquit(int); void cgdirty(struct bufarea *); struct bufarea *cglookup(int cg); -int changeino(ino_t dir, const char *name, ino_t newnum); -int check_cgmagic(int cg, struct bufarea *cgbp, int requestrebuild); +int changeino(ino_t dir, const char *name, ino_t newnum, int depth); +void check_blkcnt(struct inode *ip); +int check_cgmagic(int cg, struct bufarea *cgbp); +void rebuild_cg(int cg, struct bufarea *cgbp); +void check_dirdepth(struct inoinfo *inp); +int chkfilesize(mode_t mode, u_int64_t filesize); int chkrange(ufs2_daddr_t blk, int cnt); void ckfini(int markclean); int ckinode(union dinode *dp, struct inodesc *); void clri(struct inodesc *, const char *type, int flag); int clearentry(struct inodesc *); +void copyonwrite(struct fs *, struct bufarea *, + ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long)); void direrror(ino_t ino, const char *errmesg); int dirscan(struct inodesc *); int dofix(struct inodesc *, const char *msg); @@ -469,16 +499,19 @@ int findino(struct inodesc *); int findname(struct inodesc *); void flush(int fd, struct bufarea *bp); int freeblock(struct inodesc *); +void freedirino(ino_t ino, ino_t parent); void freeino(ino_t ino); void freeinodebuf(void); +void fsckinit(void); void fsutilinit(void); int ftypeok(union dinode *dp); void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size); struct bufarea *getdatablk(ufs2_daddr_t blkno, long size, int type); struct inoinfo *getinoinfo(ino_t inumber); -union dinode *getnextinode(ino_t inumber, int rebuildcg); +union dinode *getnextinode(ino_t inumber, int rebuiltcg); void getpathname(char *namebuf, ino_t curdir, ino_t ino); void ginode(ino_t, struct inode *); +void gjournal_check(const char *filesys); void infohandler(int sig); void irelse(struct inode *); ufs2_daddr_t ino_blkatoff(union dinode *, ino_t, ufs_lbn_t, int *, @@ -500,17 +533,23 @@ void pass4(void); void pass5(void); void pfatal(const char *fmt, ...) __printflike(1, 2); void propagate(void); +void prtbuf(struct bufarea *, const char *, ...) __printflike(2, 3); void prtinode(struct inode *); void pwarn(const char *fmt, ...) __printflike(1, 2); -int readsb(int listerr); +int readsb(void); +int removecachedino(ino_t); int reply(const char *question); void rwerror(const char *mesg, ufs2_daddr_t blk); void sblock_init(void); void setinodebuf(int, ino_t); int setup(char *dev); -void gjournal_check(const char *filesys); +int snapblkfree(struct fs *, ufs2_daddr_t, long, ino_t, + ufs2_daddr_t (*)(ufs2_daddr_t, long)); +void snapremove(ino_t); +void snapflush(ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long)); +ufs2_daddr_t std_checkblkavail(ufs2_daddr_t blkno, long frags); +ufs2_daddr_t suj_checkblkavail(ufs2_daddr_t, long); int suj_check(const char *filesys); void update_maps(struct cg *, struct cg*, int); -void fsckinit(void); #endif /* !_FSCK_H_ */ diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8 index 35f3e112007b..1eb9ebeb3b9b 100644 --- a/sbin/fsck_ffs/fsck_ffs.8 +++ b/sbin/fsck_ffs/fsck_ffs.8 @@ -26,10 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)fsck.8 8.4 (Berkeley) 5/9/95 -.\" $FreeBSD$ -.\" -.Dd May 3, 2019 +.Dd November 17, 2023 .Dt FSCK_FFS 8 .Os .Sh NAME @@ -426,6 +423,7 @@ are fully enumerated and explained in Appendix A of .Sh SEE ALSO .Xr fs 5 , .Xr fstab 5 , +.Xr ffs 7 , .Xr fsck 8 , .Xr fsdb 8 , .Xr newfs 8 , diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c index 424f8d9794f6..2acf825b159f 100644 --- a/sbin/fsck_ffs/fsutil.c +++ b/sbin/fsck_ffs/fsutil.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/time.h> #include <sys/types.h> @@ -60,7 +52,6 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <time.h> #include <unistd.h> -#include <libufs.h> #include "fsck.h" @@ -71,7 +62,6 @@ static void cg_write(struct bufarea *); static void slowio_start(void); static void slowio_end(void); static void printIOstats(void); -static void prtbuf(const char *, struct bufarea *); static long diskreads, totaldiskreads, totalreads; /* Disk cache statistics */ static struct timespec startpass, finishpass; @@ -79,11 +69,13 @@ struct timeval slowio_starttime; int slowio_delay_usec = 10000; /* Initial IO delay for background fsck */ int slowio_pollcnt; static struct bufarea cgblk; /* backup buffer for cylinder group blocks */ +static struct bufarea failedbuf; /* returned by failed getdatablk() */ static TAILQ_HEAD(bufqueue, bufarea) bufqueuehd; /* head of buffer cache LRU */ static LIST_HEAD(bufhash, bufarea) bufhashhd[HASHSIZE]; /* buffer hash list */ -static int numbufs; /* size of buffer cache */ -static int cachelookups; /* number of cache lookups */ -static int cachereads; /* number of cache reads */ +static struct bufhash freebufs; /* unused buffers */ +static int numbufs; /* size of buffer cache */ +static int cachelookups; /* number of cache lookups */ +static int cachereads; /* number of cache reads */ static int flushtries; /* number of tries to reclaim memory */ char *buftype[BT_NUMBUFTYPES] = BT_NAMES; @@ -165,11 +157,11 @@ reply(const char *question) struct inostat * inoinfo(ino_t inum) { - static struct inostat unallocated = { USTATE, 0, 0 }; + static struct inostat unallocated = { USTATE, 0, 0, 0 }; struct inostatlist *ilp; int iloff; - if (inum > maxino) + if (inum >= maxino) errx(EEXIT, "inoinfo: inumber %ju out of range", (uintmax_t)inum); ilp = &inostathead[inum / sblock.fs_ipg]; @@ -187,11 +179,15 @@ bufinit(void) { int i; - if ((cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize)) == NULL) + initbarea(&failedbuf, BT_UNKNOWN); + failedbuf.b_errs = -1; + failedbuf.b_un.b_buf = NULL; + if ((cgblk.b_un.b_buf = Balloc((unsigned int)sblock.fs_bsize)) == NULL) errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize); initbarea(&cgblk, BT_CYLGRP); numbufs = cachelookups = cachereads = 0; TAILQ_INIT(&bufqueuehd); + LIST_INIT(&freebufs); for (i = 0; i < HASHSIZE; i++) LIST_INIT(&bufhashhd[i]); for (i = 0; i < BT_NUMBUFTYPES; i++) { @@ -208,7 +204,7 @@ allocbuf(const char *failreason) char *bufp; bp = (struct bufarea *)Malloc(sizeof(struct bufarea)); - bufp = Malloc((unsigned int)sblock.fs_bsize); + bufp = Balloc((unsigned int)sblock.fs_bsize); if (bp == NULL || bufp == NULL) { errx(EEXIT, "%s", failreason); /* NOTREACHED */ @@ -238,7 +234,7 @@ cglookup(int cg) if ((unsigned) cg >= sblock.fs_ncg) errx(EEXIT, "cglookup: out of range cylinder group %d", cg); if (cgbufs == NULL) { - cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea)); + cgbufs = Calloc(sblock.fs_ncg, sizeof(struct bufarea)); if (cgbufs == NULL) errx(EEXIT, "Cannot allocate cylinder group buffers"); } @@ -247,7 +243,7 @@ cglookup(int cg) return (cgbp); cgp = NULL; if (flushtries == 0) - cgp = Malloc((unsigned int)sblock.fs_cgsize); + cgp = Balloc((unsigned int)sblock.fs_cgsize); if (cgp == NULL) { if (sujrecovery) errx(EEXIT,"Ran out of memory during journal recovery"); @@ -300,7 +296,7 @@ flushentry(void) } /* - * Manage a cache of directory blocks. + * Manage a cache of filesystem disk blocks. */ struct bufarea * getdatablk(ufs2_daddr_t blkno, long size, int type) @@ -309,19 +305,25 @@ getdatablk(ufs2_daddr_t blkno, long size, int type) struct bufhash *bhdp; cachelookups++; - /* If out of range, return empty buffer with b_err == -1 */ + /* + * If out of range, return empty buffer with b_err == -1 + * + * Skip check for inodes because chkrange() considers + * metadata areas invalid to write data. + */ if (type != BT_INODES && chkrange(blkno, size / sblock.fs_fsize)) { - blkno = -1; - type = BT_EMPTY; + failedbuf.b_refcnt++; + return (&failedbuf); } bhdp = &bufhashhd[HASH(blkno)]; LIST_FOREACH(bp, bhdp, b_hash) if (bp->b_bno == fsbtodb(&sblock, blkno)) { if (debug && bp->b_size != size) { - prtbuf("getdatablk: size mismatch", bp); + prtbuf(bp, "getdatablk: size mismatch"); pfatal("getdatablk: b_size %d != size %ld\n", bp->b_size, size); } + TAILQ_REMOVE(&bufqueuehd, bp, b_list); goto foundit; } /* @@ -340,7 +342,9 @@ getdatablk(ufs2_daddr_t blkno, long size, int type) if (size > sblock.fs_bsize) errx(EEXIT, "Excessive buffer size %ld > %d\n", size, sblock.fs_bsize); - if (numbufs < MINBUFS) { + if ((bp = LIST_FIRST(&freebufs)) != NULL) { + LIST_REMOVE(bp, b_hash); + } else if (numbufs < MINBUFS) { bp = allocbuf("cannot create minimal buffer pool"); } else if (sujrecovery) { /* @@ -368,6 +372,7 @@ getdatablk(ufs2_daddr_t blkno, long size, int type) else LIST_REMOVE(bp, b_hash); } + TAILQ_REMOVE(&bufqueuehd, bp, b_list); flush(fswritefd, bp); bp->b_type = type; LIST_INSERT_HEAD(bhdp, bp, b_hash); @@ -375,13 +380,12 @@ getdatablk(ufs2_daddr_t blkno, long size, int type) cachereads++; /* fall through */ foundit: + TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); if (debug && bp->b_type != type) { printf("getdatablk: buffer type changed to %s", BT_BUFTYPE(type)); - prtbuf("", bp); + prtbuf(bp, ""); } - TAILQ_REMOVE(&bufqueuehd, bp, b_list); - TAILQ_INSERT_HEAD(&bufqueuehd, bp, b_list); if (bp->b_errs == 0) bp->b_refcnt++; return (bp); @@ -401,11 +405,7 @@ getblk(struct bufarea *bp, ufs2_daddr_t blk, long size) readcnt[bp->b_type]++; clock_gettime(CLOCK_REALTIME_PRECISE, &start); } - if (bp->b_type != BT_EMPTY) - bp->b_errs = - blread(fsreadfd, bp->b_un.b_buf, dblk, size); - else - bp->b_errs = -1; + bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, dblk, size); if (debug) { clock_gettime(CLOCK_REALTIME_PRECISE, &finish); timespecsub(&finish, &start, &finish); @@ -422,11 +422,20 @@ brelse(struct bufarea *bp) { if (bp->b_refcnt <= 0) - prtbuf("brelse: buffer with negative reference count", bp); + prtbuf(bp, "brelse: buffer with negative reference count"); bp->b_refcnt--; } void +binval(struct bufarea *bp) +{ + + bp->b_flags &= ~B_DIRTY; + LIST_REMOVE(bp, b_hash); + LIST_INSERT_HEAD(&freebufs, bp, b_hash); +} + +void flush(int fd, struct bufarea *bp) { struct inode ip; @@ -451,10 +460,18 @@ flush(int fd, struct bufarea *bp) if (bp != &sblk) pfatal("BUFFER %p DOES NOT MATCH SBLK %p\n", bp, &sblk); + /* + * Superblocks are always pre-copied so we do not need + * to check them for copy-on-write. + */ if (sbput(fd, bp->b_un.b_fs, 0) == 0) fsmodified = 1; break; case BT_CYLGRP: + /* + * Cylinder groups are always pre-copied so we do not + * need to check them for copy-on-write. + */ if (sujrecovery) cg_write(bp); if (cgput(fswritefd, &sblock, bp->b_un.b_cg) == 0) @@ -465,13 +482,13 @@ flush(int fd, struct bufarea *bp) struct ufs2_dinode *dp = bp->b_un.b_dinode2; int i; - for (i = 0; i < INOPB(&sblock); dp++, i++) { + for (i = 0; i < bp->b_size; dp++, i += sizeof(*dp)) { if (ffs_verify_dinode_ckhash(&sblock, dp) == 0) continue; pwarn("flush: INODE CHECK-HASH FAILED"); ip.i_bp = bp; ip.i_dp = (union dinode *)dp; - ip.i_number = bp->b_index + i; + ip.i_number = bp->b_index + (i / sizeof(*dp)); prtinode(&ip); if (preen || reply("FIX") != 0) { if (preen) @@ -483,12 +500,39 @@ flush(int fd, struct bufarea *bp) } /* FALLTHROUGH */ default: + copyonwrite(&sblock, bp, std_checkblkavail); blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size); break; } } /* + * If there are any snapshots, ensure that all the blocks that they + * care about have been copied, then release the snapshot inodes. + * These operations need to be done before we rebuild the cylinder + * groups so that any block allocations are properly recorded. + * Since all the cylinder group maps have already been copied in + * the snapshots, no further snapshot copies will need to be done. + */ +void +snapflush(ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long)) +{ + struct bufarea *bp; + int cnt; + + if (snapcnt > 0) { + if (debug) + printf("Check for snapshot copies\n"); + TAILQ_FOREACH_REVERSE(bp, &bufqueuehd, bufqueue, b_list) + if ((bp->b_flags & B_DIRTY) != 0) + copyonwrite(&sblock, bp, checkblkavail); + for (cnt = 0; cnt < snapcnt; cnt++) + irelse(&snaplist[cnt]); + snapcnt = 0; + } +} + +/* * Journaled soft updates does not maintain cylinder group summary * information during cleanup, so this routine recalculates the summary * information and updates the superblock summary in preparation for @@ -499,6 +543,7 @@ cg_write(struct bufarea *bp) { ufs1_daddr_t fragno, cgbno, maxbno; u_int8_t *blksfree; + struct csum *csp; struct cg *cgp; int blk; int i; @@ -536,6 +581,11 @@ cg_write(struct bufarea *bp) * Update the superblock cg summary from our now correct values * before writing the block. */ + csp = &sblock.fs_cs(&sblock, cgp->cg_cgx); + sblock.fs_cstotal.cs_ndir += cgp->cg_cs.cs_ndir - csp->cs_ndir; + sblock.fs_cstotal.cs_nbfree += cgp->cg_cs.cs_nbfree - csp->cs_nbfree; + sblock.fs_cstotal.cs_nifree += cgp->cg_cs.cs_nifree - csp->cs_nifree; + sblock.fs_cstotal.cs_nffree += cgp->cg_cs.cs_nffree - csp->cs_nffree; sblock.fs_cs(&sblock, cgp->cg_cgx) = cgp->cg_cs; } @@ -556,11 +606,9 @@ void ckfini(int markclean) { struct bufarea *bp, *nbp; - struct inoinfo *inp, *ninp; - int ofsmodified, cnt, cg, i; + int ofsmodified, cnt, cg; if (bkgrdflag) { - unlink(snapname); if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) { cmd.value = FS_UNCLEAN; cmd.size = markclean ? -1 : 1; @@ -587,6 +635,7 @@ ckfini(int markclean) (void)close(fsreadfd); return; } + /* * To remain idempotent with partial truncations the buffers * must be flushed in this order: @@ -629,14 +678,9 @@ ckfini(int markclean) case BT_SUPERBLK: case BT_CYLGRP: default: - prtbuf("ckfini: improper buffer type on cache list",bp); + prtbuf(bp,"ckfini: improper buffer type on cache list"); continue; /* These are the ones to flush in this step */ - case BT_EMPTY: - if (bp->b_bno >= 0) - pfatal("Unused BT_EMPTY buffer for block %jd\n", - (intmax_t)bp->b_bno); - /* FALLTHROUGH */ case BT_LEVEL1: case BT_LEVEL2: case BT_LEVEL3: @@ -648,11 +692,10 @@ ckfini(int markclean) case BT_INODES: continue; } - if (debug && bp->b_refcnt != 0) { - prtbuf("ckfini: clearing in-use buffer", bp); - pfatal("ckfini: clearing in-use buffer\n"); - } + if (debug && bp->b_refcnt != 0) + prtbuf(bp, "ckfini: clearing in-use buffer"); TAILQ_REMOVE(&bufqueuehd, bp, b_list); + LIST_REMOVE(bp, b_hash); cnt++; flush(fswritefd, bp); free(bp->b_un.b_buf); @@ -666,11 +709,10 @@ ckfini(int markclean) icachebp = NULL; } TAILQ_FOREACH_REVERSE_SAFE(bp, &bufqueuehd, bufqueue, b_list, nbp) { - if (debug && bp->b_refcnt != 0) { - prtbuf("ckfini: clearing in-use buffer", bp); - pfatal("ckfini: clearing in-use buffer\n"); - } + if (debug && bp->b_refcnt != 0) + prtbuf(bp, "ckfini: clearing in-use buffer"); TAILQ_REMOVE(&bufqueuehd, bp, b_list); + LIST_REMOVE(bp, b_hash); cnt++; flush(fswritefd, bp); free(bp->b_un.b_buf); @@ -682,14 +724,17 @@ ckfini(int markclean) if (debug) printf("Flush the superblock\n"); flush(fswritefd, &sblk); - if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC && - sblk.b_bno != sblock.fs_sblockloc / dev_bsize && - !preen && reply("UPDATE STANDARD SUPERBLOCK")) { - /* Change the write destination to standard superblock */ - sblock.fs_sblockactualloc = sblock.fs_sblockloc; - sblk.b_bno = sblock.fs_sblockloc / dev_bsize; - sbdirty(); - flush(fswritefd, &sblk); + if (havesb && cursnapshot == 0 && + sblk.b_bno != sblock.fs_sblockloc / dev_bsize) { + if (preen || reply("UPDATE STANDARD SUPERBLOCK")) { + /* Change write destination to standard superblock */ + sblock.fs_sblockactualloc = sblock.fs_sblockloc; + sblk.b_bno = sblock.fs_sblockloc / dev_bsize; + sbdirty(); + flush(fswritefd, &sblk); + } else { + markclean = 0; + } } if (cursnapshot == 0 && sblock.fs_clean != markclean) { if ((sblock.fs_clean = markclean) != 0) { @@ -728,19 +773,7 @@ ckfini(int markclean) free(inostathead); } inostathead = NULL; - if (inpsort != NULL) - free(inpsort); - inpsort = NULL; - if (inphead != NULL) { - for (i = 0; i < dirhash; i++) { - for (inp = inphead[i]; inp != NULL; inp = ninp) { - ninp = inp->i_nexthash; - free(inp); - } - } - free(inphead); - } - inphead = NULL; + inocleanup(); finalIOstats(); (void)close(fsreadfd); (void)close(fswritefd); @@ -926,7 +959,7 @@ blzero(int fd, ufs2_daddr_t blk, long size) if (fd < 0) return; if (zero == NULL) { - zero = calloc(ZEROBUFSIZE, 1); + zero = Balloc(ZEROBUFSIZE); if (zero == NULL) errx(EEXIT, "cannot allocate buffer pool"); } @@ -945,13 +978,26 @@ blzero(int fd, ufs2_daddr_t blk, long size) /* * Verify cylinder group's magic number and other parameters. If the * test fails, offer an option to rebuild the whole cylinder group. + * + * Return 1 if the cylinder group is good or return 0 if it is bad. */ +#undef CHK +#define CHK(lhs, op, rhs, fmt) \ + if (lhs op rhs) { \ + pwarn("UFS%d cylinder group %d failed: " \ + "%s (" #fmt ") %s %s (" #fmt ")\n", \ + sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, cg, \ + #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs); \ + error = 1; \ + } int -check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) +check_cgmagic(int cg, struct bufarea *cgbp) { struct cg *cgp = cgbp->b_un.b_cg; uint32_t cghash, calchash; static int prevfailcg = -1; + long start; + int error; /* * Extended cylinder group checks. @@ -964,32 +1010,69 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) calchash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); cgp->cg_ckhash = cghash; } - if (cgp->cg_ckhash == calchash && - cg_chkmagic(cgp) && - cgp->cg_cgx == cg && - ((sblock.fs_magic == FS_UFS1_MAGIC && - cgp->cg_old_niblk == sblock.fs_ipg && - cgp->cg_ndblk <= sblock.fs_fpg && - cgp->cg_old_ncyl <= sblock.fs_old_cpg) || - (sblock.fs_magic == FS_UFS2_MAGIC && - cgp->cg_niblk == sblock.fs_ipg && - cgp->cg_ndblk <= sblock.fs_fpg && - cgp->cg_initediblk <= sblock.fs_ipg))) { - return (1); + error = 0; + CHK(cgp->cg_ckhash, !=, calchash, "%jd"); + CHK(cg_chkmagic(cgp), ==, 0, "%jd"); + CHK(cgp->cg_cgx, !=, cg, "%jd"); + CHK(cgp->cg_ndblk, >, sblock.fs_fpg, "%jd"); + if (sblock.fs_magic == FS_UFS1_MAGIC) { + CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd"); + } else if (sblock.fs_magic == FS_UFS2_MAGIC) { + CHK(cgp->cg_niblk, !=, sblock.fs_ipg, "%jd"); + CHK(cgp->cg_initediblk, >, sblock.fs_ipg, "%jd"); + } + if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size) { + CHK(cgp->cg_ndblk, !=, sblock.fs_fpg, "%jd"); + } else { + CHK(cgp->cg_ndblk, !=, sblock.fs_size - cgbase(&sblock, cg), + "%jd"); + } + start = sizeof(*cgp); + if (sblock.fs_magic == FS_UFS2_MAGIC) { + CHK(cgp->cg_iusedoff, !=, start, "%jd"); + } else if (sblock.fs_magic == FS_UFS1_MAGIC) { + CHK(cgp->cg_niblk, !=, 0, "%jd"); + CHK(cgp->cg_initediblk, !=, 0, "%jd"); + CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd"); + CHK(cgp->cg_old_btotoff, !=, start, "%jd"); + CHK(cgp->cg_old_boff, !=, cgp->cg_old_btotoff + + sblock.fs_old_cpg * sizeof(int32_t), "%jd"); + CHK(cgp->cg_iusedoff, !=, cgp->cg_old_boff + + sblock.fs_old_cpg * sizeof(u_int16_t), "%jd"); } + CHK(cgp->cg_freeoff, !=, + cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT), "%jd"); + if (sblock.fs_contigsumsize == 0) { + CHK(cgp->cg_nextfreeoff, !=, + cgp->cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT), "%jd"); + } else { + CHK(cgp->cg_nclusterblks, !=, cgp->cg_ndblk / sblock.fs_frag, + "%jd"); + CHK(cgp->cg_clustersumoff, !=, + roundup(cgp->cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT), + sizeof(u_int32_t)) - sizeof(u_int32_t), "%jd"); + CHK(cgp->cg_clusteroff, !=, cgp->cg_clustersumoff + + (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t), "%jd"); + CHK(cgp->cg_nextfreeoff, !=, cgp->cg_clusteroff + + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT), + "%jd"); + } + if (error == 0) + return (1); if (prevfailcg == cg) return (0); prevfailcg = cg; pfatal("CYLINDER GROUP %d: INTEGRITY CHECK FAILED", cg); - if (!request_rebuild) { - printf("\n"); - return (0); - } - if (!reply("REBUILD CYLINDER GROUP")) { - printf("YOU WILL NEED TO RERUN FSCK.\n"); - rerun = 1; - return (1); - } + printf("\n"); + return (0); +} + +void +rebuild_cg(int cg, struct bufarea *cgbp) +{ + struct cg *cgp = cgbp->b_un.b_cg; + long start; + /* * Zero out the cylinder group and then initialize critical fields. * Bit maps and summaries will be recalculated by later passes. @@ -1003,13 +1086,15 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) cgp->cg_ndblk = sblock.fs_fpg; else cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg); - cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield); - if (sblock.fs_magic == FS_UFS1_MAGIC) { + start = sizeof(*cgp); + if (sblock.fs_magic == FS_UFS2_MAGIC) { + cgp->cg_iusedoff = start; + } else if (sblock.fs_magic == FS_UFS1_MAGIC) { cgp->cg_niblk = 0; cgp->cg_initediblk = 0; cgp->cg_old_ncyl = sblock.fs_old_cpg; cgp->cg_old_niblk = sblock.fs_ipg; - cgp->cg_old_btotoff = cgp->cg_iusedoff; + cgp->cg_old_btotoff = start; cgp->cg_old_boff = cgp->cg_old_btotoff + sblock.fs_old_cpg * sizeof(int32_t); cgp->cg_iusedoff = cgp->cg_old_boff + @@ -1027,58 +1112,115 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild) cgp->cg_nextfreeoff = cgp->cg_clusteroff + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); } + cgp->cg_ckhash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); cgdirty(cgbp); - return (0); } /* * allocate a data block with the specified number of fragments */ ufs2_daddr_t -allocblk(long frags) +allocblk(long startcg, long frags, + ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags)) +{ + ufs2_daddr_t blkno, newblk; + + if (sujrecovery && checkblkavail == std_checkblkavail) { + pfatal("allocblk: std_checkblkavail used for SUJ recovery\n"); + return (0); + } + if (frags <= 0 || frags > sblock.fs_frag) + return (0); + for (blkno = MAX(cgdata(&sblock, startcg), 0); + blkno < maxfsblock - sblock.fs_frag; + blkno += sblock.fs_frag) { + if ((newblk = (*checkblkavail)(blkno, frags)) == 0) + continue; + if (newblk > 0) + return (newblk); + if (newblk < 0) + blkno = -newblk; + } + for (blkno = MAX(cgdata(&sblock, 0), 0); + blkno < cgbase(&sblock, startcg) - sblock.fs_frag; + blkno += sblock.fs_frag) { + if ((newblk = (*checkblkavail)(blkno, frags)) == 0) + continue; + if (newblk > 0) + return (newblk); + if (newblk < 0) + blkno = -newblk; + } + return (0); +} + +ufs2_daddr_t +std_checkblkavail(ufs2_daddr_t blkno, long frags) { - int i, j, k, cg, baseblk; struct bufarea *cgbp; struct cg *cgp; + ufs2_daddr_t j, k, baseblk; + long cg; - if (frags <= 0 || frags > sblock.fs_frag) + if ((u_int64_t)blkno > sblock.fs_size) return (0); - for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { - for (j = 0; j <= sblock.fs_frag - frags; j++) { - if (testbmap(i + j)) - continue; - for (k = 1; k < frags; k++) - if (testbmap(i + j + k)) - break; - if (k < frags) { - j += k; - continue; - } - cg = dtog(&sblock, i + j); - cgbp = cglookup(cg); - cgp = cgbp->b_un.b_cg; - if (!check_cgmagic(cg, cgbp, 0)) { - i = (cg + 1) * sblock.fs_fpg - sblock.fs_frag; - continue; - } - baseblk = dtogd(&sblock, i + j); - for (k = 0; k < frags; k++) { - setbmap(i + j + k); - clrbit(cg_blksfree(cgp), baseblk + k); - } - n_blks += frags; - if (frags == sblock.fs_frag) - cgp->cg_cs.cs_nbfree--; - else - cgp->cg_cs.cs_nffree -= frags; - cgdirty(cgbp); - return (i + j); + for (j = 0; j <= sblock.fs_frag - frags; j++) { + if (testbmap(blkno + j)) + continue; + for (k = 1; k < frags; k++) + if (testbmap(blkno + j + k)) + break; + if (k < frags) { + j += k; + continue; + } + cg = dtog(&sblock, blkno + j); + cgbp = cglookup(cg); + cgp = cgbp->b_un.b_cg; + if (!check_cgmagic(cg, cgbp)) + return (-((cg + 1) * sblock.fs_fpg - sblock.fs_frag)); + baseblk = dtogd(&sblock, blkno + j); + for (k = 0; k < frags; k++) { + setbmap(blkno + j + k); + clrbit(cg_blksfree(cgp), baseblk + k); } + n_blks += frags; + if (frags == sblock.fs_frag) + cgp->cg_cs.cs_nbfree--; + else + cgp->cg_cs.cs_nffree -= frags; + cgdirty(cgbp); + return (blkno + j); } return (0); } /* + * Check whether a file size is within the limits for the filesystem. + * Return 1 when valid and 0 when too big. + * + * This should match the file size limit in ffs_mountfs(). + */ +int +chkfilesize(mode_t mode, u_int64_t filesize) +{ + u_int64_t kernmaxfilesize; + + if (sblock.fs_magic == FS_UFS1_MAGIC) + kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1; + else + kernmaxfilesize = sblock.fs_maxfilesize; + if (filesize > kernmaxfilesize || + filesize > sblock.fs_maxfilesize || + (mode == IFDIR && filesize > MAXDIRSIZE)) { + if (debug) + printf("bad file size %ju:", (uintmax_t)filesize); + return (0); + } + return (1); +} + +/* * Slow down IO so as to leave some disk bandwidth for other processes */ void @@ -1152,9 +1294,11 @@ getpathname(char *namebuf, ino_t curdir, ino_t ino) ginode(ino, &ip); if ((ckinode(ip.i_dp, &idesc) & FOUND) == 0) { irelse(&ip); + free(idesc.id_name); break; } irelse(&ip); + free(idesc.id_name); namelookup: idesc.id_number = idesc.id_parent; idesc.id_parent = ino; @@ -1246,12 +1390,17 @@ dofix(struct inodesc *idesc, const char *msg) /* * Print details about a buffer. */ -static void -prtbuf(const char *msg, struct bufarea *bp) +void +prtbuf(struct bufarea *bp, const char *fmt, ...) { - - printf("%s: bp %p, type %s, bno %jd, size %d, refcnt %d, flags %s, " - "index %jd\n", msg, bp, BT_BUFTYPE(bp->b_type), (intmax_t) bp->b_bno, + va_list ap; + va_start(ap, fmt); + if (preen) + (void)fprintf(stdout, "%s: ", cdevname); + (void)vfprintf(stdout, fmt, ap); + va_end(ap); + printf(": bp %p, type %s, bno %jd, size %d, refcnt %d, flags %s, " + "index %jd\n", bp, BT_BUFTYPE(bp->b_type), (intmax_t) bp->b_bno, bp->b_size, bp->b_refcnt, bp->b_flags & B_DIRTY ? "dirty" : "clean", (intmax_t) bp->b_index); } diff --git a/sbin/fsck_ffs/gjournal.c b/sbin/fsck_ffs/gjournal.c index b65589b5bd08..b94f27758315 100644 --- a/sbin/fsck_ffs/gjournal.c +++ b/sbin/fsck_ffs/gjournal.c @@ -1,5 +1,5 @@ /*- - * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause-FreeBSD + * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause * * Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org> * All rights reserved. @@ -54,8 +54,6 @@ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <string.h> #include <sys/stat.h> #include <ufs/ffs/fs.h> @@ -77,7 +75,6 @@ gjournal_check(const char *filesys) fs = &sblock; /* Are there any unreferenced inodes in this file system? */ if (fs->fs_unrefs == 0) { - //printf("No unreferenced inodes.\n"); sbdirty(); ckfini(1); return; @@ -97,7 +94,7 @@ gjournal_check(const char *filesys) } cgbp = cglookup(cg); cgp = cgbp->b_un.b_cg; - if (!check_cgmagic(cg, cgbp, 0)) { + if (!check_cgmagic(cg, cgbp)) { rerun = 1; ckfini(0); return; @@ -105,7 +102,6 @@ gjournal_check(const char *filesys) /* Are there any unreferenced inodes in this cylinder group? */ if (cgp->cg_unrefs == 0) continue; - //printf("Analizing cylinder group %d (count=%d)\n", cg, cgp->cg_unrefs); /* * Now go through the list of all inodes in this cylinder group * to find unreferenced ones. @@ -150,20 +146,15 @@ gjournal_check(const char *filesys) inodirty(&ip); irelse(&ip); cgdirty(cgbp); - if (cgp->cg_unrefs == 0) { - //printf("No more unreferenced inodes in cg=%d.\n", cg); + if (cgp->cg_unrefs == 0) break; - } } /* - * If there are no more unreferenced inodes, there is no need to - * check other cylinder groups. + * If there are no more unreferenced inodes, there is no + * need to check other cylinder groups. */ - if (fs->fs_unrefs == 0) { - //printf("No more unreferenced inodes (cg=%d/%d).\n", cg, - // fs->fs_ncg); + if (fs->fs_unrefs == 0) break; - } } /* Write back updated statistics and super-block. */ sbdirty(); diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c index 09dbcc6694a2..1f04311e2e5c 100644 --- a/sbin/fsck_ffs/globs.c +++ b/sbin/fsck_ffs/globs.c @@ -29,20 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <ufs/ufs/dinode.h> #include <ufs/ffs/fs.h> @@ -60,19 +46,19 @@ ino_t cursnapshot; long dirhash, inplast; unsigned long numdirs, listmax; long countdirs; /* number of directories we actually found */ -int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */ -int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */ -int setsize[MIBSIZE]; /* MIB command to set inode size */ -int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */ -int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */ -int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */ -int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */ -int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */ -int freefiles[MIBSIZE]; /* MIB command to free a set of files */ -int freedirs[MIBSIZE]; /* MIB command to free a set of directories */ -int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */ +int adjrefcnt[MIBSIZE]; /* MIB cmd to adjust inode reference cnt */ +int adjblkcnt[MIBSIZE]; /* MIB cmd to adjust inode block count */ +int setsize[MIBSIZE]; /* MIB cmd to set inode size */ +int adjndir[MIBSIZE]; /* MIB cmd to adjust number of directories */ +int adjnbfree[MIBSIZE]; /* MIB cmd to adjust number of free blocks */ +int adjnifree[MIBSIZE]; /* MIB cmd to adjust number of free inodes */ +int adjnffree[MIBSIZE]; /* MIB cmd to adjust number of free frags */ +int adjnumclusters[MIBSIZE]; /* MIB cmd to adjust number of free clusters */ +int adjdepth[MIBSIZE]; /* MIB cmd to adjust directory depth count */ +int freefiles[MIBSIZE]; /* MIB cmd to free a set of files */ +int freedirs[MIBSIZE]; /* MIB cmd to free a set of directories */ +int freeblks[MIBSIZE]; /* MIB cmd to free a set of data blocks */ struct fsck_cmd cmd; /* sysctl file system update commands */ -char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ char *cdevname; /* name of device being checked */ long dev_bsize; /* computed value of DEV_BSIZE */ long secsize; /* actual disk sector size */ @@ -90,13 +76,12 @@ char ckclean; /* only do work if not cleanly unmounted */ int cvtlevel; /* convert to newer file system format */ int ckhashadd; /* check hashes to be added */ int bkgrdcheck; /* determine if background check is possible */ -int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */ +int bkgrdsumadj; /* kernel able to adjust superblock summary */ char usedsoftdep; /* just fix soft dependency inconsistencies */ char preen; /* just fix normal inconsistencies */ char rerun; /* rerun fsck. Only used in non-preen mode */ int returntosingle; /* 1 => return to single user mode on exit */ char resolved; /* cleared if unresolved changes => not clean */ -int sbhashfailed; /* when reading superblock check hash failed */ char havesb; /* superblock has been read */ char skipclean; /* skip clean file systems if preening */ int fsmodified; /* 1 => write done to file system */ @@ -111,6 +96,7 @@ ino_t lfdir; /* lost & found directory inode number */ const char *lfname; /* lost & found directory name */ int lfmode; /* lost & found directory creation mode */ ufs2_daddr_t n_blks; /* number of blocks in use */ +int cgheader_corrupt; /* one or more CG headers are corrupt */ ino_t n_files; /* number of files in use */ volatile sig_atomic_t got_siginfo; /* received a SIGINFO */ volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */ @@ -140,11 +126,11 @@ fsckinit(void) bzero(adjnifree, sizeof(int) * MIBSIZE); bzero(adjnffree, sizeof(int) * MIBSIZE); bzero(adjnumclusters, sizeof(int) * MIBSIZE); + bzero(adjdepth, sizeof(int) * MIBSIZE); bzero(freefiles, sizeof(int) * MIBSIZE); bzero(freedirs, sizeof(int) * MIBSIZE); bzero(freeblks, sizeof(int) * MIBSIZE); bzero(&cmd, sizeof(struct fsck_cmd)); - bzero(snapname, sizeof(char) * BUFSIZ); cdevname = NULL; dev_bsize = 0; secsize = 0; @@ -156,7 +142,6 @@ fsckinit(void) resolved = 0; havesb = 0; fsmodified = 0; - sbhashfailed = 0; fsreadfd = -1; fswritefd = -1; maxfsblock = 0; @@ -166,6 +151,7 @@ fsckinit(void) lfmode = 0700; n_blks = 0; n_files = 0; + cgheader_corrupt = 0; got_siginfo = 0; got_sigalarm = 0; bzero(&zino.dp1, sizeof(struct ufs1_dinode)); diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c index 47f72c84a1f2..5b004cd29c90 100644 --- a/sbin/fsck_ffs/inode.c +++ b/sbin/fsck_ffs/inode.c @@ -29,15 +29,8 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)inode.c 8.8 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> +#include <sys/stat.h> #include <sys/stdint.h> #include <sys/sysctl.h> @@ -49,7 +42,6 @@ __FBSDID("$FreeBSD$"); #include <pwd.h> #include <string.h> #include <time.h> -#include <libufs.h> #include "fsck.h" @@ -58,6 +50,9 @@ struct bufarea *icachebp; /* inode cache buffer */ static int iblock(struct inodesc *, off_t isize, int type); static ufs2_daddr_t indir_blkatoff(ufs2_daddr_t, ino_t, ufs_lbn_t, ufs_lbn_t, struct bufarea **); +static int snapclean(struct inodesc *idesc); +static void chkcopyonwrite(struct fs *, ufs2_daddr_t, + ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t, long)); int ckinode(union dinode *dp, struct inodesc *idesc) @@ -86,6 +81,10 @@ ckinode(union dinode *dp, struct inodesc *idesc) dino.dp1 = dp->dp1; else dino.dp2 = dp->dp2; + if (DIP(&dino, di_size) < 0) { + pfatal("NEGATIVE INODE SIZE %jd\n", DIP(&dino, di_size)); + return (STOP); + } ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize); for (i = 0; i < UFS_NDADDR; i++) { idesc->id_lbn++; @@ -112,6 +111,7 @@ ckinode(union dinode *dp, struct inodesc *idesc) inodirty(&ip); irelse(&ip); } + return (STOP); } continue; } @@ -377,9 +377,13 @@ chkrange(ufs2_daddr_t blk, int cnt) { int c; - if (cnt <= 0 || blk <= 0 || blk > maxfsblock || - cnt - 1 > maxfsblock - blk) + if (cnt <= 0 || blk <= 0 || blk >= maxfsblock || + cnt > maxfsblock - blk) { + if (debug) + printf("out of range: blk %ld, offset %i, size %d\n", + (long)blk, (int)fragnum(&sblock, blk), cnt); return (1); + } if (cnt > sblock.fs_frag || fragnum(&sblock, blk) + cnt > sblock.fs_frag) { if (debug) @@ -415,21 +419,33 @@ chkrange(ufs2_daddr_t blk, int cnt) /* * General purpose interface for reading inodes. + * + * firstinum and lastinum track contents of getnextino() cache (below). */ +static ino_t firstinum, lastinum; +static struct bufarea inobuf; + void ginode(ino_t inumber, struct inode *ip) { ufs2_daddr_t iblk; + struct ufs2_dinode *dp; - if (inumber < UFS_ROOTINO || inumber > maxino) + if (inumber < UFS_ROOTINO || inumber >= maxino) errx(EEXIT, "bad inode number %ju to ginode", (uintmax_t)inumber); ip->i_number = inumber; - if (icachebp != NULL && + if (inumber >= firstinum && inumber < lastinum) { + /* contents in getnextino() cache */ + ip->i_bp = &inobuf; + inobuf.b_refcnt++; + inobuf.b_index = firstinum; + } else if (icachebp != NULL && inumber >= icachebp->b_index && inumber < icachebp->b_index + INOPB(&sblock)) { /* take an additional reference for the returned inode */ icachebp->b_refcnt++; + ip->i_bp = icachebp; } else { iblk = ino_to_fsba(&sblock, inumber); /* release our cache-hold reference on old icachebp */ @@ -445,23 +461,24 @@ ginode(ino_t inumber, struct inode *ip) /* take a cache-hold reference on new icachebp */ icachebp->b_refcnt++; icachebp->b_index = rounddown(inumber, INOPB(&sblock)); + ip->i_bp = icachebp; } - ip->i_bp = icachebp; if (sblock.fs_magic == FS_UFS1_MAGIC) { ip->i_dp = (union dinode *) - &icachebp->b_un.b_dinode1[inumber % INOPB(&sblock)]; + &ip->i_bp->b_un.b_dinode1[inumber - ip->i_bp->b_index]; return; } ip->i_dp = (union dinode *) - &icachebp->b_un.b_dinode2[inumber % INOPB(&sblock)]; - if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)ip->i_dp)) { + &ip->i_bp->b_un.b_dinode2[inumber - ip->i_bp->b_index]; + dp = (struct ufs2_dinode *)ip->i_dp; + /* Do not check hash of inodes being created */ + if (dp->di_mode != 0 && ffs_verify_dinode_ckhash(&sblock, dp)) { pwarn("INODE CHECK-HASH FAILED"); prtinode(ip); if (preen || reply("FIX") != 0) { if (preen) printf(" (FIXED)\n"); - ffs_update_dinode_ckhash(&sblock, - (struct ufs2_dinode *)ip->i_dp); + ffs_update_dinode_ckhash(&sblock, dp); inodirty(ip); } } @@ -474,6 +491,14 @@ void irelse(struct inode *ip) { + /* Check for failed inode read */ + if (ip->i_bp == NULL) + return; + if (debug && sblock.fs_magic == FS_UFS2_MAGIC && + ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)ip->i_dp)) { + pwarn("irelse: releasing inode with bad check-hash"); + prtinode(ip); + } if (ip->i_bp->b_refcnt <= 0) pfatal("irelse: releasing unreferenced ino %ju\n", (uintmax_t) ip->i_number); @@ -484,12 +509,11 @@ irelse(struct inode *ip) * Special purpose version of ginode used to optimize first pass * over all the inodes in numerical order. */ -static ino_t nextino, lastinum, lastvalidinum; +static ino_t nextinum, lastvalidinum; static long readcount, readpercg, fullcnt, inobufsize, partialcnt, partialsize; -static struct bufarea inobuf; union dinode * -getnextinode(ino_t inumber, int rebuildcg) +getnextinode(ino_t inumber, int rebuiltcg) { int j; long size; @@ -499,11 +523,12 @@ getnextinode(ino_t inumber, int rebuildcg) struct inode ip; static caddr_t nextinop; - if (inumber != nextino++ || inumber > lastvalidinum) + if (inumber != nextinum++ || inumber > lastvalidinum) errx(EEXIT, "bad inode number %ju to nextinode", (uintmax_t)inumber); if (inumber >= lastinum) { readcount++; + firstinum = lastinum; blk = ino_to_fsba(&sblock, lastinum); if (readcount % readpercg == 0) { size = partialsize; @@ -517,6 +542,9 @@ getnextinode(ino_t inumber, int rebuildcg) * If getblk encounters an error, it will already have zeroed * out the buffer, so we do not need to do so here. */ + if (inobuf.b_refcnt != 0) + pfatal("Non-zero getnextinode() ref count %d\n", + inobuf.b_refcnt); flush(fswritefd, &inobuf); getblk(&inobuf, blk, size); nextinop = inobuf.b_un.b_buf; @@ -544,7 +572,7 @@ getnextinode(ino_t inumber, int rebuildcg) dirty(&inobuf); } } - if (rebuildcg && (char *)dp == inobuf.b_un.b_buf) { + if (rebuiltcg && (char *)dp == inobuf.b_un.b_buf) { /* * Try to determine if we have reached the end of the * allocated inodes. @@ -601,7 +629,7 @@ setinodebuf(int cg, ino_t inosused) inum = cg * sblock.fs_ipg; lastvalidinum = inum + inosused - 1; - nextino = inum; + nextinum = inum; lastinum = inum; readcount = 0; /* Flush old contents in case they have been updated */ @@ -611,7 +639,7 @@ setinodebuf(int cg, ino_t inosused) inobufsize = blkroundup(&sblock, MAX(INOBUFSIZE, sblock.fs_bsize)); initbarea(&inobuf, BT_INODES); - if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL) + if ((inobuf.b_un.b_buf = Balloc((unsigned)inobufsize)) == NULL) errx(EEXIT, "cannot allocate space for inode buffer"); } fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? @@ -633,14 +661,23 @@ int freeblock(struct inodesc *idesc) { struct dups *dlp; + struct bufarea *cgbp; + struct cg *cgp; ufs2_daddr_t blkno; - long nfrags, res; + long size, nfrags; - res = KEEPON; blkno = idesc->id_blkno; + if (idesc->id_type == SNAP) { + pfatal("clearing a snapshot dinode\n"); + return (STOP); + } + size = lfragtosize(&sblock, idesc->id_numfrags); + if (snapblkfree(&sblock, blkno, size, idesc->id_number, + std_checkblkavail)) + return (KEEPON); for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { if (chkrange(blkno, 1)) { - res = SKIP; + return (SKIP); } else if (testbmap(blkno)) { for (dlp = duplist; dlp; dlp = dlp->next) { if (dlp->dup != blkno) @@ -657,12 +694,401 @@ freeblock(struct inodesc *idesc) } } } - return (res); + /* + * If all successfully returned, account for them. + */ + if (nfrags == 0) { + cgbp = cglookup(dtog(&sblock, idesc->id_blkno)); + cgp = cgbp->b_un.b_cg; + if (idesc->id_numfrags == sblock.fs_frag) + cgp->cg_cs.cs_nbfree++; + else + cgp->cg_cs.cs_nffree += idesc->id_numfrags; + cgdirty(cgbp); + } + return (KEEPON); +} + +/* + * Prepare a snapshot file for being removed. + */ +void +snapremove(ino_t inum) +{ + struct inodesc idesc; + struct inode ip; + int i; + + for (i = 0; i < snapcnt; i++) + if (snaplist[i].i_number == inum) + break; + if (i == snapcnt) + ginode(inum, &ip); + else + ip = snaplist[i]; + if ((DIP(ip.i_dp, di_flags) & SF_SNAPSHOT) == 0) { + printf("snapremove: inode %jd is not a snapshot\n", + (intmax_t)inum); + if (i == snapcnt) + irelse(&ip); + return; + } + if (debug) + printf("snapremove: remove %sactive snapshot %jd\n", + i == snapcnt ? "in" : "", (intmax_t)inum); + /* + * If on active snapshot list, remove it. + */ + if (i < snapcnt) { + for (i++; i < FSMAXSNAP; i++) { + if (sblock.fs_snapinum[i] == 0) + break; + snaplist[i - 1] = snaplist[i]; + sblock.fs_snapinum[i - 1] = sblock.fs_snapinum[i]; + } + sblock.fs_snapinum[i - 1] = 0; + bzero(&snaplist[i - 1], sizeof(struct inode)); + snapcnt--; + } + memset(&idesc, 0, sizeof(struct inodesc)); + idesc.id_type = SNAP; + idesc.id_func = snapclean; + idesc.id_number = inum; + (void)ckinode(ip.i_dp, &idesc); + DIP_SET(ip.i_dp, di_flags, DIP(ip.i_dp, di_flags) & ~SF_SNAPSHOT); + inodirty(&ip); + irelse(&ip); +} + +static int +snapclean(struct inodesc *idesc) +{ + ufs2_daddr_t blkno; + struct bufarea *bp; + union dinode *dp; + + blkno = idesc->id_blkno; + if (blkno == 0) + return (KEEPON); + + dp = idesc->id_dp; + if (blkno == BLK_NOCOPY || blkno == BLK_SNAP) { + if (idesc->id_lbn < UFS_NDADDR) { + DIP_SET(dp, di_db[idesc->id_lbn], 0); + } else { + bp = idesc->id_bp; + IBLK_SET(bp, bp->b_index, 0); + dirty(bp); + } + } + return (KEEPON); +} + +/* + * Notification that a block is being freed. Return zero if the free + * should be allowed to proceed. Return non-zero if the snapshot file + * wants to claim the block. The block will be claimed if it is an + * uncopied part of one of the snapshots. It will be freed if it is + * either a BLK_NOCOPY or has already been copied in all of the snapshots. + * If a fragment is being freed, then all snapshots that care about + * it must make a copy since a snapshot file can only claim full sized + * blocks. Note that if more than one snapshot file maps the block, + * we can pick one at random to claim it. Since none of the snapshots + * can change, we are assurred that they will all see the same unmodified + * image. When deleting a snapshot file (see ino_trunc above), we + * must push any of these claimed blocks to one of the other snapshots + * that maps it. These claimed blocks are easily identified as they will + * have a block number equal to their logical block number within the + * snapshot. A copied block can never have this property because they + * must always have been allocated from a BLK_NOCOPY location. + */ +int +snapblkfree(struct fs *fs, ufs2_daddr_t bno, long size, ino_t inum, + ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags)) +{ + union dinode *dp; + struct inode ip; + struct bufarea *snapbp; + ufs_lbn_t lbn; + ufs2_daddr_t blkno, relblkno; + int i, frags, claimedblk, copydone; + + /* If no snapshots, nothing to do */ + if (snapcnt == 0) + return (0); + if (debug) + printf("snapblkfree: in ino %jd free blkno %jd, size %jd\n", + (intmax_t)inum, (intmax_t)bno, (intmax_t)size); + relblkno = blknum(fs, bno); + lbn = fragstoblks(fs, relblkno); + /* Direct blocks are always pre-copied */ + if (lbn < UFS_NDADDR) + return (0); + copydone = 0; + claimedblk = 0; + for (i = 0; i < snapcnt; i++) { + /* + * Lookup block being freed. + */ + ip = snaplist[i]; + dp = ip.i_dp; + blkno = ino_blkatoff(dp, inum != 0 ? inum : ip.i_number, + lbn, &frags, &snapbp); + /* + * Check to see if block needs to be copied. + */ + if (blkno == 0) { + /* + * A block that we map is being freed. If it has not + * been claimed yet, we will claim or copy it (below). + */ + claimedblk = 1; + } else if (blkno == BLK_SNAP) { + /* + * No previous snapshot claimed the block, + * so it will be freed and become a BLK_NOCOPY + * (don't care) for us. + */ + if (claimedblk) + pfatal("snapblkfree: inconsistent block type"); + IBLK_SET(snapbp, snapbp->b_index, BLK_NOCOPY); + dirty(snapbp); + brelse(snapbp); + continue; + } else /* BLK_NOCOPY or default */ { + /* + * If the snapshot has already copied the block + * (default), or does not care about the block, + * it is not needed. + */ + brelse(snapbp); + continue; + } + /* + * If this is a full size block, we will just grab it + * and assign it to the snapshot inode. Otherwise we + * will proceed to copy it. See explanation for this + * routine as to why only a single snapshot needs to + * claim this block. + */ + if (size == fs->fs_bsize) { + if (debug) + printf("Grabonremove snapshot %ju lbn %jd " + "from inum %ju\n", (intmax_t)ip.i_number, + (intmax_t)lbn, (uintmax_t)inum); + IBLK_SET(snapbp, snapbp->b_index, relblkno); + dirty(snapbp); + brelse(snapbp); + DIP_SET(dp, di_blocks, + DIP(dp, di_blocks) + btodb(size)); + inodirty(&ip); + return (1); + } + + /* First time through, read the contents of the old block. */ + if (copydone == 0) { + copydone = 1; + if (blread(fsreadfd, copybuf, fsbtodb(fs, relblkno), + fs->fs_bsize) != 0) { + pfatal("Could not read snapshot %ju block " + "%jd\n", (intmax_t)ip.i_number, + (intmax_t)relblkno); + continue; + } + } + /* + * This allocation will never require any additional + * allocations for the snapshot inode. + */ + blkno = allocblk(dtog(fs, relblkno), fs->fs_frag, + checkblkavail); + if (blkno == 0) { + pfatal("Could not allocate block for snapshot %ju\n", + (intmax_t)ip.i_number); + continue; + } + if (debug) + printf("Copyonremove: snapino %jd lbn %jd for inum %ju " + "size %ld new blkno %jd\n", (intmax_t)ip.i_number, + (intmax_t)lbn, (uintmax_t)inum, size, + (intmax_t)blkno); + blwrite(fswritefd, copybuf, fsbtodb(fs, blkno), fs->fs_bsize); + IBLK_SET(snapbp, snapbp->b_index, blkno); + dirty(snapbp); + brelse(snapbp); + DIP_SET(dp, di_blocks, + DIP(dp, di_blocks) + btodb(fs->fs_bsize)); + inodirty(&ip); + } + return (0); +} + +/* + * Notification that a block is being written. Return if the block + * is part of a snapshot as snapshots never track other snapshots. + * The block will be copied in all of the snapshots that are tracking + * it and have not yet copied it. Some buffers may hold more than one + * block. Here we need to check each block in the buffer. + */ +void +copyonwrite(struct fs *fs, struct bufarea *bp, + ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags)) +{ + ufs2_daddr_t copyblkno; + long i, numblks; + + /* If no snapshots, nothing to do. */ + if (snapcnt == 0) + return; + numblks = blkroundup(fs, bp->b_size) / fs->fs_bsize; + if (debug) + prtbuf(bp, "copyonwrite: checking %jd block%s in buffer", + (intmax_t)numblks, numblks > 1 ? "s" : ""); + copyblkno = blknum(fs, dbtofsb(fs, bp->b_bno)); + for (i = 0; i < numblks; i++) { + chkcopyonwrite(fs, copyblkno, checkblkavail); + copyblkno += fs->fs_frag; + } +} + +static void +chkcopyonwrite(struct fs *fs, ufs2_daddr_t copyblkno, + ufs2_daddr_t (*checkblkavail)(ufs2_daddr_t blkno, long frags)) +{ + struct inode ip; + union dinode *dp; + struct bufarea *snapbp; + ufs2_daddr_t blkno; + int i, frags, copydone; + ufs_lbn_t lbn; + + lbn = fragstoblks(fs, copyblkno); + /* Direct blocks are always pre-copied */ + if (lbn < UFS_NDADDR) + return; + copydone = 0; + for (i = 0; i < snapcnt; i++) { + /* + * Lookup block being freed. + */ + ip = snaplist[i]; + dp = ip.i_dp; + blkno = ino_blkatoff(dp, ip.i_number, lbn, &frags, &snapbp); + /* + * Check to see if block needs to be copied. + */ + if (blkno != 0) { + /* + * A block that we have already copied or don't track. + */ + brelse(snapbp); + continue; + } + /* First time through, read the contents of the old block. */ + if (copydone == 0) { + copydone = 1; + if (blread(fsreadfd, copybuf, fsbtodb(fs, copyblkno), + fs->fs_bsize) != 0) { + pfatal("Could not read snapshot %ju block " + "%jd\n", (intmax_t)ip.i_number, + (intmax_t)copyblkno); + continue; + } + } + /* + * This allocation will never require any additional + * allocations for the snapshot inode. + */ + if ((blkno = allocblk(dtog(fs, copyblkno), fs->fs_frag, + checkblkavail)) == 0) { + pfatal("Could not allocate block for snapshot %ju\n", + (intmax_t)ip.i_number); + continue; + } + if (debug) + prtbuf(snapbp, "Copyonwrite: snapino %jd lbn %jd using " + "blkno %ju setting in buffer", + (intmax_t)ip.i_number, (intmax_t)lbn, + (intmax_t)blkno); + blwrite(fswritefd, copybuf, fsbtodb(fs, blkno), fs->fs_bsize); + IBLK_SET(snapbp, snapbp->b_index, blkno); + dirty(snapbp); + brelse(snapbp); + DIP_SET(dp, di_blocks, + DIP(dp, di_blocks) + btodb(fs->fs_bsize)); + inodirty(&ip); + } + return; +} + +/* + * Traverse an inode and check that its block count is correct + * fixing it if necessary. + */ +void +check_blkcnt(struct inode *ip) +{ + struct inodesc idesc; + union dinode *dp; + ufs2_daddr_t ndb; + int j, ret, offset; + + dp = ip->i_dp; + memset(&idesc, 0, sizeof(struct inodesc)); + idesc.id_func = pass1check; + idesc.id_number = ip->i_number; + idesc.id_type = (DIP(dp, di_flags) & SF_SNAPSHOT) == 0 ? ADDR : SNAP; + (void)ckinode(dp, &idesc); + if (sblock.fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize > 0) { + ndb = howmany(dp->dp2.di_extsize, sblock.fs_bsize); + for (j = 0; j < UFS_NXADDR; j++) { + if (--ndb == 0 && + (offset = blkoff(&sblock, dp->dp2.di_extsize)) != 0) + idesc.id_numfrags = numfrags(&sblock, + fragroundup(&sblock, offset)); + else + idesc.id_numfrags = sblock.fs_frag; + if (dp->dp2.di_extb[j] == 0) + continue; + idesc.id_blkno = dp->dp2.di_extb[j]; + ret = (*idesc.id_func)(&idesc); + if (ret & STOP) + break; + } + } + idesc.id_entryno *= btodb(sblock.fs_fsize); + if (DIP(dp, di_blocks) != idesc.id_entryno) { + if (!(sujrecovery && preen)) { + pwarn("INCORRECT BLOCK COUNT I=%lu (%ju should be %ju)", + (u_long)idesc.id_number, + (uintmax_t)DIP(dp, di_blocks), + (uintmax_t)idesc.id_entryno); + if (preen) + printf(" (CORRECTED)\n"); + else if (reply("CORRECT") == 0) + return; + } + if (bkgrdflag == 0) { + DIP_SET(dp, di_blocks, idesc.id_entryno); + inodirty(ip); + } else { + cmd.value = idesc.id_number; + cmd.size = idesc.id_entryno - DIP(dp, di_blocks); + if (debug) + printf("adjblkcnt ino %ju amount %lld\n", + (uintmax_t)cmd.value, (long long)cmd.size); + if (sysctl(adjblkcnt, MIBSIZE, 0, 0, + &cmd, sizeof cmd) == -1) + rwerror("ADJUST INODE BLOCK COUNT", cmd.value); + } + } } void freeinodebuf(void) { + struct bufarea *bp; + int i; /* * Flush old contents in case they have been updated. @@ -671,6 +1097,15 @@ freeinodebuf(void) if (inobuf.b_un.b_buf != NULL) free((char *)inobuf.b_un.b_buf); inobuf.b_un.b_buf = NULL; + firstinum = lastinum = 0; + /* + * Reload the snapshot inodes in case any of them changed. + */ + for (i = 0; i < snapcnt; i++) { + bp = snaplist[i].i_bp; + bp->b_errs = blread(fsreadfd, bp->b_un.b_buf, bp->b_bno, + bp->b_size); + } } /* @@ -680,12 +1115,15 @@ freeinodebuf(void) * * Enter inodes into the cache. */ -void +struct inoinfo * cacheino(union dinode *dp, ino_t inumber) { - struct inoinfo *inp, **inpp; + struct inoinfo *inp; int i, blks; + if (getinoinfo(inumber) != NULL) + pfatal("cacheino: duplicate entry for ino %jd\n", + (intmax_t)inumber); if (howmany(DIP(dp, di_size), sblock.fs_bsize) > UFS_NDADDR) blks = UFS_NDADDR + UFS_NIADDR; else if (DIP(dp, di_size) > 0) @@ -696,13 +1134,13 @@ cacheino(union dinode *dp, ino_t inumber) Malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs2_daddr_t)); if (inp == NULL) errx(EEXIT, "cannot increase directory list"); - inpp = &inphead[inumber % dirhash]; - inp->i_nexthash = *inpp; - *inpp = inp; + SLIST_INSERT_HEAD(&inphash[inumber % dirhash], inp, i_hash); + inp->i_flags = 0; inp->i_parent = inumber == UFS_ROOTINO ? UFS_ROOTINO : (ino_t)0; inp->i_dotdot = (ino_t)0; inp->i_number = inumber; inp->i_isize = DIP(dp, di_size); + inp->i_depth = DIP(dp, di_dirdepth); inp->i_numblks = blks; for (i = 0; i < MIN(blks, UFS_NDADDR); i++) inp->i_blks[i] = DIP(dp, di_db[i]); @@ -717,6 +1155,7 @@ cacheino(union dinode *dp, ino_t inumber) errx(EEXIT, "cannot increase directory list"); } inpsort[inplast++] = inp; + return (inp); } /* @@ -727,13 +1166,43 @@ getinoinfo(ino_t inumber) { struct inoinfo *inp; - for (inp = inphead[inumber % dirhash]; inp; inp = inp->i_nexthash) { + SLIST_FOREACH(inp, &inphash[inumber % dirhash], i_hash) { if (inp->i_number != inumber) continue; return (inp); } - errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber); - return ((struct inoinfo *)0); + return (NULL); +} + +/* + * Remove an entry from the inode cache and disk-order sorted list. + * Return 0 on success and 1 on failure. + */ +int +removecachedino(ino_t inumber) +{ + struct inoinfo *inp, **inpp; + char *listtype; + + listtype = "hash"; + SLIST_FOREACH(inp, &inphash[inumber % dirhash], i_hash) { + if (inp->i_number != inumber) + continue; + SLIST_REMOVE(&inphash[inumber % dirhash], inp, inoinfo, i_hash); + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) { + if (*inpp != inp) + continue; + *inpp = inpsort[inplast - 1]; + inplast--; + free(inp); + return (0); + } + listtype = "sort"; + break; + } + pfatal("removecachedino: entry for ino %jd not found on %s list\n", + (intmax_t)inumber, listtype); + return (1); } /* @@ -744,13 +1213,14 @@ inocleanup(void) { struct inoinfo **inpp; - if (inphead == NULL) + if (inphash == NULL) return; for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) free((char *)(*inpp)); - free((char *)inphead); + free((char *)inphash); + inphash = NULL; free((char *)inpsort); - inphead = inpsort = NULL; + inpsort = NULL; } void @@ -782,6 +1252,10 @@ clri(struct inodesc *idesc, const char *type, int flag) printf(" (CLEARED)\n"); n_files--; if (bkgrdflag == 0) { + if (idesc->id_type == SNAP) { + snapremove(idesc->id_number); + idesc->id_type = ADDR; + } (void)ckinode(dp, idesc); inoinfo(idesc->id_number)->ino_state = USTATE; clearinode(dp); @@ -821,7 +1295,7 @@ findino(struct inodesc *idesc) if (dirp->d_ino == 0) return (KEEPON); if (strcmp(dirp->d_name, idesc->id_name) == 0 && - dirp->d_ino >= UFS_ROOTINO && dirp->d_ino <= maxino) { + dirp->d_ino >= UFS_ROOTINO && dirp->d_ino < maxino) { idesc->id_parent = dirp->d_ino; return (STOP|FOUND); } @@ -851,7 +1325,7 @@ prtinode(struct inode *ip) dp = ip->i_dp; printf(" I=%lu ", (u_long)ip->i_number); - if (ip->i_number < UFS_ROOTINO || ip->i_number > maxino) + if (ip->i_number < UFS_ROOTINO || ip->i_number >= maxino) return; printf(" OWNER="); if ((pw = getpwuid((int)DIP(dp, di_uid))) != NULL) @@ -863,8 +1337,8 @@ prtinode(struct inode *ip) printf("%s: ", cdevname); printf("SIZE=%ju ", (uintmax_t)DIP(dp, di_size)); t = DIP(dp, di_mtime); - p = ctime(&t); - printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); + if ((p = ctime(&t)) != NULL) + printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); } void @@ -923,7 +1397,7 @@ retry: cg = ino_to_cg(&sblock, ino); cgbp = cglookup(cg); cgp = cgbp->b_un.b_cg; - if (!check_cgmagic(cg, cgbp, 0)) { + if (!check_cgmagic(cg, cgbp)) { if (anyino == 0) return (0); request = (cg + 1) * sblock.fs_ipg; @@ -946,20 +1420,20 @@ retry: cgdirty(cgbp); ginode(ino, &ip); dp = ip.i_dp; - DIP_SET(dp, di_db[0], allocblk((long)1)); + memset(dp, 0, ((sblock.fs_magic == FS_UFS1_MAGIC) ? + sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode))); + DIP_SET(dp, di_db[0], allocblk(ino_to_cg(&sblock, ino), (long)1, + std_checkblkavail)); if (DIP(dp, di_db[0]) == 0) { inoinfo(ino)->ino_state = USTATE; + inodirty(&ip); irelse(&ip); return (0); } DIP_SET(dp, di_mode, type); - DIP_SET(dp, di_flags, 0); DIP_SET(dp, di_atime, time(NULL)); DIP_SET(dp, di_ctime, DIP(dp, di_atime)); DIP_SET(dp, di_mtime, DIP(dp, di_ctime)); - DIP_SET(dp, di_mtimensec, 0); - DIP_SET(dp, di_ctimensec, 0); - DIP_SET(dp, di_atimensec, 0); DIP_SET(dp, di_size, sblock.fs_fsize); DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize)); n_files++; @@ -980,7 +1454,7 @@ freeino(ino_t ino) struct inode ip; memset(&idesc, 0, sizeof(struct inodesc)); - idesc.id_type = inoinfo(ino)->ino_idtype; + idesc.id_type = ADDR; idesc.id_func = freeblock; idesc.id_number = ino; ginode(ino, &ip); diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c index 18634a93c05c..aabab489573e 100644 --- a/sbin/fsck_ffs/main.c +++ b/sbin/fsck_ffs/main.c @@ -29,21 +29,7 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#define IN_RTLD /* So we pickup the P_OSREL defines */ +#define _WANT_P_OSREL #include <sys/param.h> #include <sys/file.h> #include <sys/mount.h> @@ -61,7 +47,6 @@ __FBSDID("$FreeBSD$"); #include <fstab.h> #include <grp.h> #include <inttypes.h> -#include <libufs.h> #include <mntopts.h> #include <paths.h> #include <stdint.h> @@ -70,14 +55,13 @@ __FBSDID("$FreeBSD$"); #include "fsck.h" -static int restarts; +static int restarts; +static char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ static void usage(void) __dead2; static intmax_t argtoimax(int flag, const char *req, const char *str, int base); static int checkfilesys(char *filesys); static int setup_bkgrdchk(struct statfs *mntp, int sbrdfailed, char **filesys); -static int chkdoreload(struct statfs *mntp); -static struct statfs *getmntpt(const char *); int main(int argc, char *argv[]) @@ -258,7 +242,7 @@ checkfilesys(char *filesys) * if it is listed among the mounted file systems. Failing that * check to see if it is listed in /etc/fstab. */ - mntp = getmntpt(filesys); + mntp = getmntpoint(filesys); if (mntp != NULL) filesys = mntp->f_mntfromname; else @@ -271,14 +255,21 @@ checkfilesys(char *filesys) */ sblock_init(); sbreadfailed = 0; - if (openfilesys(filesys) == 0 || readsb(0) == 0) + if (openfilesys(filesys) == 0 || readsb() == 0) sbreadfailed = 1; if (bkgrdcheck) { if (sbreadfailed) exit(3); /* Cannot read superblock */ - /* Earlier background failed or journaled */ - if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) - exit(4); + if ((sblock.fs_flags & FS_NEEDSFSCK) == FS_NEEDSFSCK) + exit(4); /* Earlier background failed */ + if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) { + maxino = sblock.fs_ncg * sblock.fs_ipg; + maxfsblock = sblock.fs_size; + bufinit(); + preen = 1; + if (suj_check(filesys) == 0) + exit(4); /* Journal good, run it now */ + } if ((sblock.fs_flags & FS_DOSOFTDEP) == 0) exit(5); /* Not running soft updates */ size = MIBSIZE; @@ -311,7 +302,7 @@ checkfilesys(char *filesys) (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) { bufinit(); gjournal_check(filesys); - if (chkdoreload(mntp) == 0) + if (chkdoreload(mntp, pwarn) == 0) exit(0); exit(4); } else { @@ -352,17 +343,19 @@ checkfilesys(char *filesys) /* * Determine if we can and should do journal recovery. */ - if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) { - if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK && skipclean) { + if (bkgrdflag == 0 && (sblock.fs_flags & FS_SUJ) == FS_SUJ) { + if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK && + skipclean) { sujrecovery = 1; if (suj_check(filesys) == 0) { - printf("\n***** FILE SYSTEM MARKED CLEAN *****\n"); - if (chkdoreload(mntp) == 0) + pwarn("\n**** FILE SYSTEM MARKED CLEAN ****\n"); + if (chkdoreload(mntp, pwarn) == 0) exit(0); exit(4); } sujrecovery = 0; - printf("** Skipping journal, falling through to full fsck\n\n"); + pwarn("Skipping journal, " + "falling through to full fsck\n"); } if (fswritefd != -1) { /* @@ -438,7 +431,7 @@ checkfilesys(char *filesys) /* * 1: scan inodes tallying blocks used */ - if (preen == 0) { + if (preen == 0 || debug) { printf("** Last Mounted on %s\n", sblock.fs_fsmnt); if (mntp != NULL && mntp->f_flags & MNT_ROOTFS) printf("** Root file system\n"); @@ -457,7 +450,8 @@ checkfilesys(char *filesys) preen ? "-p" : "", (preen && usedsoftdep) ? " AND " : "", usedsoftdep ? "SOFTUPDATES" : ""); - printf("** Phase 1b - Rescan For More DUPS\n"); + if (preen == 0 || debug) + printf("** Phase 1b - Rescan For More DUPS\n"); pass1b(); IOstats("Pass1b"); } @@ -465,7 +459,7 @@ checkfilesys(char *filesys) /* * 2: traverse directories from root to mark all connected directories */ - if (preen == 0) + if (preen == 0 || debug) printf("** Phase 2 - Check Pathnames\n"); pass2(); IOstats("Pass2"); @@ -473,7 +467,7 @@ checkfilesys(char *filesys) /* * 3: scan inodes looking for disconnected directories */ - if (preen == 0) + if (preen == 0 || debug) printf("** Phase 3 - Check Connectivity\n"); pass3(); IOstats("Pass3"); @@ -481,7 +475,7 @@ checkfilesys(char *filesys) /* * 4: scan inodes looking for disconnected files; check reference counts */ - if (preen == 0) + if (preen == 0 || debug) printf("** Phase 4 - Check Reference Counts\n"); pass4(); IOstats("Pass4"); @@ -489,10 +483,16 @@ checkfilesys(char *filesys) /* * 5: check and repair resource counts in cylinder groups */ - if (preen == 0) + if (preen == 0 || debug) printf("** Phase 5 - Check Cyl groups\n"); - pass5(); - IOstats("Pass5"); + snapflush(std_checkblkavail); + if (cgheader_corrupt) { + printf("PHASE 5 SKIPPED DUE TO CORRUPT CYLINDER GROUP " + "HEADER(S)\n\n"); + } else { + pass5(); + IOstats("Pass5"); + } /* * print out summary statistics @@ -560,7 +560,7 @@ checkfilesys(char *filesys) return (ERESTART); printf("\n***** PLEASE RERUN FSCK *****\n"); } - if (chkdoreload(mntp) != 0) { + if (chkdoreload(mntp, pwarn) != 0) { if (!fsmodified) return (0); if (!preen) @@ -612,10 +612,6 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys) pwarn("FULL FSCK NEEDED, CANNOT RUN IN BACKGROUND\n"); return (0); } - if ((sblock.fs_flags & FS_SUJ) != 0) { - pwarn("JOURNALED FILESYSTEM, CANNOT RUN IN BACKGROUND\n"); - return (0); - } if (skipclean && ckclean && (sblock.fs_flags & (FS_UNCLEAN|FS_NEEDSFSCK)) == 0) { /* @@ -656,8 +652,7 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys) "SUPPORT\n"); } /* Find or create the snapshot directory */ - snprintf(snapname, sizeof snapname, "%s/.snap", - mntp->f_mntonname); + snprintf(snapname, sizeof snapname, "%s/.snap", mntp->f_mntonname); if (stat(snapname, &snapdir) < 0) { if (errno != ENOENT) { pwarn("CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT " @@ -705,101 +700,20 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys) "BACKGROUND\n", snapname, strerror(errno)); return (0); } + /* Immediately unlink snapshot so that it will be deleted when closed */ + unlink(snapname); free(sblock.fs_csp); free(sblock.fs_si); - havesb = 0; + if (readsb() == 0) { + pwarn("CANNOT READ SNAPSHOT SUPERBLOCK\n"); + return (0); + } *filesys = snapname; cmd.version = FFS_CMD_VERSION; cmd.handle = fsreadfd; return (1); } -static int -chkdoreload(struct statfs *mntp) -{ - struct iovec *iov; - int iovlen; - char errmsg[255]; - - if (mntp == NULL) - return (0); - - iov = NULL; - iovlen = 0; - errmsg[0] = '\0'; - /* - * We modified a mounted file system. Do a mount update on - * it unless it is read-write, so we can continue using it - * as safely as possible. - */ - if (mntp->f_flags & MNT_RDONLY) { - build_iovec(&iov, &iovlen, "fstype", "ffs", 4); - build_iovec(&iov, &iovlen, "from", mntp->f_mntfromname, - (size_t)-1); - build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname, - (size_t)-1); - build_iovec(&iov, &iovlen, "errmsg", errmsg, - sizeof(errmsg)); - build_iovec(&iov, &iovlen, "update", NULL, 0); - build_iovec(&iov, &iovlen, "reload", NULL, 0); - /* - * XX: We need the following line until we clean up - * nmount parsing of root mounts and NFS root mounts. - */ - build_iovec(&iov, &iovlen, "ro", NULL, 0); - if (nmount(iov, iovlen, mntp->f_flags) == 0) { - return (0); - } - pwarn("mount reload of '%s' failed: %s %s\n\n", - mntp->f_mntonname, strerror(errno), errmsg); - return (1); - } - return (0); -} - -/* - * Get the mount point information for name. - */ -static struct statfs * -getmntpt(const char *name) -{ - struct stat devstat, mntdevstat; - char device[sizeof(_PATH_DEV) - 1 + MNAMELEN]; - char *ddevname; - struct statfs *mntbuf, *statfsp; - int i, mntsize, isdev; - - if (stat(name, &devstat) != 0) - return (NULL); - if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode)) - isdev = 1; - else - isdev = 0; - mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); - for (i = 0; i < mntsize; i++) { - statfsp = &mntbuf[i]; - ddevname = statfsp->f_mntfromname; - if (*ddevname != '/') { - if (strlen(_PATH_DEV) + strlen(ddevname) + 1 > - sizeof(statfsp->f_mntfromname)) - continue; - strcpy(device, _PATH_DEV); - strcat(device, ddevname); - strcpy(statfsp->f_mntfromname, device); - } - if (isdev == 0) { - if (strcmp(name, statfsp->f_mntonname)) - continue; - return (statfsp); - } - if (stat(ddevname, &mntdevstat) == 0 && - mntdevstat.st_rdev == devstat.st_rdev) - return (statfsp); - } - statfsp = NULL; - return (statfsp); -} - static void usage(void) { diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c index 319a324cc070..f4f97a38097b 100644 --- a/sbin/fsck_ffs/pass1.c +++ b/sbin/fsck_ffs/pass1.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass1.c 8.6 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/stat.h> #include <sys/sysctl.h> @@ -56,7 +48,7 @@ static ufs2_daddr_t badblk; static ufs2_daddr_t dupblk; static ino_t lastino; /* last inode in use */ -static int checkinode(ino_t inumber, struct inodesc *, int rebuildcg); +static int checkinode(ino_t inumber, struct inodesc *, int rebuiltcg); void pass1(void) @@ -68,7 +60,7 @@ pass1(void) ino_t inumber, inosused, mininos; ufs2_daddr_t i, cgd; u_int8_t *cp; - int c, rebuildcg; + int c, rebuiltcg; badblk = dupblk = lastino = 0; @@ -99,10 +91,20 @@ pass1(void) inumber = c * sblock.fs_ipg; cgbp = cglookup(c); cgp = cgbp->b_un.b_cg; - rebuildcg = 0; - if (!check_cgmagic(c, cgbp, 1)) - rebuildcg = 1; - if (!rebuildcg && sblock.fs_magic == FS_UFS2_MAGIC) { + rebuiltcg = 0; + if (!check_cgmagic(c, cgbp)) { + if (!reply("REBUILD CYLINDER GROUP")) { + cgheader_corrupt = 1; + if (!nflag) { + pwarn("YOU WILL NEED TO RERUN FSCK.\n"); + rerun = 1; + } + } else { + rebuild_cg(c, cgbp); + rebuiltcg = 1; + } + } + if (!rebuiltcg && sblock.fs_magic == FS_UFS2_MAGIC) { inosused = cgp->cg_initediblk; if (inosused > sblock.fs_ipg) { pfatal("Too many initialized inodes (%ju > %d) " @@ -132,7 +134,7 @@ pass1(void) * to find the inodes that are really in use, and then * read only those inodes in from disk. */ - if ((preen || inoopt) && usedsoftdep && !rebuildcg) { + if ((preen || inoopt) && usedsoftdep && !rebuiltcg) { cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT]; for ( ; inosused != 0; cp--) { if (*cp == 0) { @@ -169,7 +171,7 @@ pass1(void) setinodebuf(c, inosused); for (i = 0; i < inosused; i++, inumber++) { if (inumber < UFS_ROOTINO) { - (void)getnextinode(inumber, rebuildcg); + (void)getnextinode(inumber, rebuiltcg); continue; } /* @@ -178,7 +180,7 @@ pass1(void) * We always keep trying until we get to the minimum * valid number for this cylinder group. */ - if (checkinode(inumber, &idesc, rebuildcg) == 0 && + if (checkinode(inumber, &idesc, rebuiltcg) == 0 && i > cgp->cg_initediblk) break; } @@ -189,7 +191,7 @@ pass1(void) * fewer in use. */ mininos = roundup(inosused + INOPB(&sblock), INOPB(&sblock)); - if (inoopt && !preen && !rebuildcg && + if (inoopt && !preen && !rebuiltcg && sblock.fs_magic == FS_UFS2_MAGIC && cgp->cg_initediblk > 2 * INOPB(&sblock) && mininos < cgp->cg_initediblk) { @@ -209,7 +211,7 @@ pass1(void) inosused = 0; else inosused = lastino - (c * sblock.fs_ipg); - if (rebuildcg && inosused > cgp->cg_initediblk && + if (rebuiltcg && inosused > cgp->cg_initediblk && sblock.fs_magic == FS_UFS2_MAGIC) { cgp->cg_initediblk = roundup(inosused, INOPB(&sblock)); pwarn("CYLINDER GROUP %d: FOUND %d VALID INODES\n", c, @@ -219,9 +221,10 @@ pass1(void) * If we were not able to determine in advance which inodes * were in use, then reduce the size of the inoinfo structure * to the size necessary to describe the inodes that we - * really found. + * really found. Always leave map space in the first cylinder + * group in case we need to a root or lost+found directory. */ - if (inumber == lastino) + if (inumber == lastino || c == 0) continue; inostathead[c].il_numalloced = inosused; if (inosused == 0) { @@ -241,18 +244,19 @@ pass1(void) } static int -checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg) +checkinode(ino_t inumber, struct inodesc *idesc, int rebuiltcg) { struct inode ip; union dinode *dp; - off_t kernmaxfilesize; ufs2_daddr_t ndb; mode_t mode; intmax_t size, fixsize; int j, ret, offset; - if ((dp = getnextinode(inumber, rebuildcg)) == NULL) + if ((dp = getnextinode(inumber, rebuiltcg)) == NULL) { + pfatal("INVALID INODE"); goto unknown; + } mode = DIP(dp, di_mode) & IFMT; if (mode == 0) { if ((sblock.fs_magic == FS_UFS1_MAGIC && @@ -280,16 +284,8 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg) return (1); } lastino = inumber; - /* This should match the file size limit in ffs_mountfs(). */ - if (sblock.fs_magic == FS_UFS1_MAGIC) - kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1; - else - kernmaxfilesize = sblock.fs_maxfilesize; - if (DIP(dp, di_size) > kernmaxfilesize || - DIP(dp, di_size) > sblock.fs_maxfilesize || - (mode == IFDIR && DIP(dp, di_size) > MAXDIRSIZE)) { - if (debug) - printf("bad size %ju:", (uintmax_t)DIP(dp, di_size)); + if (chkfilesize(mode, DIP(dp, di_size)) == 0) { + pfatal("BAD FILE SIZE"); goto unknown; } if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { @@ -305,19 +301,22 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg) if (debug) printf("bad special-file size %ju:", (uintmax_t)DIP(dp, di_size)); + pfatal("BAD SPECIAL-FILE SIZE"); goto unknown; } if ((mode == IFBLK || mode == IFCHR) && (dev_t)DIP(dp, di_rdev) == NODEV) { if (debug) printf("bad special-file rdev NODEV:"); + pfatal("BAD SPECIAL-FILE RDEV"); goto unknown; } ndb = howmany(DIP(dp, di_size), sblock.fs_bsize); if (ndb < 0) { if (debug) - printf("bad size %ju ndb %ju:", + printf("negative size %ju ndb %ju:", (uintmax_t)DIP(dp, di_size), (uintmax_t)ndb); + pfatal("NEGATIVE FILE SIZE"); goto unknown; } if (mode == IFBLK || mode == IFCHR) @@ -342,33 +341,52 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg) } } } - for (j = ndb; ndb < UFS_NDADDR && j < UFS_NDADDR; j++) - if (DIP(dp, di_db[j]) != 0) { - if (debug) - printf("bad direct addr[%d]: %ju\n", j, - (uintmax_t)DIP(dp, di_db[j])); - goto unknown; + for (j = ndb; ndb < UFS_NDADDR && j < UFS_NDADDR; j++) { + if (DIP(dp, di_db[j]) == 0) + continue; + if (debug) + printf("invalid direct addr[%d]: %ju\n", j, + (uintmax_t)DIP(dp, di_db[j])); + pfatal("INVALID DIRECT BLOCK"); + ginode(inumber, &ip); + prtinode(&ip); + if (reply("CLEAR") == 1) { + DIP_SET(ip.i_dp, di_db[j], 0); + inodirty(&ip); } + irelse(&ip); + } for (j = 0, ndb -= UFS_NDADDR; ndb > 0; j++) ndb /= NINDIR(&sblock); - for (; j < UFS_NIADDR; j++) - if (DIP(dp, di_ib[j]) != 0) { - if (debug) - printf("bad indirect addr: %ju\n", - (uintmax_t)DIP(dp, di_ib[j])); - goto unknown; + for (; j < UFS_NIADDR; j++) { + if (DIP(dp, di_ib[j]) == 0) + continue; + if (debug) + printf("invalid indirect addr: %ju\n", + (uintmax_t)DIP(dp, di_ib[j])); + pfatal("INVALID INDIRECT BLOCK"); + ginode(inumber, &ip); + prtinode(&ip); + if (reply("CLEAR") == 1) { + DIP_SET(ip.i_dp, di_ib[j], 0); + inodirty(&ip); } - if (ftypeok(dp) == 0) + irelse(&ip); + } + if (ftypeok(dp) == 0) { + pfatal("UNKNOWN FILE TYPE"); goto unknown; + } n_files++; inoinfo(inumber)->ino_linkcnt = DIP(dp, di_nlink); if (mode == IFDIR) { - if (DIP(dp, di_size) == 0) + if (DIP(dp, di_size) == 0) { inoinfo(inumber)->ino_state = DCLEAR; - else if (DIP(dp, di_nlink) <= 0) + } else if (DIP(dp, di_nlink) == 0) { inoinfo(inumber)->ino_state = DZLINK; - else + } else { inoinfo(inumber)->ino_state = DSTATE; + } cacheino(dp, inumber); countdirs++; } else if (DIP(dp, di_nlink) <= 0) @@ -483,15 +501,14 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg) } return (1); unknown: - pfatal("UNKNOWN FILE TYPE I=%lu", (u_long)inumber); - inoinfo(inumber)->ino_state = FCLEAR; + ginode(inumber, &ip); + prtinode(&ip); + inoinfo(inumber)->ino_state = USTATE; if (reply("CLEAR") == 1) { - inoinfo(inumber)->ino_state = USTATE; - ginode(inumber, &ip); clearinode(ip.i_dp); inodirty(&ip); - irelse(&ip); } + irelse(&ip); return (1); } diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c index 17a3b6495dc4..3fb9cab89078 100644 --- a/sbin/fsck_ffs/pass1b.c +++ b/sbin/fsck_ffs/pass1b.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass1b.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <ufs/ufs/dinode.h> @@ -88,10 +80,12 @@ pass1b(void) if (inoinfo(inumber)->ino_state != USTATE && (ckinode(dp, &idesc) & STOP)) { rerun = 1; + freeinodebuf(); return; } } } + freeinodebuf(); } static int diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c index 8632cf4878b1..1755a1295a9e 100644 --- a/sbin/fsck_ffs/pass2.c +++ b/sbin/fsck_ffs/pass2.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/sysctl.h> @@ -85,7 +77,7 @@ pass2(void) case DCLEAR: pfatal("DUPS/BAD IN ROOT INODE"); if (reply("REALLOCATE")) { - freeino(UFS_ROOTINO); + freedirino(UFS_ROOTINO, UFS_ROOTINO); if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO) errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); @@ -210,16 +202,18 @@ pass2(void) if (inp->i_parent == 0 || inp->i_isize == 0) continue; if (inoinfo(inp->i_parent)->ino_state == DFOUND && - INO_IS_DUNFOUND(inp->i_number)) + INO_IS_DUNFOUND(inp->i_number)) { inoinfo(inp->i_number)->ino_state = DFOUND; + check_dirdepth(inp); + } if (inp->i_dotdot == inp->i_parent || inp->i_dotdot == (ino_t)-1) continue; if (inp->i_dotdot == 0) { inp->i_dotdot = inp->i_parent; - fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); - if (reply("FIX") == 0) - continue; + if (debug) + fileerror(inp->i_parent, inp->i_number, + "DEFERRED MISSING '..' FIX"); (void)makeentry(inp->i_number, inp->i_parent, ".."); inoinfo(inp->i_parent)->ino_linkcnt--; continue; @@ -271,7 +265,8 @@ pass2(void) inoinfo(inp->i_dotdot)->ino_linkcnt++; inoinfo(inp->i_parent)->ino_linkcnt--; inp->i_dotdot = inp->i_parent; - (void)changeino(inp->i_number, "..", inp->i_parent); + (void)changeino(inp->i_number, "..", inp->i_parent, + getinoinfo(inp->i_parent)->i_depth + 1); } /* * Mark all the directories that can be found from the root. @@ -289,57 +284,62 @@ pass2check(struct inodesc *idesc) struct inode ip; union dinode *dp; const char *errmsg; - struct direct proto; + struct direct proto, *newdirp; /* * check for "." */ - if (dirp->d_ino > maxino) - goto chk2; if (idesc->id_entryno != 0) goto chk1; if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { if (dirp->d_ino != idesc->id_number) { direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); - dirp->d_ino = idesc->id_number; - if (reply("FIX") == 1) + if (reply("FIX") == 1) { + dirp->d_ino = idesc->id_number; ret |= ALTERED; + } } if (dirp->d_type != DT_DIR) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); - dirp->d_type = DT_DIR; - if (reply("FIX") == 1) + if (reply("FIX") == 1) { + dirp->d_type = DT_DIR; ret |= ALTERED; + } } goto chk1; } - direrror(idesc->id_number, "MISSING '.'"); proto.d_ino = idesc->id_number; proto.d_type = DT_DIR; proto.d_namlen = 1; (void)strcpy(proto.d_name, "."); entrysize = DIRSIZ(0, &proto); - if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { - pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", - dirp->d_name); - } else if (dirp->d_reclen < entrysize) { - pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); - } else if (dirp->d_reclen < 2 * entrysize) { + direrror(idesc->id_number, "MISSING '.'"); + errmsg = "ADD '.' ENTRY"; + if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) { + /* Not enough space to add '.', replace first entry with '.' */ + if (dirp->d_ino != 0) { + pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + errmsg = "REPLACE WITH '.'"; + } + if (reply(errmsg) == 0) + goto chk1; proto.d_reclen = dirp->d_reclen; memmove(dirp, &proto, (size_t)entrysize); - if (reply("FIX") == 1) - ret |= ALTERED; + ret |= ALTERED; } else { - n = dirp->d_reclen - entrysize; + /* Move over first entry and add '.' entry */ + if (reply(errmsg) == 0) + goto chk1; + newdirp = (struct direct *)((char *)(dirp) + entrysize); + dirp->d_reclen -= entrysize; + memmove(newdirp, dirp, dirp->d_reclen); proto.d_reclen = entrysize; memmove(dirp, &proto, (size_t)entrysize); idesc->id_entryno++; - inoinfo(dirp->d_ino)->ino_linkcnt--; - dirp = (struct direct *)((char *)(dirp) + entrysize); - memset(dirp, 0, (size_t)n); - dirp->d_reclen = n; - if (reply("FIX") == 1) - ret |= ALTERED; + inoinfo(idesc->id_number)->ino_linkcnt--; + dirp = newdirp; + ret |= ALTERED; } chk1: if (idesc->id_entryno > 1) @@ -363,6 +363,20 @@ chk1: dirp->d_reclen = proto.d_reclen; } if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { + if (dirp->d_ino >= maxino) { + direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'"); + /* + * If we know parent set it now, otherwise let it + * point to the root inode and it will get cleaned + * up later if that is not correct. + */ + if (inp->i_parent != 0) + dirp->d_ino = inp->i_parent; + else + dirp->d_ino = UFS_ROOTINO; + if (reply("FIX") == 1) + ret |= ALTERED; + } inp->i_dotdot = dirp->d_ino; if (dirp->d_type != DT_DIR) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); @@ -372,30 +386,60 @@ chk1: } goto chk2; } - if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { - fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); - pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", - dirp->d_name); - inp->i_dotdot = (ino_t)-1; - } else if (dirp->d_reclen < entrysize) { - fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); - pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); - inp->i_dotdot = (ino_t)-1; - } else if (inp->i_parent != 0) { - /* - * We know the parent, so fix now. - */ - inp->i_dotdot = inp->i_parent; - fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number, + idesc->id_number, "MISSING '..'"); + errmsg = "ADD '..' ENTRY"; + if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) { + /* No space to add '..', replace second entry with '..' */ + if (dirp->d_ino != 0) { + pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + errmsg = "REPLACE WITH '..'"; + } + if (reply(errmsg) == 0) { + inp->i_dotdot = (ino_t)-1; + goto chk2; + } + if (proto.d_ino == 0) { + /* Defer processing until parent known */ + idesc->id_entryno++; + if (debug) + printf("(FIX DEFERRED)\n"); + } + inp->i_dotdot = proto.d_ino; proto.d_reclen = dirp->d_reclen; memmove(dirp, &proto, (size_t)entrysize); - if (reply("FIX") == 1) - ret |= ALTERED; + ret |= ALTERED; + } else { + /* Move over second entry and add '..' entry */ + if (reply(errmsg) == 0) { + inp->i_dotdot = (ino_t)-1; + goto chk2; + } + if (proto.d_ino == 0) { + /* Defer processing until parent known */ + idesc->id_entryno++; + if (debug) + printf("(FIX DEFERRED)\n"); + } + inp->i_dotdot = proto.d_ino; + if (dirp->d_ino == 0) { + proto.d_reclen = dirp->d_reclen; + memmove(dirp, &proto, (size_t)entrysize); + } else { + newdirp = (struct direct *)((char *)(dirp) + entrysize); + dirp->d_reclen -= entrysize; + memmove(newdirp, dirp, dirp->d_reclen); + proto.d_reclen = entrysize; + memmove(dirp, &proto, (size_t)entrysize); + if (dirp->d_ino != 0) { + idesc->id_entryno++; + inoinfo(dirp->d_ino)->ino_linkcnt--; + } + dirp = newdirp; + } + ret |= ALTERED; } - idesc->id_entryno++; - if (dirp->d_ino != 0) - inoinfo(dirp->d_ino)->ino_linkcnt--; - return (ret|KEEPON); chk2: if (dirp->d_ino == 0) return (ret|KEEPON); @@ -419,7 +463,7 @@ chk2: } idesc->id_entryno++; n = 0; - if (dirp->d_ino > maxino) { + if (dirp->d_ino >= maxino) { fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); n = reply("REMOVE"); } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) || @@ -499,10 +543,12 @@ again: case DFOUND: inp = getinoinfo(dirp->d_ino); if (idesc->id_entryno > 2) { - if (inp->i_parent == 0) + if (inp->i_parent == 0) { inp->i_parent = idesc->id_number; - else if ((n = fix_extraneous(inp, idesc)) == 1) + check_dirdepth(inp); + } else if ((n = fix_extraneous(inp, idesc))) { break; + } } /* FALLTHROUGH */ @@ -553,6 +599,7 @@ fix_extraneous(struct inoinfo *inp, struct inodesc *idesc) if ((ckinode(ip.i_dp, &dotdesc) & FOUND)) inp->i_dotdot = dotdesc.id_parent; irelse(&ip); + free(dotdesc.id_name); } /* * We have the previously found old name (inp->i_parent) and the diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c index 22cb0393905b..853f5d62382f 100644 --- a/sbin/fsck_ffs/pass3.c +++ b/sbin/fsck_ffs/pass3.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass3.c 8.2 (Berkeley) 4/27/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <ufs/ufs/dinode.h> @@ -74,7 +66,7 @@ pass3(void) if (inp->i_number == UFS_ROOTINO || (inp->i_parent != 0 && !S_IS_DUNFOUND(state))) continue; - if (state == DCLEAR) + if (state == DCLEAR || state == DZLINK) continue; /* * If we are running with soft updates and we come @@ -100,9 +92,10 @@ pass3(void) if (linkup(orphan, inp->i_dotdot, NULL)) { inp->i_parent = inp->i_dotdot = lfdir; inoinfo(lfdir)->ino_linkcnt--; + inoinfo(orphan)->ino_state = DFOUND; + check_dirdepth(inp); + propagate(); } - inoinfo(orphan)->ino_state = DFOUND; - propagate(); continue; } pfatal("ORPHANED DIRECTORY LOOP DETECTED I=%lu", @@ -127,6 +120,7 @@ pass3(void) } irelse(&ip); inoinfo(orphan)->ino_state = DFOUND; + check_dirdepth(inp); propagate(); } } diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c index cfcd36296887..a261051c432a 100644 --- a/sbin/fsck_ffs/pass4.c +++ b/sbin/fsck_ffs/pass4.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass4.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c index 324e725929f6..528a6956c4a1 100644 --- a/sbin/fsck_ffs/pass5.c +++ b/sbin/fsck_ffs/pass5.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass5.c 8.9 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/sysctl.h> @@ -47,7 +39,6 @@ __FBSDID("$FreeBSD$"); #include <inttypes.h> #include <limits.h> #include <string.h> -#include <libufs.h> #include "fsck.h" @@ -116,7 +107,7 @@ pass5(void) } } } - basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_firstfield); + basesize = sizeof(*newcg); if (sblock.fs_magic == FS_UFS2_MAGIC) { newcg->cg_iusedoff = basesize; } else { @@ -131,7 +122,7 @@ pass5(void) fs->fs_old_cpg * sizeof(int32_t); newcg->cg_iusedoff = newcg->cg_old_boff + fs->fs_old_cpg * fs->fs_old_nrpos * sizeof(u_int16_t); - memset(&newcg->cg_space[0], 0, newcg->cg_iusedoff - basesize); + memset(&newcg[1], 0, newcg->cg_iusedoff - basesize); } inomapsize = howmany(fs->fs_ipg, CHAR_BIT); newcg->cg_freeoff = newcg->cg_iusedoff + inomapsize; @@ -375,6 +366,22 @@ pass5(void) if (cursnapshot == 0 && memcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal) != 0 && dofix(&idesc[0], "SUMMARY BLK COUNT(S) WRONG IN SUPERBLK")) { + if (debug) { + printf("cstotal is currently: %jd dirs, %jd blks free, " + "%jd frags free, %jd inos free, %jd clusters\n", + (intmax_t)fs->fs_cstotal.cs_ndir, + (intmax_t)fs->fs_cstotal.cs_nbfree, + (intmax_t)fs->fs_cstotal.cs_nffree, + (intmax_t)fs->fs_cstotal.cs_nifree, + (intmax_t)fs->fs_cstotal.cs_numclusters); + printf("cstotal ought to be: %jd dirs, %jd blks free, " + "%jd frags free, %jd inos free, %jd clusters\n", + (intmax_t)cstotal.cs_ndir, + (intmax_t)cstotal.cs_nbfree, + (intmax_t)cstotal.cs_nffree, + (intmax_t)cstotal.cs_nifree, + (intmax_t)cstotal.cs_numclusters); + } memmove(&fs->fs_cstotal, &cstotal, sizeof cstotal); fs->fs_ronly = 0; fs->fs_fmod = 0; @@ -395,43 +402,53 @@ pass5(void) printf("adjndir by %+" PRIi64 "\n", cmd.value); if (bkgrdsumadj == 0 || sysctl(adjndir, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) - rwerror("ADJUST NUMBER OF DIRECTORIES", cmd.value); + rwerror("ADJUST NUMBER OF DIRECTORIES", + cmd.value); } cmd.value = cstotal.cs_nbfree - fs->fs_cstotal.cs_nbfree; if (cmd.value != 0) { if (debug) - printf("adjnbfree by %+" PRIi64 "\n", cmd.value); + printf("adjnbfree by %+" PRIi64 "\n", + cmd.value); if (bkgrdsumadj == 0 || sysctl(adjnbfree, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) - rwerror("ADJUST NUMBER OF FREE BLOCKS", cmd.value); + rwerror("ADJUST NUMBER OF FREE BLOCKS", + cmd.value); } cmd.value = cstotal.cs_nifree - fs->fs_cstotal.cs_nifree; if (cmd.value != 0) { if (debug) - printf("adjnifree by %+" PRIi64 "\n", cmd.value); + printf("adjnifree by %+" PRIi64 "\n", + cmd.value); if (bkgrdsumadj == 0 || sysctl(adjnifree, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) - rwerror("ADJUST NUMBER OF FREE INODES", cmd.value); + rwerror("ADJUST NUMBER OF FREE INODES", + cmd.value); } cmd.value = cstotal.cs_nffree - fs->fs_cstotal.cs_nffree; if (cmd.value != 0) { if (debug) - printf("adjnffree by %+" PRIi64 "\n", cmd.value); + printf("adjnffree by %+" PRIi64 "\n", + cmd.value); if (bkgrdsumadj == 0 || sysctl(adjnffree, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) - rwerror("ADJUST NUMBER OF FREE FRAGS", cmd.value); + rwerror("ADJUST NUMBER OF FREE FRAGS", + cmd.value); } - cmd.value = cstotal.cs_numclusters - fs->fs_cstotal.cs_numclusters; + cmd.value = cstotal.cs_numclusters - + fs->fs_cstotal.cs_numclusters; if (cmd.value != 0) { if (debug) - printf("adjnumclusters by %+" PRIi64 "\n", cmd.value); - if (bkgrdsumadj == 0 || sysctl(adjnumclusters, MIBSIZE, 0, 0, - &cmd, sizeof cmd) == -1) - rwerror("ADJUST NUMBER OF FREE CLUSTERS", cmd.value); + printf("adjnumclusters by %+" PRIi64 "\n", + cmd.value); + if (bkgrdsumadj == 0 || sysctl(adjnumclusters, MIBSIZE, + 0, 0, &cmd, sizeof cmd) == -1) + rwerror("ADJUST NUMBER OF FREE CLUSTERS", + cmd.value); } } } diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index cb95d18859b0..f10f02d159c3 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/disk.h> #include <sys/stat.h> @@ -54,17 +46,23 @@ __FBSDID("$FreeBSD$"); #include <limits.h> #include <stdint.h> #include <string.h> -#include <libufs.h> #include "fsck.h" -struct inoinfo **inphead, **inpsort; /* info about all inodes */ +struct inohash *inphash; /* hash list of directory inode info */ +struct inoinfo **inpsort; /* disk order list of directory inodes */ +struct inode snaplist[FSMAXSNAP + 1]; /* list of active snapshots */ +int snapcnt; /* number of active snapshots */ +char *copybuf; /* buffer to copy snapshot blocks */ +static int sbhashfailed; #define POWEROF2(num) (((num) & ((num) - 1)) == 0) static int calcsb(char *dev, int devfd, struct fs *fs); static void saverecovery(int readfd, int writefd); static int chkrecovery(int devfd); +static int getlbnblkno(struct inodesc *); +static int checksnapinfo(struct inode *); /* * Read in a superblock finding an alternate if necessary. @@ -74,39 +72,20 @@ static int chkrecovery(int devfd); int setup(char *dev) { - long cg, bmapsize; - struct fs proto; + long i, bmapsize; + struct inode ip; /* - * We are expected to have an open file descriptor + * We are expected to have an open file descriptor and a superblock. */ - if (fsreadfd < 0) - return (0); - /* - * If we do not yet have a superblock, read it in looking - * for alternates if necessary. - */ - if (havesb == 0 && readsb(1) == 0) { - skipclean = 0; - if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) - return(0); - if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) - return (0); - for (cg = 0; cg < proto.fs_ncg; cg++) { - bflag = fsbtodb(&proto, cgsblock(&proto, cg)); - if (readsb(0) != 0) - break; - } - if (cg >= proto.fs_ncg) { - printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. " - "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY " - "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO " - "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n"); - bflag = 0; - return(0); + if (fsreadfd < 0 || havesb == 0) { + if (debug) { + if (fsreadfd < 0) + printf("setup: missing fsreadfd\n"); + else + printf("setup: missing superblock\n"); } - pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); - bflag = 0; + return (0); } if (preen == 0) printf("** %s", dev); @@ -168,6 +147,7 @@ setup(char *dev) /* * allocate and initialize the necessary maps */ + bufinit(); bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short)); blockmap = Calloc((unsigned)bmapsize, sizeof (char)); if (blockmap == NULL) { @@ -181,22 +161,57 @@ setup(char *dev) (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg))); goto badsb; } - numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128); - dirhash = numdirs; + numdirs = sblock.fs_cstotal.cs_ndir; + dirhash = MAX(numdirs / 2, 1); inplast = 0; listmax = numdirs + 10; inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *)); - inphead = (struct inoinfo **)Calloc(numdirs, sizeof(struct inoinfo *)); - if (inpsort == NULL || inphead == NULL) { - printf("cannot alloc %ju bytes for inphead\n", + inphash = (struct inohash *)Calloc(dirhash, sizeof(struct inohash)); + if (inpsort == NULL || inphash == NULL) { + printf("cannot alloc %ju bytes for inphash\n", (uintmax_t)numdirs * sizeof(struct inoinfo *)); goto badsb; } - bufinit(); if (sblock.fs_flags & FS_DOSOFTDEP) usedsoftdep = 1; else usedsoftdep = 0; + /* + * Collect any snapshot inodes so that we can allow them to + * claim any blocks that we free. The code for doing this is + * imported here and into inode.c from sys/ufs/ffs/ffs_snapshot.c. + */ + for (snapcnt = 0; snapcnt < FSMAXSNAP; snapcnt++) { + if (sblock.fs_snapinum[snapcnt] == 0) + break; + ginode(sblock.fs_snapinum[snapcnt], &ip); + if ((DIP(ip.i_dp, di_mode) & IFMT) == IFREG && + (DIP(ip.i_dp, di_flags) & SF_SNAPSHOT) != 0 && + checksnapinfo(&ip)) { + if (debug) + printf("Load snapshot %jd\n", + (intmax_t)sblock.fs_snapinum[snapcnt]); + snaplist[snapcnt] = ip; + continue; + } + printf("Removing non-snapshot inode %ju from snapshot list\n", + (uintmax_t)sblock.fs_snapinum[snapcnt]); + irelse(&ip); + for (i = snapcnt + 1; i < FSMAXSNAP; i++) { + if (sblock.fs_snapinum[i] == 0) + break; + sblock.fs_snapinum[i - 1] = sblock.fs_snapinum[i]; + } + sblock.fs_snapinum[i - 1] = 0; + snapcnt--; + sbdirty(); + } + if (snapcnt > 0 && copybuf == NULL) { + copybuf = Balloc(sblock.fs_bsize); + if (copybuf == NULL) + errx(EEXIT, "cannot allocate space for snapshot " + "copy buffer"); + } return (1); badsb: @@ -205,6 +220,146 @@ badsb: } /* + * Check for valid snapshot information. + * + * Each snapshot has a list of blocks that have been copied. This list + * is consulted before checking the snapshot inode. Its purpose is to + * speed checking of commonly checked blocks and to avoid recursive + * checks of the snapshot inode. In particular, the list must contain + * the superblock, the superblock summary information, and all the + * cylinder group blocks. The list may contain other commonly checked + * pointers such as those of the blocks that contain the snapshot inodes. + * The list is sorted into block order to allow binary search lookup. + * + * The twelve direct direct block pointers of the snapshot are always + * copied, so we test for them first before checking the list itself + * (i.e., they are not in the list). + * + * The checksnapinfo() routine needs to ensure that the list contains at + * least the super block, its summary information, and the cylinder groups. + * Here we check the list first for the superblock, zero or more cylinder + * groups up to the location of the superblock summary information, the + * summary group information, and any remaining cylinder group maps that + * follow it. We skip over any other entries in the list. + */ +#define CHKBLKINLIST(chkblk) \ + /* All UFS_NDADDR blocks are copied */ \ + if ((chkblk) >= UFS_NDADDR) { \ + /* Skip over blocks that are not of interest */ \ + while (*blkp < (chkblk) && blkp < lastblkp) \ + blkp++; \ + /* Fail if end of list and not all blocks found */ \ + if (blkp >= lastblkp) { \ + pwarn("UFS%d snapshot inode %jd failed: " \ + "improper block list length (%jd)\n", \ + sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, \ + (intmax_t)snapip->i_number, \ + (intmax_t)(lastblkp - &snapblklist[0])); \ + status = 0; \ + } \ + /* Fail if block we seek is missing */ \ + else if (*blkp++ != (chkblk)) { \ + pwarn("UFS%d snapshot inode %jd failed: " \ + "block list (%jd) != %s (%jd)\n", \ + sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, \ + (intmax_t)snapip->i_number, \ + (intmax_t)blkp[-1], #chkblk, \ + (intmax_t)chkblk); \ + status = 0; \ + } \ + } + +static int +checksnapinfo(struct inode *snapip) +{ + struct fs *fs; + struct bufarea *bp; + struct inodesc idesc; + daddr_t *snapblklist, *blkp, *lastblkp, csblkno; + int cg, loc, len, status; + ufs_lbn_t lbn; + size_t size; + + fs = &sblock; + memset(&idesc, 0, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = getlbnblkno; + idesc.id_number = snapip->i_number; + lbn = howmany(fs->fs_size, fs->fs_frag); + idesc.id_parent = lbn; /* sought after blkno */ + if ((ckinode(snapip->i_dp, &idesc) & FOUND) == 0) + return (0); + size = fragroundup(fs, + DIP(snapip->i_dp, di_size) - lblktosize(fs, lbn)); + bp = getdatablk(idesc.id_parent, size, BT_DATA); + if (bp->b_errs != 0) + return (0); + snapblklist = (daddr_t *)bp->b_un.b_buf; + /* + * snapblklist[0] is the size of the list + * snapblklist[1] is the first element of the list + * + * We need to be careful to bound the size of the list and verify + * that we have not run off the end of it if it or its size has + * been corrupted. + */ + blkp = &snapblklist[1]; + lastblkp = &snapblklist[MAX(0, + MIN(snapblklist[0] + 1, size / sizeof(daddr_t)))]; + status = 1; + /* Check that the superblock is listed. */ + CHKBLKINLIST(lblkno(fs, fs->fs_sblockloc)); + if (status == 0) + goto out; + /* + * Calculate where the summary information is located. + * Usually it is in the first cylinder group, but growfs + * may move it to the first cylinder group that it adds. + * + * Check all cylinder groups up to the summary information. + */ + csblkno = fragstoblks(fs, fs->fs_csaddr); + for (cg = 0; cg < fs->fs_ncg; cg++) { + if (fragstoblks(fs, cgtod(fs, cg)) > csblkno) + break; + CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg))); + if (status == 0) + goto out; + } + /* Check the summary information block(s). */ + len = howmany(fs->fs_cssize, fs->fs_bsize); + for (loc = 0; loc < len; loc++) { + CHKBLKINLIST(csblkno + loc); + if (status == 0) + goto out; + } + /* Check the remaining cylinder groups. */ + for (; cg < fs->fs_ncg; cg++) { + CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg))); + if (status == 0) + goto out; + } +out: + brelse(bp); + return (status); +} + +/* + * Return the block number associated with a specified inode lbn. + * Requested lbn is in id_parent. If found, block is returned in + * id_parent. + */ +static int +getlbnblkno(struct inodesc *idesc) +{ + + if (idesc->id_lbn < idesc->id_parent) + return (KEEPON); + idesc->id_parent = idesc->id_blkno; + return (STOP | FOUND); +} + +/* * Open a device or file to be checked by fsck. */ int @@ -218,14 +373,14 @@ openfilesys(char *dev) if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) { - pfatal("BACKGROUND FSCK LACKS A SNAPSHOT\n"); - exit(EEXIT); + pwarn("BACKGROUND FSCK LACKS A SNAPSHOT\n"); + return (0); } if (bkgrdflag != 0) { cursnapshot = statb.st_ino; } else { - pfatal("%s IS NOT A DISK DEVICE\n", dev); - if (reply("CONTINUE") == 0) + pwarn("%s IS NOT A DISK DEVICE\n", dev); + if (preen || reply("CONTINUE") == 0) return (0); } } @@ -243,40 +398,64 @@ openfilesys(char *dev) * Read in the super block and its summary info. */ int -readsb(int listerr) +readsb(void) { - off_t super; - int ret, flags; struct fs *fs; - super = bflag ? bflag * dev_bsize : UFS_STDSB; - flags = sbhashfailed ? UFS_NOHASHFAIL | UFS_NOMSG : UFS_NOMSG; + sbhashfailed = 0; readcnt[sblk.b_type]++; - while ((ret = sbget(fsreadfd, &fs, super, flags)) != 0) { - switch (ret) { + /* + * If bflag is given, then check just that superblock. + */ + if (bflag) { + switch (sbget(fsreadfd, &fs, bflag * dev_bsize, 0)) { + case 0: + goto goodsb; case EINTEGRITY: - if (bflag || (super == UFS_STDSB && - flags == (UFS_NOHASHFAIL | UFS_NOMSG))) - return (0); - super = UFS_STDSB; - flags = UFS_NOHASHFAIL | UFS_NOMSG; - sbhashfailed = 1; - continue; + printf("Check hash failed for superblock at %jd\n", + bflag); + return (0); case ENOENT: - if (bflag) - printf("%jd is not a file system " - "superblock\n", super / dev_bsize); - else - printf("Cannot find file system " - "superblock\n"); + printf("%jd is not a file system superblock\n", bflag); return (0); case EIO: default: - printf("I/O error reading %jd\n", - super / dev_bsize); + printf("I/O error reading %jd\n", bflag); return (0); } } + /* + * Check for the standard superblock and use it if good. + */ + if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG) == 0) + goto goodsb; + /* + * Check if the only problem is a check-hash failure. + */ + skipclean = 0; + if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG | UFS_NOHASHFAIL) == 0) { + sbhashfailed = 1; + goto goodsb; + } + /* + * Do an exhaustive search for a usable superblock. + */ + switch (sbsearch(fsreadfd, &fs, 0)) { + case 0: + goto goodsb; + case ENOENT: + printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. " + "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY " + "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO " + "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n"); + return (0); + case EIO: + default: + printf("I/O error reading a usable superblock\n"); + return (0); + } + +goodsb: memcpy(&sblock, fs, fs->fs_sbsize); free(fs); /* @@ -315,7 +494,7 @@ sblock_init(void) fsmodified = 0; lfdir = 0; initbarea(&sblk, BT_SUPERBLK); - sblk.b_un.b_buf = Malloc(SBLOCKSIZE); + sblk.b_un.b_buf = Balloc(SBLOCKSIZE); if (sblk.b_un.b_buf == NULL) errx(EEXIT, "cannot allocate space for superblock"); dev_bsize = secsize = DEV_BSIZE; @@ -344,7 +523,7 @@ calcsb(char *dev, int devfd, struct fs *fs) */ if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1) return (0); - fsrbuf = Malloc(secsize); + fsrbuf = Balloc(secsize); if (fsrbuf == NULL) errx(EEXIT, "calcsb: cannot allocate recovery buffer"); if (blread(devfd, fsrbuf, @@ -387,7 +566,7 @@ chkrecovery(int devfd) rdsize = sblock.fs_fsize; if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 || rdsize % secsize != 0 || - (fsrbuf = Malloc(rdsize)) == NULL || + (fsrbuf = Balloc(rdsize)) == NULL || blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize) != 0) { free(fsrbuf); @@ -426,7 +605,7 @@ saverecovery(int readfd, int writefd) if (sblock.fs_magic != FS_UFS2_MAGIC || ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 || rdsize % secsize != 0 || - (fsrbuf = Malloc(rdsize)) == NULL || + (fsrbuf = Balloc(rdsize)) == NULL || blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize) != 0) { printf("RECOVERY DATA COULD NOT BE CREATED\n"); diff --git a/sbin/fsck_ffs/suj.c b/sbin/fsck_ffs/suj.c index 4ad86b0dc14d..73be60c5e5b4 100644 --- a/sbin/fsck_ffs/suj.c +++ b/sbin/fsck_ffs/suj.c @@ -1,5 +1,5 @@ /*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2009, 2010 Jeffrey W. Roberson <jeff@FreeBSD.org> * All rights reserved. @@ -26,9 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/disk.h> #include <sys/disklabel.h> @@ -49,7 +46,6 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> #include <stdint.h> -#include <libufs.h> #include <string.h> #include <strings.h> #include <sysexits.h> @@ -115,6 +111,7 @@ static TAILQ_HEAD(seghd, suj_seg) allsegs; static uint64_t oldseq; static struct fs *fs = NULL; static ino_t sujino; +static char *joptype[JOP_NUMJOPTYPES] = JOP_NAMES; /* * Summary statistics. @@ -183,7 +180,7 @@ cg_lookup(int cgx) if (lastcg && lastcg->sc_cgx == cgx) return (lastcg); cgbp = cglookup(cgx); - if (!check_cgmagic(cgx, cgbp, 0)) + if (!check_cgmagic(cgx, cgbp)) err_suj("UNABLE TO REBUILD CYLINDER GROUP %d", cgx); hd = &cghash[HASH(cgx)]; LIST_FOREACH(sc, hd, sc_next) @@ -321,7 +318,7 @@ blk_freemask(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags) * To be certain we're not freeing a reallocated block we lookup * this block in the blk hash and see if there is an allocation * journal record that overlaps with any fragments in the block - * we're concerned with. If any fragments have ben reallocated + * we're concerned with. If any fragments have been reallocated * the block has already been freed and re-used for another purpose. */ mask = 0; @@ -379,6 +376,50 @@ blk_isindir(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn) } /* + * Check to see if the requested block is available. + * We can just check in the cylinder-group maps as + * they will only have usable blocks in them. + */ +ufs2_daddr_t +suj_checkblkavail(ufs2_daddr_t blkno, long frags) +{ + struct bufarea *cgbp; + struct cg *cgp; + ufs2_daddr_t j, k, baseblk; + long cg; + + if ((u_int64_t)blkno > sblock.fs_size) + return (0); + cg = dtog(&sblock, blkno); + cgbp = cglookup(cg); + cgp = cgbp->b_un.b_cg; + if (!check_cgmagic(cg, cgbp)) + return (-((cg + 1) * sblock.fs_fpg - sblock.fs_frag)); + baseblk = dtogd(&sblock, blkno); + for (j = 0; j <= sblock.fs_frag - frags; j++) { + if (!isset(cg_blksfree(cgp), baseblk + j)) + continue; + for (k = 1; k < frags; k++) + if (!isset(cg_blksfree(cgp), baseblk + j + k)) + break; + if (k < frags) { + j += k; + continue; + } + for (k = 0; k < frags; k++) + clrbit(cg_blksfree(cgp), baseblk + j + k); + n_blks += frags; + if (frags == sblock.fs_frag) + cgp->cg_cs.cs_nbfree--; + else + cgp->cg_cs.cs_nffree -= frags; + cgdirty(cgbp); + return ((cg * sblock.fs_fpg) + baseblk + j); + } + return (0); +} + +/* * Clear an inode from the cg bitmap. If the inode was already clear return * 0 so the caller knows it does not have to check the inode contents. */ @@ -420,7 +461,7 @@ ino_free(ino_t ino, int mode) * set in the mask. */ static void -blk_free(ufs2_daddr_t bno, int mask, int frags) +blk_free(ino_t ino, ufs2_daddr_t bno, int mask, int frags) { ufs1_daddr_t fragno, cgbno; struct suj_cg *sc; @@ -431,6 +472,13 @@ blk_free(ufs2_daddr_t bno, int mask, int frags) if (debug) printf("Freeing %d frags at blk %jd mask 0x%x\n", frags, bno, mask); + /* + * Check to see if the block needs to be claimed by a snapshot. + * If wanted, the snapshot references it. Otherwise we free it. + */ + if (snapblkfree(fs, bno, lfragtosize(fs, frags), ino, + suj_checkblkavail)) + return; cg = dtog(fs, bno); sc = cg_lookup(cg); cgp = sc->sc_cgp; @@ -451,7 +499,8 @@ blk_free(ufs2_daddr_t bno, int mask, int frags) * deallocate the fragment */ for (i = 0; i < frags; i++) - if ((mask & (1 << i)) == 0 && isclr(blksfree, cgbno +i)) { + if ((mask & (1 << i)) == 0 && + isclr(blksfree, cgbno +i)) { freefrags++; setbit(blksfree, cgbno + i); } @@ -685,7 +734,7 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags, lbnadd *= NINDIR(fs); bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level); if (bp->b_errs != 0) - err_suj("indir_visit: UNRECOVERABLE I/O ERROR"); + err_suj("indir_visit: UNRECOVERABLE I/O ERROR\n"); for (i = 0; i < NINDIR(fs); i++) { if ((nblk = IBLK(bp, i)) == 0) continue; @@ -846,7 +895,7 @@ static void blk_free_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) { - blk_free(blk, blk_freemask(blk, ino, lbn, frags), frags); + blk_free(ino, blk, blk_freemask(blk, ino, lbn, frags), frags); } /* @@ -865,7 +914,7 @@ blk_free_lbn(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags, int follow) if (lbn <= -UFS_NDADDR && follow && mask == 0) indir_visit(ino, lbn, blk, &resid, blk_free_visit, VISIT_INDIR); else - blk_free(blk, mask, frags); + blk_free(ino, blk, mask, frags); } static void @@ -997,6 +1046,8 @@ ino_reclaim(struct inode *ip, ino_t ino, int mode) if ((DIP(dp, di_mode) & IFMT) == IFDIR) ino_visit(dp, ino, ino_free_children, 0); DIP_SET(dp, di_nlink, 0); + if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) + snapremove(ino); ino_visit(dp, ino, blk_free_visit, VISIT_EXT | VISIT_INDIR); /* Here we have to clear the inode and release any blocks it holds. */ gen = DIP(dp, di_gen); @@ -1209,7 +1260,7 @@ indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn, continue; } isdirty = 1; - blk_free(nblk, 0, fs->fs_frag); + blk_free(ino, nblk, 0, fs->fs_frag); IBLK_SET(bp, i, 0); } if (isdirty) @@ -1245,9 +1296,14 @@ ino_trunc(ino_t ino, off_t size) dp = ip.i_dp; mode = DIP(dp, di_mode) & IFMT; cursize = DIP(dp, di_size); + /* If no size change, nothing to do */ + if (size == cursize) { + irelse(&ip); + return; + } if (debug) - printf("Truncating ino %ju, mode %o to size %jd from size %jd\n", - (uintmax_t)ino, mode, size, cursize); + printf("Truncating ino %ju, mode %o to size %jd from " + "size %jd\n", (uintmax_t)ino, mode, size, cursize); /* Skip datablocks for short links and devices. */ if (mode == 0 || mode == IFBLK || mode == IFCHR || @@ -1264,13 +1320,14 @@ ino_trunc(ino_t ino, off_t size) if (size > 0) err_suj("Partial truncation of ino %ju snapshot file\n", (uintmax_t)ino); + snapremove(ino); } lastlbn = lblkno(fs, blkroundup(fs, size)); for (i = lastlbn; i < UFS_NDADDR; i++) { if ((bn = DIP(dp, di_db[i])) == 0) continue; blksize = sblksize(fs, cursize, i); - blk_free(bn, 0, numfrags(fs, blksize)); + blk_free(ino, bn, 0, numfrags(fs, blksize)); DIP_SET(dp, di_db[i], 0); } /* @@ -1283,13 +1340,13 @@ ino_trunc(ino_t ino, off_t size) /* If we're not freeing any in this indirect range skip it. */ if (lastlbn >= nextlbn) continue; - if (DIP(dp, di_ib[i]) == 0) - continue; - indir_trunc(ino, -lbn - i, DIP(dp, di_ib[i]), lastlbn, dp); - /* If we freed everything in this indirect free the indir. */ - if (lastlbn > lbn) - continue; - blk_free(DIP(dp, di_ib[i]), 0, fs->fs_frag); + if ((bn = DIP(dp, di_ib[i])) == 0) + continue; + indir_trunc(ino, -lbn - i, bn, lastlbn, dp); + /* If we freed everything in this indirect free the indir. */ + if (lastlbn > lbn) + continue; + blk_free(ino, bn, 0, fs->fs_frag); DIP_SET(dp, di_ib[i], 0); } /* @@ -1319,7 +1376,7 @@ ino_trunc(ino_t ino, off_t size) if (oldspace != newspace) { bn += numfrags(fs, newspace); frags = numfrags(fs, oldspace - newspace); - blk_free(bn, 0, frags); + blk_free(ino, bn, 0, frags); totalfrags -= frags; } } @@ -1386,9 +1443,9 @@ ino_check(struct suj_ino *sino) err_suj("Inode mode/directory type mismatch %o != %o\n", mode, rrec->jr_mode); if (debug) - printf("jrefrec: op %d ino %ju, nlink %ju, parent %ju, " + printf("jrefrec: op %s ino %ju, nlink %ju, parent %ju, " "diroff %jd, mode %o, isat %d, isdot %d\n", - rrec->jr_op, (uintmax_t)rrec->jr_ino, + JOP_OPTYPE(rrec->jr_op), (uintmax_t)rrec->jr_ino, (uintmax_t)rrec->jr_nlink, (uintmax_t)rrec->jr_parent, (uintmax_t)rrec->jr_diroff, @@ -1452,9 +1509,10 @@ blk_check(struct suj_blk *sblk) sino->si_blkadj = 1; } if (debug) - printf("op %d blk %jd ino %ju lbn %jd frags %d isat %d (%d)\n", - brec->jb_op, blk, (uintmax_t)brec->jb_ino, - brec->jb_lbn, brec->jb_frags, isat, frags); + printf("op %s blk %jd ino %ju lbn %jd frags %d isat %d " + "(%d)\n", JOP_OPTYPE(brec->jb_op), blk, + (uintmax_t)brec->jb_ino, brec->jb_lbn, + brec->jb_frags, isat, frags); /* * If we found the block at this address we still have to * determine if we need to free the tail end that was @@ -1468,7 +1526,7 @@ blk_check(struct suj_blk *sblk) mask >>= frags; blk += frags; frags = brec->jb_frags - frags; - blk_free(blk, mask, frags); + blk_free(brec->jb_ino, blk, mask, frags); continue; } /* @@ -1636,9 +1694,9 @@ ino_append(union jrec *rec) (uintmax_t)mvrec->jm_newoff, (uintmax_t)mvrec->jm_oldoff); else if (debug && (refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF)) - printf("ino ref: op %d, ino %ju, nlink %ju, " + printf("ino ref: op %s, ino %ju, nlink %ju, " "parent %ju, diroff %jd\n", - refrec->jr_op, (uintmax_t)refrec->jr_ino, + JOP_OPTYPE(refrec->jr_op), (uintmax_t)refrec->jr_ino, (uintmax_t)refrec->jr_nlink, (uintmax_t)refrec->jr_parent, (uintmax_t)refrec->jr_diroff); sino = ino_lookup(((struct jrefrec *)rec)->jr_ino, 1); @@ -1798,8 +1856,8 @@ ino_build_ref(struct suj_ino *sino, struct suj_rec *srec) TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); break; default: - err_suj("ino_build_ref: Unknown op %d\n", - srn->sr_rec->rec_jrefrec.jr_op); + err_suj("ino_build_ref: Unknown op %s\n", + JOP_OPTYPE(srn->sr_rec->rec_jrefrec.jr_op)); } } ino_add_ref(sino, srec); @@ -1828,8 +1886,8 @@ ino_build(struct suj_ino *sino) TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next); break; default: - err_suj("ino_build: Unknown op %d\n", - srec->sr_rec->rec_jrefrec.jr_op); + err_suj("ino_build: Unknown op %s\n", + JOP_OPTYPE(srec->sr_rec->rec_jrefrec.jr_op)); } } if (TAILQ_EMPTY(&sino->si_recs)) @@ -1851,14 +1909,17 @@ blk_build(struct jblkrec *blkrec) int frag; if (debug) - printf("blk_build: op %d blkno %jd frags %d oldfrags %d " + printf("blk_build: op %s blkno %jd frags %d oldfrags %d " "ino %ju lbn %jd\n", - blkrec->jb_op, (uintmax_t)blkrec->jb_blkno, + JOP_OPTYPE(blkrec->jb_op), (uintmax_t)blkrec->jb_blkno, blkrec->jb_frags, blkrec->jb_oldfrags, (uintmax_t)blkrec->jb_ino, (uintmax_t)blkrec->jb_lbn); blk = blknum(fs, blkrec->jb_blkno); frag = fragnum(fs, blkrec->jb_blkno); + if (blkrec->jb_blkno < 0 || blk + fs->fs_frag - frag > fs->fs_size) + err_suj("Out-of-bounds journal block number %jd\n", + blkrec->jb_blkno); sblk = blk_lookup(blk, 1); /* * Rewrite the record using oldfrags to indicate the offset into @@ -1906,6 +1967,9 @@ ino_build_trunc(struct jtrncrec *rec) printf("ino_build_trunc: op %d ino %ju, size %jd\n", rec->jt_op, (uintmax_t)rec->jt_ino, (uintmax_t)rec->jt_size); + if (chkfilesize(IFREG, rec->jt_size) == 0) + err_suj("ino_build: truncation size too large %ju\n", + (intmax_t)rec->jt_size); sino = ino_lookup(rec->jt_ino, 1); if (rec->jt_op == JOP_SYNC) { sino->si_trunc = NULL; @@ -1952,8 +2016,8 @@ suj_build(void) ino_build_trunc((struct jtrncrec *)rec); break; default: - err_suj("Unknown journal operation %d (%d)\n", - rec->rec_jrefrec.jr_op, off); + err_suj("Unknown journal operation %s at %d\n", + JOP_OPTYPE(rec->rec_jrefrec.jr_op), off); } i++; } @@ -2067,7 +2131,9 @@ suj_verifyino(union dinode *dp) } if (DIP(dp, di_modrev) != fs->fs_mtime) { - printf("Journal timestamp does not match fs mount time\n"); + if (!bkgrdcheck || debug) + printf("Journal timestamp does not match " + "fs mount time\n"); return (-1); } @@ -2194,7 +2260,7 @@ jblocks_add(struct jblocks *jblocks, ufs2_daddr_t daddr, int blocks) /* * Add a file block from the journal to the extent map. We can't read * each file block individually because the kernel treats it as a circular - * buffer and segments may span mutliple contiguous blocks. + * buffer and segments may span multiple contiguous blocks. */ static void suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) @@ -2206,7 +2272,7 @@ suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) static void suj_read(void) { - uint8_t block[1 * 1024 * 1024]; + uint8_t block[1 * 1024 * 1024] __aligned(LIBUFS_BUFALIGN); struct suj_seg *seg; struct jsegrec *recn; struct jsegrec *rec; @@ -2270,8 +2336,8 @@ restart: recsize <= fs->fs_bsize) goto restart; if (debug) - printf("Found invalid segsize %d > %d\n", - recsize, size); + printf("Found invalid segsize " + "%d > %d\n", recsize, size); recsize = real_dev_bsize; jblocks_advance(suj_jblocks, recsize); continue; @@ -2312,7 +2378,7 @@ suj_check(const char *filesys) { struct inodesc idesc; struct csum *cgsum; - union dinode *jip; + union dinode *dp, *jip; struct inode ip; uint64_t blocks; int i, retval; @@ -2354,11 +2420,23 @@ suj_check(const char *filesys) idesc.id_func = findino; idesc.id_name = SUJ_FILE; ginode(UFS_ROOTINO, &ip); - if ((ckinode(ip.i_dp, &idesc) & FOUND) == FOUND) { + dp = ip.i_dp; + if ((DIP(dp, di_mode) & IFMT) != IFDIR) { + irelse(&ip); + err_suj("root inode is not a directory\n"); + } + if (DIP(dp, di_size) < 0 || DIP(dp, di_size) > MAXDIRSIZE) { + irelse(&ip); + err_suj("negative or oversized root directory %jd\n", + (uintmax_t)DIP(dp, di_size)); + } + if ((ckinode(dp, &idesc) & FOUND) == FOUND) { sujino = idesc.id_parent; irelse(&ip); } else { - printf("Journal inode removed. Use tunefs to re-create.\n"); + if (!bkgrdcheck || debug) + printf("Journal inode removed. " + "Use tunefs to re-create.\n"); sblock.fs_flags &= ~FS_SUJ; sblock.fs_sujfree = 0; irelse(&ip); @@ -2369,7 +2447,8 @@ suj_check(const char *filesys) */ ginode(sujino, &ip); jip = ip.i_dp; - printf("** SU+J Recovering %s\n", filesys); + if (!bkgrdcheck || debug) + printf("** SU+J Recovering %s\n", filesys); if (suj_verifyino(jip) != 0 || (!preen && !reply("USE JOURNAL"))) { irelse(&ip); return (-1); @@ -2378,15 +2457,23 @@ suj_check(const char *filesys) * Build a list of journal blocks in jblocks before parsing the * available journal blocks in with suj_read(). */ - printf("** Reading %jd byte journal from inode %ju.\n", - DIP(jip, di_size), (uintmax_t)sujino); + if (!bkgrdcheck || debug) + printf("** Reading %jd byte journal from inode %ju.\n", + DIP(jip, di_size), (uintmax_t)sujino); suj_jblocks = jblocks_create(); blocks = ino_visit(jip, sujino, suj_add_block, 0); if (blocks != numfrags(fs, DIP(jip, di_size))) { - printf("Sparse journal inode %ju.\n", (uintmax_t)sujino); + if (!bkgrdcheck || debug) + printf("Sparse journal inode %ju.\n", + (uintmax_t)sujino); irelse(&ip); return (-1); } + /* If journal is valid then do journal check rather than background */ + if (bkgrdcheck) { + irelse(&ip); + return (0); + } irelse(&ip); suj_read(); jblocks_destroy(suj_jblocks); @@ -2404,9 +2491,17 @@ suj_check(const char *filesys) cg_apply(cg_adj_blk); cg_apply(cg_check_ino); } - if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0) + if (preen == 0 && (jrecs > 0 || jbytes > 0) && + reply("WRITE CHANGES") == 0) return (0); /* + * Check block counts of snapshot inodes and + * make copies of any needed snapshot blocks. + */ + for (i = 0; i < snapcnt; i++) + check_blkcnt(&snaplist[i]); + snapflush(suj_checkblkavail); + /* * Recompute the fs summary info from correct cs summaries. */ bzero(&fs->fs_cstotal, sizeof(struct csum_total)); @@ -2425,10 +2520,11 @@ suj_check(const char *filesys) sbdirty(); ckfini(1); if (jrecs > 0 || jbytes > 0) { - printf("** %jd journal records in %jd bytes for %.2f%% utilization\n", - jrecs, jbytes, ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100); - printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd frags.\n", - freeinos, freedir, freeblocks, freefrags); + printf("** %jd journal records in %jd bytes for %.2f%% " + "utilization\n", jrecs, jbytes, + ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100); + printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd " + "frags.\n", freeinos, freedir, freeblocks, freefrags); } return (0); diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c index 4f56f1ed8c26..56655089b9d6 100644 --- a/sbin/fsck_ffs/utilities.c +++ b/sbin/fsck_ffs/utilities.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> |