From nobody Fri Feb 04 19:48:05 2022 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 2C3B219B8503; Fri, 4 Feb 2022 19:48:06 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Jr5gB0kt9z4Rmv; Fri, 4 Feb 2022 19:48:06 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1644004086; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=pL7RDojBpgW5e7PUjJnWTuVNc0f0lWSaEjUUktvKZtY=; b=kqw2YHHZ0k+7h9ZxuTPpCkRAXx3+cNQ5DbTe8/5dKQ9CG4AczqhBZVkaDm8iEvSt7W/Uzx hrYHR9gO7MPppqzd6DThgHDdRDaWh5aMhO34dKwK/oWCggT9OAMQUintC9QIF4B/Th6uF7 1IsdXNLIGOjUtiw/tJ733M8GDZ+PBJE2Lnrorg8UaPR56bJG+p/wXecOhB4g/GcZWhLxdV AUSD1xUzuqMSzq1FGN95DjZGWpmgs3mDJDyIwIzCPfoMIHVNifDNFfJNQbNx99j6PMImKj eYEz9XjxjSKfTDEnnDiTmE05aREhNXRKOY9PC48tcnKvfOqimwwLJ8z2FUdEFw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id E941911C62; Fri, 4 Feb 2022 19:48:05 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 214Jm56Q079248; Fri, 4 Feb 2022 19:48:05 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 214Jm51o079247; Fri, 4 Feb 2022 19:48:05 GMT (envelope-from git) Date: Fri, 4 Feb 2022 19:48:05 GMT Message-Id: <202202041948.214Jm51o079247@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kirk McKusick Subject: git: c0bfa109b942 - main - Have fsck_ffs(8) properly correct superblock check-hash failures. List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: mckusick X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: c0bfa109b942659f609b7e2bf3ba042ec0cb3f9d Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1644004086; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=pL7RDojBpgW5e7PUjJnWTuVNc0f0lWSaEjUUktvKZtY=; b=xQCkh6DOP7BKxQrVSV2rXctWab5+TehEdun3Q4wIrYHT8g70fCxnZcGd+s5yFqAGypcSg5 y8vMix2cbIPQ2Am40F2TLP5TGS+nZvPj6IacrYNRGjcx1fS9KhouD80m7EB1gWjxk4KX9H DgN41MYhN9dCwaPsc0hX10Be51eTQB4PSt8wTW75X4O9tgxPX8qTbUBCgG9MmuH/uixsu7 mIuaL2fa3VRcQnmV7KGV09tpYo0TNeooP6RhNyg60CaIcoo5Iy5djBnhS4hqnhtG4bFQXa mrCDDjEUL6JXiqQck7tNDJo/fQOzJG9X+0yo+h/oq0Agao5+50xujYalWalObA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1644004086; a=rsa-sha256; cv=none; b=vdPEzUmOFd6jBf2eks23ga7epaCxIwokWB1ojfQiof4OnjHEkVLwzNIWIP2es7Svmputr0 CoBdY8Uja6xKRJblckWdgG3IBEWkh+QcY48w+lnjS05drE9ugqR1WKmXYIRXj2wEPK6pDE hAXWecrBE7XPSZizmkLKsc7M7wWVAXhRL+q4tMimTKbdehAyi/VlMIameC0RMthaN7p3wd Kn8cim0xVxG20/oOQf6Gy2urmrBslZ98ChNBjgVcflZyOYtw3iRNYyAxHIuQqMvXod3MRq kvn6gneooxHgBtsay27YhDKlxC4CrA9qTX7mJGfhHCHn6WeWKuiJoZ6rReCj8g== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by mckusick: URL: https://cgit.FreeBSD.org/src/commit/?id=c0bfa109b942659f609b7e2bf3ba042ec0cb3f9d commit c0bfa109b942659f609b7e2bf3ba042ec0cb3f9d Author: Kirk McKusick AuthorDate: 2022-02-04 19:46:36 +0000 Commit: Kirk McKusick CommitDate: 2022-02-04 19:47:48 +0000 Have fsck_ffs(8) properly correct superblock check-hash failures. Part of the problem was that fsck_ffs would read the superblock multiple times complaining and repairing the superblock check hash each time and then at the end failing to write out the superblock with the corrected check hash. This fix reads the superblock just once and if the check hash is corrected ensures that the fixed superblock gets written. Tested by: Peter Holm PR: 245916 MFC after: 1 week Sponsored by: Netflix --- sbin/fsck_ffs/fsck.h | 1 + sbin/fsck_ffs/fsutil.c | 4 +- sbin/fsck_ffs/globs.c | 6 +- sbin/fsck_ffs/main.c | 309 +++++++++++++++++++++++++++++++++---------------- sbin/fsck_ffs/setup.c | 182 +++++++---------------------- 5 files changed, 258 insertions(+), 244 deletions(-) diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 9ecc5793e644..690a98038884 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -356,6 +356,7 @@ extern char preen; /* just fix normal inconsistencies */ extern char rerun; /* rerun fsck. Only used in non-preen mode */ extern int returntosingle; /* 1 => return to single user mode on exit */ extern char resolved; /* cleared if unresolved changes => not clean */ +extern int sbhashfailed; /* when reading superblock check hash failed */ extern char havesb; /* superblock has been read */ extern char skipclean; /* skip clean file systems if preening */ extern int fsmodified; /* 1 => write done to file system */ diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c index db22ee5b20cf..711c9bb63549 100644 --- a/sbin/fsck_ffs/fsutil.c +++ b/sbin/fsck_ffs/fsutil.c @@ -250,6 +250,7 @@ cglookup(int cg) if (cgp == NULL) { if (sujrecovery) errx(EEXIT,"Ran out of memory during journal recovery"); + flush(fswritefd, &cgblk); getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); return (&cgblk); } @@ -564,7 +565,7 @@ ckfini(int markclean) cmd.size = markclean ? -1 : 1; if (sysctlbyname("vfs.ffs.setflags", 0, 0, &cmd, sizeof cmd) == -1) - rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN); + pwarn("CANNOT SET FILE SYSTEM DIRTY FLAG\n"); if (!preen) { printf("\n***** FILE SYSTEM MARKED %s *****\n", markclean ? "CLEAN" : "DIRTY"); @@ -575,6 +576,7 @@ ckfini(int markclean) printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); rerun = 1; } + bkgrdflag = 0; } if (debug && cachelookups > 0) printf("cache with %d buffers missed %d of %d (%d%%)\n", diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c index be4434ce38ca..09dbcc6694a2 100644 --- a/sbin/fsck_ffs/globs.c +++ b/sbin/fsck_ffs/globs.c @@ -96,6 +96,7 @@ 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 */ @@ -155,8 +156,9 @@ fsckinit(void) resolved = 0; havesb = 0; fsmodified = 0; - fsreadfd = 0; - fswritefd = 0; + sbhashfailed = 0; + fsreadfd = -1; + fswritefd = -1; maxfsblock = 0; maxino = 0; lfdir = 0; diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c index acf26d9fc558..e776add4a115 100644 --- a/sbin/fsck_ffs/main.c +++ b/sbin/fsck_ffs/main.c @@ -75,6 +75,8 @@ static int restarts; 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 openfilesys(char *dev); static int chkdoreload(struct statfs *mntp); static struct statfs *getmntpt(const char *); @@ -181,6 +183,11 @@ main(int argc, char *argv[]) if (!argc) usage(); + if (bkgrdflag && cvtlevel > 0) { + pfatal("CANNOT CONVERT A SNAPSHOT\n"); + exit(EEXIT); + } + if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void)signal(SIGINT, catch); if (ckclean) @@ -237,18 +244,10 @@ checkfilesys(char *filesys) ufs2_daddr_t n_ffree, n_bfree; struct dups *dp; struct statfs *mntp; - struct stat snapdir; - struct group *grp; - struct iovec *iov; - char errmsg[255]; - int ofsmodified; - int iovlen; intmax_t blks, files; size_t size; + int sbreadfailed, ofsmodified; - iov = NULL; - iovlen = 0; - errmsg[0] = '\0'; fsutilinit(); fsckinit(); @@ -272,10 +271,12 @@ checkfilesys(char *filesys) * exit status will cause a foreground check to be run. */ sblock_init(); + sbreadfailed = 0; + if (openfilesys(filesys) == 0 || readsb(0) == 0) + sbreadfailed = 1; if (bkgrdcheck) { - if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0) + if (sbreadfailed) exit(3); /* Cannot read superblock */ - close(fsreadfd); /* Earlier background failed or journaled */ if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) exit(4); @@ -293,7 +294,7 @@ checkfilesys(char *filesys) /* * If file system is gjournaled, check it here. */ - if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0) + if (sbreadfailed) exit(3); /* Cannot read superblock */ if (bkgrdflag == 0 && (nflag || (fswritefd = open(filesys, O_WRONLY)) < 0)) { @@ -307,107 +308,30 @@ checkfilesys(char *filesys) pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); exit(0); } - if ((sblock.fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) { + if ((sblock.fs_flags & + (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) { bufinit(); gjournal_check(filesys); if (chkdoreload(mntp) == 0) exit(0); exit(4); } else { - pfatal("UNEXPECTED INCONSISTENCY, CANNOT RUN " - "FAST FSCK\n"); + pfatal("FULL FSCK NEEDED, CANNOT RUN FAST " + "FSCK\n"); } } - close(fsreadfd); close(fswritefd); + fswritefd = -1; } - /* - * If we are to do a background check: - * Get the mount point information of the file system - * create snapshot file - * return created snapshot file - * if not found, clear bkgrdflag and proceed with normal fsck - */ if (bkgrdflag) { - /* Get the mount point information of the file system */ - if (mntp == NULL) { - bkgrdflag = 0; - pfatal("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n"); - } else if ((mntp->f_flags & MNT_SOFTDEP) == 0) { - bkgrdflag = 0; - pfatal("NOT USING SOFT UPDATES, CANNOT RUN IN " - "BACKGROUND\n"); - } else if ((mntp->f_flags & MNT_RDONLY) != 0) { - bkgrdflag = 0; - pfatal("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n"); - } else if ((fsreadfd = open(filesys, O_RDONLY)) >= 0) { - if (readsb(0) != 0) { - if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) { - bkgrdflag = 0; - pfatal( - "UNEXPECTED INCONSISTENCY, CANNOT RUN IN BACKGROUND\n"); - } - if ((sblock.fs_flags & FS_UNCLEAN) == 0 && - skipclean && ckclean) { - /* - * file system is clean; - * skip snapshot and report it clean - */ - pwarn( - "FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); - goto clean; - } - } - close(fsreadfd); - } - if (bkgrdflag) { - snprintf(snapname, sizeof snapname, "%s/.snap", - mntp->f_mntonname); - if (stat(snapname, &snapdir) < 0) { - if (errno != ENOENT) { - bkgrdflag = 0; - pfatal( - "CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n", - snapname, strerror(errno)); - } else if ((grp = getgrnam("operator")) == NULL || - mkdir(snapname, 0770) < 0 || - chown(snapname, -1, grp->gr_gid) < 0 || - chmod(snapname, 0770) < 0) { - bkgrdflag = 0; - pfatal( - "CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n", - snapname, strerror(errno)); - } - } else if (!S_ISDIR(snapdir.st_mode)) { - bkgrdflag = 0; - pfatal( - "%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n", - snapname); - } - } - if (bkgrdflag) { - snprintf(snapname, sizeof snapname, - "%s/.snap/fsck_snapshot", mntp->f_mntonname); - build_iovec(&iov, &iovlen, "fstype", "ffs", 4); - build_iovec(&iov, &iovlen, "from", snapname, - (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, "snapshot", NULL, 0); - - while (nmount(iov, iovlen, mntp->f_flags) < 0) { - if (errno == EEXIST && unlink(snapname) == 0) - continue; - bkgrdflag = 0; - pfatal("CANNOT CREATE SNAPSHOT %s: %s %s\n", - snapname, strerror(errno), errmsg); - break; - } - if (bkgrdflag != 0) - filesys = snapname; + switch (setup_bkgrdchk(mntp, sbreadfailed, &filesys)) { + case -1: /* filesystem clean */ + goto clean; + case 0: /* cannot do background, give up */ + exit(EEXIT); + case 1: /* doing background check, preen rules apply */ + preen = 1; + break; } } @@ -648,6 +572,187 @@ checkfilesys(char *filesys) return (rerun ? ERERUN : 0); } +/* + * If we are to do a background check: + * Get the mount point information of the file system + * If already clean, return -1 + * Check that kernel supports background fsck + * Find or create the snapshot directory + * Create the snapshot file + * Open snapshot + * If anything fails print reason and return 0 which exits + */ +static int +setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys) +{ + struct stat snapdir; + struct group *grp; + struct iovec *iov; + char errmsg[255]; + int iovlen; + long size; + + /* Get the mount point information of the file system */ + if (mntp == NULL) { + pwarn("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n"); + return (0); + } + if ((mntp->f_flags & MNT_RDONLY) != 0) { + pwarn("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n"); + return (0); + } + if ((mntp->f_flags & MNT_SOFTDEP) == 0) { + pwarn("NOT USING SOFT UPDATES, CANNOT RUN IN BACKGROUND\n"); + return (0); + } + if (sbreadfailed) { + pwarn("SUPERBLOCK READ FAILED, CANNOT RUN IN BACKGROUND\n"); + return (0); + } + if ((sblock.fs_flags & FS_NEEDSFSCK) != 0) { + 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) { + /* + * file system is clean; + * skip snapshot and report it clean + */ + pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); + return (-1); + } + /* Check that kernel supports background fsck */ + size = MIBSIZE; + if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0|| + sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0|| + sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 || + sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0|| + sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 || + sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) { + pwarn("KERNEL LACKS BACKGROUND FSCK SUPPORT\n"); + return (0); + } + /* + * When kernel lacks runtime bgfsck superblock summary + * adjustment functionality, it does not mean we can not + * continue, as old kernels will recompute the summary at + * mount time. However, it will be an unexpected softupdates + * inconsistency if it turns out that the summary is still + * incorrect. Set a flag so subsequent operation can know this. + */ + bkgrdsumadj = 1; + if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 || + sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 || + sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 || + sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 || + sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, + &size) < 0) { + bkgrdsumadj = 0; + pwarn("KERNEL LACKS RUNTIME SUPERBLOCK SUMMARY ADJUSTMENT " + "SUPPORT\n"); + } + /* Find or create the snapshot directory */ + 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 " + "RUN IN BACKGROUND\n", snapname, strerror(errno)); + return (0); + } + if ((grp = getgrnam("operator")) == NULL || + mkdir(snapname, 0770) < 0 || + chown(snapname, -1, grp->gr_gid) < 0 || + chmod(snapname, 0770) < 0) { + pwarn("CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, " + "CANNOT RUN IN BACKGROUND\n", snapname, + strerror(errno)); + return (0); + } + } else if (!S_ISDIR(snapdir.st_mode)) { + pwarn("%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n", + snapname); + return (0); + } + /* Create the snapshot file */ + iov = NULL; + iovlen = 0; + errmsg[0] = '\0'; + snprintf(snapname, sizeof snapname, "%s/.snap/fsck_snapshot", + mntp->f_mntonname); + build_iovec(&iov, &iovlen, "fstype", "ffs", 4); + build_iovec(&iov, &iovlen, "from", snapname, (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, "snapshot", NULL, 0); + /* Create snapshot, removing old snapshot it it exists */ + while (nmount(iov, iovlen, mntp->f_flags) < 0) { + if (errno == EEXIST && unlink(snapname) == 0) + continue; + pwarn("CANNOT CREATE SNAPSHOT %s: %s %s\n", snapname, + strerror(errno), errmsg); + return (0); + } + /* Open snapshot */ + if (openfilesys(snapname) == 0) { + unlink(snapname); + pwarn("CANNOT OPEN SNAPSHOT %s: %s, CANNOT RUN IN " + "BACKGROUND\n", snapname, strerror(errno)); + return (0); + } + free(sblock.fs_csp); + free(sblock.fs_si); + havesb = 0; + *filesys = snapname; + cmd.version = FFS_CMD_VERSION; + cmd.handle = fsreadfd; + return (1); +} + +/* + * Open a device or file to be checked by fsck. + */ +static int +openfilesys(char *dev) +{ + struct stat statb; + int saved_fsreadfd; + + if (stat(dev, &statb) < 0) { + pfatal("CANNOT STAT %s: %s\n", dev, strerror(errno)); + return (0); + } + 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); + } + if (bkgrdflag != 0) { + cursnapshot = statb.st_ino; + } else { + pfatal("%s IS NOT A DISK DEVICE\n", dev); + if (reply("CONTINUE") == 0) + return (0); + } + } + saved_fsreadfd = fsreadfd; + if ((fsreadfd = open(dev, O_RDONLY)) < 0) { + fsreadfd = saved_fsreadfd; + pfatal("CANNOT OPEN %s: %s\n", dev, strerror(errno)); + return (0); + } + if (saved_fsreadfd != -1) + close(saved_fsreadfd); + return (1); +} + static int chkdoreload(struct statfs *mntp) { diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index 0ae7f1bbb28f..506a027f40ac 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -76,114 +76,19 @@ static int chkrecovery(int devfd); int setup(char *dev) { - long cg, asked, i, j; - long bmapsize; - struct stat statb; + long cg, bmapsize; struct fs proto; - size_t size; - havesb = 0; - fswritefd = -1; - cursnapshot = 0; - if (stat(dev, &statb) < 0) { - printf("Can't stat %s: %s\n", dev, strerror(errno)); - if (bkgrdflag) { - unlink(snapname); - bkgrdflag = 0; - } - return (0); - } - if ((statb.st_mode & S_IFMT) != S_IFCHR && - (statb.st_mode & S_IFMT) != S_IFBLK) { - if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) { - unlink(snapname); - printf("background fsck lacks a snapshot\n"); - exit(EEXIT); - } - if ((statb.st_flags & SF_SNAPSHOT) != 0 && cvtlevel == 0) { - cursnapshot = statb.st_ino; - } else { - if (cvtlevel == 0 || - (statb.st_flags & SF_SNAPSHOT) == 0) { - if (preen && bkgrdflag) { - unlink(snapname); - bkgrdflag = 0; - } - pfatal("%s is not a disk device", dev); - if (reply("CONTINUE") == 0) { - if (bkgrdflag) { - unlink(snapname); - bkgrdflag = 0; - } - return (0); - } - } else { - if (bkgrdflag) { - unlink(snapname); - bkgrdflag = 0; - } - pfatal("cannot convert a snapshot"); - exit(EEXIT); - } - } - } - if ((fsreadfd = open(dev, O_RDONLY)) < 0) { - if (bkgrdflag) { - unlink(snapname); - bkgrdflag = 0; - } - printf("Can't open %s: %s\n", dev, strerror(errno)); + /* + * We are expected to have an open file descriptor + */ + if (fsreadfd < 0) return (0); - } - if (bkgrdflag) { - unlink(snapname); - size = MIBSIZE; - if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0|| - sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0|| - sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 || - sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0|| - sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 || - sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) { - pfatal("kernel lacks background fsck support\n"); - exit(EEXIT); - } - /* - * When kernel is lack of runtime bgfsck superblock summary - * adjustment functionality, it does not mean we can not - * continue, as old kernels will recompute the summary at - * mount time. However, it will be an unexpected softupdates - * inconsistency if it turns out that the summary is still - * incorrect. Set a flag so subsequent operation can know - * this. - */ - bkgrdsumadj = 1; - if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 || - sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 || - sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 || - sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 || - sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, &size) < 0) { - bkgrdsumadj = 0; - pwarn("kernel lacks runtime superblock summary adjustment support"); - } - cmd.version = FFS_CMD_VERSION; - cmd.handle = fsreadfd; - fswritefd = -1; - } - if (preen == 0) - printf("** %s", dev); - if (bkgrdflag == 0 && - (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) { - fswritefd = -1; - if (preen) - pfatal("NO WRITE ACCESS"); - printf(" (NO WRITE)"); - } - if (preen == 0) - printf("\n"); /* - * Read in the superblock, looking for alternates if necessary + * If we do not yet have a superblock, read it in looking + * for alternates if necessary. */ - if (readsb(1) == 0) { + if (havesb == 0 && readsb(1) == 0) { skipclean = 0; if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) return(0); @@ -195,19 +100,38 @@ setup(char *dev) break; } if (cg >= proto.fs_ncg) { - printf("%s %s\n%s %s\n%s %s\n", - "SEARCH FOR ALTERNATE SUPER-BLOCK", - "FAILED. YOU MUST USE THE", - "-b OPTION TO FSCK TO SPECIFY THE", - "LOCATION OF AN ALTERNATE", - "SUPER-BLOCK TO SUPPLY NEEDED", - "INFORMATION; SEE fsck_ffs(8)."); + 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); } pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); bflag = 0; } + if (preen == 0) + printf("** %s", dev); + if (bkgrdflag == 0 && + (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) { + fswritefd = -1; + if (preen) + pfatal("NO WRITE ACCESS"); + printf(" (NO WRITE)"); + } + if (preen == 0) + printf("\n"); + if (sbhashfailed != 0) { + pwarn("SUPERBLOCK CHECK HASH FAILED"); + if (fswritefd == -1) + pwarn("OPENED READONLY SO CANNOT CORRECT CHECK HASH\n"); + else if (preen || reply("CORRECT CHECK HASH") != 0) { + if (preen) + printf(" (CORRECTED)\n"); + sblock.fs_clean = 0; + sbdirty(); + } + } if (skipclean && ckclean && sblock.fs_clean) { pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); return (-1); @@ -247,30 +171,6 @@ setup(char *dev) fswritefd != -1 && chkrecovery(fsreadfd) == 0 && reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0) saverecovery(fsreadfd, fswritefd); - /* - * read in the summary info. - */ - asked = 0; - sblock.fs_csp = Calloc(1, sblock.fs_cssize); - if (sblock.fs_csp == NULL) { - printf("cannot alloc %u bytes for cg summary info\n", - (unsigned)sblock.fs_cssize); - goto badsb; - } - for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { - size = MIN(sblock.fs_cssize - i, sblock.fs_bsize); - readcnt[sblk.b_type]++; - if (blread(fsreadfd, (char *)sblock.fs_csp + i, - fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), - size) != 0 && !asked) { - pfatal("BAD SUMMARY INFORMATION"); - if (reply("CONTINUE") == 0) { - ckfini(0); - exit(EEXIT); - } - asked++; - } - } /* * allocate and initialize the necessary maps */ @@ -320,13 +220,17 @@ readsb(int listerr) int bad, ret; struct fs *fs; - super = bflag ? bflag * dev_bsize : STDSB_NOHASHFAIL; + super = bflag ? bflag * dev_bsize : + sbhashfailed ? STDSB_NOHASHFAIL_NOMSG : STDSB_NOMSG; readcnt[sblk.b_type]++; - if ((ret = sbget(fsreadfd, &fs, super)) != 0) { + while ((ret = sbget(fsreadfd, &fs, super)) != 0) { switch (ret) { - case EINVAL: - /* Superblock check-hash failed */ - return (0); + case EINTEGRITY: + if (bflag || super == STDSB_NOHASHFAIL_NOMSG) + return (0); + super = STDSB_NOHASHFAIL_NOMSG; + sbhashfailed = 1; + continue; case ENOENT: if (bflag) printf("%jd is not a file system "