Author: mckusick
Date: Wed Aug  9 05:17:21 2017
New Revision: 322297
URL: https://svnweb.freebsd.org/changeset/base/322297

Log:
  Since the switch to GPT disk labels, fsck for UFS/FFS has been
  unable to automatically find alternate superblocks. This checkin
  places the information needed to find alternate superblocks to the
  end of the area reserved for the boot block.
  
  Filesystems created with a newfs of this vintage or later will
  create the recovery information. If you have a filesystem created
  prior to this change and wish to have a recovery block created for
  your filesystem, you can do so by running fsck in forground mode
  (i.e., do not use the -p or -y options). As it starts, fsck will
  ask ``SAVE DATA TO FIND ALTERNATE SUPERBLOCKS'' to which you should
  answer yes.
  
  Discussed with: kib, imp
  MFC after: 2 weeks
  Differential Revision: https://reviews.freebsd.org/D11589

Modified:
  head/sbin/fsck_ffs/setup.c
  head/sbin/newfs/mkfs.c
  head/sys/ufs/ffs/fs.h

Modified: head/sbin/fsck_ffs/setup.c
==============================================================================
--- head/sbin/fsck_ffs/setup.c  Wed Aug  9 04:23:04 2017        (r322296)
+++ head/sbin/fsck_ffs/setup.c  Wed Aug  9 05:17:21 2017        (r322297)
@@ -58,6 +58,10 @@ struct bufarea asblk;
 #define altsblock (*asblk.b_un.b_fs)
 #define POWEROF2(num)  (((num) & ((num) - 1)) == 0)
 
+static int calcsb(char *dev, int devfd, struct fs *fs);
+static void saverecovery(int readfd, int writefd);
+static int chkrecovery(int devfd);
+
 /*
  * Read in a superblock finding an alternate if necessary.
  * Return 1 if successful, 0 if unsuccessful, -1 if file system
@@ -66,9 +70,10 @@ struct bufarea asblk;
 int
 setup(char *dev)
 {
-       long asked, i, j;
+       long cg, asked, i, j;
        long bmapsize;
        struct stat statb;
+       struct fs proto;
        size_t size;
 
        havesb = 0;
@@ -173,10 +178,28 @@ setup(char *dev)
         */
        if (readsb(1) == 0) {
                skipclean = 0;
-               if (bflag || preen)
+               if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
                        return(0);
-               /* Looking for alternates is hard punt for now but retain 
structure */
-               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("%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).");
+                       bflag = 0;
+                       return(0);
+               }
+               pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag);
+               bflag = 0;
        }
        if (skipclean && ckclean && sblock.fs_clean) {
                pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
@@ -213,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.
         */
@@ -427,4 +454,74 @@ sblock_init(void)
        if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
                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));
 }

Modified: head/sbin/newfs/mkfs.c
==============================================================================
--- head/sbin/newfs/mkfs.c      Wed Aug  9 04:23:04 2017        (r322296)
+++ head/sbin/newfs/mkfs.c      Wed Aug  9 05:17:21 2017        (r322297)
@@ -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:
                wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
                        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.

Modified: head/sys/ufs/ffs/fs.h
==============================================================================
--- head/sys/ufs/ffs/fs.h       Wed Aug  9 04:23:04 2017        (r322296)
+++ head/sys/ufs/ffs/fs.h       Wed Aug  9 05:17:21 2017        (r322297)
@@ -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
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to