From nobody Sat Aug 13 19:45:22 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 4M4rcM0xk4z4Yp73; Sat, 13 Aug 2022 19:45:23 +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 4M4rcM0T6Vz3tB7; Sat, 13 Aug 2022 19:45:23 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1660419923; 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=RBuvgp004QRdxvFdpG60kHzzW0T7IRZvxyJA4QGkLVs=; b=tpZfHobp19pkmWLu73aKFAwxx6Ap4v9Abmp2hYaVSW+y4/1JJLULDpJXe/R6VKLvv/B5Z0 8ka2WGqz1+ctrs6EL8975/2Zu6d3XLeapDkQ5J66aNR4m3MYqMm0WUBYQfkUMdvOjVdwQs klYQUi2HZvYa3+d5+kd5Pz7eXdut7tQtHJICAL9JSM8xb6sbmHRNpoLwPye3UIcW+xl3d7 C3O7oV39rUDq4Pk9+ZiWYyKhJ73BfY41qNJJBVH/4Pr+Q2KX+Iptjds4nS2hlGobXOuRLs mEuWfiYJLolBTbiY91l5Tg1Jw+2fiuiEdhjxGvDxZvxeWXUbmkh1otcn34hlzw== 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 4M4rcL6bzdzNQk; Sat, 13 Aug 2022 19:45:22 +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 27DJjMIl043448; Sat, 13 Aug 2022 19:45:22 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 27DJjMox043447; Sat, 13 Aug 2022 19:45:22 GMT (envelope-from git) Date: Sat, 13 Aug 2022 19:45:22 GMT Message-Id: <202208131945.27DJjMox043447@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: e68866164212 - main - Move the ability to search for alternate UFS superblocks from fsck_ffs(8) into ffs_sbsearch() to allow use by other parts of the system. 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: e68866164212d62b8158e93f09bd29554428eef0 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1660419923; 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=RBuvgp004QRdxvFdpG60kHzzW0T7IRZvxyJA4QGkLVs=; b=NvIWGRjFymtka5AYhrLyAzYNriIAtduGfbZEKXk7agTC/+wXpYKjG/UtgOsvnOCI1U+RiR y1yVzHHS5XfyVPcvkI0t7Fw8jIwgeJEIdEZzk6mVgQ57PMHUK58B9HFVRubOj0qpZ3thOw li227DHnV+BxFzV+gD5Vgs4AcSNcg2+Ii4phAEcEMCPXRpnrgT4XdCaQydt5AH/UvsLIYJ ubZasXnh50rjVdYAJFqLtdAx59QSnYymDiwJXe/7M+H+gf5iTktqBUtS8g+4+LddWaqLWb cgYrFKtygc5+hlFJuh64J+c1RcdyWHE6DY03DNzKT3LTMKI2Rm7B52qRHKYkZg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1660419923; a=rsa-sha256; cv=none; b=eFm0u9V3OUPcmelhdLWvbrAyB1sfvC+FwIalS1M+HbkiAX88t3fyi+1RYFNIL/F7Xcc2Rw agUybK86odUdM52eeshOX76JEWPUsAGedPjide1zljc4OeIE3OA2jy39/FmGg5O2rD/7tQ TuEGuc80GmsJTRjLVisZNFbyggHGBG9BF/bepi5ECB2eMIb3rFK50aTIykB8kSSWgESBxo s5W0c8jLc9+eI+hAWiK0ul9ulX9IJ5db1GnQHDC27fp1z/aA0Fkvn+3/G2eCOt52eI+cCR zXZe2hh4yu992jK/Cl6r+taVdx/j2p0q6DXQSLYUuIF53whkjTd12qIoKFw1nw== 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=e68866164212d62b8158e93f09bd29554428eef0 commit e68866164212d62b8158e93f09bd29554428eef0 Author: Kirk McKusick AuthorDate: 2022-08-13 19:41:53 +0000 Commit: Kirk McKusick CommitDate: 2022-08-13 19:43:40 +0000 Move the ability to search for alternate UFS superblocks from fsck_ffs(8) into ffs_sbsearch() to allow use by other parts of the system. Historically only fsck_ffs(8), the UFS filesystem checker, had code to track down and use alternate UFS superblocks. Since fsdb(8) used much of the fsck_ffs(8) implementation it had some ability to track down alternate superblocks. This change extracts the code to track down alternate superblocks from fsck_ffs(8) and puts it into a new function ffs_sbsearch() in sys/ufs/ffs/ffs_subr.c. Like ffs_sbget() and ffs_sbput() also found in ffs_subr.c, these functions can be used directly by the kernel subsystems. Additionally they are exported to the UFS library, libufs(8) so that they can be used by user-level programs. The new functions added to libufs(8) are sbfind(3) that is an alternative to sbread(3) and sbsearch(3) that is an alternative to sbget(3). See their manual pages for further details. The utilities that have been changed to search for superblocks are dumpfs(8), fsdb(8), ffsinfo(8), and fsck_ffs(8). Also, the prtblknos(8) tool found in tools/diag/prtblknos searches for superblocks. The UFS specific mount code uses the superblock search interface when mounting the root filesystem and when the administrator doing a mount(8) command specifies the force flag (-f). The standalone UFS boot code (found in stand/libsa/ufs.c) uses the superblock search code in the hope of being able to get the system up and running so that fsck_ffs(8) can be used to get the filesystem cleaned up. The following utilities have not been changed to search for superblocks: clri(8), tunefs(8), snapinfo(8), fstyp(8), quot(8), dump(8), fsirand(8), growfs(8), quotacheck(8), gjournal(8), and glabel(8). When these utilities fail, they do report the cause of the failure. The one exception is the tasting code used to try and figure what a given disk contains. The tasting code will remain silent so as not to put out a slew of messages as it trying to taste every new mass storage device that shows up. Reviewed by: kib Reviewed by: Warner Losh Tested by: Peter Holm Differential Revision: https://reviews.freebsd.org/D36053 Sponsored by: The FreeBSD Foundation --- lib/libufs/Makefile | 2 + lib/libufs/libufs.h | 4 + lib/libufs/sblock.c | 54 ++++++++++++-- lib/libufs/sbread.3 | 69 ++++++++++++++---- sbin/dumpfs/dumpfs.c | 9 +-- sbin/ffsinfo/ffsinfo.c | 5 +- sbin/fsck_ffs/fsck.h | 3 +- sbin/fsck_ffs/globs.c | 2 - sbin/fsck_ffs/main.c | 2 +- sbin/fsck_ffs/setup.c | 103 +++++++++++++------------- sbin/fsdb/fsdb.c | 2 +- stand/libsa/ufs.c | 6 +- sys/ufs/ffs/ffs_extern.h | 2 + sys/ufs/ffs/ffs_subr.c | 174 ++++++++++++++++++++++++++++++++++++++++++++ sys/ufs/ffs/ffs_vfsops.c | 13 ++-- sys/ufs/ffs/fs.h | 8 ++ tools/diag/prtblknos/main.c | 6 +- 17 files changed, 364 insertions(+), 100 deletions(-) diff --git a/lib/libufs/Makefile b/lib/libufs/Makefile index a767546f457f..623d5ae541a0 100644 --- a/lib/libufs/Makefile +++ b/lib/libufs/Makefile @@ -20,6 +20,8 @@ MLINKS+= cgread.3 cgput.3 MLINKS+= getinode.3 putinode.3 MLINKS+= sbread.3 sbwrite.3 MLINKS+= sbread.3 sbget.3 +MLINKS+= sbread.3 sbsearch.3 +MLINKS+= sbread.3 sbfind.3 MLINKS+= sbread.3 sbput.3 MLINKS+= ufs_disk_close.3 ufs_disk_fillout.3 MLINKS+= ufs_disk_close.3 ufs_disk_fillout_blank.3 diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h index cb8774454b38..5dbf9c2a5c28 100644 --- a/lib/libufs/libufs.h +++ b/lib/libufs/libufs.h @@ -111,6 +111,8 @@ void ffs_clusteracct(struct fs *, struct cg *, ufs1_daddr_t, int); void ffs_fragacct(struct fs *, int, int32_t [], int); int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); +int ffs_sbsearch(void *, struct fs **, int, char *, + int (*)(void *, off_t, void **, int)); void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_sbget(void *, struct fs **, off_t, int, char *, int (*)(void *, off_t, void **, int)); @@ -149,9 +151,11 @@ int putinode(struct uufsd *); * sblock.c */ int sbread(struct uufsd *); +int sbfind(struct uufsd *, int); int sbwrite(struct uufsd *, int); /* low level superblock read/write functions */ int sbget(int, struct fs **, off_t, int); +int sbsearch(int, struct fs **, int); int sbput(int, struct fs *, int); /* diff --git a/lib/libufs/sblock.c b/lib/libufs/sblock.c index 5708d50aa4f9..4dfa0e79a711 100644 --- a/lib/libufs/sblock.c +++ b/lib/libufs/sblock.c @@ -73,6 +73,30 @@ sbread(struct uufsd *disk) return (handle_disk_read(disk, fs, error)); } +/* + * Make an extensive search to find a superblock. If the superblock + * in the standard place cannot be used, try looking for one of the + * backup superblocks. + * + * The flags parameter is made up of the following or'ed together options: + * + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. + * + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxillary data structures like the cylinder group + * summary information. + */ +int +sbfind(struct uufsd *disk, int flags) +{ + struct fs *fs; + int error; + + error = sbsearch(disk->d_fd, &fs, flags); + return (handle_disk_read(disk, fs, error)); +} + static int handle_disk_read(struct uufsd *disk, struct fs *fs, int error) { @@ -174,8 +198,27 @@ static int use_pwrite(void *devfd, off_t loc, void *buf, int size); int sbget(int devfd, struct fs **fsp, off_t sblockloc, int flags) { + int error; + + error = ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread); + fflush(NULL); /* flush any messages */ + return (error); +} + +/* + * Make an extensive search of the devfd device to find a superblock. + * If the superblock in the standard place cannot be used, try looking + * for one of the backup superblocks. If found, memory is allocated and + * returned in fsp. + */ +int +sbsearch(int devfd, struct fs **fsp, int flags) +{ + int error; - return (ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread)); + error = ffs_sbsearch(&devfd, fsp, flags, "user", use_pread); + fflush(NULL); /* flush any messages */ + return (error); } /* @@ -209,11 +252,10 @@ sbput(int devfd, struct fs *fs, int numaltwrite) off_t savedactualloc; int i, error; - if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, - use_pwrite)) != 0) + error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite); + fflush(NULL); /* flush any messages */ + if (error != 0 || numaltwrite == 0) return (error); - if (numaltwrite == 0) - return (0); savedactualloc = fs->fs_sblockactualloc; if (fs->fs_si != NULL) { savedcsp = fs->fs_csp; @@ -223,6 +265,7 @@ sbput(int devfd, struct fs *fs, int numaltwrite) fs->fs_sblockactualloc = dbtob(fsbtodb(fs, cgsblock(fs, i))); if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite)) != 0) { + fflush(NULL); /* flush any messages */ fs->fs_sblockactualloc = savedactualloc; fs->fs_csp = savedcsp; return (error); @@ -231,6 +274,7 @@ sbput(int devfd, struct fs *fs, int numaltwrite) fs->fs_sblockactualloc = savedactualloc; if (fs->fs_si != NULL) fs->fs_csp = savedcsp; + fflush(NULL); /* flush any messages */ return (0); } diff --git a/lib/libufs/sbread.3 b/lib/libufs/sbread.3 index f579fc7ffdf1..460699842036 100644 --- a/lib/libufs/sbread.3 +++ b/lib/libufs/sbread.3 @@ -3,19 +3,21 @@ .\" Description: .\" Manual page for libufs functions: .\" sbget(3) +.\" sbsearch(3) .\" sbput(3) .\" sbread(3) +.\" sbfind(3) .\" sbwrite(3) .\" .\" This file is in the public domain. .\" .\" $FreeBSD$ .\" -.Dd September 2, 2020 +.Dd August 8, 2022 .Dt SBREAD 3 .Os .Sh NAME -.Nm sbget , sbput , sbread , sbwrite +.Nm sbget , sbsearch , sbput , sbread , sbfind , sbwrite .Nd read and write superblocks of a UFS file system .Sh LIBRARY .Lb libufs @@ -29,16 +31,22 @@ .Ft int .Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" "int flags" .Ft int +.Fn sbsearch "int devfd" "struct fs **fsp" "int flags" +.Ft int .Fn sbput "int devfd" "struct fs *fs" "int numaltwrite" .Ft int .Fn sbread "struct uufsd *disk" .Ft int +.Fn sbfind "struct uufsd *disk" "int flags" +.Ft int .Fn sbwrite "struct uufsd *disk" "int all" .Sh DESCRIPTION The -.Fn sbget +.Fn sbget , +.Fn sbsearch , +.Fn sbread , and -.Fn sbread +.Fn sbfind functions provide superblock reads for .Xr libufs 3 consumers. @@ -52,7 +60,9 @@ consumers. .Pp The .Fn sbget -function first allocates a buffer to hold the superblock. +and +.Fn sbsearch +functions first allocate a buffer to hold the superblock. Using the .Va devfd file descriptor that references the filesystem disk, @@ -65,26 +75,38 @@ The value may be specified for .Va sblockloc to request that the standard location for the superblock be read. +The +.Fn sbsearch +function uses the +.Va devfd +file descriptor that references the filesystem disk, +to search first for the superblock at the standard location. +If it is not found or is too damaged to use +.Fn sbsearch +will attempt to find one of the filesystem's alternate superblocks. Flags are specified by .Em or Ns 'ing the following values: .Pp -.Bl -tag -width UFS_NOHASHFAIL -.It Cm UFS_NOHASHFAIL -Will note if the check hash is wrong but will still return the superblock. +.Bl -tag -width UFS_NOCSUM +.It Cm UFS_NOCSUM +Causes only the superblock itself to be returned, but does not read in any +auxiliary data structures like the cylinder group summary information. .It Cm UFS_NOMSG Indicates that superblock inconsistency error messages should not be printed. -.It Cm UFS_NOCSUM -Causes only the superblock itself to be returned, but does not read in any auxiliary data structures like the cylinder group summary information. .El .Pp If successful, .Fn sbget -returns a pointer to the buffer containing the superblock in +and +.Fn sbsearch +functions return a pointer to the buffer containing the superblock in .Va fsp . The .Fn sbget -function is safe to use in threaded applications. +and +.Fn sbsearch +functions are safe to use in threaded applications. .Pp The .Fn sbput @@ -113,7 +135,19 @@ modified and the on-disk copy needs to be updated. .Pp The .Fn sbread -function reads the standard filesystem superblock into the +function reads the standard filesystem superblock. +The +.Fn sbfind +function tries to find a usable superblock. +It searchs first for the superblock at the standard location. +If it is not found or is too damaged to use +.Fn sbfind +will attempt to find one of the filesystem's alternate superblocks. +If successful +.Fn sbread +and +.Fn sbfind +return a superblock in the .Va d_sb , structure embedded in the given user-land UFS disk structure. .Pp @@ -131,16 +165,19 @@ value is non-zero. .Sh RETURN VALUES .Rv -std sbread sbwrite The -.Fn sbget +.Fn sbget , +.Fn sbsearch , and .Fn sbput functions return the value 0 if successful; otherwise they return one of the errors described below. .Sh ERRORS The errors returned by -.Fn sbget +.Fn sbget , +.Fn sbsearch , +.Fn sbread , and -.Fn sbread +.Fn sbfind , include any of the errors specified for the library function .Xr bread 3 . Additionally, they may follow the diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index 6a586ffb95ec..4983645156ff 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -130,13 +130,8 @@ main(int argc, char *argv[]) usage(); while ((name = *argv++) != NULL) { - if (ufs_disk_fillout_blank(&disk, name) == -1) { - ufserr(name); - eval |= 1; - continue; - } - disk.d_lookupflags |= UFS_NOHASHFAIL; - if (sbread(&disk) == -1) { + if (ufs_disk_fillout_blank(&disk, name) == -1 || + sbfind(&disk, 0) == -1) { ufserr(name); eval |= 1; continue; diff --git a/sbin/ffsinfo/ffsinfo.c b/sbin/ffsinfo/ffsinfo.c index 33ec5f175cbd..9d447d209ffd 100644 --- a/sbin/ffsinfo/ffsinfo.c +++ b/sbin/ffsinfo/ffsinfo.c @@ -223,8 +223,9 @@ main(int argc, char **argv) device = special; } - if (ufs_disk_fillout(&disk, device) == -1) - err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error); + if (ufs_disk_fillout_blank(&disk, device) == -1 || + sbfind(&disk, 0) == -1) + err(1, "superblock fetch(%s) failed: %s", device, disk.d_error); DBG_OPEN(out_file); /* already here we need a superblock */ diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 1fb0df0c5124..65c7056532be 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -363,7 +363,6 @@ 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 */ @@ -502,7 +501,7 @@ void pfatal(const char *fmt, ...) __printflike(1, 2); void propagate(void); void prtinode(struct inode *); void pwarn(const char *fmt, ...) __printflike(1, 2); -int readsb(int listerr); +int readsb(void); int reply(const char *question); void rwerror(const char *mesg, ufs2_daddr_t blk); void sblock_init(void); diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c index 09dbcc6694a2..306944fa95b9 100644 --- a/sbin/fsck_ffs/globs.c +++ b/sbin/fsck_ffs/globs.c @@ -96,7 +96,6 @@ 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 */ @@ -156,7 +155,6 @@ fsckinit(void) resolved = 0; havesb = 0; fsmodified = 0; - sbhashfailed = 0; fsreadfd = -1; fswritefd = -1; maxfsblock = 0; diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c index 18634a93c05c..b638d31040dd 100644 --- a/sbin/fsck_ffs/main.c +++ b/sbin/fsck_ffs/main.c @@ -271,7 +271,7 @@ 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) diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index cb95d18859b0..022799117eb0 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$"); struct inoinfo **inphead, **inpsort; /* info about all inodes */ +static int sbhashfailed; #define POWEROF2(num) (((num) & ((num) - 1)) == 0) static int calcsb(char *dev, int devfd, struct fs *fs); @@ -74,39 +75,15 @@ static int chkrecovery(int devfd); int setup(char *dev) { - long cg, bmapsize; - struct fs proto; + long bmapsize; /* - * We are expected to have an open file descriptor + * We are expected to have an open file descriptor and a superblock. */ - if (fsreadfd < 0) + if (fsreadfd < 0 || havesb == 0) { + if (debug) + printf("setup: bad fsreadfd or missing superblock\n"); 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); - } - pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); - bflag = 0; } if (preen == 0) printf("** %s", dev); @@ -243,40 +220,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, UFS_NOMSG)) { + 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); /* diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c index c935f88952b4..f9775c7da089 100644 --- a/sbin/fsdb/fsdb.c +++ b/sbin/fsdb/fsdb.c @@ -111,7 +111,7 @@ main(int argc, char *argv[]) fsys = argv[0]; sblock_init(); - if (openfilesys(fsys) == 0 || readsb(0) == 0 || setup(fsys) == 0) + if (openfilesys(fsys) == 0 || readsb() == 0 || setup(fsys) == 0) errx(1, "cannot set up file system `%s'", fsys); if (fswritefd < 0) nflag++; diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c index ef7cb07e75da..65cda349879a 100644 --- a/stand/libsa/ufs.c +++ b/stand/libsa/ufs.c @@ -151,7 +151,7 @@ static int search_directory(char *, struct open_file *, ino_t *); static int ufs_use_sa_read(void *, off_t, void **, int); /* from ffs_subr.c */ -int ffs_sbget(void *, struct fs **, off_t, int, char *, +int ffs_sbsearch(void *, struct fs **, int, char *, int (*)(void *, off_t, void **, int)); /* @@ -529,8 +529,8 @@ ufs_open(const char *upath, struct open_file *f) if (mnt == NULL) { /* read super block */ twiddle(1); - if ((rc = ffs_sbget(f, &fs, UFS_STDSB, UFS_NOHASHFAIL, "stand", - ufs_use_sa_read)) != 0) { + if ((rc = ffs_sbsearch(f, &fs, 0, "stand", ufs_use_sa_read)) + != 0) { goto out; } } else { diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index 54820fc5b3ce..4f17b71dd20f 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -86,6 +86,8 @@ int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); void ffs_oldfscompat_write(struct fs *, struct ufsmount *); int ffs_own_mount(const struct mount *mp); +int ffs_sbsearch(void *, struct fs **, int, struct malloc_type *, + int (*)(void *, off_t, void **, int)); int ffs_reallocblks(struct vop_reallocblks_args *); int ffs_realloccg(struct inode *, ufs2_daddr_t, ufs2_daddr_t, ufs2_daddr_t, int, int, int, struct ucred *, struct buf **); diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c index c6043765cfb0..8c081313f873 100644 --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -378,6 +378,40 @@ validate_sblock(struct fs *fs, int flags) prtmsg = ((flags & UFS_NOMSG) == 0); warnerr = (flags & UFS_NOWARNFAIL) == UFS_NOWARNFAIL ? 0 : ENOENT; wmsg = warnerr ? "" : " (Ignored)"; + /* + * If just validating for recovery, then do just the minimal + * checks needed for the superblock fields needed to find + * alternate superblocks. + */ + if ((flags & UFS_FSRONLY) == UFS_FSRONLY && + (fs->fs_magic == FS_UFS1_MAGIC || fs->fs_magic == FS_UFS2_MAGIC)) { + if (fs->fs_magic == FS_UFS2_MAGIC) { + FCHK(fs->fs_sblockloc, !=, SBLOCK_UFS2, %#jx); + } else if (fs->fs_magic == FS_UFS1_MAGIC) { + FCHK(fs->fs_sblockloc, <, 0, %jd); + FCHK(fs->fs_sblockloc, >, SBLOCK_UFS1, %jd); + } + FCHK(fs->fs_frag, <, 1, %jd); + FCHK(fs->fs_frag, >, MAXFRAG, %jd); + FCHK(fs->fs_bsize, <, MINBSIZE, %jd); + FCHK(fs->fs_bsize, >, MAXBSIZE, %jd); + FCHK(fs->fs_bsize, <, roundup(sizeof(struct fs), DEV_BSIZE), + %jd); + FCHK(fs->fs_fsize, <, sectorsize, %jd); + FCHK(fs->fs_fsize * fs->fs_frag, !=, fs->fs_bsize, %jd); + FCHK(powerof2(fs->fs_fsize), ==, 0, %jd); + FCHK(fs->fs_fpg, <, 3 * fs->fs_frag, %jd); + FCHK(fs->fs_ncg, <, 1, %jd); + FCHK(fs->fs_fsbtodb, !=, ILOG2(fs->fs_fsize / sectorsize), %jd); + FCHK(fs->fs_old_cgoffset, <, 0, %jd); + FCHK2(fs->fs_old_cgoffset, >, 0, ~fs->fs_old_cgmask, <, 0, %jd); + FCHK(fs->fs_old_cgoffset * (~fs->fs_old_cgmask), >, fs->fs_fpg, + %jd); + FCHK(fs->fs_sblkno, !=, roundup( + howmany(fs->fs_sblockloc + SBLOCKSIZE, fs->fs_fsize), + fs->fs_frag), %jd); + return (error); + } if (fs->fs_magic == FS_UFS2_MAGIC) { if ((flags & UFS_ALTSBLK) == 0) FCHK2(fs->fs_sblockactualloc, !=, SBLOCK_UFS2, @@ -530,6 +564,146 @@ validate_sblock(struct fs *fs, int flags) return (error); } +/* + * Make an extensive search to find a superblock. If the superblock + * in the standard place cannot be used, try looking for one of the + * backup superblocks. + * + * Flags are made up of the following or'ed together options: + * + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. + * + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxillary data structures like the cylinder group + * summary information. + */ +int +ffs_sbsearch(void *devfd, struct fs **fsp, int reqflags, + struct malloc_type *filltype, + int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) +{ + struct fsrecovery *fsr; + struct fs *protofs; + void *fsrbuf; + char *cp; + long nocsum, flags, msg, cg; + off_t sblk, secsize; + int error; + + msg = (reqflags & UFS_NOMSG) == 0; + nocsum = reqflags & UFS_NOCSUM; + /* + * Try normal superblock read and return it if it works. + * + * Suppress messages if it fails until we find out if + * failure can be avoided. + */ + flags = UFS_NOMSG | nocsum; + if (ffs_sbget(devfd, fsp, UFS_STDSB, flags, filltype, readfunc) == 0) + return (0); + /* + * First try: ignoring hash failures. + */ + flags |= UFS_NOHASHFAIL; + if (msg) + flags &= ~UFS_NOMSG; + if (ffs_sbget(devfd, fsp, UFS_STDSB, flags, filltype, readfunc) == 0) + return (0); + /* + * Next up is to check if fields of the superblock that are + * needed to find backup superblocks are usable. + */ + if (msg) + printf("Attempted recovery for standard superblock: failed\n"); + flags = UFS_FSRONLY | UFS_NOHASHFAIL | UFS_NOMSG; + if (ffs_sbget(devfd, &protofs, UFS_STDSB, flags, filltype, + readfunc) == 0) { + if (msg) + printf("Attempted extraction of recovery data from " + "standard superblock: "); + } else { + /* + * Final desperation is to see if alternate superblock + * parameters have been saved in the boot area. + */ + if (msg) + printf("Attempted extraction of recovery data from " + "standard superblock: failed\nAttempt to find " + "boot zone recovery data: "); + /* + * Look to see if recovery information has been saved. + * If so we can generate a prototype superblock based + * on that information. + * + * We need fragments-per-group, number of cylinder groups, + * location of the superblock within the cylinder group, and + * the conversion from filesystem fragments to disk blocks. + * + * When building a UFS2 filesystem, newfs(8) stores these + * details at the end of the boot block area at the start + * of the filesystem partition. If they have been overwritten + * by a boot block, we fail. But usually they are there + * and we can use them. + * + * We could ask the underlying device for its sector size, + * but some devices lie. So we just try a plausible range. + */ + error = ENOENT; + for (secsize = dbtob(1); secsize <= SBLOCKSIZE; secsize *= 2) + if ((error = (*readfunc)(devfd, (SBLOCK_UFS2 - secsize), + &fsrbuf, secsize)) == 0) + break; + if (error != 0) + goto trynowarn; + cp = fsrbuf; /* type change to keep compiler happy */ + fsr = (struct fsrecovery *)&cp[secsize - sizeof *fsr]; + if (fsr->fsr_magic != FS_UFS2_MAGIC || + (protofs = UFS_MALLOC(SBLOCKSIZE, filltype, M_NOWAIT)) + == NULL) { + UFS_FREE(fsrbuf, filltype); + goto trynowarn; + } + memset(protofs, 0, sizeof(struct fs)); + protofs->fs_fpg = fsr->fsr_fpg; + protofs->fs_fsbtodb = fsr->fsr_fsbtodb; + protofs->fs_sblkno = fsr->fsr_sblkno; + protofs->fs_magic = fsr->fsr_magic; + protofs->fs_ncg = fsr->fsr_ncg; + UFS_FREE(fsrbuf, filltype); + } + /* + * Scan looking for alternative superblocks. + */ + for (cg = 0; cg < protofs->fs_ncg; cg++) { + sblk = dbtob(fsbtodb(protofs, cgsblock(protofs, cg))); + if (ffs_sbget(devfd, fsp, sblk, UFS_NOMSG | nocsum, filltype, + readfunc) == 0) { + if (msg) + printf("succeeded with alternate superblock " + "at %jd\n", (intmax_t)btodb(sblk)); + UFS_FREE(protofs, filltype); + return (0); + } + } + UFS_FREE(protofs, filltype); + /* + * Our alternate superblock strategies failed. Our last ditch effort + * is to see if the standard superblock has only non-critical errors. + */ +trynowarn: + flags = UFS_NOWARNFAIL | UFS_NOMSG | nocsum; + if (msg) { + printf("failed\n"); + flags &= ~UFS_NOMSG; + } + if (ffs_sbget(devfd, fsp, UFS_STDSB, flags, filltype, readfunc) != 0) + return (ENOENT); + if (msg) + printf("Using standard superblock with non-critical errors.\n"); + return (0); +} + /* * Write a superblock to the devfd device from the memory pointed to by fs. * Write out the superblock summary information if it is present. diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 2944d0c5077d..38d5be895318 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -913,8 +913,7 @@ ffs_mountfs(struct vnode *odevvp, struct mount *mp, struct thread *td) struct g_consumer *cp; struct mount *nmp; struct vnode *devvp; - int candelete, canspeedup, flags; - off_t loc; + int candelete, canspeedup; fs = NULL; ump = NULL; @@ -958,12 +957,12 @@ ffs_mountfs(struct vnode *odevvp, struct mount *mp, struct thread *td) goto out; } /* fetch the superblock and summary information */ - loc = UFS_STDSB; - flags = 0; if ((mp->mnt_flag & (MNT_ROOTFS | MNT_FORCE)) != 0) - flags = UFS_NOHASHFAIL; - if ((error = ffs_sbget(devvp, &fs, loc, flags, M_UFSMNT, ffs_use_bread)) - != 0) + error = ffs_sbsearch(devvp, &fs, 0, M_UFSMNT, ffs_use_bread); + else + error = ffs_sbget(devvp, &fs, UFS_STDSB, 0, M_UFSMNT, + ffs_use_bread); + if (error != 0) goto out; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h index 0a546e40e82a..bd2b9157e467 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -102,11 +102,19 @@ * UFS_NOWARNFAIL will warn about inconsistencies but still return the * superblock. It includes UFS_NOHASHFAIL. UFS_NOWARNFAIL is used by * programs like fsck_ffs(8) to debug broken filesystems. + * + * UFS_FSRONLY will only validate the superblock fields needed to + * calculate where the backup filesystem superblocks are located. + * If these values pass their validation tests, then the superblock + * is returned. This flag is used as part of the attempt to find + * alternate superblocks when using ffs_sbsearch(). */ #define UFS_NOHASHFAIL 0x0001 /* Ignore check-hash failure */ #define UFS_NOWARNFAIL 0x0003 /* Ignore non-fatal inconsistencies */ #define UFS_NOMSG 0x0004 /* Print no error message */ #define UFS_NOCSUM 0x0008 /* Read just the superblock without csum */ +#define UFS_FSRONLY 0x0010 /* Validate only values needed for recovery + of alternate superblocks */ #define UFS_ALTSBLK 0x1000 /* Flag used internally */ /* diff --git a/tools/diag/prtblknos/main.c b/tools/diag/prtblknos/main.c index 25a717760922..65efa786b700 100644 --- a/tools/diag/prtblknos/main.c +++ b/tools/diag/prtblknos/main.c @@ -56,7 +56,6 @@ main(argc, argv) char ibuf[64]; char *fsname, *filename; ino_t inonum; - int error; filename = NULL; if (argc == 2) { @@ -83,7 +82,8 @@ main(argc, argv) fsname = *++argv; /* get the superblock. */ - if ((error = ufs_disk_fillout(&disk, fsname)) < 0) + if (ufs_disk_fillout_blank(&disk, fsname) == -1 || + sbfind(&disk, 0) == -1) err(1, "Cannot access file system superblock on %s", fsname); fs = (struct fs *)&disk.d_sb; @@ -99,7 +99,7 @@ main(argc, argv) (void)printf("%s (inode #%jd): ", filename, (intmax_t)inonum); - if ((error = getinode(&disk, &dp, inonum)) < 0) + if (getinode(&disk, &dp, inonum) < 0) warn("Read of inode %jd on %s failed: %s", (intmax_t)inonum, fsname, disk.d_error);