From b21582ee03ec1394e08173e276df311979856e54 Mon Sep 17 00:00:00 2001 From: Kirk McKusick Date: Sat, 30 Jul 2022 22:44:01 -0700 Subject: [PATCH] Add a flags parameter to the ffs_sbget() function that reads UFS superblocks. Rather than trying to shoehorn flags into the requested superblock address, create a separate flags parameter to the ffs_sbget() function in sys/ufs/ffs/ffs_subr.c. The ffs_sbget() function is used both in the kernel and in user-level utilities through export to the sbget() function in the libufs(3) library (see sbget(3) for details). The kernel uses ffs_sbget() when mounting UFS filesystems, in the glabel(8) and gjournal(8) GEOM utilities, and in the standalone library used when booting the system from a UFS root filesystem. The ffs_sbget() function reads the superblock located at the byte offset specified by its sblockloc parameter. The value UFS_STDSB may be specified for sblockloc to request that the standard location for the superblock be read. The two existing options are now flags: UFS_NOHASHFAIL will note if the check hash is wrong but will still return the superblock. This is used by the bootstrap code to give the system a chance to come up so that fsck can be run to correct the problem. UFS_NOMSG indicates that superblock inconsistency error messages should not be printed. It is used by programs like fsck that want to print their own error message and programs like glabel(8) that just want to know if a UFS filesystem exists on a partition. One additional flag is added: 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 is used by clients like glabel(8) that just want to check for possible filesystem types. Using UFS_NOCSUM skips the superblock checks for csum data which allows superblocks that have corrupted csum data to be read and used. The validate_sblock() function checks that the superblock has not been corrupted in a way that can crash or hang the system. Unless the UFS_NOMSG flag is specified, it will print out any errors that it finds. Prior to this commit, validate_sblock() returned as soon as it found an inconsistency so would print at most one message. It now does all its checks so when UFS_NOMSG has not been specified will print out everything that it finds inconsistent. Sponsored by: The FreeBSD Foundation --- lib/libufs/libufs.h | 7 +-- lib/libufs/sblock.c | 36 ++++++++++++--- lib/libufs/sbread.3 | 24 +++++++++- lib/libufs/type.c | 3 +- sbin/dump/main.c | 2 +- sbin/dumpfs/dumpfs.c | 5 +-- sbin/fsck_ffs/setup.c | 14 +++--- sbin/fsirand/fsirand.c | 8 ++-- sbin/growfs/growfs.c | 2 +- sbin/quotacheck/quotacheck.c | 2 +- stand/libsa/ufs.c | 4 +- sys/geom/journal/g_journal_ufs.c | 3 +- sys/geom/label/g_label_ufs.c | 7 ++- sys/ufs/ffs/ffs_extern.h | 2 +- sys/ufs/ffs/ffs_subr.c | 77 +++++++++++++++++++------------- sys/ufs/ffs/ffs_vfsops.c | 10 +++-- sys/ufs/ffs/fs.h | 42 +++++++++-------- usr.sbin/fstyp/ufs.c | 4 +- usr.sbin/quot/quot.c | 4 +- 19 files changed, 159 insertions(+), 97 deletions(-) diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h index 5045117b5d7..cb8774454b3 100644 --- a/lib/libufs/libufs.h +++ b/lib/libufs/libufs.h @@ -65,7 +65,8 @@ struct uufsd { int d_ccg; /* current cylinder group */ int d_lcg; /* last cylinder group (in d_cg) */ const char *d_error; /* human readable disk error */ - off_t d_sblockloc; /* where to look for the superblock */ + off_t d_sblockloc; /* where to look for the superblock */ + int d_lookupflags; /* flags to superblock lookup */ int d_mine; /* internal flags */ #define d_fs d_sbunion.d_fs #define d_sb d_sbunion.d_sb @@ -111,7 +112,7 @@ 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); void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t); -int ffs_sbget(void *, struct fs **, off_t, char *, +int ffs_sbget(void *, struct fs **, off_t, int, char *, int (*)(void *, off_t, void **, int)); int ffs_sbput(void *, struct fs *, off_t, int (*)(void *, off_t, void *, int)); @@ -150,7 +151,7 @@ int putinode(struct uufsd *); int sbread(struct uufsd *); int sbwrite(struct uufsd *, int); /* low level superblock read/write functions */ -int sbget(int, struct fs **, off_t); +int sbget(int, struct fs **, off_t, int); int sbput(int, struct fs *, int); /* diff --git a/lib/libufs/sblock.c b/lib/libufs/sblock.c index 9ba012400f1..5708d50aa4f 100644 --- a/lib/libufs/sblock.c +++ b/lib/libufs/sblock.c @@ -53,6 +53,15 @@ static int handle_disk_read(struct uufsd *, struct fs *, int); /* * Read the standard superblock. + * + * The following option flags can be or'ed into disk->d_lookupflags: + * + * 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 sbread(struct uufsd *disk) @@ -60,7 +69,7 @@ sbread(struct uufsd *disk) struct fs *fs; int error; - error = sbget(disk->d_fd, &fs, disk->d_sblockloc); + error = sbget(disk->d_fd, &fs, disk->d_sblockloc, disk->d_lookupflags); return (handle_disk_read(disk, fs, error)); } @@ -149,14 +158,24 @@ static int use_pread(void *devfd, off_t loc, void **bufp, int size); static int use_pwrite(void *devfd, off_t loc, void *buf, int size); /* + * The following two functions read a superblock. Their flags + * parameter 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. + * * Read a superblock from the devfd device allocating memory returned - * in fsp. Also read the superblock summary information. + * in fsp. */ int -sbget(int devfd, struct fs **fsp, off_t sblockloc) +sbget(int devfd, struct fs **fsp, off_t sblockloc, int flags) { - return (ffs_sbget(&devfd, fsp, sblockloc, "user", use_pread)); + return (ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread)); } /* @@ -196,8 +215,10 @@ sbput(int devfd, struct fs *fs, int numaltwrite) if (numaltwrite == 0) return (0); savedactualloc = fs->fs_sblockactualloc; - savedcsp = fs->fs_csp; - fs->fs_csp = NULL; + if (fs->fs_si != NULL) { + savedcsp = fs->fs_csp; + fs->fs_csp = NULL; + } for (i = 0; i < numaltwrite; i++) { fs->fs_sblockactualloc = dbtob(fsbtodb(fs, cgsblock(fs, i))); if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, @@ -208,7 +229,8 @@ sbput(int devfd, struct fs *fs, int numaltwrite) } } fs->fs_sblockactualloc = savedactualloc; - fs->fs_csp = savedcsp; + if (fs->fs_si != NULL) + fs->fs_csp = savedcsp; return (0); } diff --git a/lib/libufs/sbread.3 b/lib/libufs/sbread.3 index e97469f8687..f579fc7ffdf 100644 --- a/lib/libufs/sbread.3 +++ b/lib/libufs/sbread.3 @@ -27,7 +27,7 @@ .In ufs/ffs/fs.h .In libufs.h .Ft int -.Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" +.Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" "int flags" .Ft int .Fn sbput "int devfd" "struct fs *fs" "int numaltwrite" .Ft int @@ -60,7 +60,27 @@ file descriptor that references the filesystem disk, reads the superblock located at the byte offset specified by .Va sblockloc into the allocated buffer. -If successful, it returns a pointer to the buffer containing the superblock in +The value +.Cm UFS_STDSB +may be specified for +.Va sblockloc +to request that the standard location for the superblock be read. +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. +.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 .Va fsp . The .Fn sbget diff --git a/lib/libufs/type.c b/lib/libufs/type.c index ac5b8d17e84..80b26cdb9c8 100644 --- a/lib/libufs/type.c +++ b/lib/libufs/type.c @@ -168,7 +168,8 @@ again: if ((ret = stat(name, &st)) < 0) { disk->d_ufs = 0; disk->d_error = NULL; disk->d_si = NULL; - disk->d_sblockloc = STDSB; + disk->d_sblockloc = UFS_STDSB; + disk->d_lookupflags = 0; if (oname != name) { name = strdup(name); diff --git a/sbin/dump/main.c b/sbin/dump/main.c index 8752f2c1bea..779db5fb4b4 100644 --- a/sbin/dump/main.c +++ b/sbin/dump/main.c @@ -457,7 +457,7 @@ main(int argc, char *argv[]) msgtail("to %s\n", tape); sync(); - if ((ret = sbget(diskfd, &sblock, STDSB)) != 0) { + if ((ret = sbget(diskfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index 7f2acdf435e..6a586ffb95e 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -135,7 +135,7 @@ main(int argc, char *argv[]) eval |= 1; continue; } - disk.d_sblockloc = STDSB_NOHASHFAIL; + disk.d_lookupflags |= UFS_NOHASHFAIL; if (sbread(&disk) == -1) { ufserr(name); eval |= 1; @@ -314,9 +314,6 @@ dumpfs(const char *name, int dosb) afs.fs_volname, (uintmax_t)afs.fs_swuid, (uintmax_t)afs.fs_providersize); printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); - afs.fs_csp = calloc(1, afs.fs_cssize); - if (bread(&disk, fsbtodb(&afs, afs.fs_csaddr), afs.fs_csp, afs.fs_cssize) == -1) - goto err; for (i = 0; i < afs.fs_ncg; i++) { struct csum *cs = &afs.fs_cs(&afs, i); if (i && i % 4 == 0) diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index 0f0d9b61a07..0daac100499 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -252,18 +252,20 @@ int readsb(int listerr) { off_t super; - int bad, ret; + int bad, ret, flags; struct fs *fs; - super = bflag ? bflag * dev_bsize : - sbhashfailed ? STDSB_NOHASHFAIL_NOMSG : STDSB_NOMSG; + super = bflag ? bflag * dev_bsize : UFS_STDSB; + flags = sbhashfailed ? UFS_NOHASHFAIL | UFS_NOMSG : UFS_NOMSG; readcnt[sblk.b_type]++; - while ((ret = sbget(fsreadfd, &fs, super)) != 0) { + while ((ret = sbget(fsreadfd, &fs, super, flags)) != 0) { switch (ret) { case EINTEGRITY: - if (bflag || super == STDSB_NOHASHFAIL_NOMSG) + if (bflag || (super == UFS_STDSB && + flags == (UFS_NOHASHFAIL | UFS_NOMSG))) return (0); - super = STDSB_NOHASHFAIL_NOMSG; + super = UFS_STDSB; + flags = UFS_NOHASHFAIL | UFS_NOMSG; sbhashfailed = 1; continue; case ENOENT: diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c index c4db8848f18..90305416f55 100644 --- a/sbin/fsirand/fsirand.c +++ b/sbin/fsirand/fsirand.c @@ -112,7 +112,7 @@ fsirand(char *device) struct fs *sblock; ino_t inumber; ufs2_daddr_t dblk; - int devfd, n, cg, ret; + int devfd, n, cg; u_int32_t bsize = DEV_BSIZE; if ((devfd = open(device, printonly ? O_RDONLY : O_RDWR)) < 0) { @@ -124,10 +124,10 @@ fsirand(char *device) dp2 = NULL; /* Read in master superblock */ - if ((ret = sbget(devfd, &sblock, STDSB)) != 0) { - switch (ret) { + if ((errno = sbget(devfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { + switch (errno) { case ENOENT: - warn("Cannot find file system superblock"); + warnx("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c index 69e6f04dd4c..4a8d935d91c 100644 --- a/sbin/growfs/growfs.c +++ b/sbin/growfs/growfs.c @@ -1455,7 +1455,7 @@ main(int argc, char **argv) /* * Read the current superblock, and take a backup. */ - if ((ret = sbget(fsi, &fs, STDSB)) != 0) { + if ((ret = sbget(fsi, &fs, UFS_STDSB, 0)) != 0) { switch (ret) { case ENOENT: errx(1, "superblock not recognized"); diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c index 9a01be11d9d..1e656d2a581 100644 --- a/sbin/quotacheck/quotacheck.c +++ b/sbin/quotacheck/quotacheck.c @@ -321,7 +321,7 @@ chkquota(char *specname, struct quotafile *qfu, struct quotafile *qfg) } } sync(); - if ((ret = sbget(fi, &fs, STDSB)) != 0) { + if ((ret = sbget(fi, &fs, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c index 12703a3e18a..de3dbe58789 100644 --- a/stand/libsa/ufs.c +++ b/stand/libsa/ufs.c @@ -152,7 +152,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, char *, +int ffs_sbget(void *, struct fs **, off_t, int, char *, int (*)(void *, off_t, void **, int)); /* @@ -530,7 +530,7 @@ ufs_open(const char *upath, struct open_file *f) if (mnt == NULL) { /* read super block */ twiddle(1); - if ((rc = ffs_sbget(f, &fs, STDSB_NOHASHFAIL, "stand", + if ((rc = ffs_sbget(f, &fs, UFS_STDSB, UFS_NOHASHFAIL, "stand", ufs_use_sa_read)) != 0) { goto out; } diff --git a/sys/geom/journal/g_journal_ufs.c b/sys/geom/journal/g_journal_ufs.c index dec28e547f8..dc9604abae2 100644 --- a/sys/geom/journal/g_journal_ufs.c +++ b/sys/geom/journal/g_journal_ufs.c @@ -73,7 +73,8 @@ g_journal_ufs_dirty(struct g_consumer *cp) fs = NULL; if (SBLOCKSIZE % cp->provider->sectorsize != 0 || - ffs_sbget(cp, &fs, STDSB, M_GEOM, g_use_g_read_data) != 0) { + ffs_sbget(cp, &fs, UFS_STDSB, UFS_NOCSUM, M_GEOM, g_use_g_read_data) + != 0) { GJ_DEBUG(0, "Cannot find superblock to mark file system %s " "as dirty.", cp->provider->name); KASSERT(fs == NULL, diff --git a/sys/geom/label/g_label_ufs.c b/sys/geom/label/g_label_ufs.c index 0de09ec9389..691f875cf6c 100644 --- a/sys/geom/label/g_label_ufs.c +++ b/sys/geom/label/g_label_ufs.c @@ -139,8 +139,9 @@ g_label_ufs_taste_common(struct g_consumer *cp, char *label, size_t size, int wh label[0] = '\0'; fs = NULL; - if (SBLOCKSIZE % pp->sectorsize != 0 || ffs_sbget(cp, &fs, - STDSB_NOHASHFAIL_NOMSG, M_GEOM, g_use_g_read_data) != 0) { + if (SBLOCKSIZE % pp->sectorsize != 0 || ffs_sbget(cp, &fs, UFS_STDSB, + UFS_NOHASHFAIL | UFS_NOCSUM | UFS_NOMSG, M_GEOM, g_use_g_read_data) + != 0) { KASSERT(fs == NULL, ("g_label_ufs_taste_common: non-NULL fs %p\n", fs)); return; @@ -172,8 +173,6 @@ g_label_ufs_taste_common(struct g_consumer *cp, char *label, size_t size, int wh break; } out: - g_free(fs->fs_csp); - g_free(fs->fs_si); g_free(fs); } diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index fdc08dc6aaf..54820fc5b3c 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -90,7 +90,7 @@ 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 **); int ffs_reload(struct mount *, int); -int ffs_sbget(void *, struct fs **, off_t, struct malloc_type *, +int ffs_sbget(void *, struct fs **, off_t, int, struct malloc_type *, int (*)(void *, off_t, void **, int)); int ffs_sbput(void *, struct fs *, off_t, int (*)(void *, off_t, void *, int)); diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c index 502ef79c7bd..611f1989e8f 100644 --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -125,7 +125,7 @@ ffs_update_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip) * the superblock and its associated data. */ static off_t sblock_try[] = SBLOCKSEARCH; -static int readsuper(void *, struct fs **, off_t, int, int, +static int readsuper(void *, struct fs **, off_t, int, int (*)(void *, off_t, void **, int)); static int validate_sblock(struct fs *, int); @@ -149,7 +149,7 @@ static int validate_sblock(struct fs *, int); * The administrator must complete newfs before using this volume. */ int -ffs_sbget(void *devfd, struct fs **fsp, off_t altsblock, +ffs_sbget(void *devfd, struct fs **fsp, off_t sblock, int flags, struct malloc_type *filltype, int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) { @@ -163,18 +163,23 @@ ffs_sbget(void *devfd, struct fs **fsp, off_t altsblock, fs = NULL; *fsp = NULL; - if (altsblock >= 0) { - if ((error = readsuper(devfd, &fs, altsblock, 1, 0, - readfunc)) != 0) { + if (sblock != UFS_STDSB) { + if ((error = readsuper(devfd, &fs, sblock, + flags | UFS_ALTSBLK, readfunc)) != 0) { if (fs != NULL) UFS_FREE(fs, filltype); return (error); } } else { for (i = 0; sblock_try[i] != -1; i++) { - if ((error = readsuper(devfd, &fs, sblock_try[i], 0, - altsblock, readfunc)) == 0) + if ((error = readsuper(devfd, &fs, sblock_try[i], + flags, readfunc)) == 0) { + if ((flags & UFS_NOCSUM) != 0) { + *fsp = fs; + return (0); + } break; + } if (fs != NULL) { UFS_FREE(fs, filltype); fs = NULL; @@ -243,8 +248,8 @@ ffs_sbget(void *devfd, struct fs **fsp, off_t altsblock, * Return zero on success or an errno on failure. */ static int -readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int isaltsblk, - int chkhash, int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) +readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int flags, + int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) { struct fs *fs; int error, res; @@ -263,10 +268,10 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int isaltsblk, * that will be accessed. Here we fail the lookup so that we can * retry with the correct location for the UFS1 superblock. */ - if (fs->fs_magic == FS_UFS1_MAGIC && !isaltsblk && + if (fs->fs_magic == FS_UFS1_MAGIC && (flags & UFS_ALTSBLK) == 0 && fs->fs_bsize == SBLOCK_UFS2 && sblockloc == SBLOCK_UFS2) return (ENOENT); - if ((error = validate_sblock(fs, isaltsblk)) != 0) + if ((error = validate_sblock(fs, flags)) > 0) return (error); /* * If the filesystem has been run on a kernel without @@ -281,15 +286,16 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int isaltsblk, fs->fs_metackhash &= CK_SUPPORTED; fs->fs_flags &= FS_SUPPORTED; if (fs->fs_ckhash != (ckhash = ffs_calc_sbhash(fs))) { - if (chkhash == STDSB_NOMSG) - return (EINTEGRITY); - if (chkhash == STDSB_NOHASHFAIL_NOMSG) + if ((flags & (UFS_NOMSG | UFS_NOHASHFAIL)) == + (UFS_NOMSG | UFS_NOHASHFAIL)) return (0); + if ((flags & UFS_NOMSG) != 0) + return (EINTEGRITY); #ifdef _KERNEL res = uprintf("Superblock check-hash failed: recorded " "check-hash 0x%x != computed check-hash 0x%x%s\n", fs->fs_ckhash, ckhash, - chkhash == STDSB_NOHASHFAIL ? " (Ignored)" : ""); + (flags & UFS_NOHASHFAIL) != 0 ? " (Ignored)" : ""); #else res = 0; #endif @@ -301,12 +307,10 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int isaltsblk, printf("Superblock check-hash failed: recorded " "check-hash 0x%x != computed check-hash " "0x%x%s\n", fs->fs_ckhash, ckhash, - chkhash == STDSB_NOHASHFAIL ? - " (Ignored)" : ""); - if (chkhash == STDSB) - return (EINTEGRITY); - /* chkhash == STDSB_NOHASHFAIL */ - return (0); + (flags & UFS_NOHASHFAIL) ? " (Ignored)" : ""); + if ((flags & UFS_NOHASHFAIL) != 0) + return (0); + return (EINTEGRITY); } /* Have to set for old filesystems that predate this field */ fs->fs_sblockactualloc = sblockloc; @@ -318,34 +322,40 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int isaltsblk, /* * Verify the filesystem values. */ -#define ILOG2(num) (fls(num) - 1) +#define ILOG2(num) (fls(num) - 1) +#define MPRINT if (prtmsg) printf #undef CHK #define CHK(lhs, op, rhs, fmt) \ if (lhs op rhs) { \ - printf("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ + MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ #fmt ")\n", fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, \ #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs); \ - return (ENOENT); \ + if (error == 0) \ + error = ENOENT; \ } #define CHK2(lhs1, op1, rhs1, lhs2, op2, rhs2, fmt) \ if (lhs1 op1 rhs1 && lhs2 op2 rhs2) { \ - printf("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ + MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ #fmt ") && %s (" #fmt ") %s %s (" #fmt ")\n", \ fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, #lhs1, \ (intmax_t)lhs1, #op1, #rhs1, (intmax_t)rhs1, #lhs2, \ (intmax_t)lhs2, #op2, #rhs2, (intmax_t)rhs2); \ - return (ENOENT); \ + if (error == 0) \ + error = ENOENT; \ } static int -validate_sblock(struct fs *fs, int isaltsblk) +validate_sblock(struct fs *fs, int flags) { u_long i, sectorsize; u_int64_t maxfilesize, sizepb; + int error, prtmsg; + error = 0; sectorsize = dbtob(1); + prtmsg = ((flags & UFS_NOMSG) == 0); if (fs->fs_magic == FS_UFS2_MAGIC) { - if (!isaltsblk) { + if ((flags & UFS_ALTSBLK) == 0) { CHK(fs->fs_sblockloc, !=, SBLOCK_UFS2, %#jx); CHK2(fs->fs_sblockactualloc, !=, SBLOCK_UFS2, fs->fs_sblockactualloc, !=, 0, %jd); @@ -358,7 +368,7 @@ validate_sblock(struct fs *fs, int isaltsblk) CHK(fs->fs_inopb, !=, fs->fs_bsize / sizeof(struct ufs2_dinode), %jd); } else if (fs->fs_magic == FS_UFS1_MAGIC) { - if (!isaltsblk) { + if ((flags & UFS_ALTSBLK) == 0) { CHK(fs->fs_sblockloc, >, SBLOCK_UFS1, %jd); CHK(fs->fs_sblockactualloc, >, SBLOCK_UFS1, %jd); } @@ -459,6 +469,13 @@ validate_sblock(struct fs *fs, int isaltsblk) CHK(fs->fs_size, <, 8 * fs->fs_frag, %jd); CHK(fs->fs_size, <=, (fs->fs_ncg - 1) * fs->fs_fpg, %jd); CHK(fs->fs_size, >, fs->fs_ncg * fs->fs_fpg, %jd); + /* + * If we are not requested to read in the csum data stop here + * as the correctness of the remaining values is only important + * to bound the space needed to be allocated to hold the csum data. + */ + if ((flags & UFS_NOCSUM) != 0) + return (error); CHK(fs->fs_csaddr, <, 0, %jd); CHK(fs->fs_cssize, !=, fragroundup(fs, fs->fs_ncg * sizeof(struct csum)), %jd); @@ -490,7 +507,7 @@ validate_sblock(struct fs *fs, int isaltsblk) CHK2(fs->fs_maxcontig, ==, 0, fs->fs_contigsumsize, !=, 0, %jd); CHK2(fs->fs_maxcontig, >, 1, fs->fs_contigsumsize, !=, MIN(fs->fs_maxcontig, FS_MAXCONTIG), %jd); - return (0); + return (error); } /* diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index b41c162dea7..2944d0c5077 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -913,7 +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; + int candelete, canspeedup, flags; off_t loc; fs = NULL; @@ -958,10 +958,12 @@ ffs_mountfs(struct vnode *odevvp, struct mount *mp, struct thread *td) goto out; } /* fetch the superblock and summary information */ - loc = STDSB; + loc = UFS_STDSB; + flags = 0; if ((mp->mnt_flag & (MNT_ROOTFS | MNT_FORCE)) != 0) - loc = STDSB_NOHASHFAIL; - if ((error = ffs_sbget(devvp, &fs, loc, M_UFSMNT, ffs_use_bread)) != 0) + flags = UFS_NOHASHFAIL; + if ((error = ffs_sbget(devvp, &fs, loc, flags, M_UFSMNT, ffs_use_bread)) + != 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 8c0047f17ca..81cb416b383 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -79,26 +79,30 @@ { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } /* * Request standard superblock location in ffs_sbget(). - * - * STDSB will fail if the superblock has a check hash and it is wrong. - * - * STDSB_NOHASHFAIL will note that the check hash is wrong but will - * still return the superblock. This is used by the bootstrap code - * to give the system a chance to come up so that fsck can be run - * to correct the problem. - * - * STDSB_NOMSG is the same as STDSB but the kernel does not print an - * error message. It is used by programs like fsck that want to - * print their own error message. - * - * STDSB_NOHASHFAIL_NOMSG is the same as STDSB_NOHASHFAIL but the kernel - * does not print an error message. It is used by clients like glabel - * that just want to check for possible filesystem types. */ -#define STDSB -1 /* Fail if check-hash is bad */ -#define STDSB_NOHASHFAIL -2 /* Ignore check-hash failure */ -#define STDSB_NOMSG -3 /* STDSB with no kernel message */ -#define STDSB_NOHASHFAIL_NOMSG -4 /* STDSB_NOHASHFAIL with no message */ +#define UFS_STDSB -1 /* Search standard places for superblock */ + +/* + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. It is used by programs like fsck that + * want to print their own error message. + * + * 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 is used by clients like glabel that just + * want to check for possible filesystem types. Using UFS_NOCSUM + * skips the superblock checks for csum data which allows superblocks + * that have corrupted csum data to be read and used. + * + * UFS_NOHASHFAIL will note that the check hash is wrong but will still + * return the superblock. This is used by the bootstrap code to + * give the system a chance to come up so that fsck can be run to + * correct the problem. + */ +#define UFS_NOHASHFAIL 0x0001 /* Ignore check-hash failure */ +#define UFS_NOMSG 0x0004 /* Print no error message */ +#define UFS_NOCSUM 0x0008 /* Read just the superblock without csum */ +#define UFS_ALTSBLK 0x1000 /* Flag used internally */ /* * Max number of fragments per block. This value is NOT tweakable. diff --git a/usr.sbin/fstyp/ufs.c b/usr.sbin/fstyp/ufs.c index e4de1283e76..69ce90d600f 100644 --- a/usr.sbin/fstyp/ufs.c +++ b/usr.sbin/fstyp/ufs.c @@ -50,11 +50,9 @@ fstyp_ufs(FILE *fp, char *label, size_t labelsize) { struct fs *fs; - switch (sbget(fileno(fp), &fs, STDSB)) { + switch (sbget(fileno(fp), &fs, UFS_STDSB, UFS_NOCSUM)) { case 0: strlcpy(label, fs->fs_volname, labelsize); - free(fs->fs_csp); - free(fs->fs_si); free(fs); return (0); case ENOENT: diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c index fa1ee1c65fe..7e84dd79e14 100644 --- a/usr.sbin/quot/quot.c +++ b/usr.sbin/quot/quot.c @@ -550,7 +550,7 @@ quot(char *name, char *mp) close(fd); return; } - switch (sbget(fd, &fs, STDSB)) { + switch (errno = sbget(fd, &fs, UFS_STDSB, UFS_NOCSUM)) { case 0: break; case ENOENT: @@ -567,8 +567,6 @@ quot(char *name, char *mp) printf(" (%s)",mp); putchar('\n'); (*func)(fd, fs, name); - free(fs->fs_csp); - free(fs->fs_si); free(fs); close(fd); }