#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.50 2022/08/13 09:34:47 martin Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/bootblock.h>
#include <sys/syslog.h>
#include <sys/bswap.h>
#define NUM_PARTS 32
#define ROOT_PART 1
#define UFS_PART 2
#define SWAP_PART 3
#define HFS_PART 4
#define SCRATCH_PART 5
static int getFreeLabelEntry(struct disklabel *);
static int whichType(struct part_map_entry *, u_int8_t *, int *);
static void setpartition(struct part_map_entry *,
struct partition *, int);
static int getNamedType(struct part_map_entry *, int,
struct disklabel *, int, int, int *);
static const char *read_mac_label(dev_t, void (*)(struct buf *),
struct disklabel *, struct cpu_disklabel *);
static const char *read_dos_label(dev_t, void (*)(struct buf *),
struct disklabel *, struct cpu_disklabel *);
static const char *read_bsd_label(dev_t, void (*)(struct buf *),
struct disklabel *, struct cpu_disklabel *);
static int get_netbsd_label(dev_t, void (*)(struct buf *),
struct disklabel *, struct cpu_disklabel *);
static int
getFreeLabelEntry(struct disklabel *lp)
{
int i = 0;
for (i = 0; i < MAXPARTITIONS; i++) {
if ((i != RAW_PART)
&& (lp->d_partitions[i].p_fstype == FS_UNUSED))
return i;
}
return -1;
}
static int
whichType(struct part_map_entry *part, u_int8_t *fstype, int *clust)
{
struct blockzeroblock *bzb;
char typestr[32], *s;
int type;
*fstype = FS_OTHER;
*clust = 0;
if (part->pmSig != PART_ENTRY_MAGIC || part->pmPartType[0] == '\0')
return 0;
strncpy(typestr, (char *)part->pmPartType, sizeof(typestr));
typestr[sizeof(typestr) - 1] = '\0';
for (s = typestr; *s; s++)
if ((*s >= 'a') && (*s <= 'z'))
*s = (*s - 'a' + 'A');
if (strncmp(PART_TYPE_DRIVER, typestr, strlen(PART_TYPE_DRIVER)) == 0 ||
strcmp(PART_TYPE_DRIVER43, typestr) == 0 ||
strcmp(PART_TYPE_DRIVERATA, typestr) == 0 ||
strcmp(PART_TYPE_DRIVERIOKIT, typestr) == 0 ||
strcmp(PART_TYPE_FWDRIVER, typestr) == 0 ||
strcmp(PART_TYPE_FWB_COMPONENT, typestr) == 0 ||
strcmp(PART_TYPE_PARTMAP, typestr) == 0 ||
strcmp(PART_TYPE_PATCHES, typestr) == 0)
type = 0;
else if (strcmp(PART_TYPE_NBSD_PPCBOOT, typestr) == 0) {
type = ROOT_PART;
bzb = (struct blockzeroblock *)(&part->pmBootArgs);
if ((bzb->bzbMagic == BZB_MAGIC) &&
(bzb->bzbType < FSMAXTYPES))
*fstype = bzb->bzbType;
else
*fstype = FS_BSDFFS;
} else if (strcmp(PART_TYPE_NETBSD, typestr) == 0 ||
strcmp(PART_TYPE_NBSD_68KBOOT, typestr) == 0) {
type = UFS_PART;
bzb = (struct blockzeroblock *)(&part->pmBootArgs);
if ((bzb->bzbMagic == BZB_MAGIC) &&
(bzb->bzbType < FSMAXTYPES))
*fstype = bzb->bzbType;
else
*fstype = FS_BSDFFS;
} else if (strcmp(PART_TYPE_UNIX, typestr) == 0) {
bzb = (struct blockzeroblock *)(&part->pmBootArgs);
*clust = bzb->bzbCluster;
if (bzb->bzbMagic != BZB_MAGIC) {
type = 0;
} else if (bzb->bzbFlags & BZB_ROOTFS) {
type = ROOT_PART;
*fstype = FS_BSDFFS;
} else if (bzb->bzbFlags & (BZB_USRFS | BZB_USRFS_NEW)) {
type = UFS_PART;
*fstype = FS_BSDFFS;
} else if (bzb->bzbType == BZB_TYPESWAP) {
type = SWAP_PART;
*fstype = FS_SWAP;
} else {
type = SCRATCH_PART;
*fstype = FS_OTHER;
}
} else if (strcmp(PART_TYPE_MAC, typestr) == 0) {
type = HFS_PART;
*fstype = FS_HFS;
} else if (strcmp(PART_TYPE_APPLEUFS, typestr) == 0) {
type = SCRATCH_PART;
*fstype = FS_APPLEUFS;
} else if (strcmp(PART_TYPE_LINUX, typestr) == 0) {
type = SCRATCH_PART;
*fstype = FS_OTHER;
} else if (strcmp(PART_TYPE_LINUX_SWAP, typestr) == 0) {
type = SCRATCH_PART;
*fstype = FS_OTHER;
} else {
type = SCRATCH_PART;
*fstype = FS_OTHER;
}
return type;
}
static void
setpartition(struct part_map_entry *part, struct partition *pp, int fstype)
{
pp->p_size = part->pmPartBlkCnt;
pp->p_offset = part->pmPyPartStart;
pp->p_fstype = fstype;
part->pmPartType[0] = '\0';
}
static int
getNamedType(struct part_map_entry *part, int num_parts, struct disklabel *lp, int type, int alt, int *maxslot)
{
int i = 0, clust;
u_int8_t realtype;
for (i = 0; i < num_parts; i++) {
if (whichType(part + i, &realtype, &clust) != type)
continue;
if (type == ROOT_PART) {
if (alt >= 0 && alt != clust)
continue;
setpartition(part + i, &lp->d_partitions[0], realtype);
} else if (type == UFS_PART) {
if (alt >= 0 && alt != clust)
continue;
setpartition(part + i, &lp->d_partitions[6], realtype);
if (*maxslot < 6)
*maxslot = 6;
} else if (type == SWAP_PART) {
setpartition(part + i, &lp->d_partitions[1], realtype);
if (*maxslot < 1)
*maxslot = 1;
} else if (type == HFS_PART) {
setpartition(part + i, &lp->d_partitions[3], realtype);
if (*maxslot < 3)
*maxslot = 3;
} else
printf("disksubr.c: can't do type %d\n", type);
return 0;
}
return -1;
}
static const char *
read_mac_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
struct part_map_entry *part;
struct partition *pp;
struct buf *bp;
const char *msg = NULL;
int i, slot, maxslot = 0, clust;
u_int8_t realtype;
bp = geteblk((int)lp->d_secsize * NUM_PARTS);
bp->b_dev = dev;
bp->b_blkno = 1;
bp->b_bcount = lp->d_secsize * NUM_PARTS;
bp->b_flags |= B_READ;
bp->b_cylinder = 1 / lp->d_secpercyl;
(*strat)(bp);
if (biowait(bp)) {
msg = "Macintosh partition map I/O error";
goto done;
}
part = (struct part_map_entry *)bp->b_data;
lp->d_npartitions = RAW_PART + 1;
if (getNamedType(part, NUM_PARTS, lp, ROOT_PART, 0, &maxslot))
getNamedType(part, NUM_PARTS, lp, ROOT_PART, -1, &maxslot);
if (getNamedType(part, NUM_PARTS, lp, UFS_PART, 0, &maxslot))
getNamedType(part, NUM_PARTS, lp, UFS_PART, -1, &maxslot);
getNamedType(part, NUM_PARTS, lp, SWAP_PART, -1, &maxslot);
getNamedType(part, NUM_PARTS, lp, HFS_PART, -1, &maxslot);
for (i = 0; i < NUM_PARTS; i++) {
slot = getFreeLabelEntry(lp);
if (slot < 0)
break;
pp = &lp->d_partitions[slot];
if (whichType(part + i, &realtype, &clust)) {
setpartition(part + i, pp, realtype);
} else {
slot = 0;
}
if (slot > maxslot)
maxslot = slot;
}
lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
done:
brelse(bp, 0);
return msg;
}
static const char *
read_bsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
struct cpu_disklabel *osdep)
{
struct disklabel *dlp;
struct buf *bp;
const char *msg;
struct disklabel *blk_start, *blk_end;
int size, match;
msg = NULL;
size = roundup((NUM_PARTS + 1) << DEV_BSHIFT, lp->d_secsize);
bp = geteblk(size);
bp->b_dev = dev;
bp->b_blkno = 0;
bp->b_resid = 0;
bp->b_bcount = size;
bp->b_flags |= B_READ;
bp->b_cylinder = 1 / lp->d_secpercyl;
(*strat)(bp);
match = 0;
if (biowait(bp)) {
msg = "I/O error reading BSD disklabel";
} else {
blk_start = (struct disklabel *)bp->b_data;
blk_end = (struct disklabel *)((char *)bp->b_data +
(NUM_PARTS << DEV_BSHIFT) - sizeof(struct disklabel));
for (dlp = blk_start; dlp <= blk_end;
dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
if (dlp->d_magic == DISKMAGIC &&
dlp->d_magic2 == DISKMAGIC) {
if (dlp->d_npartitions <= MAXPARTITIONS &&
dkcksum(dlp) == 0) {
*lp = *dlp;
match = -1;
break;
#ifdef DIAGNOSTIC
} else {
printf("read_bsd_label() found "
"damaged disklabel starting at "
"0x0%p, ignore\n", dlp);
#endif
}
}
}
if (!match)
msg = "BSD disklabel not found";
}
brelse(bp, 0);
return msg;
}
static const char *
read_dos_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
struct mbr_partition *dp;
struct buf *bp;
const char *msg = NULL;
int i, slot, maxslot = 0;
u_int32_t bsdpartoff;
struct mbr_partition *bsdp;
bp = geteblk((int)lp->d_secsize);
bp->b_dev = dev;
bp->b_blkno = MBR_BBSECTOR;
bp->b_bcount = lp->d_secsize;
bp->b_flags |= B_READ;
bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
(*strat)(bp);
bsdpartoff = 0;
if (biowait(bp)) {
msg = "dos partition I/O error";
goto done;
}
dp = (struct mbr_partition *)((char *)bp->b_data + MBR_PART_OFFSET);
bsdp = NULL;
for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
switch (dp->mbrp_type) {
case MBR_PTYPE_PMBR:
goto done;
case MBR_PTYPE_NETBSD:
bsdp = dp;
break;
case MBR_PTYPE_OPENBSD:
case MBR_PTYPE_386BSD:
if (!bsdp)
bsdp = dp;
break;
}
}
if (!bsdp) {
dp = (struct mbr_partition *)((char *)bp->b_data +
MBR_PART_OFFSET);
for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
if (!dp->mbrp_type)
continue;
slot = getFreeLabelEntry(lp);
if (slot < 0)
break;
if (slot > maxslot)
maxslot = slot;
lp->d_partitions[slot].p_offset = bswap32(dp->mbrp_start);
lp->d_partitions[slot].p_size = bswap32(dp->mbrp_size);
switch (dp->mbrp_type) {
case MBR_PTYPE_FAT12:
case MBR_PTYPE_FAT16S:
case MBR_PTYPE_FAT16B:
case MBR_PTYPE_FAT32:
case MBR_PTYPE_FAT32L:
case MBR_PTYPE_FAT16L:
lp->d_partitions[slot].p_fstype = FS_MSDOS;
break;
default:
lp->d_partitions[slot].p_fstype = FS_OTHER;
break;
}
}
msg = "no NetBSD disk label";
} else {
bsdpartoff = bswap32(bsdp->mbrp_start);
lp->d_partitions[2].p_size = bswap32(bsdp->mbrp_size);
lp->d_partitions[2].p_offset = bswap32(bsdp->mbrp_start);
if (2 > maxslot)
maxslot = 2;
osdep->cd_labelsector = bsdpartoff + MBR_LABELSECTOR;
osdep->cd_labeloffset = MBR_LABELOFFSET;
if (get_netbsd_label(dev, strat, lp, osdep))
goto done;
msg = "no NetBSD disk label";
}
lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
done:
brelse(bp, 0);
return (msg);
}
static int
get_netbsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
struct buf *bp;
struct disklabel *dlp;
bp = geteblk((int)lp->d_secsize);
bp->b_dev = dev;
bp->b_blkno = osdep->cd_labelsector;
bp->b_bcount = lp->d_secsize;
bp->b_flags |= B_READ;
bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
(*strat)(bp);
if (biowait(bp))
goto done;
for (dlp = (struct disklabel *)((char *)bp->b_data +
osdep->cd_labeloffset);
dlp <= (struct disklabel *)((char *)bp->b_data + lp->d_secsize -
sizeof (*dlp));
dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
if (dlp->d_magic == DISKMAGIC
&& dlp->d_magic2 == DISKMAGIC
&& dlp->d_npartitions <= MAXPARTITIONS
&& dkcksum(dlp) == 0) {
*lp = *dlp;
osdep->cd_labeloffset = (char *)dlp -
(char *)bp->b_data;
brelse(bp, 0);
return 1;
}
}
done:
brelse(bp, 0);
return 0;
}
const char *
readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
struct buf *bp;
const char *msg = NULL;
if (lp->d_secperunit == 0)
lp->d_secperunit = 0x1fffffff;
if (lp->d_secpercyl == 0) {
return msg = "Zero secpercyl";
}
bp = geteblk((int)lp->d_secsize);
bp->b_dev = dev;
bp->b_blkno = 0;
bp->b_resid = 0;
bp->b_bcount = lp->d_secsize;
bp->b_flags |= B_READ;
bp->b_cylinder = 1 / lp->d_secpercyl;
(*strat)(bp);
osdep->cd_start = -1;
if (biowait(bp)) {
msg = "I/O error reading block zero";
goto done;
}
osdep->cd_labelsector = LABELSECTOR;
osdep->cd_labeloffset = LABELOFFSET;
if (get_netbsd_label(dev, strat, lp, osdep))
osdep->cd_start = 0;
else {
u_int16_t *sbSigp;
sbSigp = (u_int16_t *)bp->b_data;
if (*sbSigp == 0x4552) {
msg = read_mac_label(dev, strat, lp, osdep);
} else if (bswap16(*(u_int16_t *)((char *)bp->b_data +
MBR_MAGIC_OFFSET)) == MBR_MAGIC) {
msg = read_dos_label(dev, strat, lp, osdep);
if (!msg)
osdep->cd_start = 0;
} else {
msg = read_bsd_label(dev, strat, lp, osdep);
if (!msg)
osdep->cd_start = 0;
}
}
done:
brelse(bp, 0);
return (msg);
}
int
setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, struct cpu_disklabel *osdep)
{
if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
|| (nlp->d_secsize % DEV_BSIZE) != 0)
return EINVAL;
if (nlp->d_magic == 0xffffffff) {
*olp = *nlp;
return 0;
}
if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC
|| dkcksum(nlp) != 0)
return EINVAL;
*olp = *nlp;
return 0;
}
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
struct buf *bp;
int error;
struct disklabel label;
label = *lp;
readdisklabel(dev, strat, &label, osdep);
if (osdep->cd_start < 0)
return EINVAL;
bp = geteblk(lp->d_secsize);
bp->b_dev = dev;
bp->b_blkno = osdep->cd_start + osdep->cd_labelsector;
bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
bp->b_bcount = lp->d_secsize;
bp->b_flags |= B_READ;
(*strat)(bp);
error = biowait(bp);
if (error != 0)
goto done;
bp->b_flags &= ~B_READ;
bp->b_flags |= B_WRITE;
bp->b_oflags &= ~BO_DONE;
memcpy((char *)bp->b_data + osdep->cd_labeloffset, (void *)lp,
sizeof *lp);
(*strat)(bp);
error = biowait(bp);
done:
brelse(bp, 0);
return error;
}