summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/fsck_ffs/setup.c162
-rw-r--r--sbin/newfs/mkfs.c20
-rw-r--r--sys/ufs/ffs/fs.h14
3 files changed, 156 insertions, 40 deletions
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index 5c2b49f..69d4afc 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -58,7 +58,9 @@ struct bufarea asblk;
#define altsblock (*asblk.b_un.b_fs)
#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
-static void badsb(int listerr, const char *s);
+static int calcsb(char *dev, int devfd, struct fs *fs);
+static void saverecovery(int readfd, int writefd);
+static int chkrecovery(int devfd);
/*
* Read in a superblock finding an alternate if necessary.
@@ -176,7 +178,7 @@ setup(char *dev)
*/
if (readsb(1) == 0) {
skipclean = 0;
- if (bflag || preen)
+ if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
return(0);
if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
return (0);
@@ -234,6 +236,10 @@ setup(char *dev)
memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize);
flush(fswritefd, &asblk);
}
+ if (preen == 0 && yflag == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
+ fswritefd != -1 && chkrecovery(fsreadfd) == 0 &&
+ reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0)
+ saverecovery(fsreadfd, fswritefd);
/*
* read in the summary info.
*/
@@ -313,7 +319,7 @@ int
readsb(int listerr)
{
ufs2_daddr_t super;
- int i;
+ int i, bad;
if (bflag) {
super = bflag;
@@ -363,39 +369,56 @@ readsb(int listerr)
dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
sblk.b_bno = super / dev_bsize;
sblk.b_size = SBLOCKSIZE;
- if (bflag)
- goto out;
/*
* Compare all fields that should not differ in alternate super block.
* When an alternate super-block is specified this check is skipped.
*/
+ if (bflag)
+ goto out;
getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
if (asblk.b_errs)
return (0);
- if (altsblock.fs_sblkno != sblock.fs_sblkno ||
- altsblock.fs_cblkno != sblock.fs_cblkno ||
- altsblock.fs_iblkno != sblock.fs_iblkno ||
- altsblock.fs_dblkno != sblock.fs_dblkno ||
- altsblock.fs_ncg != sblock.fs_ncg ||
- altsblock.fs_bsize != sblock.fs_bsize ||
- altsblock.fs_fsize != sblock.fs_fsize ||
- altsblock.fs_frag != sblock.fs_frag ||
- altsblock.fs_bmask != sblock.fs_bmask ||
- altsblock.fs_fmask != sblock.fs_fmask ||
- altsblock.fs_bshift != sblock.fs_bshift ||
- altsblock.fs_fshift != sblock.fs_fshift ||
- altsblock.fs_fragshift != sblock.fs_fragshift ||
- altsblock.fs_fsbtodb != sblock.fs_fsbtodb ||
- altsblock.fs_sbsize != sblock.fs_sbsize ||
- altsblock.fs_nindir != sblock.fs_nindir ||
- altsblock.fs_inopb != sblock.fs_inopb ||
- altsblock.fs_cssize != sblock.fs_cssize ||
- altsblock.fs_ipg != sblock.fs_ipg ||
- altsblock.fs_fpg != sblock.fs_fpg ||
- altsblock.fs_magic != sblock.fs_magic) {
- badsb(listerr,
- "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
- return (0);
+ bad = 0;
+#define CHK(x, y) \
+ if (altsblock.x != sblock.x) { \
+ bad++; \
+ if (listerr && debug) \
+ printf("SUPER BLOCK VS ALTERNATE MISMATCH %s: " y " vs " y "\n", \
+ #x, (intmax_t)sblock.x, (intmax_t)altsblock.x); \
+ }
+ CHK(fs_sblkno, "%jd");
+ CHK(fs_cblkno, "%jd");
+ CHK(fs_iblkno, "%jd");
+ CHK(fs_dblkno, "%jd");
+ CHK(fs_ncg, "%jd");
+ CHK(fs_bsize, "%jd");
+ CHK(fs_fsize, "%jd");
+ CHK(fs_frag, "%jd");
+ CHK(fs_bmask, "%#jx");
+ CHK(fs_fmask, "%#jx");
+ CHK(fs_bshift, "%jd");
+ CHK(fs_fshift, "%jd");
+ CHK(fs_fragshift, "%jd");
+ CHK(fs_fsbtodb, "%jd");
+ CHK(fs_sbsize, "%jd");
+ CHK(fs_nindir, "%jd");
+ CHK(fs_inopb, "%jd");
+ CHK(fs_cssize, "%jd");
+ CHK(fs_ipg, "%jd");
+ CHK(fs_fpg, "%jd");
+ CHK(fs_magic, "%#jx");
+#undef CHK
+ if (bad) {
+ if (listerr == 0)
+ return (0);
+ if (preen)
+ printf("%s: ", cdevname);
+ printf(
+ "VALUES IN SUPER BLOCK LSB=%jd DISAGREE WITH THOSE IN\n"
+ "LAST ALTERNATE LSB=%jd\n",
+ sblk.b_bno, asblk.b_bno);
+ if (reply("IGNORE ALTERNATE SUPER BLOCK") == 0)
+ return (0);
}
out:
/*
@@ -417,17 +440,6 @@ out:
return (1);
}
-static void
-badsb(int listerr, const char *s)
-{
-
- if (!listerr)
- return;
- if (preen)
- printf("%s: ", cdevname);
- pfatal("BAD SUPER BLOCK: %s\n", s);
-}
-
void
sblock_init(void)
{
@@ -443,3 +455,73 @@ sblock_init(void)
errx(EEXIT, "cannot allocate space for superblock");
dev_bsize = secsize = DEV_BSIZE;
}
+
+/*
+ * Calculate a prototype superblock based on information in the boot area.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+static int
+calcsb(char *dev, int devfd, struct fs *fs)
+{
+ struct fsrecovery fsr;
+
+ /*
+ * We need fragments-per-group and the partition-size.
+ *
+ * Newfs 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.
+ */
+ if (blread(devfd, (char *)&fsr,
+ (SBLOCK_UFS2 - sizeof(fsr)) / dev_bsize, sizeof(fsr)) ||
+ fsr.fsr_magic != FS_UFS2_MAGIC)
+ return (0);
+ memset(fs, 0, sizeof(struct fs));
+ fs->fs_fpg = fsr.fsr_fpg;
+ fs->fs_fsbtodb = fsr.fsr_fsbtodb;
+ fs->fs_sblkno = fsr.fsr_sblkno;
+ fs->fs_magic = fsr.fsr_magic;
+ fs->fs_ncg = fsr.fsr_ncg;
+ return (1);
+}
+
+/*
+ * Check to see if recovery information exists.
+ */
+static int
+chkrecovery(int devfd)
+{
+ struct fsrecovery fsr;
+
+ if (blread(devfd, (char *)&fsr,
+ (SBLOCK_UFS2 - sizeof(fsr)) / dev_bsize, sizeof(fsr)) ||
+ fsr.fsr_magic != FS_UFS2_MAGIC)
+ return (0);
+ return (1);
+}
+
+/*
+ * Read the last sector of the boot block, replace the last
+ * 20 bytes with the recovery information, then write it back.
+ * The recovery information only works for UFS2 filesystems.
+ */
+static void
+saverecovery(int readfd, int writefd)
+{
+ struct fsrecovery fsr;
+
+ if (sblock.fs_magic != FS_UFS2_MAGIC ||
+ blread(readfd, (char *)&fsr,
+ (SBLOCK_UFS2 - sizeof(fsr)) / dev_bsize, sizeof(fsr)))
+ return;
+ fsr.fsr_magic = sblock.fs_magic;
+ fsr.fsr_fpg = sblock.fs_fpg;
+ fsr.fsr_fsbtodb = sblock.fs_fsbtodb;
+ fsr.fsr_sblkno = sblock.fs_sblkno;
+ fsr.fsr_ncg = sblock.fs_ncg;
+ blwrite(writefd, (char *)&fsr, (SBLOCK_UFS2 - sizeof(fsr)) / dev_bsize,
+ sizeof(fsr));
+}
diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c
index b736978..b7af4d2 100644
--- a/sbin/newfs/mkfs.c
+++ b/sbin/newfs/mkfs.c
@@ -121,6 +121,7 @@ mkfs(struct partition *pp, char *fsys)
ino_t maxinum;
int minfragsperinode; /* minimum ratio of frags to inodes */
char tmpbuf[100]; /* XXX this will break in about 2,500 years */
+ struct fsrecovery fsr;
union {
struct fs fdummy;
char cdummy[SBLOCKSIZE];
@@ -616,6 +617,25 @@ restart:
MIN(sblock.fs_cssize - i, sblock.fs_bsize),
((char *)fscs) + i);
/*
+ * Read the last sector of the boot block, replace the last
+ * 20 bytes with the recovery information, then write it back.
+ * The recovery information only works for UFS2 filesystems.
+ */
+ if (sblock.fs_magic == FS_UFS2_MAGIC) {
+ i = bread(&disk,
+ part_ofs + (SBLOCK_UFS2 - sizeof(fsr)) / disk.d_bsize,
+ (char *)&fsr, sizeof(fsr));
+ if (i == -1)
+ err(1, "can't read recovery area: %s", disk.d_error);
+ fsr.fsr_magic = sblock.fs_magic;
+ fsr.fsr_fpg = sblock.fs_fpg;
+ fsr.fsr_fsbtodb = sblock.fs_fsbtodb;
+ fsr.fsr_sblkno = sblock.fs_sblkno;
+ fsr.fsr_ncg = sblock.fs_ncg;
+ wtfs((SBLOCK_UFS2 - sizeof(fsr)) / disk.d_bsize, sizeof(fsr),
+ (char *)&fsr);
+ }
+ /*
* Update information about this partition in pack
* label, to that it may be updated on disk.
*/
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
index 620fee9..35ee270 100644
--- a/sys/ufs/ffs/fs.h
+++ b/sys/ufs/ffs/fs.h
@@ -234,6 +234,20 @@ struct fsck_cmd {
};
/*
+ * A recovery structure placed at the end of the boot block area by newfs
+ * that can be used by fsck to search for alternate superblocks.
+ */
+#define RESID (4096 - 20) /* disk sector size minus recovery area size */
+struct fsrecovery {
+ char block[RESID]; /* unused part of sector */
+ int32_t fsr_magic; /* magic number */
+ int32_t fsr_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ int32_t fsr_sblkno; /* offset of super-block in filesys */
+ int32_t fsr_fpg; /* blocks per group * fs_frag */
+ u_int32_t fsr_ncg; /* number of cylinder groups */
+};
+
+/*
* Per cylinder group information; summarized in blocks allocated
* from first cylinder group data blocks. These blocks have to be
* read in from fs_csaddr (size fs_cssize) in addition to the
OpenPOWER on IntegriCloud