diff options
Diffstat (limited to 'sbin/fsck_ffs/setup.c')
-rw-r--r-- | sbin/fsck_ffs/setup.c | 559 |
1 files changed, 312 insertions, 247 deletions
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index 0ae7f1bbb28f..f10f02d159c3 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/disk.h> #include <sys/stat.h> @@ -54,19 +46,23 @@ __FBSDID("$FreeBSD$"); #include <limits.h> #include <stdint.h> #include <string.h> -#include <libufs.h> #include "fsck.h" -struct inoinfo **inphead, **inpsort; /* info about all inodes */ +struct inohash *inphash; /* hash list of directory inode info */ +struct inoinfo **inpsort; /* disk order list of directory inodes */ +struct inode snaplist[FSMAXSNAP + 1]; /* list of active snapshots */ +int snapcnt; /* number of active snapshots */ +char *copybuf; /* buffer to copy snapshot blocks */ -struct bufarea asblk; -#define altsblock (*asblk.b_un.b_fs) +static int sbhashfailed; #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); +static int getlbnblkno(struct inodesc *); +static int checksnapinfo(struct inode *); /* * Read in a superblock finding an alternate if necessary. @@ -76,99 +72,21 @@ static int chkrecovery(int devfd); int setup(char *dev) { - long cg, asked, i, j; - long bmapsize; - struct stat statb; - struct fs proto; - size_t size; + long i, bmapsize; + struct inode ip; - 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; + /* + * We are expected to have an open file descriptor and a superblock. + */ + if (fsreadfd < 0 || havesb == 0) { + if (debug) { + if (fsreadfd < 0) + printf("setup: missing fsreadfd\n"); + else + printf("setup: missing superblock\n"); } - printf("Can't open %s: %s\n", dev, strerror(errno)); 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 && @@ -180,33 +98,16 @@ setup(char *dev) } if (preen == 0) printf("\n"); - /* - * Read in the superblock, looking for alternates if necessary - */ - if (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("%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); + 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(); } - pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); - bflag = 0; } if (skipclean && ckclean && sblock.fs_clean) { pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); @@ -239,41 +140,14 @@ setup(char *dev) pfatal("from before 2002 with the command ``fsck -c 2''\n"); exit(EEXIT); } - if ((asblk.b_flags & B_DIRTY) != 0 && !bflag) { - 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. - */ - 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 */ + bufinit(); bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short)); blockmap = Calloc((unsigned)bmapsize, sizeof (char)); if (blockmap == NULL) { @@ -287,22 +161,57 @@ setup(char *dev) (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg))); goto badsb; } - numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128); - dirhash = numdirs; + numdirs = sblock.fs_cstotal.cs_ndir; + dirhash = MAX(numdirs / 2, 1); inplast = 0; listmax = numdirs + 10; inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *)); - inphead = (struct inoinfo **)Calloc(numdirs, sizeof(struct inoinfo *)); - if (inpsort == NULL || inphead == NULL) { - printf("cannot alloc %ju bytes for inphead\n", + inphash = (struct inohash *)Calloc(dirhash, sizeof(struct inohash)); + if (inpsort == NULL || inphash == NULL) { + printf("cannot alloc %ju bytes for inphash\n", (uintmax_t)numdirs * sizeof(struct inoinfo *)); goto badsb; } - bufinit(); if (sblock.fs_flags & FS_DOSOFTDEP) usedsoftdep = 1; else usedsoftdep = 0; + /* + * Collect any snapshot inodes so that we can allow them to + * claim any blocks that we free. The code for doing this is + * imported here and into inode.c from sys/ufs/ffs/ffs_snapshot.c. + */ + for (snapcnt = 0; snapcnt < FSMAXSNAP; snapcnt++) { + if (sblock.fs_snapinum[snapcnt] == 0) + break; + ginode(sblock.fs_snapinum[snapcnt], &ip); + if ((DIP(ip.i_dp, di_mode) & IFMT) == IFREG && + (DIP(ip.i_dp, di_flags) & SF_SNAPSHOT) != 0 && + checksnapinfo(&ip)) { + if (debug) + printf("Load snapshot %jd\n", + (intmax_t)sblock.fs_snapinum[snapcnt]); + snaplist[snapcnt] = ip; + continue; + } + printf("Removing non-snapshot inode %ju from snapshot list\n", + (uintmax_t)sblock.fs_snapinum[snapcnt]); + irelse(&ip); + for (i = snapcnt + 1; i < FSMAXSNAP; i++) { + if (sblock.fs_snapinum[i] == 0) + break; + sblock.fs_snapinum[i - 1] = sblock.fs_snapinum[i]; + } + sblock.fs_snapinum[i - 1] = 0; + snapcnt--; + sbdirty(); + } + if (snapcnt > 0 && copybuf == NULL) { + copybuf = Balloc(sblock.fs_bsize); + if (copybuf == NULL) + errx(EEXIT, "cannot allocate space for snapshot " + "copy buffer"); + } return (1); badsb: @@ -311,37 +220,242 @@ badsb: } /* + * Check for valid snapshot information. + * + * Each snapshot has a list of blocks that have been copied. This list + * is consulted before checking the snapshot inode. Its purpose is to + * speed checking of commonly checked blocks and to avoid recursive + * checks of the snapshot inode. In particular, the list must contain + * the superblock, the superblock summary information, and all the + * cylinder group blocks. The list may contain other commonly checked + * pointers such as those of the blocks that contain the snapshot inodes. + * The list is sorted into block order to allow binary search lookup. + * + * The twelve direct direct block pointers of the snapshot are always + * copied, so we test for them first before checking the list itself + * (i.e., they are not in the list). + * + * The checksnapinfo() routine needs to ensure that the list contains at + * least the super block, its summary information, and the cylinder groups. + * Here we check the list first for the superblock, zero or more cylinder + * groups up to the location of the superblock summary information, the + * summary group information, and any remaining cylinder group maps that + * follow it. We skip over any other entries in the list. + */ +#define CHKBLKINLIST(chkblk) \ + /* All UFS_NDADDR blocks are copied */ \ + if ((chkblk) >= UFS_NDADDR) { \ + /* Skip over blocks that are not of interest */ \ + while (*blkp < (chkblk) && blkp < lastblkp) \ + blkp++; \ + /* Fail if end of list and not all blocks found */ \ + if (blkp >= lastblkp) { \ + pwarn("UFS%d snapshot inode %jd failed: " \ + "improper block list length (%jd)\n", \ + sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, \ + (intmax_t)snapip->i_number, \ + (intmax_t)(lastblkp - &snapblklist[0])); \ + status = 0; \ + } \ + /* Fail if block we seek is missing */ \ + else if (*blkp++ != (chkblk)) { \ + pwarn("UFS%d snapshot inode %jd failed: " \ + "block list (%jd) != %s (%jd)\n", \ + sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2, \ + (intmax_t)snapip->i_number, \ + (intmax_t)blkp[-1], #chkblk, \ + (intmax_t)chkblk); \ + status = 0; \ + } \ + } + +static int +checksnapinfo(struct inode *snapip) +{ + struct fs *fs; + struct bufarea *bp; + struct inodesc idesc; + daddr_t *snapblklist, *blkp, *lastblkp, csblkno; + int cg, loc, len, status; + ufs_lbn_t lbn; + size_t size; + + fs = &sblock; + memset(&idesc, 0, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = getlbnblkno; + idesc.id_number = snapip->i_number; + lbn = howmany(fs->fs_size, fs->fs_frag); + idesc.id_parent = lbn; /* sought after blkno */ + if ((ckinode(snapip->i_dp, &idesc) & FOUND) == 0) + return (0); + size = fragroundup(fs, + DIP(snapip->i_dp, di_size) - lblktosize(fs, lbn)); + bp = getdatablk(idesc.id_parent, size, BT_DATA); + if (bp->b_errs != 0) + return (0); + snapblklist = (daddr_t *)bp->b_un.b_buf; + /* + * snapblklist[0] is the size of the list + * snapblklist[1] is the first element of the list + * + * We need to be careful to bound the size of the list and verify + * that we have not run off the end of it if it or its size has + * been corrupted. + */ + blkp = &snapblklist[1]; + lastblkp = &snapblklist[MAX(0, + MIN(snapblklist[0] + 1, size / sizeof(daddr_t)))]; + status = 1; + /* Check that the superblock is listed. */ + CHKBLKINLIST(lblkno(fs, fs->fs_sblockloc)); + if (status == 0) + goto out; + /* + * Calculate where the summary information is located. + * Usually it is in the first cylinder group, but growfs + * may move it to the first cylinder group that it adds. + * + * Check all cylinder groups up to the summary information. + */ + csblkno = fragstoblks(fs, fs->fs_csaddr); + for (cg = 0; cg < fs->fs_ncg; cg++) { + if (fragstoblks(fs, cgtod(fs, cg)) > csblkno) + break; + CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg))); + if (status == 0) + goto out; + } + /* Check the summary information block(s). */ + len = howmany(fs->fs_cssize, fs->fs_bsize); + for (loc = 0; loc < len; loc++) { + CHKBLKINLIST(csblkno + loc); + if (status == 0) + goto out; + } + /* Check the remaining cylinder groups. */ + for (; cg < fs->fs_ncg; cg++) { + CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg))); + if (status == 0) + goto out; + } +out: + brelse(bp); + return (status); +} + +/* + * Return the block number associated with a specified inode lbn. + * Requested lbn is in id_parent. If found, block is returned in + * id_parent. + */ +static int +getlbnblkno(struct inodesc *idesc) +{ + + if (idesc->id_lbn < idesc->id_parent) + return (KEEPON); + idesc->id_parent = idesc->id_blkno; + return (STOP | FOUND); +} + +/* + * Open a device or file to be checked by fsck. + */ +int +openfilesys(char *dev) +{ + struct stat statb; + int saved_fsreadfd; + + if (stat(dev, &statb) < 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) { + pwarn("BACKGROUND FSCK LACKS A SNAPSHOT\n"); + return (0); + } + if (bkgrdflag != 0) { + cursnapshot = statb.st_ino; + } else { + pwarn("%s IS NOT A DISK DEVICE\n", dev); + if (preen || reply("CONTINUE") == 0) + return (0); + } + } + saved_fsreadfd = fsreadfd; + if ((fsreadfd = open(dev, O_RDONLY)) < 0) { + fsreadfd = saved_fsreadfd; + return (0); + } + if (saved_fsreadfd != -1) + close(saved_fsreadfd); + return (1); +} + +/* * Read in the super block and its summary info. */ int -readsb(int listerr) +readsb(void) { - off_t super; - int bad, ret; struct fs *fs; - super = bflag ? bflag * dev_bsize : STDSB_NOHASHFAIL; + sbhashfailed = 0; readcnt[sblk.b_type]++; - if ((ret = sbget(fsreadfd, &fs, super)) != 0) { - switch (ret) { - case EINVAL: - /* Superblock check-hash failed */ + /* + * If bflag is given, then check just that superblock. + */ + if (bflag) { + switch (sbget(fsreadfd, &fs, bflag * dev_bsize, 0)) { + case 0: + goto goodsb; + case EINTEGRITY: + 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); /* @@ -353,58 +467,6 @@ readsb(int listerr) sblk.b_bno = sblock.fs_sblockactualloc / dev_bsize; sblk.b_size = SBLOCKSIZE; /* - * 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); - 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: - /* * If not yet done, update UFS1 superblock with new wider fields. */ if (sblock.fs_magic == FS_UFS1_MAGIC && @@ -427,14 +489,13 @@ void sblock_init(void) { + fsreadfd = -1; fswritefd = -1; fsmodified = 0; lfdir = 0; initbarea(&sblk, BT_SUPERBLK); - initbarea(&asblk, BT_SUPERBLK); - sblk.b_un.b_buf = Malloc(SBLOCKSIZE); - asblk.b_un.b_buf = Malloc(SBLOCKSIZE); - if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) + sblk.b_un.b_buf = Balloc(SBLOCKSIZE); + if (sblk.b_un.b_buf == NULL) errx(EEXIT, "cannot allocate space for superblock"); dev_bsize = secsize = DEV_BSIZE; } @@ -462,7 +523,7 @@ calcsb(char *dev, int devfd, struct fs *fs) */ if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1) return (0); - fsrbuf = Malloc(secsize); + fsrbuf = Balloc(secsize); if (fsrbuf == NULL) errx(EEXIT, "calcsb: cannot allocate recovery buffer"); if (blread(devfd, fsrbuf, @@ -495,17 +556,19 @@ chkrecovery(int devfd) { struct fsrecovery *fsr; char *fsrbuf; - u_int secsize; + u_int secsize, rdsize; /* * Could not determine if backup material exists, so do not * offer to create it. */ fsrbuf = NULL; + rdsize = sblock.fs_fsize; if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 || - (fsrbuf = Malloc(secsize)) == NULL || - blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize, - secsize) != 0) { + rdsize % secsize != 0 || + (fsrbuf = Balloc(rdsize)) == NULL || + blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, + rdsize) != 0) { free(fsrbuf); return (1); } @@ -513,7 +576,7 @@ chkrecovery(int devfd) * Recovery material has already been created, so do not * need to create it again. */ - fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr]; + fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr]; if (fsr->fsr_magic == FS_UFS2_MAGIC) { free(fsrbuf); return (1); @@ -526,8 +589,8 @@ chkrecovery(int devfd) } /* - * Read the last sector of the boot block, replace the last - * 20 bytes with the recovery information, then write it back. + * Read the last filesystem-size piece 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 @@ -535,24 +598,26 @@ saverecovery(int readfd, int writefd) { struct fsrecovery *fsr; char *fsrbuf; - u_int secsize; + u_int secsize, rdsize; fsrbuf = NULL; + rdsize = sblock.fs_fsize; if (sblock.fs_magic != FS_UFS2_MAGIC || ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 || - (fsrbuf = Malloc(secsize)) == NULL || - blread(readfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize, - secsize) != 0) { + rdsize % secsize != 0 || + (fsrbuf = Balloc(rdsize)) == NULL || + blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, + rdsize) != 0) { printf("RECOVERY DATA COULD NOT BE CREATED\n"); free(fsrbuf); return; } - fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr]; + fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr]; 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, fsrbuf, (SBLOCK_UFS2 - secsize) / secsize, secsize); + blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize); free(fsrbuf); } |