freebsd-skq/lib/libdisk/open_disk.c
lulf 1d90e269af - Sanitize disk parameters retrieved from GEOM, as they are not guaranteed to
have sane values. It caused sysinstall to crash when installing on certain SD
  cards.

Discussed with:		marcel
2009-02-09 21:34:06 +00:00

312 lines
7.5 KiB
C

/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
#include <sys/sysctl.h>
#include <sys/stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/gpt.h>
#include <paths.h>
#include "libdisk.h"
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#ifdef DEBUG
#define DPRINT(x) warn x
#define DPRINTX(x) warnx x
#else
#define DPRINT(x)
#define DPRINTX(x)
#endif
struct disk *
Int_Open_Disk(const char *name, char *conftxt)
{
struct disk *d;
int i, line = 1;
char *p, *q, *r, *a, *b, *n, *t, *sn;
daddr_t o, len, off;
u_int l, s, ty, sc, hd, alt;
daddr_t lo[10];
/*
* Locate the disk (by name) in our sysctl output
*/
for (p = conftxt; p != NULL && *p; p = strchr(p, '\n'), line++) {
if (*p == '\n')
p++;
a = strsep(&p, " ");
/* Skip anything not with index 0 */
if (strcmp(a, "0"))
continue;
/* Skip anything not a disk */
a = strsep(&p, " ");
if (strcmp(a, "DISK"))
continue;
a = strsep(&p, " ");
if (strcmp(a, name))
continue;
break;
}
q = strchr(p, '\n');
if (q != NULL)
*q++ = '\0';
d = (struct disk *)calloc(sizeof *d, 1);
if(d == NULL)
return NULL;
d->name = strdup(name);
a = strsep(&p, " "); /* length in bytes */
len = strtoimax(a, &r, 0);
if (*r) {
printf("libdisk: Int_Open_Disk(%s): can't parse length in line %d (r='%s')\n",
name, line, r);
return NULL;
}
a = strsep(&p, " "); /* sectorsize */
s = strtoul(a, &r, 0);
if (*r) {
printf("libdisk: Int_Open_Disk(%s): can't parse sector size in line %d (r='%s')\n",
name, line, r);
return NULL;
}
if (s == 0)
return (NULL);
d->sector_size = s;
len /= s; /* media size in number of sectors. */
if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-")) {
DPRINT(("Failed to add 'whole' chunk"));
}
/* Try to parse any fields after the sector size in the DISK entry line */
for (;;) {
a = strsep(&p, " ");
if (a == NULL)
break;
b = strsep(&p, " ");
o = strtoimax(b, &r, 0);
if (*r) {
printf("libdisk: Int_Open_Disk(%s): can't parse parameter '%s' in line %d (r='%s')\n",
name, a, line, r);
return NULL;
}
if (!strcmp(a, "hd"))
d->bios_hd = o;
else if (!strcmp(a, "sc"))
d->bios_sect = o;
else
printf("libdisk: Int_Open_Disk(%s): unknown parameter '%s' with value '%s' in line %d, ignored\n",
name, a, b, line);
}
/* Sanitize the parameters. */
Sanitize_Bios_Geom(d);
/*
* Calculate the number of cylinders this disk must have. If we have
* an obvious insanity, we set the number of cylinders to zero.
*/
o = d->bios_hd * d->bios_sect;
d->bios_cyl = (o != 0) ? len / o : 0;
p = q; line++; /* p is now the start of the line _after_ the DISK entry */
lo[0] = 0;
for (; p != NULL && *p; p = q, line++) {
sn = NULL;
q = strchr(p, '\n');
if (q != NULL)
*q++ = '\0';
a = strsep(&p, " "); /* Index */
/*
* If we find index 0 again, this means we've encountered another disk, so it's safe to assume this disk
* has been processed.
*/
if (!strcmp(a, "0"))
break;
l = strtoimax(a, &r, 0);
if (*r) {
printf("libdisk: Int_Open_Disk(%s): can't parse depth '%s' in line %d (r='%s')\n",
name, a, line, r);
return NULL;
}
t = strsep(&p, " "); /* Type {SUN, BSD, MBR, PC98, GPT} */
n = strsep(&p, " "); /* name */
a = strsep(&p, " "); /* len */
len = strtoimax(a, &r, 0);
if (*r) {
printf("libdisk: Int_Open_Disk(%s): can't parse length '%s' in line %d (r='%s')\n",
name, a, line, r);
continue;
}
a = strsep(&p, " "); /* secsize */
s = strtoimax(a, &r, 0);
if (*r) {
printf("libdisk: Int_Open_Disk(%s): can't parse sector size '%s' in line %d (r='%s')\n",
name, a, line, r);
continue;
}
for (;;) {
a = strsep(&p, " ");
if (a == NULL)
break;
/* XXX: Slice name may include a space. */
if (!strcmp(a, "sn")) {
sn = p;
break;
}
b = strsep(&p, " ");
o = strtoimax(b, &r, 0);
/* APPLE have ty as a string */
if ((*r) && strcmp(t, "APPLE") &&
strcmp(t, "GPT") && strcmp(t, "PART")) {
printf("libdisk: Int_Open_Disk(%s): can't parse parameter '%s' in line %d (r='%s')\n",
name, a, line, r);
break;
}
if (!strcmp(a, "o"))
off = o;
else if (!strcmp(a, "i"))
i = (!strcmp(t, "PART")) ? o - 1 : o;
else if (!strcmp(a, "ty"))
ty = o;
else if (!strcmp(a, "sc"))
sc = o;
else if (!strcmp(a, "hd"))
hd = o;
else if (!strcmp(a, "alt"))
alt = o;
else if (!strcmp(a, "xs"))
t = b;
else if (!strcmp(a, "xt")) {
if (*r)
sn = b;
else
ty = o;
}
}
/* PLATFORM POLICY BEGIN ----------------------------------- */
if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2)
continue;
if (platform == p_sparc64 && !strcmp(t, "SUN") &&
d->chunks->part->part == NULL) {
d->bios_hd = hd;
d->bios_sect = sc;
o = d->chunks->size / (hd * sc);
o *= (hd * sc);
o -= alt * hd * sc;
if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) {
DPRINT(("Failed to add 'freebsd' chunk"));
}
}
if (platform == p_alpha && !strcmp(t, "BSD") &&
d->chunks->part->part == NULL) {
if (Add_Chunk(d, 0, d->chunks->size, name, freebsd,
0, 0, "-")) {
DPRINT(("Failed to add 'freebsd' chunk"));
}
}
if (!strcmp(t, "BSD") && i == RAW_PART)
continue;
/* PLATFORM POLICY END ------------------------------------- */
off /= s;
len /= s;
off += lo[l - 1];
lo[l] = off;
if (!strcmp(t, "SUN"))
i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
else if (!strncmp(t, "MBR", 3)) {
switch (ty) {
case 0xa5:
i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
break;
case 0x01:
case 0x04:
case 0x06:
case 0x0b:
case 0x0c:
case 0x0e:
i = Add_Chunk(d, off, len, n, fat, ty, 0, 0);
break;
case 0xef: /* EFI */
i = Add_Chunk(d, off, len, n, efi, ty, 0, 0);
break;
default:
i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0);
break;
}
} else if (!strcmp(t, "BSD"))
i = Add_Chunk(d, off, len, n, part, ty, 0, 0);
else if (!strcmp(t, "PC98")) {
switch (ty & 0x7f) {
case 0x14:
i = Add_Chunk(d, off, len, n, freebsd, ty, 0,
sn);
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
i = Add_Chunk(d, off, len, n, fat, ty, 0, sn);
break;
default:
i = Add_Chunk(d, off, len, n, pc98, ty, 0, sn);
break;
}
} else if (!strcmp(t, "GPT"))
i = Add_Chunk(d, off, len, n, gpt, 0, 0, b);
else if (!strcmp(t, "APPLE"))
i = Add_Chunk(d, off, len, n, apple, 0, 0, sn);
else
; /* Ignore unknown classes. */
}
/* PLATFORM POLICY BEGIN ------------------------------------- */
/* We have a chance to do things on a blank disk here */
if (platform == p_sparc64 && d->chunks->part->part == NULL) {
hd = d->bios_hd;
sc = d->bios_sect;
o = d->chunks->size / (hd * sc);
o *= (hd * sc);
o -= 2 * hd * sc;
if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) {
DPRINT(("Failed to add 'freebsd' chunk"));
}
}
/* PLATFORM POLICY END --------------------------------------- */
return (d);
i = 0;
}