#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/int_limits.h>
#include <sys/stat.h>
#include <sys/uadmin.h>
#include <sys/open.h>
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/isa_defs.h>
#include <sys/efi_partition.h>
#if defined(_SUNOS_VTOC_16)
#include <sys/dklabel.h>
#endif
#include <sys/sysmacros.h>
#ifndef SECSIZE
#define SECSIZE DEV_BSIZE
#endif
extern int main(int, char **);
static void display(struct dk_geom *, struct extvtoc *, char *);
static void display64(struct dk_gpt *, char *);
static void insert(char *, struct extvtoc *);
static void insert64(char *, struct dk_gpt *);
static void load(FILE *, struct dk_geom *, struct extvtoc *);
static void load64(FILE *, int fd, struct dk_gpt **);
static void usage(void);
static void validate(struct dk_geom *, struct extvtoc *);
static void validate64(struct dk_gpt *);
static int vread(int, struct extvtoc *, char *);
static void vread64(int, struct dk_gpt **, char *);
static void vwrite(int, struct extvtoc *, char *);
static void vwrite64(int, struct dk_gpt *, char *);
static char *delta;
static short eflag;
static short iflag;
static short qflag;
static short uflag;
static diskaddr_t lastlba = 0;
#if defined(sparc)
static char *uboot = "boot";
#elif defined(i386)
static char *uboot = "";
#else
#error No platform defined.
#endif
static char *ufirm = "firm";
static int sectsiz;
#if defined(_SUNOS_VTOC_16)
static struct extvtoc disk_vtoc;
#endif
int
main(int argc, char **argv)
{
int fd;
int c;
char *dfile;
char *vname;
struct stat statbuf;
#if defined(_SUNOS_VTOC_8)
struct extvtoc disk_vtoc;
#endif
struct dk_gpt *disk_efi;
struct dk_geom disk_geom;
struct dk_minfo minfo;
int n;
disk_efi = NULL;
dfile = NULL;
vname = NULL;
#if defined(sparc)
while ((c = getopt(argc, argv, "ed:u:in:qs:")) != EOF)
#elif defined(i386)
while ((c = getopt(argc, argv, "ed:u:in:qb:p:s:")) != EOF)
#else
#error No platform defined.
#endif
switch (c) {
#if defined(i386)
case 'p':
case 'b':
(void) fprintf(stderr,
"fmthard: -p and -b no longer supported."
" Use installboot(8) to install boot blocks\n");
break;
#endif
case 'd':
delta = optarg;
break;
case 'e':
++eflag;
break;
case 'i':
++iflag;
break;
case 'n':
vname = optarg;
break;
case 'q':
++qflag;
break;
case 's':
dfile = optarg;
break;
case 'u':
if (strcmp(uboot, optarg) == 0)
++uflag;
else if (strcmp(ufirm, optarg) == 0)
uflag = 2;
break;
default:
usage();
}
if (argc - optind != 1)
usage();
if (stat(argv[optind], (struct stat *)&statbuf) == -1) {
(void) fprintf(stderr,
"fmthard: Cannot stat device %s\n",
argv[optind]);
exit(1);
}
if ((statbuf.st_mode & S_IFMT) != S_IFCHR) {
(void) fprintf(stderr,
"fmthard: %s must be a raw device.\n",
argv[optind]);
exit(1);
}
if ((fd = open(argv[optind], O_RDWR|O_NDELAY)) < 0) {
(void) fprintf(stderr, "fmthard: Cannot open device %s - %s\n",
argv[optind], strerror(errno));
exit(1);
}
if (ioctl(fd, DKIOCGMEDIAINFO, &minfo) == 0) {
sectsiz = minfo.dki_lbsize;
}
if (sectsiz == 0) {
sectsiz = SECSIZE;
}
if (!eflag && ioctl(fd, DKIOCGGEOM, &disk_geom)) {
#ifdef DEBUG
perror("DKIOCGGEOM failed");
#endif
if (errno == ENOTSUP) {
eflag++;
} else {
(void) fprintf(stderr,
"%s: Cannot get disk geometry\n", argv[optind]);
(void) close(fd);
exit(1);
}
}
if (!eflag) {
if (vread(fd, &disk_vtoc, argv[optind]) == 1)
eflag++;
}
if (eflag && ((dfile == NULL) || qflag)) {
vread64(fd, &disk_efi, argv[optind]);
}
if (qflag) {
(void) close(fd);
if (!eflag) {
exit(disk_vtoc.v_sanity == VTOC_SANE ? 0 : 1);
} else {
exit(disk_efi->efi_version <= EFI_VERSION102 ? 0 : 1);
}
}
if (delta) {
if (!eflag) {
insert(delta, &disk_vtoc);
validate(&disk_geom, &disk_vtoc);
vwrite(fd, &disk_vtoc, argv[optind]);
} else {
insert64(delta, disk_efi);
validate64(disk_efi);
vwrite64(fd, disk_efi, argv[optind]);
}
(void) close(fd);
exit(0);
}
if (!dfile && !vname)
usage();
if (dfile) {
if (strcmp(dfile, "-") == 0) {
if (!eflag)
load(stdin, &disk_geom, &disk_vtoc);
else
load64(stdin, fd, &disk_efi);
} else {
FILE *fp;
if ((fp = fopen(dfile, "r")) == NULL) {
(void) fprintf(stderr, "Cannot open file %s\n",
dfile);
(void) close(fd);
exit(1);
}
if (!eflag)
load(fp, &disk_geom, &disk_vtoc);
else
load64(fp, fd, &disk_efi);
(void) fclose(fp);
}
}
if (iflag) {
if (!eflag)
display(&disk_geom, &disk_vtoc, argv[optind]);
else
display64(disk_efi, argv[optind]);
(void) close(fd);
exit(0);
}
if (vname) {
n = MIN(strlen(vname) + 1, LEN_DKL_VVOL);
if (!eflag) {
(void) memcpy(disk_vtoc.v_volume, vname, n);
} else {
for (c = 0; c < disk_efi->efi_nparts; c++) {
if (disk_efi->efi_parts[c].p_tag ==
V_RESERVED) {
(void) memcpy(&disk_efi->efi_parts[c].p_name,
vname, n);
}
}
}
}
if (!eflag) {
validate(&disk_geom, &disk_vtoc);
vwrite(fd, &disk_vtoc, argv[optind]);
} else {
validate64(disk_efi);
vwrite64(fd, disk_efi, argv[optind]);
}
if (uflag == 1)
(void) uadmin(A_REBOOT, AD_BOOT, 0);
else if (uflag == 2)
(void) uadmin(A_REBOOT, AD_IBOOT, 0);
(void) printf("fmthard: New volume table of contents now in place.\n");
return (0);
}
static void
display(struct dk_geom *geom, struct extvtoc *vtoc, char *device)
{
int i;
int c;
(void) printf("* %s default partition map\n", device);
if (*vtoc->v_volume) {
(void) printf("* Volume Name: ");
for (i = 0; i < LEN_DKL_VVOL; i++) {
if ((c = vtoc->v_volume[i]) == 0)
break;
(void) printf("%c", c);
}
(void) printf("\n");
}
(void) printf("*\n");
(void) printf("* Dimensions:\n");
(void) printf("* %d bytes/sector\n", sectsiz);
(void) printf("* %d sectors/track\n", geom->dkg_nsect);
(void) printf("* %d tracks/cylinder\n", geom->dkg_nhead);
(void) printf("* %d cylinders\n", geom->dkg_pcyl);
(void) printf("* %d accessible cylinders\n", geom->dkg_ncyl);
(void) printf("*\n");
(void) printf("* Flags:\n");
(void) printf("* 1: unmountable\n");
(void) printf("* 10: read-only\n");
(void) printf("*\n");
(void) printf(
"\n* Partition Tag Flag First Sector Sector Count\n");
for (i = 0; i < V_NUMPAR; i++) {
if (vtoc->v_part[i].p_size > 0)
(void) printf(
" %d %d 0%x %llu %llu\n",
i, vtoc->v_part[i].p_tag,
vtoc->v_part[i].p_flag,
vtoc->v_part[i].p_start,
vtoc->v_part[i].p_size);
}
exit(0);
}
static void
display64(struct dk_gpt *efi, char *device)
{
int i;
(void) printf("* %s default partition map\n", device);
(void) printf("*\n");
(void) printf("* Dimensions:\n");
(void) printf("* %d bytes/sector\n", efi->efi_lbasize);
(void) printf("* N/A sectors/track\n");
(void) printf("* N/A tracks/cylinder\n");
(void) printf("* N/A cylinders\n");
(void) printf("* N/A accessible cylinders\n");
(void) printf("*\n");
(void) printf("* Flags:\n");
(void) printf("* 1: unmountable\n");
(void) printf("* 10: read-only\n");
(void) printf("*\n");
(void) printf(
"\n* Partition Tag Flag First Sector Sector Count\n");
for (i = 0; i < efi->efi_nparts; i++) {
if (efi->efi_parts[i].p_size > 0)
(void) printf(
" %d %d 0%x %8lld %8lld\n",
i, efi->efi_parts[i].p_tag,
efi->efi_parts[i].p_flag,
efi->efi_parts[i].p_start,
efi->efi_parts[i].p_size);
}
exit(0);
}
static void
insert(char *data, struct extvtoc *vtoc)
{
int part;
int tag;
uint_t flag;
diskaddr_t start;
uint64_t size;
if (sscanf(data, "%d:%d:%x:%llu:%llu",
&part, &tag, &flag, &start, &size) != 5) {
(void) fprintf(stderr, "Delta syntax error on \"%s\"\n", data);
exit(1);
}
if (part >= V_NUMPAR) {
(void) fprintf(stderr,
"Error in data \"%s\": No such partition %x\n",
data, part);
exit(1);
}
vtoc->v_part[part].p_tag = (ushort_t)tag;
vtoc->v_part[part].p_flag = (ushort_t)flag;
vtoc->v_part[part].p_start = start;
vtoc->v_part[part].p_size = size;
}
static void
insert64(char *data, struct dk_gpt *efi)
{
int part;
int tag;
uint_t flag;
diskaddr_t start;
diskaddr_t size;
if (sscanf(data, "%d:%d:%x:%lld:%lld",
&part, &tag, &flag, &start, &size) != 5) {
(void) fprintf(stderr, "Delta syntax error on \"%s\"\n", data);
exit(1);
}
if (part >= efi->efi_nparts) {
(void) fprintf(stderr,
"Error in data \"%s\": No such partition %x\n",
data, part);
exit(1);
}
efi->efi_parts[part].p_tag = (ushort_t)tag;
efi->efi_parts[part].p_flag = (ushort_t)flag;
efi->efi_parts[part].p_start = start;
efi->efi_parts[part].p_size = size;
}
static void
load(FILE *fp, struct dk_geom *geom, struct extvtoc *vtoc)
{
int part;
int tag;
uint_t flag;
diskaddr_t start;
uint64_t size;
char line[256];
int i;
uint64_t nblks;
uint64_t fullsz;
for (i = 0; i < V_NUMPAR; ++i) {
vtoc->v_part[i].p_tag = 0;
vtoc->v_part[i].p_flag = V_UNMNT;
vtoc->v_part[i].p_start = 0;
vtoc->v_part[i].p_size = 0;
}
fullsz = (uint64_t)geom->dkg_ncyl * geom->dkg_nsect * geom->dkg_nhead;
vtoc->v_part[2].p_tag = V_BACKUP;
vtoc->v_part[2].p_flag = V_UNMNT;
vtoc->v_part[2].p_start = 0;
vtoc->v_part[2].p_size = fullsz;
nblks = geom->dkg_nsect * geom->dkg_nhead;
while (fgets(line, sizeof (line) - 1, fp)) {
if (line[0] == '\0' || line[0] == '\n' || line[0] == '*')
continue;
line[strlen(line) - 1] = '\0';
if (sscanf(line, "%d %d %x %llu %llu",
&part, &tag, &flag, &start, &size) != 5) {
(void) fprintf(stderr, "Syntax error: \"%s\"\n",
line);
exit(1);
}
if (part >= V_NUMPAR) {
(void) fprintf(stderr,
"No such partition %x: \"%s\"\n",
part, line);
exit(1);
}
if (!eflag && ((start % nblks) != 0 || (size % nblks) != 0)) {
(void) fprintf(stderr,
"Partition %d not aligned on cylinder boundary: \"%s\"\n",
part, line);
exit(1);
}
vtoc->v_part[part].p_tag = (ushort_t)tag;
vtoc->v_part[part].p_flag = (ushort_t)flag;
vtoc->v_part[part].p_start = start;
vtoc->v_part[part].p_size = size;
}
for (part = 0; part < V_NUMPAR; part++) {
vtoc->timestamp[part] = (time_t)0;
}
}
static void
load64(FILE *fp, int fd, struct dk_gpt **efi)
{
int part;
int tag;
uint_t flag;
diskaddr_t start;
diskaddr_t size;
int nlines = 0;
char line[256];
int i;
uint_t max_part = 0;
char **mem = NULL;
while (fgets(line, sizeof (line) - 1, fp)) {
if (line[0] == '\0' || line[0] == '\n' || line[0] == '*')
continue;
line[strlen(line) - 1] = '\0';
if (sscanf(line, "%d %d %x %lld %lld",
&part, &tag, &flag, &start, &size) != 5) {
(void) fprintf(stderr, "Syntax error: \"%s\"\n",
line);
exit(1);
}
mem = realloc(mem, sizeof (*mem) * (nlines + 1));
if (mem == NULL) {
(void) fprintf(stderr, "realloc failed\n");
exit(1);
}
mem[nlines] = strdup(line);
if (mem[nlines] == NULL) {
(void) fprintf(stderr, "strdup failed\n");
exit(1);
}
nlines++;
if (part > max_part)
max_part = part;
}
max_part++;
if ((i = efi_alloc_and_init(fd, max_part, efi)) < 0) {
(void) fprintf(stderr,
"efi_alloc_and_init failed: %d\n", i);
exit(1);
}
for (i = 0; i < (*efi)->efi_nparts; ++i) {
(*efi)->efi_parts[i].p_tag = V_UNASSIGNED;
(*efi)->efi_parts[i].p_flag = V_UNMNT;
(*efi)->efi_parts[i].p_start = 0;
(*efi)->efi_parts[i].p_size = 0;
}
lastlba = (*efi)->efi_last_u_lba;
for (i = 0; i < nlines; i++) {
if (sscanf(mem[i], "%d %d %x %lld %lld",
&part, &tag, &flag, &start, &size) != 5) {
(void) fprintf(stderr, "Syntax error: \"%s\"\n",
line);
exit(1);
}
free(mem[i]);
if (part >= (*efi)->efi_nparts) {
(void) fprintf(stderr,
"No such partition %x: \"%s\"\n",
part, line);
exit(1);
}
(*efi)->efi_parts[part].p_tag = (ushort_t)tag;
(*efi)->efi_parts[part].p_flag = (ushort_t)flag;
(*efi)->efi_parts[part].p_start = start;
(*efi)->efi_parts[part].p_size = size;
}
(*efi)->efi_nparts = max_part;
free(mem);
}
static void
usage()
{
#if defined(sparc)
(void) fprintf(stderr,
"Usage: fmthard [ -i ] [ -n volumename ] [ -s datafile ] [ -d arguments] \
raw-device\n");
#elif defined(i386)
(void) fprintf(stderr,
"Usage: fmthard [ -i ] [ -S ] [-I geom_file] \
-n volumename | -s datafile [ -d arguments] raw-device\n");
#else
#error No platform defined.
#endif
exit(2);
}
static void
validate(struct dk_geom *geom, struct extvtoc *vtoc)
{
int i;
int j;
uint64_t fullsz;
diskaddr_t endsect;
diskaddr_t istart;
diskaddr_t jstart;
uint64_t isize;
uint64_t jsize;
uint64_t nblks;
nblks = geom->dkg_nsect * geom->dkg_nhead;
fullsz = (uint64_t)geom->dkg_ncyl * geom->dkg_nsect * geom->dkg_nhead;
#if defined(_SUNOS_VTOC_16)
vtoc->v_version = V_VERSION;
vtoc->v_sanity = VTOC_SANE;
vtoc->v_nparts = V_NUMPAR;
if (vtoc->v_sectorsz == 0)
vtoc->v_sectorsz = sectsiz;
#endif
for (i = 0; i < V_NUMPAR; i++) {
if (vtoc->v_part[i].p_tag == V_BACKUP) {
if (vtoc->v_part[i].p_size != fullsz) {
(void) fprintf(stderr, "\
fmthard: Partition %d specifies the full disk and is not equal\n\
full size of disk. The full disk capacity is %llu sectors.\n", i, fullsz);
#if defined(sparc)
exit(1);
#endif
}
}
if (vtoc->v_part[i].p_size == 0)
continue;
if ((vtoc->v_part[i].p_start % nblks) ||
(vtoc->v_part[i].p_size % nblks)) {
(void) fprintf(stderr, "\
fmthard: Partition %d not aligned on cylinder boundary \n", i);
exit(1);
}
if (vtoc->v_part[i].p_start > fullsz ||
vtoc->v_part[i].p_start +
vtoc->v_part[i].p_size > fullsz) {
(void) fprintf(stderr, "\
fmthard: Partition %d specified as %llu sectors starting at %llu\n\
\tdoes not fit. The full disk contains %llu sectors.\n",
i, vtoc->v_part[i].p_size,
vtoc->v_part[i].p_start, fullsz);
#if defined(sparc)
exit(1);
#endif
}
if (vtoc->v_part[i].p_tag != V_BACKUP &&
vtoc->v_part[i].p_size != fullsz) {
for (j = 0; j < V_NUMPAR; j++) {
if (vtoc->v_part[j].p_tag == V_BACKUP)
continue;
if (vtoc->v_part[j].p_size == fullsz)
continue;
isize = vtoc->v_part[i].p_size;
jsize = vtoc->v_part[j].p_size;
istart = vtoc->v_part[i].p_start;
jstart = vtoc->v_part[j].p_start;
if ((i != j) &&
(isize != 0) && (jsize != 0)) {
endsect = jstart + jsize -1;
if ((jstart <= istart) &&
(istart <= endsect)) {
(void) fprintf(stderr, "\
fmthard: Partition %d overlaps partition %d. Overlap is allowed\n\
\tonly on partition on the full disk partition).\n",
i, j);
#if defined(sparc)
exit(1);
#endif
}
}
}
}
}
}
static void
validate64(struct dk_gpt *efi)
{
int i;
int j;
int resv_part = 0;
diskaddr_t endsect;
diskaddr_t fullsz;
diskaddr_t istart;
diskaddr_t jstart;
diskaddr_t isize;
diskaddr_t jsize;
fullsz = lastlba + 1;
for (i = 0; i < efi->efi_nparts; i++) {
if (efi->efi_parts[i].p_size == 0)
continue;
if (efi->efi_parts[i].p_tag == V_RESERVED)
resv_part++;
if (efi->efi_parts[i].p_start > fullsz ||
efi->efi_parts[i].p_start +
efi->efi_parts[i].p_size > fullsz) {
(void) fprintf(stderr, "\
fmthard: Partition %d specified as %lld sectors starting at %lld\n\
\tdoes not fit. The full disk contains %lld sectors.\n",
i, efi->efi_parts[i].p_size,
efi->efi_parts[i].p_start, fullsz);
exit(1);
}
if (efi->efi_parts[i].p_tag != V_BACKUP &&
efi->efi_parts[i].p_size != fullsz) {
for (j = 0; j < efi->efi_nparts; j++) {
if (efi->efi_parts[j].p_size == fullsz)
continue;
isize = efi->efi_parts[i].p_size;
jsize = efi->efi_parts[j].p_size;
istart = efi->efi_parts[i].p_start;
jstart = efi->efi_parts[j].p_start;
if ((i != j) &&
(isize != 0) && (jsize != 0)) {
endsect = jstart + jsize - 1;
if ((jstart <= istart) &&
(istart <= endsect)) {
(void) fprintf(stderr, "\
fmthard: Partition %d overlaps partition %d. Overlap is allowed\n\
\tonly on partition on the full disk partition).\n",
i, j);
#if defined(sparc)
exit(1);
#endif
}
}
}
}
}
if (resv_part != 1) {
(void) fprintf(stderr,
"expected one reserved partition, but found %d\n",
resv_part);
exit(1);
}
}
int
vread(int fd, struct extvtoc *vtoc, char *devname)
{
int i;
if ((i = read_extvtoc(fd, vtoc)) < 0) {
if (i == VT_ENOTSUP) {
return (1);
}
if (i == VT_EINVAL) {
(void) fprintf(stderr, "%s: Invalid VTOC\n",
devname);
} else {
(void) fprintf(stderr, "%s: Cannot read VTOC\n",
devname);
}
exit(1);
}
return (0);
}
void
vread64(int fd, struct dk_gpt **efi_hdr, char *devname)
{
int i;
if ((i = efi_alloc_and_read(fd, efi_hdr)) < 0) {
if (i == VT_EINVAL)
(void) fprintf(stderr,
"%s: this disk must be labeled first\n",
devname);
else
(void) fprintf(stderr,
"%s: read_efi failed %d\n",
devname, i);
exit(1);
}
lastlba = (*efi_hdr)->efi_last_u_lba;
}
void
vwrite(int fd, struct extvtoc *vtoc, char *devname)
{
int i;
if ((i = write_extvtoc(fd, vtoc)) != 0) {
if (i == VT_EINVAL) {
(void) fprintf(stderr,
"%s: invalid entry exists in vtoc\n",
devname);
} else {
(void) fprintf(stderr, "%s: Cannot write VTOC\n",
devname);
}
exit(1);
}
}
void
vwrite64(int fd, struct dk_gpt *efi, char *devname)
{
int i;
if ((i = efi_write(fd, efi)) != 0) {
if (i == VT_EINVAL) {
(void) fprintf(stderr,
"%s: invalid entry exists in vtoc\n",
devname);
} else {
(void) fprintf(stderr, "%s: Cannot write EFI\n",
devname);
}
exit(1);
}
}