#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.58 2019/04/03 22:10:50 christos Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.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
int fat_types[] = {
MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S,
MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32,
MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L,
-1
};
static int getFreeLabelEntry(struct disklabel *);
static int whichType(struct part_map_entry *);
static void setpartition(struct part_map_entry *, struct partition *, int);
static int getNamedType(struct part_map_entry *, int, struct disklabel *, int,
int, int *);
static char *read_mac_label(char *, struct disklabel *, int *);
static char *read_mbr_label(char *, struct disklabel *, int *);
static const char *read_bsd_label(char *, struct disklabel *, int *);
static int
getFreeLabelEntry(struct disklabel *lp)
{
int i;
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)
{
struct blockzeroblock *bzb;
char typestr[32], *s;
int type;
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 (strcmp(PART_TYPE_DRIVER, typestr) == 0 ||
strcmp(PART_TYPE_DRIVER43, typestr) == 0 ||
strcmp(PART_TYPE_DRIVERATA, typestr) == 0 ||
strcmp(PART_TYPE_FWB_COMPONENT, typestr) == 0 ||
strcmp(PART_TYPE_PARTMAP, typestr) == 0)
type = 0;
else if (strcmp(PART_TYPE_UNIX, typestr) == 0) {
bzb = (struct blockzeroblock *)(&part->pmBootArgs);
if (bzb->bzbMagic != BZB_MAGIC)
type = 0;
else if (bzb->bzbFlags & BZB_ROOTFS)
type = ROOT_PART;
else if (bzb->bzbFlags & BZB_USRFS)
type = UFS_PART;
else if (bzb->bzbType == BZB_TYPESWAP)
type = SWAP_PART;
else
type = SCRATCH_PART;
} else if (strcmp(PART_TYPE_MAC, typestr) == 0)
type = HFS_PART;
else
type = SCRATCH_PART;
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)
{
struct blockzeroblock *bzb;
int i;
for (i = 0; i < num_parts; i++) {
if (whichType(part + i) != type)
continue;
if (type == ROOT_PART) {
bzb = (struct blockzeroblock *)
(&(part + i)->pmBootArgs);
if (alt >= 0 && alt != bzb->bzbCluster)
continue;
setpartition(part + i, &lp->d_partitions[0], FS_BSDFFS);
} else if (type == UFS_PART) {
bzb = (struct blockzeroblock *)
(&(part + i)->pmBootArgs);
if (alt >= 0 && alt != bzb->bzbCluster)
continue;
setpartition(part + i, &lp->d_partitions[6], FS_BSDFFS);
if (*maxslot < 6)
*maxslot = 6;
} else if (type == SWAP_PART) {
setpartition(part + i, &lp->d_partitions[1], FS_SWAP);
if (*maxslot < 1)
*maxslot = 1;
} else
printf("disksubr.c: can't do type %d\n", type);
return 0;
}
return -1;
}
static char *
read_mac_label(char *dlbuf, struct disklabel *lp, int *match)
{
u_int16_t *sbSigp;
struct part_map_entry *part;
struct partition *pp;
char *msg;
int i, slot, maxslot;
maxslot = 0;
*match = 0;
msg = NULL;
sbSigp = (u_int16_t *)dlbuf;
if (*sbSigp != DRIVER_MAP_MAGIC)
return msg;
*match = (-1);
part = (struct part_map_entry *)(dlbuf + DEV_BSIZE);
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);
for (i = 0; i < NUM_PARTS; i++) {
slot = getFreeLabelEntry(lp);
if (slot < 0)
break;
pp = &lp->d_partitions[slot];
switch (whichType(part + i)) {
case ROOT_PART:
case UFS_PART:
setpartition(part + i, pp, FS_BSDFFS);
break;
case SWAP_PART:
setpartition(part + i, pp, FS_SWAP);
break;
case HFS_PART:
setpartition(part + i, pp, FS_HFS);
break;
case SCRATCH_PART:
setpartition(part + i, pp, FS_OTHER);
break;
default:
slot = 0;
break;
}
if (slot > maxslot)
maxslot = slot;
}
lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
return msg;
}
static char *
read_mbr_label(char *dlbuf, struct disklabel *lp, int *match)
{
struct mbr_partition *dp;
struct partition *pp;
char *msg;
size_t mbr_lbl_off;
int i, *ip, slot, maxslot;
maxslot = 0;
*match = 0;
msg = NULL;
if (MBR_MAGIC != bswap16(*(u_int16_t *)(dlbuf + MBR_MAGIC_OFFSET)))
return msg;
*match = (-1);
mbr_lbl_off = MBR_BBSECTOR * lp->d_secsize + MBR_PART_OFFSET;
dp = (struct mbr_partition *)(dlbuf + mbr_lbl_off);
for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
if (dp->mbrp_type == 0)
continue;
slot = getFreeLabelEntry(lp);
maxslot = (slot > maxslot) ? maxslot : slot;
pp = &lp->d_partitions[slot];
pp->p_fstype = FS_OTHER;
pp->p_offset = bswap32(dp->mbrp_start);
pp->p_size = bswap32(dp->mbrp_size);
for (ip = fat_types; *ip != -1; ip++) {
if (dp->mbrp_type == *ip) {
pp->p_fstype = FS_MSDOS;
break;
}
}
}
lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
return msg;
}
static const char *
read_bsd_label(char *dlbuf, struct disklabel *lp, int *match)
{
struct disklabel *dlp;
const char *msg;
struct disklabel *blk_start, *blk_end;
*match = 0;
msg = NULL;
blk_start = (struct disklabel *)dlbuf;
blk_end = (struct disklabel *)(dlbuf + (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);
} else
msg = "Disk label corrupted";
break;
}
}
return msg;
}
const char *
readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
struct cpu_disklabel *osdep)
{
struct buf *bp;
const char *msg;
int size;
if (lp->d_secperunit == 0)
lp->d_secperunit = 0x1fffffff;
if (lp->d_secpercyl == 0)
return msg = "Zero secpercyl";
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);
if (biowait(bp)) {
msg = "I/O error reading block zero";
} else {
int match;
msg = read_mac_label(bp->b_data, lp, &match);
if (!match && msg == NULL)
msg = read_mbr_label(bp->b_data, lp, &match);
if (!match && msg == NULL)
msg = read_bsd_label(bp->b_data, lp, &match);
if (!match && msg == NULL)
msg = "no disk label";
}
brelse(bp, 0);
return (msg);
}
int
setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask,
struct cpu_disklabel *osdep)
{
return 0;
}
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
struct cpu_disklabel *osdep)
{
#if 0
struct buf *bp;
struct disklabel *dlp;
int labelpart;
int error = 0;
labelpart = DISKPART(dev);
if (lp->d_partitions[labelpart].p_offset != 0) {
if (lp->d_partitions[0].p_offset != 0)
return (EXDEV);
labelpart = 0;
}
bp = geteblk((int)lp->d_secsize);
bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), labelpart);
bp->b_blkno = LABELSECTOR;
bp->b_bcount = lp->d_secsize;
bp->b_flags |= B_READ;
(*strat)(bp);
if (error = biowait(bp))
goto done;
for (dlp = (struct disklabel *)bp->b_data;
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 &&
dkcksum(dlp) == 0) {
*dlp = *lp;
bp->b_oflags &= ~(BO_DONE);
bp->b_flags &= ~(B_READ);
bp->b_flags |= B_WRITE;
(*strat)(bp);
error = biowait(bp);
goto done;
}
}
error = ESRCH;
done:
brelse(bp, 0);
return (error);
#else
int i;
lp->d_npartitions = 0;
for (i = 0; i < MAXPARTITIONS; i++) {
lp->d_partitions[i].p_fstype = FS_UNUSED;
lp->d_partitions[i].p_offset = 0;
if (i != RAW_PART)
lp->d_partitions[i].p_size = 0;
}
return (readdisklabel(dev, strat, lp, osdep) ? EINVAL : 0);
#endif
}