Пример #1
0
/*
 * Given a UniOS/ISI disk label, set lp to a BSD disk label.
 *
 * The BSD label is cleared out before this is called.
 */
int
disklabel_om_to_bsd(struct sun_disklabel *sl, struct disklabel *lp)
{
	struct partition *npp;
	struct sun_dkpart *spp;
	int i, secpercyl;
	u_short cksum = 0, *sp1, *sp2;

	/* Verify the XOR check. */
	sp1 = (u_short *)sl;
	sp2 = (u_short *)(sl + 1);
	while (sp1 < sp2)
		cksum ^= *sp1++;
	if (cksum != 0)
		return (EINVAL);	/* UniOS disk label, bad checksum */

	/* Format conversion. */
	lp->d_magic = DISKMAGIC;
	lp->d_magic2 = DISKMAGIC;
	lp->d_flags = D_VENDOR;
	memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));

	lp->d_secsize = DEV_BSIZE;
	lp->d_nsectors = sl->sl_nsectors;
	lp->d_ntracks = sl->sl_ntracks;
	lp->d_ncylinders = sl->sl_ncylinders;

	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
	lp->d_secpercyl = secpercyl;
	/* If unset or initialized as full disk, permit refinement */
	if (DL_GETDSIZE(lp) == 0 || DL_GETDSIZE(lp) == MAXDISKSIZE)
		DL_SETDSIZE(lp, (u_int64_t)secpercyl * sl->sl_ncylinders);
	lp->d_version = 1;

	memcpy(&lp->d_uid, &sl->sl_uid, sizeof(sl->sl_uid));

	lp->d_acylinders = sl->sl_acylinders;

	lp->d_npartitions = MAXPARTITIONS;
	/* These are as defined in <ufs/ffs/fs.h> */
	lp->d_bbsize = 8192;	/* XXX */
	lp->d_sbsize = 8192;	/* XXX */

	for (i = 0; i < 8; i++) {
		spp = &sl->sl_part[i];
		npp = &lp->d_partitions[i];
		/* UniOS label uses blkoffset, not cyloffset */
		DL_SETPOFFSET(npp, spp->sdkp_cyloffset);
		DL_SETPSIZE(npp, spp->sdkp_nsectors);
		if (DL_GETPSIZE(npp) == 0) {
			npp->p_fstype = FS_UNUSED;
		} else {
			npp->p_fstype = sun_fstypes[i];
			if (npp->p_fstype == FS_BSDFFS) {
				/*
				 * The sun label does not store the FFS fields,
				 * so just set them with default values here.
				 */
				npp->p_fragblock =
				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
				npp->p_cpg = 16;
			}
		}
	}

	/*
	 * XXX BandAid XXX
	 * UniOS rootfs sits on part c which don't begin at sect 0,
	 * and impossible to mount.  Thus, make it usable as part b.
	 * XXX how to setup a swap partition on disks shared with UniOS???
	 */
	if (sl->sl_rpm == 0 && DL_GETPOFFSET(&lp->d_partitions[2]) != 0) {
		lp->d_partitions[1] = lp->d_partitions[2];
		lp->d_partitions[1].p_fstype = FS_BSDFFS;
	}

	/* Clear "extended" partition info, tentatively */
	for (i = 0; i < SUNXPART; i++) {
		npp = &lp->d_partitions[i+8];
		DL_SETPOFFSET(npp, 0);
		DL_SETPSIZE(npp, 0);
		npp->p_fstype = FS_UNUSED;
	}

	/* Check to see if there's an "extended" partition table
	 * SL_XPMAG partitions had checksums up to just before the
	 * (new) sl_types variable, while SL_XPMAGTYP partitions have
	 * checksums up to the just before the (new) sl_xxx1 variable.
	 */
	if ((sl->sl_xpmag == SL_XPMAG &&
	    sun_extended_sum(sl, &sl->sl_types) == sl->sl_xpsum) ||
	    (sl->sl_xpmag == SL_XPMAGTYP &&
	    sun_extended_sum(sl, &sl->sl_xxx1) == sl->sl_xpsum)) {
		/*
		 * There is.  Copy over the "extended" partitions.
		 * This code parallels the loop for partitions a-h.
		 */
		for (i = 0; i < SUNXPART; i++) {
			spp = &sl->sl_xpart[i];
			npp = &lp->d_partitions[i+8];
			DL_SETPOFFSET(npp, spp->sdkp_cyloffset);
			DL_SETPSIZE(npp, spp->sdkp_nsectors);
			if (DL_GETPSIZE(npp) == 0) {
				npp->p_fstype = FS_UNUSED;
				continue;
			}
			npp->p_fstype = sun_fstypes[i+8];
			if (npp->p_fstype == FS_BSDFFS) {
				npp->p_fragblock =
				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
				npp->p_cpg = 16;
			}
		}
		if (sl->sl_xpmag == SL_XPMAGTYP) {
			for (i = 0; i < MAXPARTITIONS; i++) {
				npp = &lp->d_partitions[i];
				npp->p_fstype = sl->sl_types[i];
				npp->p_fragblock = sl->sl_fragblock[i];
				npp->p_cpg = sl->sl_cpg[i];
			}
		}
	}

	lp->d_checksum = 0;
	lp->d_checksum = dkcksum(lp);
	return (checkdisklabel(lp, lp, 0, DL_GETDSIZE(lp)));
}
Пример #2
0
/*
 * Given a SunOS disk label, set lp to a BSD disk label.
 * The BSD label is cleared out before this is called.
 */
static int
disklabel_sun_to_bsd(struct sun_disklabel *sl, struct disklabel *lp)
{
	struct sun_preamble *preamble = (struct sun_preamble *)sl;
	struct sun_partinfo *ppp;
	struct sun_dkpart *spp;
	struct partition *npp;
	u_short cksum = 0, *sp1, *sp2;
	int i, secpercyl;

	/* Verify the XOR check. */
	sp1 = (u_short *)sl;
	sp2 = (u_short *)(sl + 1);
	while (sp1 < sp2)
		cksum ^= *sp1++;
	if (cksum != 0)
		return (EINVAL);	/* SunOS disk label, bad checksum */

	/* Format conversion. */
	lp->d_magic = DISKMAGIC;
	lp->d_magic2 = DISKMAGIC;
	lp->d_flags = D_VENDOR;
	memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));

	lp->d_secsize = DEV_BSIZE;
	lp->d_nsectors = sl->sl_nsectors;
	lp->d_ntracks = sl->sl_ntracks;
	lp->d_ncylinders = sl->sl_ncylinders;

	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
	lp->d_secpercyl = secpercyl;
	if (DL_GETDSIZE(lp) == 0)
		DL_SETDSIZE(lp, (daddr64_t)secpercyl * sl->sl_ncylinders);
	lp->d_version = 1;

	memcpy(&lp->d_uid, &sl->sl_uid, sizeof(lp->d_uid));

	lp->d_acylinders = sl->sl_acylinders;

	lp->d_npartitions = MAXPARTITIONS;
	/* These are as defined in <ufs/ffs/fs.h> */
	lp->d_bbsize = 8192;	/* XXX */
	lp->d_sbsize = 8192;	/* XXX */

	for (i = 0; i < 8; i++) {
		spp = &sl->sl_part[i];
		npp = &lp->d_partitions[i];
		DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
		DL_SETPSIZE(npp, spp->sdkp_nsectors);
		if (DL_GETPSIZE(npp) == 0) {
			npp->p_fstype = FS_UNUSED;
		} else {
			npp->p_fstype = sun_fstypes[i];
			if (npp->p_fstype == FS_BSDFFS) {
				/*
				 * The sun label does not store the FFS fields,
				 * so just set them with default values here.
				 */
				npp->p_fragblock =
				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
				npp->p_cpg = 16;
			}
		}
	}

	/* Clear "extended" partition info, tentatively */
	for (i = 0; i < SUNXPART; i++) {
		npp = &lp->d_partitions[i+8];
		DL_SETPOFFSET(npp, 0);
		DL_SETPSIZE(npp, 0);
		npp->p_fstype = FS_UNUSED;
	}

	/* Check to see if there's an "extended" partition table
	 * SL_XPMAG partitions had checksums up to just before the
	 * (new) sl_types variable, while SL_XPMAGTYP partitions have
	 * checksums up to the just before the (new) sl_xxx1 variable.
	 * Also, disklabels created prior to the addition of sl_uid will
	 * have a checksum to just before the sl_uid variable.
	 */
	if ((sl->sl_xpmag == SL_XPMAG &&
	    sun_extended_sum(sl, &sl->sl_types) == sl->sl_xpsum) ||
	    (sl->sl_xpmag == SL_XPMAGTYP &&
	    sun_extended_sum(sl, &sl->sl_uid) == sl->sl_xpsum) ||
	    (sl->sl_xpmag == SL_XPMAGTYP &&
	    sun_extended_sum(sl, &sl->sl_xxx1) == sl->sl_xpsum)) {
		/*
		 * There is.  Copy over the "extended" partitions.
		 * This code parallels the loop for partitions a-h.
		 */
		for (i = 0; i < SUNXPART; i++) {
			spp = &sl->sl_xpart[i];
			npp = &lp->d_partitions[i+8];
			DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
			DL_SETPSIZE(npp, spp->sdkp_nsectors);
			if (DL_GETPSIZE(npp) == 0) {
				npp->p_fstype = FS_UNUSED;
				continue;
			}
			npp->p_fstype = sun_fstypes[i+8];
			if (npp->p_fstype == FS_BSDFFS) {
				npp->p_fragblock =
				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
				npp->p_cpg = 16;
			}
		}
		if (sl->sl_xpmag == SL_XPMAGTYP) {
			for (i = 0; i < MAXPARTITIONS; i++) {
				npp = &lp->d_partitions[i];
				npp->p_fstype = sl->sl_types[i];
				npp->p_fragblock = sl->sl_fragblock[i];
				npp->p_cpg = sl->sl_cpg[i];
			}
		}
	} else if (preamble->sl_nparts <= 8) {
		/*
		 * A more traditional Sun label.  Recognise certain filesystem
		 * types from it, if they are available.
		 */
		i = preamble->sl_nparts;
		if (i == 0)
			i = 8;

		npp = &lp->d_partitions[i-1];
		ppp = &preamble->sl_part[i-1];
		for (; i > 0; i--, npp--, ppp--) {
			if (npp->p_size == 0)
				continue;
			if ((ppp->spi_tag == 0) && (ppp->spi_flag == 0))
				continue;

			switch (ppp->spi_tag) {
			case SPTAG_SUNOS_ROOT:
			case SPTAG_SUNOS_USR:
			case SPTAG_SUNOS_VAR:
			case SPTAG_SUNOS_HOME:
				npp->p_fstype = FS_BSDFFS;
				npp->p_fragblock =
				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
				npp->p_cpg = 16;
				break;
			case SPTAG_LINUX_EXT2:
				npp->p_fstype = FS_EXT2FS;
				break;
			default:
				/* FS_SWAP for _SUNOS_SWAP and _LINUX_SWAP? */
				npp->p_fstype = FS_UNUSED;
				break;
			}
		}
	}

	lp->d_checksum = 0;
	lp->d_checksum = dkcksum(lp);
	DNPRINTF(BOOT_D_OFDEV, "disklabel_sun_to_bsd: success!\n");
	return (0);
}
Пример #3
0
/*
 * Load the label information on the named device
 */
int
sdgetdisklabel(dev_t dev, struct sd_softc *sc, struct disklabel *lp,
    int spoofonly)
{
	size_t len;
	char packname[sizeof(lp->d_packname) + 1];
	char product[17], vendor[9];

	bzero(lp, sizeof(struct disklabel));

	lp->d_secsize = sc->params.secsize;
	lp->d_ntracks = sc->params.heads;
	lp->d_nsectors = sc->params.sectors;
	lp->d_ncylinders = sc->params.cyls;
	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
	if (lp->d_secpercyl == 0) {
		lp->d_secpercyl = 100;
		/* as long as it's not 0 - readdisklabel divides by it */
	}

	lp->d_type = DTYPE_SCSI;
	if ((sc->sc_link->inqdata.device & SID_TYPE) == T_OPTICAL)
		strncpy(lp->d_typename, "SCSI optical",
		    sizeof(lp->d_typename));
	else
		strncpy(lp->d_typename, "SCSI disk",
		    sizeof(lp->d_typename));

	/*
	 * Try to fit '<vendor> <product>' into d_packname. If that doesn't fit
	 * then leave out '<vendor> ' and use only as much of '<product>' as
	 * does fit.
	 */
	viscpy(vendor, sc->sc_link->inqdata.vendor, 8);
	viscpy(product, sc->sc_link->inqdata.product, 16);
	len = snprintf(packname, sizeof(packname), "%s %s", vendor, product);
	if (len > sizeof(lp->d_packname)) {
		strlcpy(packname, product, sizeof(packname));
		len = strlen(packname);
	}
	/*
	 * It is safe to use len as the count of characters to copy because
	 * packname is sizeof(lp->d_packname)+1, the string in packname is
	 * always null terminated and len does not count the terminating null.
	 * d_packname is not a null terminated string.
	 */
	bcopy(packname, lp->d_packname, len);

	DL_SETDSIZE(lp, sc->params.disksize);
	lp->d_version = 1;
	lp->d_flags = 0;

	/* XXX - these values for BBSIZE and SBSIZE assume ffs */
	lp->d_bbsize = BBSIZE;
	lp->d_sbsize = SBSIZE;

	lp->d_magic = DISKMAGIC;
	lp->d_magic2 = DISKMAGIC;
	lp->d_checksum = dkcksum(lp);

	/*
	 * Call the generic disklabel extraction routine
	 */
	return readdisklabel(DISKLABELDEV(dev), sdstrategy, lp, spoofonly);
}
Пример #4
0
void
cdprobe(void)
{
	struct diskinfo *dip;
	int cddev = bios_cddev & 0xff;

	/* Another BIOS boot device... */

	if (bios_cddev == -1)			/* Not been set, so don't use */
		return;

	dip = alloc(sizeof(struct diskinfo));
	bzero(dip, sizeof(*dip));

#if 0
	if (bios_getdiskinfo(cddev, &dip->bios_info)) {
		printf(" <!cd0>");	/* XXX */
		free(dip, 0);
		return;
	}
#endif

	printf(" cd0");

	dip->bios_info.bios_number = cddev;
	dip->bios_info.bios_edd = 1;		/* Use the LBA calls */
	dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
	dip->bios_info.checksum = 0;		 /* just in case */
	dip->bios_info.bsd_dev =
	    MAKEBOOTDEV(6, 0, 0, 0, RAW_PART);

	/* Create an imaginary disk label */
	dip->disklabel.d_secsize = 2048;
	dip->disklabel.d_ntracks = 1;
	dip->disklabel.d_nsectors = 100;
	dip->disklabel.d_ncylinders = 1;
	dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
	    dip->disklabel.d_nsectors;
	if (dip->disklabel.d_secpercyl == 0) {
		dip->disklabel.d_secpercyl = 100;
		/* as long as it's not 0, since readdisklabel divides by it */
	}

	strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
	    sizeof(dip->disklabel.d_typename));
	dip->disklabel.d_type = DTYPE_ATAPI;

	strncpy(dip->disklabel.d_packname, "fictitious",
	    sizeof(dip->disklabel.d_packname));
	DL_SETDSIZE(&dip->disklabel, 100);

	dip->disklabel.d_bbsize = 2048;
	dip->disklabel.d_sbsize = 2048;

	/* 'a' partition covering the "whole" disk */
	DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0);
	DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100);
	dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;

	/* The raw partition is special */
	DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0);
	DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100);
	dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;

	dip->disklabel.d_npartitions = MAXPARTITIONS;

	dip->disklabel.d_magic = DISKMAGIC;
	dip->disklabel.d_magic2 = DISKMAGIC;
	dip->disklabel.d_checksum = dkcksum(&dip->disklabel);

	/* Add to queue of disks */
	TAILQ_INSERT_TAIL(&disklist, dip, list);
}
Пример #5
0
int
edit(struct disklabel *lp, int f)
{
	int first, ch, fd, error = 0;
	struct disklabel label;
	FILE *fp;
	u_int64_t total_sectors, starting_sector, ending_sector;

	if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
		warn("%s", tmpfil);
		if (fd != -1)
			close(fd);
		return (1);
	}
	display(fp, lp, 0, 1);
	fprintf(fp, "\n# Notes:\n");
	fprintf(fp,
"# Up to 16 partitions are valid, named from 'a' to 'p'.  Partition 'a' is\n"
"# your root filesystem, 'b' is your swap, and 'c' should cover your whole\n"
"# disk. Any other partition is free for any use.  'size' and 'offset' are\n"
"# in 512-byte blocks. fstype should be '4.2BSD', 'swap', or 'none' or some\n"
"# other values.  fsize/bsize/cpg should typically be '2048 16384 16' for a\n"
"# 4.2BSD filesystem (or '512 4096 16' except on alpha, sun4, ...)\n");
	fclose(fp);
	for (;;) {
		if (editit(tmpfil) == -1)
			break;
		fp = fopen(tmpfil, "r");
		if (fp == NULL) {
			warn("%s", tmpfil);
			break;
		}
		/* Get values set by OS and not the label. */
		if (ioctl(f, DIOCGPDINFO, &label) < 0)
			err(4, "ioctl DIOCGPDINFO");
		ending_sector = DL_GETBEND(&label);
		starting_sector = DL_GETBSTART(&label);
		total_sectors = DL_GETDSIZE(&label);
		error = getasciilabel(fp, &label);
		DL_SETBEND(&label, ending_sector);
		DL_SETBSTART(&label, starting_sector);
		DL_SETDSIZE(&label, total_sectors);

		if (error == 0) {
			if (cmplabel(lp, &label) == 0) {
				puts("No changes.");
				fclose(fp);
				(void) unlink(tmpfil);
				return (0);
			}
			*lp = label;
			if (writelabel(f, lp) == 0) {
				fclose(fp);
				(void) unlink(tmpfil);
				return (0);
			}
		}
		fclose(fp);
		printf("re-edit the label? [y]: ");
		fflush(stdout);
		first = ch = getchar();
		while (ch != '\n' && ch != EOF)
			ch = getchar();
		if (first == 'n' || first == 'N')
			break;
	}
	(void)unlink(tmpfil);
	return (1);
}
Пример #6
0
/*
 * Check disklabel for errors and fill in
 * derived fields according to supplied values.
 */
int
checklabel(struct disklabel *lp)
{
	struct partition *pp;
	int i, errors = 0;
	char part;

	if (lp->d_secsize == 0) {
		warnx("sector size %d", lp->d_secsize);
		return (1);
	}
	if (lp->d_nsectors == 0) {
		warnx("sectors/track %d", lp->d_nsectors);
		return (1);
	}
	if (lp->d_ntracks == 0) {
		warnx("tracks/cylinder %d", lp->d_ntracks);
		return (1);
	}
	if  (lp->d_ncylinders == 0) {
		warnx("cylinders/unit %d", lp->d_ncylinders);
		errors++;
	}
	if (lp->d_secpercyl == 0)
		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
	if (DL_GETDSIZE(lp) == 0)
		DL_SETDSIZE(lp, (u_int64_t)lp->d_secpercyl * lp->d_ncylinders);
	if (lp->d_bbsize == 0) {
		warnx("boot block size %d", lp->d_bbsize);
		errors++;
	} else if (lp->d_bbsize % lp->d_secsize)
		warnx("warning, boot block size %% sector-size != 0");
	if (lp->d_sbsize == 0) {
		warnx("super block size %d", lp->d_sbsize);
		errors++;
	} else if (lp->d_sbsize % lp->d_secsize)
		warnx("warning, super block size %% sector-size != 0");
	if (lp->d_npartitions > MAXPARTITIONS)
		warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
		    lp->d_npartitions, MAXPARTITIONS);
	for (i = 0; i < lp->d_npartitions; i++) {
		part = 'a' + i;
		pp = &lp->d_partitions[i];
		if (DL_GETPSIZE(pp) == 0 && DL_GETPOFFSET(pp) != 0)
			warnx("warning, partition %c: size 0, but offset %llu",
			    part, DL_GETPOFFSET(pp));
#ifdef SUN_CYLCHECK
		if (lp->d_flags & D_VENDOR) {
			if (i != RAW_PART && DL_GETPSIZE(pp) % lp->d_secpercyl)
				warnx("warning, partition %c: size %% "
				    "cylinder-size != 0", part);
			if (i != RAW_PART && DL_GETPOFFSET(pp) % lp->d_secpercyl)
				warnx("warning, partition %c: offset %% "
				    "cylinder-size != 0", part);
		}
#endif
#ifdef SUN_AAT0
		if ((lp->d_flags & D_VENDOR) &&
		    i == 0 && DL_GETPSIZE(pp) != 0 && DL_GETPOFFSET(pp) != 0) {
			warnx("this architecture requires partition 'a' to "
			    "start at sector 0");
			errors++;
		}
#endif
		if (DL_GETPOFFSET(pp) > DL_GETDSIZE(lp)) {
			warnx("partition %c: offset past end of unit", part);
			errors++;
		}
		if (DL_GETPOFFSET(pp) + DL_GETPSIZE(pp) > DL_GETDSIZE(lp)) {
			warnx("partition %c: partition extends past end of unit",
			    part);
			errors++;
		}
#if 0
		if (pp->p_frag == 0 && pp->p_fsize != 0) {
			warnx("partition %c: block size < fragment size", part);
			errors++;
		}
#endif
	}
	for (; i < MAXPARTITIONS; i++) {
		part = 'a' + i;
		pp = &lp->d_partitions[i];
		if (DL_GETPSIZE(pp) || DL_GETPOFFSET(pp))
			warnx("warning, unused partition %c: size %llu "
			    "offset %llu", part, DL_GETPSIZE(pp),
			    DL_GETPOFFSET(pp));
	}
	return (errors > 0);
}
Пример #7
0
int
main(int argc, char *argv[])
{
	int ch;
	struct partition *pp;
	struct disklabel *lp;
	struct disklabel mfsfakelabel;
	struct partition oldpartition;
	struct stat st;
	struct statfs *mp;
	struct rlimit rl;
	int fsi = -1, oflagset = 0, fso, len, n, maxpartitions;
	char *cp = NULL, *s1, *s2, *special, *opstring, *realdev;
#ifdef MFS
	char mountfromname[BUFSIZ];
	char *pop = NULL, node[PATH_MAX];
	pid_t pid, res;
	struct statfs sf;
	struct stat mountpoint;
	int status;
#endif
	uid_t mfsuid = 0;
	gid_t mfsgid = 0;
	mode_t mfsmode = 0;
	char *fstype = NULL;
	char **saveargv = argv;
	int ffsflag = 1;
	const char *errstr;
	long long fssize_input = 0;
	int fssize_usebytes = 0;
	u_int64_t nsecs;

	if (strstr(__progname, "mfs"))
		mfs = Nflag = quiet = 1;

	getphysmem();
	maxpartitions = getmaxpartitions();
	if (maxpartitions > 26)
		fatal("insane maxpartitions value %d", maxpartitions);

	opstring = mfs ?
	    "P:T:b:c:e:f:i:m:o:s:" :
	    "NO:S:T:b:c:e:f:g:h:i:m:o:qs:t:";
	while ((ch = getopt(argc, argv, opstring)) != -1) {
		switch (ch) {
		case 'N':
			Nflag = 1;
			break;
		case 'O':
			Oflag = strtonum(optarg, 0, 2, &errstr);
			if (errstr)
				fatal("%s: invalid ffs version", optarg);
			oflagset = 1;
			break;
		case 'S':
			if (scan_scaled(optarg, &sectorsize) == -1 ||
			    sectorsize <= 0 || (sectorsize % DEV_BSIZE))
				fatal("sector size invalid: %s", optarg);
			break;
		case 'T':
			disktype = optarg;
			break;
		case 'b':
			bsize = strtonum(optarg, MINBSIZE, MAXBSIZE, &errstr);
			if (errstr)
				fatal("block size is %s: %s", errstr, optarg);
			break;
		case 'c':
			maxfrgspercg = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr)
				fatal("fragments per cylinder group is %s: %s",
				    errstr, optarg);
			break;
		case 'e':
			maxbpg = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr)
				fatal("blocks per file in a cylinder group is"
				    " %s: %s", errstr, optarg);
			break;
		case 'f':
			fsize = strtonum(optarg, MINBSIZE / MAXFRAG, MAXBSIZE,
			    &errstr);
			if (errstr)
				fatal("fragment size is %s: %s",
				    errstr, optarg);
			break;
		case 'g':
			avgfilesize = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr)
				fatal("average file size is %s: %s",
				    errstr, optarg);
			break;
		case 'h':
			avgfilesperdir = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr)
				fatal("average files per dir is %s: %s",
				    errstr, optarg);
			break;
		case 'i':
			density = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr)
				fatal("bytes per inode is %s: %s",
				    errstr, optarg);
			break;
		case 'm':
			minfree = strtonum(optarg, 0, 99, &errstr);
			if (errstr)
				fatal("free space %% is %s: %s",
				    errstr, optarg);
			break;
		case 'o':
			if (mfs)
				getmntopts(optarg, mopts, &mntflags);
			else {
				if (strcmp(optarg, "space") == 0)
					reqopt = opt = FS_OPTSPACE;
				else if (strcmp(optarg, "time") == 0)
					reqopt = opt = FS_OPTTIME;
				else
					fatal("%s: unknown optimization "
					    "preference: use `space' or `time'.",
					    optarg);
			}
			break;
		case 'q':
			quiet = 1;
			break;
		case 's':
			if (scan_scaled(optarg, &fssize_input) == -1 ||
			    fssize_input <= 0)
				fatal("file system size invalid: %s", optarg);
			fssize_usebytes = 0;    /* in case of multiple -s */
			for (s1 = optarg; *s1 != '\0'; s1++)
				if (isalpha((unsigned char)*s1)) {
					fssize_usebytes = 1;
					break;
				}
			break;
		case 't':
			fstype = optarg;
			if (strcmp(fstype, "ffs"))
				ffsflag = 0;
			break;
#ifdef MFS
		case 'P':
			pop = optarg;
			break;
#endif
		case '?':
		default:
			usage();
		}
		if (!ffsflag)
			break;
	}
	argc -= optind;
	argv += optind;

	if (ffsflag && argc - mfs != 1)
		usage();

	if (mfs) {
		/* Increase our data size to the max */
		if (getrlimit(RLIMIT_DATA, &rl) == 0) {
			rl.rlim_cur = rl.rlim_max;
			(void)setrlimit(RLIMIT_DATA, &rl);
		}
	}

	special = argv[0];

	if (!mfs) {
		char execname[PATH_MAX], name[PATH_MAX];

		if (fstype == NULL)
			fstype = readlabelfs(special, 0);
		if (fstype != NULL && strcmp(fstype, "ffs")) {
			snprintf(name, sizeof name, "newfs_%s", fstype);
			saveargv[0] = name;
			snprintf(execname, sizeof execname, "%s/newfs_%s",
			    _PATH_SBIN, fstype);
			(void)execv(execname, saveargv);
			snprintf(execname, sizeof execname, "%s/newfs_%s",
			    _PATH_USRSBIN, fstype);
			(void)execv(execname, saveargv);
			err(1, "%s not found", name);
		}
	}

	if (mfs && !strcmp(special, "swap")) {
		/*
		 * it's an MFS, mounted on "swap."  fake up a label.
		 * XXX XXX XXX
		 */
		fso = -1;	/* XXX; normally done below. */

		memset(&mfsfakelabel, 0, sizeof(mfsfakelabel));
		mfsfakelabel.d_secsize = 512;
		mfsfakelabel.d_nsectors = 64;
		mfsfakelabel.d_ntracks = 16;
		mfsfakelabel.d_ncylinders = 16;
		mfsfakelabel.d_secpercyl = 1024;
		DL_SETDSIZE(&mfsfakelabel, 16384);
		mfsfakelabel.d_npartitions = 1;
		mfsfakelabel.d_version = 1;
		DL_SETPSIZE(&mfsfakelabel.d_partitions[0], 16384);
		mfsfakelabel.d_partitions[0].p_fragblock =
		    DISKLABELV1_FFS_FRAGBLOCK(1024, 8);
		mfsfakelabel.d_partitions[0].p_cpg = 16;

		lp = &mfsfakelabel;
		pp = &mfsfakelabel.d_partitions[0];

		goto havelabel;
	}
	if (Nflag) {
		fso = -1;
	} else {
		fso = opendev(special, O_WRONLY, 0, &realdev);
		if (fso < 0)
			fatal("%s: %s", special, strerror(errno));
		special = realdev;

		/* Bail if target special is mounted */
		n = getmntinfo(&mp, MNT_NOWAIT);
		if (n == 0)
			fatal("%s: getmntinfo: %s", special, strerror(errno));

		len = sizeof(_PATH_DEV) - 1;
		s1 = special;
		if (strncmp(_PATH_DEV, s1, len) == 0)
			s1 += len;

		while (--n >= 0) {
			s2 = mp->f_mntfromname;
			if (strncmp(_PATH_DEV, s2, len) == 0) {
				s2 += len - 1;
				*s2 = 'r';
			}
			if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0)
				fatal("%s is mounted on %s",
				    special, mp->f_mntonname);
			++mp;
		}
	}
	if (mfs && disktype != NULL) {
		lp = (struct disklabel *)getdiskbyname(disktype);
		if (lp == NULL)
			fatal("%s: unknown disk type", disktype);
		pp = &lp->d_partitions[1];
	} else {
		fsi = opendev(special, O_RDONLY, 0, NULL);
		if (fsi < 0)
			fatal("%s: %s", special, strerror(errno));
		if (fstat(fsi, &st) < 0)
			fatal("%s: %s", special, strerror(errno));
		if (!mfs) {
			if (S_ISBLK(st.st_mode))
				fatal("%s: block device", special);
			if (!S_ISCHR(st.st_mode))
				warnx("%s: not a character-special device",
				    special);
		}
		if (*argv[0] == '\0')
			fatal("empty partition name supplied");
		cp = argv[0] + strlen(argv[0]) - 1;
		if ((*cp < 'a' || *cp > ('a' + maxpartitions - 1))
		    && !isdigit((unsigned char)*cp))
			fatal("%s: can't figure out file system partition",
			    argv[0]);
		lp = getdisklabel(special, fsi);
		if (!mfs) {
			if (pledge("stdio disklabel tty", NULL) == -1)
				err(1, "pledge");
		}
		if (isdigit((unsigned char)*cp))
			pp = &lp->d_partitions[0];
		else
			pp = &lp->d_partitions[*cp - 'a'];
		if (DL_GETPSIZE(pp) == 0)
			fatal("%s: `%c' partition is unavailable",
			    argv[0], *cp);
		if (pp->p_fstype == FS_BOOT)
			fatal("%s: `%c' partition overlaps boot program",
			      argv[0], *cp);
	}
havelabel:
	if (sectorsize == 0) {
		sectorsize = lp->d_secsize;
		if (sectorsize <= 0)
			fatal("%s: no default sector size", argv[0]);
	}

	if (fssize_usebytes) {
		nsecs = fssize_input / sectorsize;
		if (fssize_input % sectorsize != 0)
			nsecs++;
	} else if (fssize_input == 0)
		nsecs = DL_GETPSIZE(pp);
	else
		nsecs = fssize_input;

	if (nsecs > DL_GETPSIZE(pp) && !mfs)
	       fatal("%s: maximum file system size on the `%c' partition is "
		   "%llu sectors", argv[0], *cp, DL_GETPSIZE(pp));

	/* Can't use DL_SECTOBLK() because sectorsize may not be from label! */
	fssize = nsecs * (sectorsize / DEV_BSIZE);
	if (oflagset == 0 && fssize >= INT_MAX)
		Oflag = 2;	/* FFS2 */
	if (fsize == 0) {
		fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
		if (fsize <= 0)
			fsize = MAXIMUM(DFL_FRAGSIZE, lp->d_secsize);
	}
	if (bsize == 0) {
		bsize = DISKLABELV1_FFS_BSIZE(pp->p_fragblock);
		if (bsize <= 0)
			bsize = MINIMUM(DFL_BLKSIZE, 8 * fsize);
	}
	if (density == 0)
		density = NFPI * fsize;
	if (minfree < MINFREE && opt != FS_OPTSPACE && reqopt == -1) {
		warnx("warning: changing optimization to space "
		    "because minfree is less than %d%%\n", MINFREE);
		opt = FS_OPTSPACE;
	}
	if (maxbpg == 0) {
		if (Oflag <= 1)
			maxbpg = MAXBLKPG_FFS1(bsize);
		else
			maxbpg = MAXBLKPG_FFS2(bsize);
	}
	oldpartition = *pp;
#ifdef MFS
	if (mfs) {
		if (realpath(argv[1], node) == NULL)
			err(1, "realpath %s", argv[1]);
		if (stat(node, &mountpoint) < 0)
			err(ECANCELED, "stat %s", node);
		mfsuid = mountpoint.st_uid;
		mfsgid = mountpoint.st_gid;
		mfsmode = mountpoint.st_mode & ALLPERMS;
	}
#endif

	mkfs(pp, special, fsi, fso, mfsmode, mfsuid, mfsgid);
	if (!Nflag && memcmp(pp, &oldpartition, sizeof(oldpartition)))
		rewritelabel(special, fso, lp);
	if (!Nflag)
		close(fso);
	close(fsi);
#ifdef MFS
	if (mfs) {
		struct mfs_args args;
		memset(&args, 0, sizeof(args));
		args.base = membase;
		args.size = fssize * DEV_BSIZE;
		args.export_info.ex_root = -2;
		if (mntflags & MNT_RDONLY)
			args.export_info.ex_flags = MNT_EXRDONLY;

		switch (pid = fork()) {
		case -1:
			err(10, "mfs");
		case 0:
			snprintf(mountfromname, sizeof(mountfromname),
			    "mfs:%d", getpid());
			break;
		default:
			snprintf(mountfromname, sizeof(mountfromname),
			    "mfs:%d", pid);
			for (;;) {
				/*
				 * spin until the mount succeeds
				 * or the child exits
				 */
				usleep(1);

				/*
				 * XXX Here is a race condition: another process
				 * can mount a filesystem which hides our
				 * ramdisk before we see the success.
				 */
				if (statfs(node, &sf) < 0)
					err(ECANCELED, "statfs %s", node);
				if (!strcmp(sf.f_mntfromname, mountfromname) &&
				    !strncmp(sf.f_mntonname, node,
					     MNAMELEN) &&
				    !strcmp(sf.f_fstypename, "mfs")) {
					if (pop != NULL)
						copy(pop, node, &args);
					exit(0);
				}
				res = waitpid(pid, &status, WNOHANG);
				if (res == -1)
					err(EDEADLK, "waitpid");
				if (res != pid)
					continue;
				if (WIFEXITED(status)) {
					if (WEXITSTATUS(status) == 0)
						exit(0);
					errx(1, "%s: mount: %s", node,
					     strerror(WEXITSTATUS(status)));
				} else
					errx(EDEADLK, "abnormal termination");
			}
			/* NOTREACHED */
		}

		(void) setsid();
		(void) close(0);
		(void) close(1);
		(void) close(2);
		(void) chdir("/");

		args.fspec = mountfromname;
		if (mntflags & MNT_RDONLY && pop != NULL)
			mntflags &= ~MNT_RDONLY;
		if (mount(MOUNT_MFS, node, mntflags, &args) < 0)
			exit(errno); /* parent prints message */
	}
#endif
	exit(0);
}