5a70d8a008
struct disklabel. Functionality lost: 1. struct disklabel used to be updated to contain bsize, fsize, cpg. This information was used to locate the alternative superblock in the filesystem if the primary superblock was corrupted. We need to find a new place to store this information if we need this functionality. 2. On vax SMD drives that contained bad sector lists, the newfs program knew how to get the offset and skip to the correct location in order to place the label.
966 lines
27 KiB
C
966 lines
27 KiB
C
/* $NetBSD: setup.c,v 1.80 2006/08/26 22:03:47 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1980, 1986, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95";
|
|
#else
|
|
__RCSID("$NetBSD: setup.c,v 1.80 2006/08/26 22:03:47 christos Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/file.h>
|
|
#include <sys/disk.h>
|
|
|
|
#include <ufs/ufs/dinode.h>
|
|
#include <ufs/ufs/dir.h>
|
|
#include <ufs/ufs/ufs_bswap.h>
|
|
#include <ufs/ffs/fs.h>
|
|
#include <ufs/ffs/ffs_extern.h>
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "fsck.h"
|
|
#include "extern.h"
|
|
#include "fsutil.h"
|
|
#include "partutil.h"
|
|
|
|
#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
|
|
|
|
static void badsb(int, const char *);
|
|
static int calcsb(const char *, int, struct fs *);
|
|
static int readsb(int);
|
|
static int readappleufs(void);
|
|
|
|
int16_t sblkpostbl[256];
|
|
|
|
/*
|
|
* Read in a superblock finding an alternate if necessary.
|
|
* Return 1 if successful, 0 if unsuccessful, -1 if filesystem
|
|
* is already clean (preen mode only).
|
|
*/
|
|
int
|
|
setup(const char *dev)
|
|
{
|
|
long cg, size, asked, i, j;
|
|
long bmapsize;
|
|
struct disk_geom geo;
|
|
struct dkwedge_info dkw;
|
|
off_t sizepb;
|
|
struct stat statb;
|
|
struct fs proto;
|
|
int doskipclean;
|
|
u_int64_t maxfilesize;
|
|
struct csum *ccsp;
|
|
|
|
havesb = 0;
|
|
fswritefd = -1;
|
|
doskipclean = skipclean;
|
|
if (stat(dev, &statb) < 0) {
|
|
printf("Can't stat %s: %s\n", dev, strerror(errno));
|
|
return (0);
|
|
}
|
|
if (!forceimage && !S_ISCHR(statb.st_mode)) {
|
|
pfatal("%s is not a character device", dev);
|
|
if (reply("CONTINUE") == 0)
|
|
return (0);
|
|
}
|
|
if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
|
|
printf("Can't open %s: %s\n", dev, strerror(errno));
|
|
return (0);
|
|
}
|
|
if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
|
|
fswritefd = -1;
|
|
if (preen)
|
|
pfatal("NO WRITE ACCESS");
|
|
printf("** %s (NO WRITE)\n", dev);
|
|
quiet = 0;
|
|
} else
|
|
if (!preen && !quiet)
|
|
printf("** %s\n", dev);
|
|
fsmodified = 0;
|
|
lfdir = 0;
|
|
initbarea(&sblk);
|
|
initbarea(&asblk);
|
|
sblk.b_un.b_buf = malloc(SBLOCKSIZE);
|
|
sblock = malloc(SBLOCKSIZE);
|
|
asblk.b_un.b_buf = malloc(SBLOCKSIZE);
|
|
altsblock = malloc(SBLOCKSIZE);
|
|
if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL ||
|
|
sblock == NULL || altsblock == NULL)
|
|
errx(EEXIT, "cannot allocate space for superblock");
|
|
if (!forceimage && getdiskinfo(dev, fsreadfd, NULL, &geo, &dkw) != -1)
|
|
dev_bsize = secsize = geo.dg_secsize;
|
|
else
|
|
dev_bsize = secsize = DEV_BSIZE;
|
|
/*
|
|
* Read in the superblock, looking for alternates if necessary
|
|
*/
|
|
if (readsb(1) == 0) {
|
|
if (bflag || preen || forceimage ||
|
|
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_ffs TO SPECIFY THE",
|
|
"LOCATION OF AN ALTERNATE",
|
|
"SUPER-BLOCK TO SUPPLY NEEDED",
|
|
"INFORMATION; SEE fsck_ffs(8).");
|
|
return(0);
|
|
}
|
|
doskipclean = 0;
|
|
pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
|
|
}
|
|
if (debug)
|
|
printf("clean = %d\n", sblock->fs_clean);
|
|
if (doswap)
|
|
doskipclean = 0;
|
|
if (sblock->fs_clean & FS_ISCLEAN) {
|
|
if (doskipclean) {
|
|
if (!quiet)
|
|
pwarn("%sile system is clean; not checking\n",
|
|
preen ? "f" : "** F");
|
|
return (-1);
|
|
}
|
|
if (!preen && !doswap)
|
|
pwarn("** File system is already clean\n");
|
|
}
|
|
maxfsblock = sblock->fs_size;
|
|
maxino = sblock->fs_ncg * sblock->fs_ipg;
|
|
sizepb = sblock->fs_bsize;
|
|
maxfilesize = sblock->fs_bsize * NDADDR - 1;
|
|
for (i = 0; i < NIADDR; i++) {
|
|
sizepb *= NINDIR(sblock);
|
|
maxfilesize += sizepb;
|
|
}
|
|
if ((!is_ufs2 && cvtlevel >= 4) &&
|
|
(sblock->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
|
|
if (preen)
|
|
pwarn("CONVERTING TO NEW SUPERBLOCK LAYOUT\n");
|
|
else if (!reply("CONVERT TO NEW SUPERBLOCK LAYOUT"))
|
|
return(0);
|
|
sblock->fs_old_flags |= FS_FLAGS_UPDATED;
|
|
/* Disable the postbl tables */
|
|
sblock->fs_old_cpc = 0;
|
|
sblock->fs_old_nrpos = 1;
|
|
sblock->fs_old_trackskew = 0;
|
|
/* The other fields have already been updated by
|
|
* sb_oldfscompat_read
|
|
*/
|
|
sbdirty();
|
|
}
|
|
if (!is_ufs2 && cvtlevel == 3 &&
|
|
(sblock->fs_old_flags & FS_FLAGS_UPDATED)) {
|
|
if (preen)
|
|
pwarn("DOWNGRADING TO OLD SUPERBLOCK LAYOUT\n");
|
|
else if (!reply("DOWNGRADE TO OLD SUPERBLOCK LAYOUT"))
|
|
return(0);
|
|
sblock->fs_old_flags &= ~FS_FLAGS_UPDATED;
|
|
sb_oldfscompat_write(sblock, sblock);
|
|
sblock->fs_old_flags &= ~FS_FLAGS_UPDATED; /* just in case */
|
|
/* Leave postbl tables disabled, but blank its superblock region anyway */
|
|
sblock->fs_old_postblformat = FS_DYNAMICPOSTBLFMT;
|
|
sblock->fs_old_cpc = 0;
|
|
sblock->fs_old_nrpos = 1;
|
|
sblock->fs_old_trackskew = 0;
|
|
memset(&sblock->fs_old_postbl_start, 0xff, 256);
|
|
sb_oldfscompat_read(sblock, &sblocksave);
|
|
sbdirty();
|
|
}
|
|
/*
|
|
* Check and potentially fix certain fields in the super block.
|
|
*/
|
|
if (sblock->fs_optim != FS_OPTTIME && sblock->fs_optim != FS_OPTSPACE) {
|
|
pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
|
|
if (reply("SET TO DEFAULT") == 1) {
|
|
sblock->fs_optim = FS_OPTTIME;
|
|
sbdirty();
|
|
}
|
|
}
|
|
if ((sblock->fs_minfree < 0 || sblock->fs_minfree > 99)) {
|
|
pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
|
|
sblock->fs_minfree);
|
|
if (reply("SET TO DEFAULT") == 1) {
|
|
sblock->fs_minfree = 10;
|
|
sbdirty();
|
|
}
|
|
}
|
|
if (!is_ufs2 && sblock->fs_old_postblformat != FS_42POSTBLFMT &&
|
|
(sblock->fs_old_interleave < 1 ||
|
|
sblock->fs_old_interleave > sblock->fs_old_nsect)) {
|
|
pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK",
|
|
sblock->fs_old_interleave);
|
|
sblock->fs_old_interleave = 1;
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("SET TO DEFAULT") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if (!is_ufs2 && sblock->fs_old_postblformat != FS_42POSTBLFMT &&
|
|
(sblock->fs_old_npsect < sblock->fs_old_nsect ||
|
|
sblock->fs_old_npsect > sblock->fs_old_nsect*2)) {
|
|
pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK",
|
|
sblock->fs_old_npsect);
|
|
sblock->fs_old_npsect = sblock->fs_old_nsect;
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("SET TO DEFAULT") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if (sblock->fs_bmask != ~(sblock->fs_bsize - 1)) {
|
|
pwarn("INCORRECT BMASK=0x%x IN SUPERBLOCK",
|
|
sblock->fs_bmask);
|
|
sblock->fs_bmask = ~(sblock->fs_bsize - 1);
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("FIX") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if (sblock->fs_fmask != ~(sblock->fs_fsize - 1)) {
|
|
pwarn("INCORRECT FMASK=0x%x IN SUPERBLOCK",
|
|
sblock->fs_fmask);
|
|
sblock->fs_fmask = ~(sblock->fs_fsize - 1);
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("FIX") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if (is_ufs2 || sblock->fs_old_inodefmt >= FS_44INODEFMT) {
|
|
if (sblock->fs_maxfilesize != maxfilesize) {
|
|
pwarn("INCORRECT MAXFILESIZE=%lld IN SUPERBLOCK",
|
|
(unsigned long long)sblock->fs_maxfilesize);
|
|
sblock->fs_maxfilesize = maxfilesize;
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("FIX") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if ((is_ufs2 && sblock->fs_maxsymlinklen != MAXSYMLINKLEN_UFS2)
|
|
||
|
|
(!is_ufs2 && sblock->fs_maxsymlinklen != MAXSYMLINKLEN_UFS1))
|
|
{
|
|
pwarn("INCORRECT MAXSYMLINKLEN=%d IN SUPERBLOCK",
|
|
sblock->fs_maxsymlinklen);
|
|
sblock->fs_maxsymlinklen = is_ufs2 ?
|
|
MAXSYMLINKLEN_UFS2 : MAXSYMLINKLEN_UFS1;
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("FIX") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if (sblock->fs_qbmask != ~sblock->fs_bmask) {
|
|
pwarn("INCORRECT QBMASK=%#llx IN SUPERBLOCK",
|
|
(unsigned long long)sblock->fs_qbmask);
|
|
sblock->fs_qbmask = ~sblock->fs_bmask;
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("FIX") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
if (sblock->fs_qfmask != ~sblock->fs_fmask) {
|
|
pwarn("INCORRECT QFMASK=%#llx IN SUPERBLOCK",
|
|
(unsigned long long)sblock->fs_qfmask);
|
|
sblock->fs_qfmask = ~sblock->fs_fmask;
|
|
if (preen)
|
|
printf(" (FIXED)\n");
|
|
if (preen || reply("FIX") == 1) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
}
|
|
newinofmt = 1;
|
|
} else {
|
|
sblock->fs_qbmask = ~sblock->fs_bmask;
|
|
sblock->fs_qfmask = ~sblock->fs_fmask;
|
|
newinofmt = 0;
|
|
}
|
|
/*
|
|
* Convert to new inode format.
|
|
*/
|
|
if (!is_ufs2 && cvtlevel >= 2 &&
|
|
sblock->fs_old_inodefmt < FS_44INODEFMT) {
|
|
if (preen)
|
|
pwarn("CONVERTING TO NEW INODE FORMAT\n");
|
|
else if (!reply("CONVERT TO NEW INODE FORMAT"))
|
|
return(0);
|
|
doinglevel2++;
|
|
sblock->fs_old_inodefmt = FS_44INODEFMT;
|
|
sblock->fs_maxfilesize = maxfilesize;
|
|
sblock->fs_maxsymlinklen = MAXSYMLINKLEN_UFS1;
|
|
sblock->fs_qbmask = ~sblock->fs_bmask;
|
|
sblock->fs_qfmask = ~sblock->fs_fmask;
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
/*
|
|
* Convert to new cylinder group format.
|
|
*/
|
|
if (!is_ufs2 && cvtlevel >= 1 &&
|
|
sblock->fs_old_postblformat == FS_42POSTBLFMT) {
|
|
if (preen)
|
|
pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n");
|
|
else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT"))
|
|
return(0);
|
|
doinglevel1++;
|
|
sblock->fs_old_postblformat = FS_DYNAMICPOSTBLFMT;
|
|
sblock->fs_old_nrpos = 8;
|
|
sblock->fs_old_postbloff =
|
|
(char *)(&sblock->fs_old_postbl_start) -
|
|
(char *)(&sblock->fs_firstfield);
|
|
sblock->fs_old_rotbloff =
|
|
(char *)(&sblock->fs_magic+1) -
|
|
(char *)(&sblock->fs_firstfield);
|
|
sblock->fs_cgsize =
|
|
fragroundup(sblock, CGSIZE(sblock));
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
if (asblk.b_dirty && !bflag) {
|
|
memmove(sblk.b_un.b_fs, sblock, SBLOCKSIZE);
|
|
sb_oldfscompat_write(sblk.b_un.b_fs, sblocksave);
|
|
if (needswap)
|
|
ffs_sb_swap(sblk.b_un.b_fs, sblk.b_un.b_fs);
|
|
memmove(asblk.b_un.b_fs, sblk.b_un.b_fs, (size_t)sblock->fs_sbsize);
|
|
flush(fswritefd, &asblk);
|
|
}
|
|
/*
|
|
* read in the summary info.
|
|
*/
|
|
asked = 0;
|
|
sblock->fs_csp = (struct csum *)calloc(1, sblock->fs_cssize);
|
|
if (sblock->fs_csp == NULL) {
|
|
pwarn("cannot alloc %u bytes for summary info\n",
|
|
sblock->fs_cssize);
|
|
goto badsblabel;
|
|
}
|
|
for (i = 0, j = 0; i < sblock->fs_cssize; i += sblock->fs_bsize, j++) {
|
|
size = sblock->fs_cssize - i < sblock->fs_bsize ?
|
|
sblock->fs_cssize - i : sblock->fs_bsize;
|
|
ccsp = (struct csum *)((char *)sblock->fs_csp + i);
|
|
if (bread(fsreadfd, (char *)ccsp,
|
|
fsbtodb(sblock, sblock->fs_csaddr + j * sblock->fs_frag),
|
|
size) != 0 && !asked) {
|
|
pfatal("BAD SUMMARY INFORMATION");
|
|
if (reply("CONTINUE") == 0) {
|
|
markclean = 0;
|
|
exit(EEXIT);
|
|
}
|
|
asked++;
|
|
}
|
|
if (doswap) {
|
|
ffs_csum_swap(ccsp, ccsp, size);
|
|
bwrite(fswritefd, (char *)ccsp,
|
|
fsbtodb(sblock,
|
|
sblock->fs_csaddr + j * sblock->fs_frag),
|
|
size);
|
|
}
|
|
if (needswap)
|
|
ffs_csum_swap(ccsp, ccsp, size);
|
|
}
|
|
/*
|
|
* allocate and initialize the necessary maps
|
|
*/
|
|
bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t));
|
|
blockmap = calloc((unsigned)bmapsize, sizeof (char));
|
|
if (blockmap == NULL) {
|
|
pwarn("cannot alloc %u bytes for blockmap\n",
|
|
(unsigned)bmapsize);
|
|
goto badsblabel;
|
|
}
|
|
inostathead = calloc((unsigned)(sblock->fs_ncg),
|
|
sizeof(struct inostatlist));
|
|
if (inostathead == NULL) {
|
|
pwarn("cannot alloc %u bytes for inostathead\n",
|
|
(unsigned)(sizeof(struct inostatlist) * (sblock->fs_ncg)));
|
|
goto badsblabel;
|
|
}
|
|
/*
|
|
* cs_ndir may be inaccurate, particularly if we're using the -b
|
|
* option, so set a minimum to prevent bogus subdirectory reconnects
|
|
* and really inefficient directory scans.
|
|
* Also set a maximum in case the value is too large.
|
|
*/
|
|
numdirs = sblock->fs_cstotal.cs_ndir;
|
|
if (numdirs < 1024)
|
|
numdirs = 1024;
|
|
if (numdirs > maxino + 1)
|
|
numdirs = maxino + 1;
|
|
dirhash = numdirs;
|
|
inplast = 0;
|
|
listmax = numdirs + 10;
|
|
inpsort = (struct inoinfo **)calloc((unsigned)listmax,
|
|
sizeof(struct inoinfo *));
|
|
inphead = (struct inoinfo **)calloc((unsigned)numdirs,
|
|
sizeof(struct inoinfo *));
|
|
if (inpsort == NULL || inphead == NULL) {
|
|
pwarn("cannot alloc %u bytes for inphead\n",
|
|
(unsigned)(numdirs * sizeof(struct inoinfo *)));
|
|
goto badsblabel;
|
|
}
|
|
cgrp = malloc(sblock->fs_cgsize);
|
|
if (cgrp == NULL) {
|
|
pwarn("cannot alloc %u bytes for cylinder group\n",
|
|
sblock->fs_cgsize);
|
|
goto badsblabel;
|
|
}
|
|
bufinit();
|
|
if (sblock->fs_flags & FS_DOSOFTDEP)
|
|
usedsoftdep = 1;
|
|
else
|
|
usedsoftdep = 0;
|
|
|
|
if (!forceimage && dkw.dkw_parent[0])
|
|
if (strcmp(dkw.dkw_ptype, DKW_PTYPE_APPLEUFS) == 0)
|
|
isappleufs = 1;
|
|
|
|
if (readappleufs())
|
|
isappleufs = 1;
|
|
|
|
dirblksiz = DIRBLKSIZ;
|
|
if (isappleufs)
|
|
dirblksiz = APPLEUFS_DIRBLKSIZ;
|
|
|
|
if (debug)
|
|
printf("isappleufs = %d, dirblksiz = %d\n", isappleufs, dirblksiz);
|
|
|
|
return (1);
|
|
|
|
badsblabel:
|
|
markclean=0;
|
|
ckfini();
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
readappleufs(void)
|
|
{
|
|
daddr_t label = APPLEUFS_LABEL_OFFSET / dev_bsize;
|
|
struct appleufslabel *appleufs;
|
|
int i;
|
|
|
|
/* XXX do we have to deal with APPLEUFS_LABEL_OFFSET not
|
|
* being block aligned (CD's?)
|
|
*/
|
|
if (bread(fsreadfd, (char *)appleufsblk.b_un.b_fs, label,
|
|
(long)APPLEUFS_LABEL_SIZE) != 0)
|
|
return 0;
|
|
appleufsblk.b_bno = label;
|
|
appleufsblk.b_size = APPLEUFS_LABEL_SIZE;
|
|
|
|
appleufs = appleufsblk.b_un.b_appleufs;
|
|
|
|
if (ntohl(appleufs->ul_magic) != APPLEUFS_LABEL_MAGIC) {
|
|
if (!isappleufs) {
|
|
return 0;
|
|
} else {
|
|
pfatal("MISSING APPLEUFS VOLUME LABEL\n");
|
|
if (reply("FIX") == 0) {
|
|
return 1;
|
|
}
|
|
ffs_appleufs_set(appleufs, NULL, -1, 0);
|
|
appleufsdirty();
|
|
}
|
|
}
|
|
|
|
if (ntohl(appleufs->ul_version) != APPLEUFS_LABEL_VERSION) {
|
|
pwarn("INCORRECT APPLE UFS VERSION NUMBER (%d should be %d)",
|
|
ntohl(appleufs->ul_version),APPLEUFS_LABEL_VERSION);
|
|
if (preen) {
|
|
printf(" (CORRECTED)\n");
|
|
}
|
|
if (preen || reply("CORRECT")) {
|
|
appleufs->ul_version = htonl(APPLEUFS_LABEL_VERSION);
|
|
appleufsdirty();
|
|
}
|
|
}
|
|
|
|
if (ntohs(appleufs->ul_namelen) > APPLEUFS_MAX_LABEL_NAME) {
|
|
pwarn("APPLE UFS LABEL NAME TOO LONG");
|
|
if (preen) {
|
|
printf(" (TRUNCATED)\n");
|
|
}
|
|
if (preen || reply("TRUNCATE")) {
|
|
appleufs->ul_namelen = htons(APPLEUFS_MAX_LABEL_NAME);
|
|
appleufsdirty();
|
|
}
|
|
}
|
|
|
|
if (ntohs(appleufs->ul_namelen) == 0) {
|
|
pwarn("MISSING APPLE UFS LABEL NAME");
|
|
if (preen) {
|
|
printf(" (FIXED)\n");
|
|
}
|
|
if (preen || reply("FIX")) {
|
|
ffs_appleufs_set(appleufs, NULL, -1, 0);
|
|
appleufsdirty();
|
|
}
|
|
}
|
|
|
|
/* Scan name for first illegal character */
|
|
for (i=0;i<ntohs(appleufs->ul_namelen);i++) {
|
|
if ((appleufs->ul_name[i] == '\0') ||
|
|
(appleufs->ul_name[i] == ':') ||
|
|
(appleufs->ul_name[i] == '/')) {
|
|
pwarn("APPLE UFS LABEL NAME CONTAINS ILLEGAL CHARACTER");
|
|
if (preen) {
|
|
printf(" (TRUNCATED)\n");
|
|
}
|
|
if (preen || reply("TRUNCATE")) {
|
|
appleufs->ul_namelen = i+1;
|
|
appleufsdirty();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check the checksum last, because if anything else was wrong,
|
|
* then the checksum gets reset anyway.
|
|
*/
|
|
appleufs->ul_checksum = 0;
|
|
appleufs->ul_checksum = ffs_appleufs_cksum(appleufs);
|
|
if (appleufsblk.b_un.b_appleufs->ul_checksum != appleufs->ul_checksum) {
|
|
pwarn("INVALID APPLE UFS CHECKSUM (%#04x should be %#04x)",
|
|
appleufsblk.b_un.b_appleufs->ul_checksum, appleufs->ul_checksum);
|
|
if (preen) {
|
|
printf(" (CORRECTED)\n");
|
|
}
|
|
if (preen || reply("CORRECT")) {
|
|
appleufsdirty();
|
|
} else {
|
|
/* put the incorrect checksum back in place */
|
|
appleufs->ul_checksum = appleufsblk.b_un.b_appleufs->ul_checksum;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Detect byte order. Return 0 if valid magic found, -1 otherwise.
|
|
*/
|
|
static int
|
|
detect_byteorder(struct fs *fs, int sblockoff)
|
|
{
|
|
if (sblockoff == SBLOCK_UFS2 && (fs->fs_magic == FS_UFS1_MAGIC ||
|
|
fs->fs_magic == bswap32(FS_UFS1_MAGIC)))
|
|
/* Likely to be the first alternate of a fs with 64k blocks */
|
|
return -1;
|
|
if (fs->fs_magic == FS_UFS1_MAGIC || fs->fs_magic == FS_UFS2_MAGIC) {
|
|
if (endian == 0 || BYTE_ORDER == endian) {
|
|
needswap = 0;
|
|
doswap = do_blkswap = do_dirswap = 0;
|
|
} else {
|
|
needswap = 1;
|
|
doswap = do_blkswap = do_dirswap = 1;
|
|
}
|
|
return 0;
|
|
} else if (fs->fs_magic == bswap32(FS_UFS1_MAGIC) ||
|
|
fs->fs_magic == bswap32(FS_UFS2_MAGIC)) {
|
|
if (endian == 0 || BYTE_ORDER != endian) {
|
|
needswap = 1;
|
|
doswap = do_blkswap = do_dirswap = 0;
|
|
} else {
|
|
needswap = 0;
|
|
doswap = do_blkswap = do_dirswap = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Possible superblock locations ordered from most to least likely.
|
|
*/
|
|
static off_t sblock_try[] = SBLOCKSEARCH;
|
|
|
|
/*
|
|
* Read in the super block and its summary info.
|
|
*/
|
|
static int
|
|
readsb(int listerr)
|
|
{
|
|
daddr_t super = 0;
|
|
struct fs *fs;
|
|
int i;
|
|
|
|
if (bflag) {
|
|
super = bflag;
|
|
if (bread(fsreadfd, (char *)sblk.b_un.b_fs, super,
|
|
(long)SBLOCKSIZE) != 0)
|
|
return (0);
|
|
fs = sblk.b_un.b_fs;
|
|
if (detect_byteorder(fs, -1) < 0) {
|
|
badsb(listerr, "MAGIC NUMBER WRONG");
|
|
return (0);
|
|
}
|
|
} else {
|
|
for (i = 0; sblock_try[i] != -1; i++) {
|
|
super = sblock_try[i] / dev_bsize;
|
|
if (bread(fsreadfd, (char *)sblk.b_un.b_fs,
|
|
super, (long)SBLOCKSIZE) != 0)
|
|
continue;
|
|
fs = sblk.b_un.b_fs;
|
|
if (detect_byteorder(fs, sblock_try[i]) == 0)
|
|
break;
|
|
}
|
|
if (sblock_try[i] == -1) {
|
|
badsb(listerr, "CAN'T FIND SUPERBLOCK");
|
|
return (0);
|
|
}
|
|
}
|
|
if (doswap) {
|
|
if (preen)
|
|
errx(EEXIT, "incompatible options -B and -p");
|
|
if (nflag)
|
|
errx(EEXIT, "incompatible options -B and -n");
|
|
if (endian == LITTLE_ENDIAN) {
|
|
if (!reply("CONVERT TO LITTLE ENDIAN"))
|
|
return 0;
|
|
} else if (endian == BIG_ENDIAN) {
|
|
if (!reply("CONVERT TO BIG ENDIAN"))
|
|
return 0;
|
|
} else
|
|
pfatal("INTERNAL ERROR: unknown endian");
|
|
}
|
|
if (needswap)
|
|
pwarn("** Swapped byte order\n");
|
|
/* swap SB byte order if asked */
|
|
if (doswap)
|
|
ffs_sb_swap(sblk.b_un.b_fs, sblk.b_un.b_fs);
|
|
|
|
memmove(sblock, sblk.b_un.b_fs, SBLOCKSIZE);
|
|
if (needswap)
|
|
ffs_sb_swap(sblk.b_un.b_fs, sblock);
|
|
|
|
is_ufs2 = sblock->fs_magic == FS_UFS2_MAGIC;
|
|
|
|
/*
|
|
* run a few consistency checks of the super block
|
|
*/
|
|
if (sblock->fs_sbsize > SBLOCKSIZE)
|
|
{ badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); }
|
|
/*
|
|
* Compute block size that the filesystem is based on,
|
|
* according to fsbtodb, and adjust superblock block number
|
|
* so we can tell if this is an alternate later.
|
|
*/
|
|
super *= dev_bsize;
|
|
dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1);
|
|
sblk.b_bno = super / dev_bsize;
|
|
sblk.b_size = SBLOCKSIZE;
|
|
if (bflag)
|
|
goto out;
|
|
/*
|
|
* Set all possible fields that could differ, then do check
|
|
* of whole super block against an alternate super block->
|
|
* When an alternate super-block is specified this check is skipped.
|
|
*/
|
|
getblk(&asblk, cgsblock(sblock, sblock->fs_ncg - 1), sblock->fs_sbsize);
|
|
if (asblk.b_errs)
|
|
return (0);
|
|
/* swap SB byte order if asked */
|
|
if (doswap)
|
|
ffs_sb_swap(asblk.b_un.b_fs, asblk.b_un.b_fs);
|
|
|
|
memmove(altsblock, asblk.b_un.b_fs, sblock->fs_sbsize);
|
|
if (needswap)
|
|
ffs_sb_swap(asblk.b_un.b_fs, altsblock);
|
|
if (cmpsblks(sblock, altsblock)) {
|
|
if (debug) {
|
|
uint32_t *nlp, *olp, *endlp;
|
|
|
|
printf("superblock mismatches\n");
|
|
nlp = (uint32_t *)altsblock;
|
|
olp = (uint32_t *)sblock;
|
|
endlp = olp + (sblock->fs_sbsize / sizeof *olp);
|
|
for ( ; olp < endlp; olp++, nlp++) {
|
|
if (*olp == *nlp)
|
|
continue;
|
|
printf("offset %#x, original 0x%08x, alternate "
|
|
"0x%08x\n",
|
|
(int)((uint8_t *)olp-(uint8_t *)sblock),
|
|
*olp, *nlp);
|
|
}
|
|
}
|
|
badsb(listerr,
|
|
"VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
|
|
/*
|
|
return (0);
|
|
*/
|
|
}
|
|
out:
|
|
|
|
sb_oldfscompat_read(sblock, &sblocksave);
|
|
|
|
/* Now we know the SB is valid, we can write it back if needed */
|
|
if (doswap) {
|
|
sbdirty();
|
|
dirty(&asblk);
|
|
}
|
|
havesb = 1;
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
cmpsblks(const struct fs *sb, struct fs *asb)
|
|
{
|
|
if (!is_ufs2 && ((sb->fs_old_flags & FS_FLAGS_UPDATED) == 0)) {
|
|
if (sb->fs_old_postblformat < FS_DYNAMICPOSTBLFMT)
|
|
return cmpsblks42(sb, asb);
|
|
else
|
|
return cmpsblks44(sb, asb);
|
|
}
|
|
if (asb->fs_sblkno != sb->fs_sblkno ||
|
|
asb->fs_cblkno != sb->fs_cblkno ||
|
|
asb->fs_iblkno != sb->fs_iblkno ||
|
|
asb->fs_dblkno != sb->fs_dblkno ||
|
|
asb->fs_ncg != sb->fs_ncg ||
|
|
asb->fs_bsize != sb->fs_bsize ||
|
|
asb->fs_fsize != sb->fs_fsize ||
|
|
asb->fs_frag != sb->fs_frag ||
|
|
asb->fs_bmask != sb->fs_bmask ||
|
|
asb->fs_fmask != sb->fs_fmask ||
|
|
asb->fs_bshift != sb->fs_bshift ||
|
|
asb->fs_fshift != sb->fs_fshift ||
|
|
asb->fs_fragshift != sb->fs_fragshift ||
|
|
asb->fs_fsbtodb != sb->fs_fsbtodb ||
|
|
asb->fs_sbsize != sb->fs_sbsize ||
|
|
asb->fs_nindir != sb->fs_nindir ||
|
|
asb->fs_inopb != sb->fs_inopb ||
|
|
asb->fs_cssize != sb->fs_cssize ||
|
|
asb->fs_ipg != sb->fs_ipg ||
|
|
asb->fs_fpg != sb->fs_fpg ||
|
|
asb->fs_magic != sb->fs_magic)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* BSD 4.2 performed the following superblock comparison
|
|
* It should correspond to FS_42POSTBLFMT
|
|
* (although note that in 4.2, the fs_old_postblformat
|
|
* field didn't exist and the corresponding bits are
|
|
* located near the end of the postbl itself, where they
|
|
* are not likely to be used.)
|
|
*/
|
|
int
|
|
cmpsblks42(const struct fs *sb, struct fs *asb)
|
|
{
|
|
asb->fs_firstfield = sb->fs_firstfield; /* fs_link */
|
|
asb->fs_unused_1 = sb->fs_unused_1; /* fs_rlink */
|
|
asb->fs_old_time = sb->fs_old_time; /* fs_time */
|
|
asb->fs_old_cstotal = sb->fs_old_cstotal; /* fs_cstotal */
|
|
asb->fs_cgrotor = sb->fs_cgrotor;
|
|
asb->fs_fmod = sb->fs_fmod;
|
|
asb->fs_clean = sb->fs_clean;
|
|
asb->fs_ronly = sb->fs_ronly;
|
|
asb->fs_old_flags = sb->fs_old_flags;
|
|
asb->fs_maxcontig = sb->fs_maxcontig;
|
|
asb->fs_minfree = sb->fs_minfree;
|
|
asb->fs_old_rotdelay = sb->fs_old_rotdelay;
|
|
asb->fs_maxbpg = sb->fs_maxbpg;
|
|
|
|
/* The former fs_csp, totaling 128 bytes */
|
|
memmove(asb->fs_ocsp, sb->fs_ocsp, sizeof sb->fs_ocsp);
|
|
asb->fs_contigdirs = sb->fs_contigdirs;
|
|
asb->fs_csp = sb->fs_csp;
|
|
asb->fs_maxcluster = sb->fs_maxcluster;
|
|
asb->fs_active = sb->fs_active;
|
|
|
|
/* The former fs_fsmnt, totaling 512 bytes */
|
|
memmove(asb->fs_fsmnt, sb->fs_fsmnt, sizeof sb->fs_fsmnt);
|
|
memmove(asb->fs_volname, sb->fs_volname, sizeof sb->fs_volname);
|
|
|
|
return memcmp(sb, asb, sb->fs_sbsize);
|
|
}
|
|
|
|
/* BSD 4.4 performed the following superblock comparison
|
|
* This was used in NetBSD through 1.6.1
|
|
*
|
|
* Note that this implementation is destructive to asb.
|
|
*/
|
|
int
|
|
cmpsblks44(const struct fs *sb, struct fs *asb)
|
|
{
|
|
/*
|
|
* "Copy fields which we don't care if they're different in the
|
|
* alternate superblocks, as they're either likely to be
|
|
* different because they're per-cylinder-group specific, or
|
|
* because they're transient details which are only maintained
|
|
* in the primary superblock."
|
|
*/
|
|
asb->fs_firstfield = sb->fs_firstfield;
|
|
asb->fs_unused_1 = sb->fs_unused_1;
|
|
asb->fs_old_time = sb->fs_old_time;
|
|
asb->fs_old_cstotal = sb->fs_old_cstotal;
|
|
asb->fs_cgrotor = sb->fs_cgrotor;
|
|
asb->fs_fmod = sb->fs_fmod;
|
|
asb->fs_clean = sb->fs_clean;
|
|
asb->fs_ronly = sb->fs_ronly;
|
|
asb->fs_old_flags = sb->fs_old_flags;
|
|
asb->fs_maxcontig = sb->fs_maxcontig;
|
|
asb->fs_minfree = sb->fs_minfree;
|
|
asb->fs_optim = sb->fs_optim;
|
|
asb->fs_old_rotdelay = sb->fs_old_rotdelay;
|
|
asb->fs_maxbpg = sb->fs_maxbpg;
|
|
|
|
/* The former fs_csp and fs_maxcluster, totaling 128 bytes */
|
|
memmove(asb->fs_ocsp, sb->fs_ocsp, sizeof sb->fs_ocsp);
|
|
asb->fs_contigdirs = sb->fs_contigdirs;
|
|
asb->fs_csp = sb->fs_csp;
|
|
asb->fs_maxcluster = sb->fs_maxcluster;
|
|
asb->fs_active = sb->fs_active;
|
|
|
|
/* The former fs_fsmnt, totaling 512 bytes */
|
|
memmove(asb->fs_fsmnt, sb->fs_fsmnt, sizeof sb->fs_fsmnt);
|
|
memmove(asb->fs_volname, sb->fs_volname, sizeof sb->fs_volname);
|
|
|
|
/* The former fs_sparecon, totaling 200 bytes */
|
|
memmove(asb->fs_snapinum,
|
|
sb->fs_snapinum, sizeof sb->fs_snapinum);
|
|
asb->fs_avgfilesize = sb->fs_avgfilesize;
|
|
asb->fs_avgfpdir = sb->fs_avgfpdir;
|
|
asb->fs_save_cgsize = sb->fs_save_cgsize;
|
|
memmove(asb->fs_sparecon32,
|
|
sb->fs_sparecon32, sizeof sb->fs_sparecon32);
|
|
asb->fs_flags = sb->fs_flags;
|
|
|
|
/* Original comment:
|
|
* "The following should not have to be copied, but need to be."
|
|
*/
|
|
asb->fs_fsbtodb = sb->fs_fsbtodb;
|
|
asb->fs_old_interleave = sb->fs_old_interleave;
|
|
asb->fs_old_npsect = sb->fs_old_npsect;
|
|
asb->fs_old_nrpos = sb->fs_old_nrpos;
|
|
asb->fs_state = sb->fs_state;
|
|
asb->fs_qbmask = sb->fs_qbmask;
|
|
asb->fs_qfmask = sb->fs_qfmask;
|
|
asb->fs_state = sb->fs_state;
|
|
asb->fs_maxfilesize = sb->fs_maxfilesize;
|
|
|
|
/*
|
|
* "Compare the superblocks, effectively checking every other
|
|
* field to see if they differ."
|
|
*/
|
|
return memcmp(sb, asb, sb->fs_sbsize);
|
|
}
|
|
|
|
|
|
static void
|
|
badsb(int listerr, const char *s)
|
|
{
|
|
|
|
if (!listerr)
|
|
return;
|
|
if (preen)
|
|
printf("%s: ", cdevname());
|
|
pfatal("BAD SUPER BLOCK: %s\n", s);
|
|
}
|
|
|
|
/*
|
|
* Calculate a prototype superblock based on information in the disk label.
|
|
* 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(const char *dev, int devfd, struct fs *fs)
|
|
{
|
|
struct dkwedge_info dkw;
|
|
struct disk_geom geo;
|
|
int i, nspf;
|
|
|
|
if (getdiskinfo(dev, fsreadfd, NULL, &geo, &dkw) == -1)
|
|
pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
|
|
if (dkw.dkw_parent[0] == '\0') {
|
|
pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
|
|
return (0);
|
|
}
|
|
if (strcmp(dkw.dkw_ptype, DKW_PTYPE_FFS) &&
|
|
strcmp(dkw.dkw_ptype, DKW_PTYPE_APPLEUFS)) {
|
|
pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
|
|
dev, dkw.dkw_ptype);
|
|
return (0);
|
|
}
|
|
memcpy(fs, &sblk.b_un.b_fs, sizeof(struct fs));
|
|
nspf = fs->fs_fsize / geo.dg_secsize;
|
|
fs->fs_old_nspf = nspf;
|
|
for (fs->fs_fsbtodb = 0, i = nspf; i > 1; i >>= 1)
|
|
fs->fs_fsbtodb++;
|
|
dev_bsize = geo.dg_secsize;
|
|
if (fs->fs_magic == FS_UFS2_MAGIC) {
|
|
fs->fs_ncg = howmany(fs->fs_size, fs->fs_fpg);
|
|
} else /* if (fs->fs_magic == FS_UFS1_MAGIC) */ {
|
|
fs->fs_old_cgmask = 0xffffffff;
|
|
for (i = geo.dg_ntracks; i > 1; i >>= 1)
|
|
fs->fs_old_cgmask <<= 1;
|
|
if (!POWEROF2(geo.dg_ntracks))
|
|
fs->fs_old_cgmask <<= 1;
|
|
fs->fs_old_cgoffset = roundup(
|
|
howmany(geo.dg_nsectors, nspf), fs->fs_frag);
|
|
fs->fs_fpg = (fs->fs_old_cpg * geo.dg_secpercyl) / nspf;
|
|
fs->fs_ncg = howmany(fs->fs_size / geo.dg_secpercyl,
|
|
fs->fs_old_cpg);
|
|
}
|
|
return (1);
|
|
}
|