#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.72 2022/07/05 19:31:04 andvar Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#define SD_C_ADJUSTS_NR
#define RDBLOCK_BID 1
#define PARTBLOCK_BID 2
#define BADBLOCK_BID 3
#define FSBLOCK_BID 4
#define LSEGBLOCK_BID 5
struct rdbmap {
long firstblk;
long lastblk;
int bigtype;
struct {
short next;
short prev;
char shortid;
} big[0];
struct {
char next;
char prev;
char shortid;
} tab[0];
};
#define baddr(bp) (void *)((bp)->b_data)
u_long rdbchksum(void *);
struct adostype getadostype(u_long);
struct rdbmap *getrdbmap(dev_t, void (*)(struct buf *), struct disklabel *,
struct cpu_disklabel *);
const char *
readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
{
struct adostype adt;
struct partition *pp = NULL;
struct partblock *pbp;
struct rdblock *rbp;
struct buf *bp;
const char *msg;
char *bcpls, *s, bcpli;
int cindex, i, nopname;
u_long nextb;
struct disklabel *dlp;
clp->rdblock = RDBNULL;
if (lp->d_secperunit == 0)
lp->d_secperunit = 0x1fffffff;
if (lp->d_secpercyl == 0)
lp->d_secpercyl = 0x1fffffff;
lp->d_npartitions = RAW_PART + 1;
if (lp->d_partitions[RAW_PART].p_size == 0)
lp->d_partitions[RAW_PART].p_size = 0x1fffffff;
lp->d_partitions[RAW_PART].p_offset = 0;
if (lp->d_partitions[0].p_size == 0) {
lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size;
lp->d_partitions[0].p_offset = 0;
lp->d_partitions[0].p_fstype = FS_BSDFFS;
lp->d_partitions[0].p_fsize = 1024;
lp->d_partitions[0].p_frag = 8;
lp->d_partitions[0].p_cpg = 0;
}
bp = geteblk((int)lp->d_secsize);
#ifdef _KERNEL
bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
#else
bp->b_dev = dev;
#endif
msg = NULL;
for (nextb = 0; nextb < RDB_MAXBLOCKS; nextb++) {
bp->b_blkno = nextb;
bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
bp->b_bcount = lp->d_secsize;
bp->b_oflags &= ~(BO_DONE);
bp->b_flags |= B_READ;
#ifdef SD_C_ADJUSTS_NR
bp->b_blkno *= (lp->d_secsize / DEV_BSIZE);
#endif
strat(bp);
if (biowait(bp)) {
msg = "rdb scan I/O error";
goto done;
}
rbp = baddr(bp);
if (rbp->id == RDBLOCK_ID) {
if (rdbchksum(rbp) == 0)
break;
else
msg = "rdb bad checksum";
}
dlp = (struct disklabel *)((char*)bp->b_data + LABELOFFSET);
if (dlp->d_magic == DISKMAGIC) {
if (dkcksum(dlp))
msg = "NetBSD disk label corrupted";
else {
*lp = *dlp;
brelse(bp, 0);
return(msg);
}
}
}
if (nextb == RDB_MAXBLOCKS) {
if (msg == NULL)
msg = "no rdb found";
goto done;
} else if (msg) {
msg = NULL;
}
clp->rdblock = nextb;
for (i = 0; i < MAXPARTITIONS; i++) {
clp->pbindex[i] = -1;
clp->pblist[i] = RDBNULL;
if (i == RAW_PART)
continue;
lp->d_partitions[i].p_size = 0;
lp->d_partitions[i].p_offset = 0;
}
if (lp->d_secsize != rbp->nbytes) {
lp->d_secsize = rbp->nbytes;
allocbuf(bp, (int)lp->d_secsize, 1);
rbp = baddr(bp);
}
lp->d_nsectors = rbp->nsectors;
lp->d_ntracks = rbp->nheads;
if (rbp->highcyl == 0)
lp->d_ncylinders = rbp->ncylinders;
else
lp->d_ncylinders = rbp->highcyl + 1;
lp->d_secpercyl = uimin(rbp->secpercyl, lp->d_nsectors * lp->d_ntracks);
if (lp->d_secpercyl == 0)
lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
#ifdef DIAGNOSTIC
if (lp->d_ncylinders != rbp->ncylinders)
printf("warning found rdb->ncylinders(%" PRIu32 ") != "
"rdb->highcyl(%" PRIu32 ") + 1\n", rbp->ncylinders,
rbp->highcyl);
if (lp->d_nsectors * lp->d_ntracks != rbp->secpercyl)
printf("warning found rdb->secpercyl(%" PRIu32 ") != "
"rdb->nsectors(%" PRIu32 ") * rdb->nheads(%" PRIu32 ")\n",
rbp->secpercyl, rbp->nsectors, rbp->nheads);
#endif
lp->d_sparespercyl =
uimax(rbp->secpercyl, lp->d_nsectors * lp->d_ntracks)
- lp->d_secpercyl;
if (lp->d_sparespercyl == 0)
lp->d_sparespertrack = 0;
else {
lp->d_sparespertrack = lp->d_sparespercyl / lp->d_ntracks;
#ifdef DIAGNOSTIC
if (lp->d_sparespercyl % lp->d_ntracks)
printf("warning lp->d_sparespercyl(%d) not multiple "
"of lp->d_ntracks(%d)\n", lp->d_sparespercyl,
lp->d_ntracks);
#endif
}
lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
lp->d_acylinders = rbp->ncylinders - (rbp->highcyl - rbp->lowcyl + 1);
lp->d_rpm = 3600;
lp->d_interleave = rbp->interleave;
lp->d_headswitch = lp->d_flags = lp->d_trackskew = lp->d_cylskew = 0;
lp->d_trkseek = 0;
lp->d_partitions[RAW_PART].p_size = rbp->ncylinders * lp->d_secpercyl;
nopname = 1;
cindex = 0;
for (nextb = rbp->partbhead; nextb != RDBNULL; nextb = pbp->next) {
bp->b_blkno = nextb;
bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
bp->b_bcount = lp->d_secsize;
bp->b_oflags &= ~(BO_DONE);
bp->b_flags |= B_READ;
#ifdef SD_C_ADJUSTS_NR
bp->b_blkno *= (lp->d_secsize / DEV_BSIZE);
#endif
strat(bp);
if (biowait(bp)) {
msg = "partition scan I/O error";
goto done;
}
pbp = baddr(bp);
if (pbp->id != PARTBLOCK_ID) {
msg = "partition block with bad id";
goto done;
}
if (rdbchksum(pbp)) {
msg = "partition block bad checksum";
goto done;
}
if (pbp->e.tabsize < 11) {
msg = "bad partition info (environ < 11)";
goto done;
}
if (pbp->e.tabsize >= 16)
adt = getadostype(pbp->e.dostype);
else {
adt.archtype = ADT_UNKNOWN;
adt.fstype = FS_UNUSED;
}
switch (adt.archtype) {
case ADT_NETBSDROOT:
pp = &lp->d_partitions[0];
if (pp->p_size) {
printf("WARN: more than one root, ignoring\n");
clp->rdblock = RDBNULL;
continue;
}
break;
case ADT_NETBSDSWAP:
pp = &lp->d_partitions[1];
if (pp->p_size) {
printf("WARN: more than one swap, ignoring\n");
clp->rdblock = RDBNULL;
continue;
}
break;
case ADT_NETBSDUSER:
case ADT_AMIGADOS:
case ADT_AMIX:
case ADT_EXT2:
case ADT_RAID:
case ADT_UNKNOWN:
pp = &lp->d_partitions[lp->d_npartitions];
break;
}
if (lp->d_npartitions <= (pp - lp->d_partitions))
lp->d_npartitions = (pp - lp->d_partitions) + 1;
#ifdef DIAGNOSTIC
if (lp->d_secpercyl * lp->d_secsize !=
(pbp->e.secpertrk * pbp->e.numheads * pbp->e.sizeblock<<2)) {
if (pbp->partname[0] + 1 < sizeof(pbp->partname))
pbp->partname[pbp->partname[0] + 1] = 0;
else
pbp->partname[sizeof(pbp->partname) - 1] = 0;
printf("Partition '%s' geometry %" PRIu32 "/%" PRIu32
" differs",
pbp->partname + 1, pbp->e.numheads,
pbp->e.secpertrk);
printf(" from RDB %d/%d=%d\n", lp->d_ntracks,
lp->d_nsectors, lp->d_secpercyl);
}
#endif
while ((pp - lp->d_partitions) > RAW_PART + 1) {
daddr_t boff;
boff = pbp->e.lowcyl * pbp->e.secpertrk
* pbp->e.numheads
* ((pbp->e.sizeblock << 2) / lp->d_secsize);
if (boff > (pp - 1)->p_offset)
break;
*pp = *(pp - 1);
pp--;
}
i = (pp - lp->d_partitions);
if (nopname || i == 1) {
s = lp->d_packname;
bcpls = &pbp->partname[1];
bcpli = pbp->partname[0];
if (sizeof(lp->d_packname) <= bcpli)
bcpli = sizeof(lp->d_packname) - 1;
while (bcpli--)
*s++ = *bcpls++;
*s = 0;
nopname = 0;
}
pp->p_size = (pbp->e.highcyl - pbp->e.lowcyl + 1)
* pbp->e.secpertrk * pbp->e.numheads
* ((pbp->e.sizeblock << 2) / lp->d_secsize);
pp->p_offset = pbp->e.lowcyl * pbp->e.secpertrk
* pbp->e.numheads
* ((pbp->e.sizeblock << 2) / lp->d_secsize);
pp->p_fstype = adt.fstype;
if (adt.archtype == ADT_AMIGADOS) {
int bsize, secperblk, minbsize, prefac;
minbsize = uimax(512, lp->d_secsize);
bsize = pbp->e.sizeblock << 2;
secperblk = pbp->e.secperblk;
prefac = pbp->e.prefac;
while (bsize > minbsize) {
bsize >>= 1;
secperblk <<= 1;
prefac <<= 1;
}
if (bsize == minbsize) {
pp->p_fsize = bsize;
pp->p_frag = secperblk;
pp->p_cpg = pbp->e.resvblocks;
pp->p_size -= prefac;
} else {
adt.archtype = ADT_UNKNOWN;
adt.fstype = FS_UNUSED;
}
} else if (pbp->e.tabsize >= 22 && ISFSARCH_NETBSD(adt)) {
pp->p_fsize = pbp->e.fsize;
pp->p_frag = pbp->e.frag;
pp->p_cpg = pbp->e.cpg;
} else if (adt.fstype == FS_ISO9660) {
pp->p_fsize = 0;
pp->p_frag = 0;
pp->p_cpg = 0;
} else {
pp->p_fsize = 1024;
pp->p_frag = 8;
pp->p_cpg = 0;
}
clp->pblist[clp->pbindex[i] = cindex++] = nextb;
}
lp->d_magic = lp->d_magic2 = DISKMAGIC;
lp->d_checksum = 0;
lp->d_checksum = dkcksum(lp);
if (clp->rdblock != RDBNULL)
clp->valid = 1;
done:
if (clp->valid == 0)
clp->rdblock = RDBNULL;
brelse(bp, 0);
return(msg);
}
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
{
struct buf *bp;
struct disklabel *dlp;
int error = 0;
if (clp->rdblock != RDBNULL)
return(EINVAL);
bp = geteblk((int)lp->d_secsize);
bp->b_dev = dev;
bp->b_blkno = LABELSECTOR;
bp->b_cylinder = 0;
bp->b_bcount = lp->d_secsize;
bp->b_flags |= B_READ;
(*strat)(bp);
if ((error = biowait(bp)) != 0)
goto done;
dlp = (struct disklabel *)((char*)bp->b_data + LABELOFFSET);
*dlp = *lp;
bp->b_oflags &= ~(BO_DONE);
bp->b_flags &= ~(B_READ);
bp->b_flags |= B_WRITE;
(*strat)(bp);
error = biowait(bp);
done:
brelse(bp, 0);
return (error);
}
u_long
rdbchksum(void *bdata)
{
u_long *blp, cnt, val;
blp = bdata;
cnt = blp[1];
val = 0;
while (cnt--)
val += *blp++;
return(val);
}
struct adostype
getadostype(u_long dostype)
{
struct adostype adt;
u_long t3, b1;
t3 = dostype & 0xffffff00;
b1 = dostype & 0x000000ff;
adt.fstype = b1;
switch (t3) {
case DOST_NBR:
adt.archtype = ADT_NETBSDROOT;
return(adt);
case DOST_NBS:
adt.archtype = ADT_NETBSDSWAP;
return(adt);
case DOST_NBU:
adt.archtype = ADT_NETBSDUSER;
return(adt);
case DOST_MUFS:
adt.archtype = ADT_AMIGADOS;
adt.fstype = FS_ADOS;
return(adt);
case DOST_DOS:
adt.archtype = ADT_AMIGADOS;
if (b1 > 5)
adt.fstype = FS_UNUSED;
else
adt.fstype = FS_ADOS;
return(adt);
case DOST_AMIX:
adt.archtype = ADT_AMIX;
if (b1 == 2)
adt.fstype = FS_BSDFFS;
else
adt.fstype = FS_UNUSED;
return(adt);
case DOST_XXXBSD:
#ifdef DIAGNOSTIC
printf("found dostype: 0x%lx which is deprecated", dostype);
#endif
if (b1 == 'S') {
dostype = DOST_NBS;
dostype |= FS_SWAP;
} else {
if (b1 == 'R')
dostype = DOST_NBR;
else
dostype = DOST_NBU;
dostype |= FS_BSDFFS;
}
#ifdef DIAGNOSTIC
printf(" using: 0x%lx instead\n", dostype);
#endif
return(getadostype(dostype));
case DOST_EXT2:
adt.archtype = ADT_EXT2;
adt.fstype = FS_EX2FS;
return(adt);
case DOST_RAID:
adt.archtype = ADT_RAID;
adt.fstype = FS_RAID;
return(adt);
default:
#ifdef DIAGNOSTIC
printf("warning unknown dostype: 0x%lx marking unused\n",
dostype);
#endif
adt.archtype = ADT_UNKNOWN;
adt.fstype = FS_UNUSED;
return(adt);
}
}
struct rdbmap *
getrdbmap(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
{
struct buf *bp;
bp = (void *)geteblk(lp->d_secsize);
bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
brelse(bp, 0);
return(NULL);
}