961 lines
23 KiB
C
961 lines
23 KiB
C
/* $OpenBSD: gpt.c,v 1.93 2023/06/20 11:52:08 krw Exp $ */
|
|
/*
|
|
* Copyright (c) 2015 Markus Muller <mmu@grummel.net>
|
|
* Copyright (c) 2015 Kenneth R Westerback <krw@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h> /* DEV_BSIZE */
|
|
#include <sys/disklabel.h>
|
|
#include <sys/dkio.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <uuid.h>
|
|
|
|
#include "part.h"
|
|
#include "disk.h"
|
|
#include "mbr.h"
|
|
#include "misc.h"
|
|
#include "gpt.h"
|
|
|
|
#ifdef DEBUG
|
|
#define DPRINTF(x...) printf(x)
|
|
#else
|
|
#define DPRINTF(x...)
|
|
#endif
|
|
|
|
struct mbr gmbr;
|
|
struct gpt_header gh;
|
|
struct gpt_partition gp[NGPTPARTITIONS];
|
|
|
|
const struct gpt_partition * const *sort_gpt(void);
|
|
int lba_start_cmp(const void *e1, const void *e2);
|
|
int lba_free(uint64_t *, uint64_t *);
|
|
int add_partition(const uint8_t *, const char *, uint64_t);
|
|
int find_partition(const uint8_t *);
|
|
int get_header(const uint64_t);
|
|
int get_partition_table(void);
|
|
int init_gh(void);
|
|
int init_gp(const int);
|
|
uint32_t crc32(const u_char *, const uint32_t);
|
|
int protective_mbr(const struct mbr *);
|
|
int gpt_chk_mbr(struct dos_partition *, uint64_t);
|
|
void string_to_name(const unsigned int, const char *);
|
|
const char *name_to_string(const unsigned int);
|
|
|
|
void
|
|
string_to_name(const unsigned int pn, const char *ch)
|
|
{
|
|
unsigned int i;
|
|
|
|
memset(gp[pn].gp_name, 0, sizeof(gp[pn].gp_name));
|
|
|
|
for (i = 0; i < sizeof(gp[pn].gp_name) && ch[i] != '\0'; i++)
|
|
gp[pn].gp_name[i] = htole16((unsigned int)ch[i]);
|
|
}
|
|
|
|
const char *
|
|
name_to_string(const unsigned int pn)
|
|
{
|
|
static char name[GPTPARTNAMESIZE + 1];
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < GPTPARTNAMESIZE && gp[pn].gp_name[i] != 0; i++)
|
|
name[i] = letoh16(gp[pn].gp_name[i]) & 0x7F;
|
|
name[i] = '\0';
|
|
|
|
return name;
|
|
}
|
|
|
|
/*
|
|
* Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
|
|
* partition exists.
|
|
*
|
|
* Taken from kern/subr_disk.c.
|
|
*
|
|
*/
|
|
int
|
|
gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
|
|
{
|
|
struct dos_partition *dp2;
|
|
int efi, eficnt, found, i;
|
|
uint32_t psize;
|
|
|
|
found = efi = eficnt = 0;
|
|
for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
|
|
if (dp2->dp_typ == DOSPTYP_UNUSED)
|
|
continue;
|
|
found++;
|
|
if (dp2->dp_typ != DOSPTYP_EFI)
|
|
continue;
|
|
if (letoh32(dp2->dp_start) != GPTSECTOR)
|
|
continue;
|
|
psize = letoh32(dp2->dp_size);
|
|
if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
|
|
efi = i;
|
|
eficnt++;
|
|
}
|
|
}
|
|
if (found == 1 && eficnt == 1)
|
|
return efi;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
protective_mbr(const struct mbr *mbr)
|
|
{
|
|
struct dos_partition dp[NDOSPART], dos_partition;
|
|
unsigned int i;
|
|
|
|
if (mbr->mbr_lba_self != 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < nitems(dp); i++) {
|
|
memset(&dos_partition, 0, sizeof(dos_partition));
|
|
if (i < nitems(mbr->mbr_prt))
|
|
PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
|
|
mbr->mbr_lba_firstembr, &dos_partition);
|
|
memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
|
|
}
|
|
|
|
return gpt_chk_mbr(dp, DL_GETDSIZE(&dl));
|
|
}
|
|
|
|
int
|
|
get_header(const uint64_t sector)
|
|
{
|
|
struct gpt_header legh;
|
|
uint64_t gpbytes, gpsectors, lba_end;
|
|
|
|
if (DISK_readbytes(&legh, sector, sizeof(legh)))
|
|
return -1;
|
|
|
|
gh.gh_sig = letoh64(legh.gh_sig);
|
|
if (gh.gh_sig != GPTSIGNATURE) {
|
|
DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
|
|
GPTSIGNATURE, gh.gh_sig);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_rev = letoh32(legh.gh_rev);
|
|
if (gh.gh_rev != GPTREVISION) {
|
|
DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
|
|
GPTREVISION, gh.gh_rev);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_lba_self = letoh64(legh.gh_lba_self);
|
|
if (gh.gh_lba_self != sector) {
|
|
DPRINTF("gpt self lba: expected %llu, got %llu\n",
|
|
sector, gh.gh_lba_self);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_size = letoh32(legh.gh_size);
|
|
if (gh.gh_size != GPTMINHDRSIZE) {
|
|
DPRINTF("gpt header size: expected %u, got %u\n",
|
|
GPTMINHDRSIZE, gh.gh_size);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_part_size = letoh32(legh.gh_part_size);
|
|
if (gh.gh_part_size != GPTMINPARTSIZE) {
|
|
DPRINTF("gpt partition size: expected %u, got %u\n",
|
|
GPTMINPARTSIZE, gh.gh_part_size);
|
|
return -1;
|
|
}
|
|
|
|
if ((dl.d_secsize % gh.gh_part_size) != 0) {
|
|
DPRINTF("gpt sector size %% partition size (%u %% %u) != 0\n",
|
|
dl.d_secsize, gh.gh_part_size);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_part_num = letoh32(legh.gh_part_num);
|
|
if (gh.gh_part_num > NGPTPARTITIONS) {
|
|
DPRINTF("gpt partition count: expected <= %u, got %u\n",
|
|
NGPTPARTITIONS, gh.gh_part_num);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_csum = letoh32(legh.gh_csum);
|
|
legh.gh_csum = 0;
|
|
legh.gh_csum = crc32((unsigned char *)&legh, gh.gh_size);
|
|
if (legh.gh_csum != gh.gh_csum) {
|
|
DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
|
|
legh.gh_csum, gh.gh_csum);
|
|
/* Accept wrong-endian checksum. */
|
|
if (swap32(legh.gh_csum) != gh.gh_csum)
|
|
return -1;
|
|
}
|
|
|
|
gpbytes = gh.gh_part_num * gh.gh_part_size;
|
|
gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
|
|
lba_end = DL_GETDSIZE(&dl) - gpsectors - 2;
|
|
|
|
gh.gh_lba_end = letoh64(legh.gh_lba_end);
|
|
if (gh.gh_lba_end > lba_end) {
|
|
DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n",
|
|
gh.gh_lba_end, lba_end);
|
|
gh.gh_lba_end = lba_end;
|
|
}
|
|
|
|
gh.gh_lba_start = letoh64(legh.gh_lba_start);
|
|
if (gh.gh_lba_start >= gh.gh_lba_end) {
|
|
DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
|
|
gh.gh_lba_end, gh.gh_lba_start);
|
|
return -1;
|
|
}
|
|
|
|
gh.gh_part_lba = letoh64(legh.gh_part_lba);
|
|
if (gh.gh_lba_self == GPTSECTOR) {
|
|
if (gh.gh_part_lba <= GPTSECTOR) {
|
|
DPRINTF("gpt partition entries start: expected > %u, "
|
|
"got %llu\n", GPTSECTOR, gh.gh_part_lba);
|
|
return -1;
|
|
}
|
|
if (gh.gh_part_lba + gpsectors > gh.gh_lba_start) {
|
|
DPRINTF("gpt partition entries end: expected < %llu, "
|
|
"got %llu\n", gh.gh_lba_start,
|
|
gh.gh_part_lba + gpsectors);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (gh.gh_part_lba <= gh.gh_lba_end) {
|
|
DPRINTF("gpt partition entries start: expected > %llu, "
|
|
"got %llu\n", gh.gh_lba_end, gh.gh_part_lba);
|
|
return -1;
|
|
}
|
|
if (gh.gh_part_lba + gpsectors > gh.gh_lba_self) {
|
|
DPRINTF("gpt partition entries end: expected < %llu, "
|
|
"got %llu\n", gh.gh_lba_self,
|
|
gh.gh_part_lba + gpsectors);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
gh.gh_lba_alt = letoh32(legh.gh_lba_alt);
|
|
gh.gh_part_csum = letoh32(legh.gh_part_csum);
|
|
gh.gh_rsvd = letoh32(legh.gh_rsvd); /* Should always be 0. */
|
|
uuid_dec_le(&legh.gh_guid, &gh.gh_guid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
get_partition_table(void)
|
|
{
|
|
struct gpt_partition *legp;
|
|
uint64_t gpbytes;
|
|
unsigned int pn;
|
|
int rslt = -1;
|
|
uint32_t gh_part_csum;
|
|
|
|
DPRINTF("gpt partition table being read from LBA %llu\n",
|
|
gh.gh_part_lba);
|
|
|
|
gpbytes = gh.gh_part_num * gh.gh_part_size;
|
|
|
|
legp = calloc(1, gpbytes);
|
|
if (legp == NULL)
|
|
err(1, "legp");
|
|
|
|
if (DISK_readbytes(legp, gh.gh_part_lba, gpbytes))
|
|
goto done;
|
|
gh_part_csum = crc32((unsigned char *)legp, gpbytes);
|
|
|
|
if (gh_part_csum != gh.gh_part_csum) {
|
|
DPRINTF("gpt partition table checksum: expected 0x%x, "
|
|
"got 0x%x\n", gh.gh_part_csum, gh_part_csum);
|
|
/* Accept wrong-endian checksum. */
|
|
if (swap32(gh_part_csum) != gh.gh_part_csum)
|
|
goto done;
|
|
}
|
|
|
|
memset(&gp, 0, sizeof(gp));
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
uuid_dec_le(&legp[pn].gp_type, &gp[pn].gp_type);
|
|
uuid_dec_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
|
|
gp[pn].gp_lba_start = letoh64(legp[pn].gp_lba_start);
|
|
gp[pn].gp_lba_end = letoh64(legp[pn].gp_lba_end);
|
|
gp[pn].gp_attrs = letoh64(legp[pn].gp_attrs);
|
|
memcpy(gp[pn].gp_name, legp[pn].gp_name,
|
|
sizeof(gp[pn].gp_name));
|
|
}
|
|
rslt = 0;
|
|
|
|
done:
|
|
free(legp);
|
|
return rslt;
|
|
}
|
|
|
|
int
|
|
GPT_read(const int which)
|
|
{
|
|
int error;
|
|
|
|
error = MBR_read(0, 0, &gmbr);
|
|
if (error)
|
|
goto done;
|
|
error = protective_mbr(&gmbr);
|
|
if (error == -1)
|
|
goto done;
|
|
|
|
switch (which) {
|
|
case PRIMARYGPT:
|
|
error = get_header(GPTSECTOR);
|
|
break;
|
|
case SECONDARYGPT:
|
|
error = get_header(DL_GETDSIZE(&dl) - 1);
|
|
break;
|
|
case ANYGPT:
|
|
error = get_header(GPTSECTOR);
|
|
if (error != 0 || get_partition_table() != 0)
|
|
error = get_header(DL_GETDSIZE(&dl) - 1);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (error == 0)
|
|
error = get_partition_table();
|
|
|
|
done:
|
|
if (error != 0) {
|
|
/* No valid GPT found. Zap any artifacts. */
|
|
memset(&gmbr, 0, sizeof(gmbr));
|
|
memset(&gh, 0, sizeof(gh));
|
|
memset(&gp, 0, sizeof(gp));
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void
|
|
GPT_print(const char *units, const int verbosity)
|
|
{
|
|
const struct unit_type *ut;
|
|
const int secsize = dl.d_secsize;
|
|
char *guidstr = NULL;
|
|
double size;
|
|
unsigned int pn;
|
|
uint32_t status;
|
|
|
|
#ifdef DEBUG
|
|
char *p;
|
|
uint64_t sig;
|
|
unsigned int i;
|
|
|
|
sig = htole64(gh.gh_sig);
|
|
p = (char *)&sig;
|
|
|
|
printf("gh_sig : ");
|
|
for (i = 0; i < sizeof(sig); i++)
|
|
printf("%c", isprint((unsigned char)p[i]) ? p[i] : '?');
|
|
printf(" (");
|
|
for (i = 0; i < sizeof(sig); i++) {
|
|
printf("%02x", p[i]);
|
|
if ((i + 1) < sizeof(sig))
|
|
printf(":");
|
|
}
|
|
printf(")\n");
|
|
printf("gh_rev : %u\n", gh.gh_rev);
|
|
printf("gh_size : %u (%zd)\n", gh.gh_size, sizeof(gh));
|
|
printf("gh_csum : 0x%x\n", gh.gh_csum);
|
|
printf("gh_rsvd : %u\n", gh.gh_rsvd);
|
|
printf("gh_lba_self : %llu\n", gh.gh_lba_self);
|
|
printf("gh_lba_alt : %llu\n", gh.gh_lba_alt);
|
|
printf("gh_lba_start : %llu\n", gh.gh_lba_start);
|
|
printf("gh_lba_end : %llu\n", gh.gh_lba_end);
|
|
p = NULL;
|
|
uuid_to_string(&gh.gh_guid, &p, &status);
|
|
printf("gh_gh_guid : %s\n", (status == uuid_s_ok) ? p : "<invalid>");
|
|
free(p);
|
|
printf("gh_gh_part_lba : %llu\n", gh.gh_part_lba);
|
|
printf("gh_gh_part_num : %u (%zu)\n", gh.gh_part_num, nitems(gp));
|
|
printf("gh_gh_part_size: %u (%zu)\n", gh.gh_part_size, sizeof(gp[0]));
|
|
printf("gh_gh_part_csum: 0x%x\n", gh.gh_part_csum);
|
|
printf("\n");
|
|
#endif /* DEBUG */
|
|
|
|
size = units_size(units, DL_GETDSIZE(&dl), &ut);
|
|
printf("Disk: %s Usable LBA: %llu to %llu [%.0f ",
|
|
disk.dk_name, gh.gh_lba_start, gh.gh_lba_end, size);
|
|
if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
|
|
printf("%d-byte ", secsize);
|
|
printf("%s]\n", ut->ut_lname);
|
|
|
|
if (verbosity == VERBOSE) {
|
|
printf("GUID: ");
|
|
uuid_to_string(&gh.gh_guid, &guidstr, &status);
|
|
if (status == uuid_s_ok)
|
|
printf("%s\n", guidstr);
|
|
else
|
|
printf("<invalid header GUID>\n");
|
|
free(guidstr);
|
|
}
|
|
|
|
GPT_print_parthdr(verbosity);
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
if (uuid_is_nil(&gp[pn].gp_type, NULL))
|
|
continue;
|
|
GPT_print_part(pn, units, verbosity);
|
|
}
|
|
}
|
|
|
|
void
|
|
GPT_print_parthdr(const int verbosity)
|
|
{
|
|
printf(" #: type "
|
|
" [ start: size ]\n");
|
|
if (verbosity == VERBOSE)
|
|
printf(" guid name\n");
|
|
printf("--------------------------------------------------------"
|
|
"----------------\n");
|
|
}
|
|
|
|
void
|
|
GPT_print_part(const unsigned int pn, const char *units, const int verbosity)
|
|
{
|
|
const struct unit_type *ut;
|
|
char *guidstr = NULL;
|
|
double size;
|
|
uint64_t attrs, end, start;
|
|
uint32_t status;
|
|
|
|
start = gp[pn].gp_lba_start;
|
|
end = gp[pn].gp_lba_end;
|
|
size = units_size(units, (start > end) ? 0 : end - start + 1, &ut);
|
|
|
|
printf(" %3u: %-36s [%12lld: %12.0f%s]\n", pn,
|
|
PRT_uuid_to_desc(&gp[pn].gp_type), start, size, ut->ut_abbr);
|
|
|
|
if (verbosity == VERBOSE) {
|
|
uuid_to_string(&gp[pn].gp_guid, &guidstr, &status);
|
|
if (status != uuid_s_ok)
|
|
printf(" <invalid partition guid> ");
|
|
else
|
|
printf(" %-36s ", guidstr);
|
|
printf("%s\n", name_to_string(pn));
|
|
free(guidstr);
|
|
attrs = gp[pn].gp_attrs;
|
|
if (attrs) {
|
|
printf(" Attributes: (0x%016llx) ", attrs);
|
|
if (attrs & GPTPARTATTR_REQUIRED)
|
|
printf("Required " );
|
|
if (attrs & GPTPARTATTR_IGNORE)
|
|
printf("Ignore ");
|
|
if (attrs & GPTPARTATTR_BOOTABLE)
|
|
printf("Bootable ");
|
|
if (attrs & GPTPARTATTR_MS_READONLY)
|
|
printf("MSReadOnly " );
|
|
if (attrs & GPTPARTATTR_MS_SHADOW)
|
|
printf("MSShadow ");
|
|
if (attrs & GPTPARTATTR_MS_HIDDEN)
|
|
printf("MSHidden ");
|
|
if (attrs & GPTPARTATTR_MS_NOAUTOMOUNT)
|
|
printf("MSNoAutoMount ");
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
if (uuid_is_nil(&gp[pn].gp_type, NULL) == 0) {
|
|
if (start > end)
|
|
printf("partition %u first LBA is > last LBA\n", pn);
|
|
if (start < gh.gh_lba_start || end > gh.gh_lba_end)
|
|
printf("partition %u extends beyond usable LBA range "
|
|
"of %s\n", pn, disk.dk_name);
|
|
}
|
|
}
|
|
|
|
int
|
|
find_partition(const uint8_t *beuuid)
|
|
{
|
|
struct uuid uuid;
|
|
unsigned int pn;
|
|
|
|
uuid_dec_be(beuuid, &uuid);
|
|
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
if (uuid_compare(&gp[pn].gp_type, &uuid, NULL) == 0)
|
|
return pn;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
|
|
{
|
|
struct uuid uuid;
|
|
int rslt;
|
|
uint64_t end, freesectors, start;
|
|
uint32_t status, pn;
|
|
|
|
uuid_dec_be(beuuid, &uuid);
|
|
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
if (uuid_is_nil(&gp[pn].gp_type, NULL))
|
|
break;
|
|
}
|
|
if (pn == gh.gh_part_num)
|
|
goto done;
|
|
|
|
rslt = lba_free(&start, &end);
|
|
if (rslt == -1)
|
|
goto done;
|
|
|
|
if (start % BLOCKALIGNMENT)
|
|
start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
|
|
if (start >= end)
|
|
goto done;
|
|
|
|
freesectors = end - start + 1;
|
|
|
|
if (sectors == 0)
|
|
sectors = freesectors;
|
|
|
|
if (freesectors < sectors)
|
|
goto done;
|
|
else if (freesectors > sectors)
|
|
end = start + sectors - 1;
|
|
|
|
gp[pn].gp_type = uuid;
|
|
gp[pn].gp_lba_start = start;
|
|
gp[pn].gp_lba_end = end;
|
|
string_to_name(pn, name);
|
|
|
|
uuid_create(&gp[pn].gp_guid, &status);
|
|
if (status == uuid_s_ok)
|
|
return 0;
|
|
|
|
done:
|
|
if (pn != gh.gh_part_num)
|
|
memset(&gp[pn], 0, sizeof(gp[pn]));
|
|
printf("unable to add %s\n", name);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
init_gh(void)
|
|
{
|
|
struct gpt_header oldgh;
|
|
const int secsize = dl.d_secsize;
|
|
int needed;
|
|
uint32_t status;
|
|
|
|
memcpy(&oldgh, &gh, sizeof(oldgh));
|
|
memset(&gh, 0, sizeof(gh));
|
|
memset(&gmbr, 0, sizeof(gmbr));
|
|
|
|
/* XXX Do we need the boot code? UEFI spec & Apple says no. */
|
|
memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
|
|
gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
|
|
gmbr.mbr_prt[0].prt_bs = 1;
|
|
gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
|
|
gmbr.mbr_signature = DOSMBR_SIGNATURE;
|
|
|
|
needed = sizeof(gp) / secsize + 2;
|
|
|
|
if (needed % BLOCKALIGNMENT)
|
|
needed += (needed - (needed % BLOCKALIGNMENT));
|
|
|
|
gh.gh_sig = GPTSIGNATURE;
|
|
gh.gh_rev = GPTREVISION;
|
|
gh.gh_size = GPTMINHDRSIZE;
|
|
gh.gh_csum = 0;
|
|
gh.gh_rsvd = 0;
|
|
gh.gh_lba_self = 1;
|
|
gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1;
|
|
gh.gh_lba_start = needed;
|
|
gh.gh_lba_end = DL_GETDSIZE(&dl) - needed;
|
|
uuid_create(&gh.gh_guid, &status);
|
|
if (status != uuid_s_ok) {
|
|
memcpy(&gh, &oldgh, sizeof(gh));
|
|
return -1;
|
|
}
|
|
gh.gh_part_lba = 2;
|
|
gh.gh_part_num = NGPTPARTITIONS;
|
|
gh.gh_part_size = GPTMINPARTSIZE;
|
|
gh.gh_part_csum = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
init_gp(const int how)
|
|
{
|
|
struct gpt_partition oldgp[NGPTPARTITIONS];
|
|
const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
|
|
const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
|
|
uint64_t prt_ns;
|
|
int pn, rslt;
|
|
|
|
memcpy(&oldgp, &gp, sizeof(oldgp));
|
|
if (how == GHANDGP)
|
|
memset(&gp, 0, sizeof(gp));
|
|
else {
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
if (PRT_protected_uuid(&gp[pn].gp_type) ||
|
|
(gp[pn].gp_attrs & GPTPARTATTR_REQUIRED))
|
|
continue;
|
|
memset(&gp[pn], 0, sizeof(gp[pn]));
|
|
}
|
|
}
|
|
|
|
rslt = 0;
|
|
if (disk.dk_bootprt.prt_ns > 0) {
|
|
pn = find_partition(gpt_uuid_efi_system);
|
|
if (pn == -1) {
|
|
rslt = add_partition(gpt_uuid_efi_system,
|
|
"EFI System Area", disk.dk_bootprt.prt_ns);
|
|
} else {
|
|
prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
|
|
if (prt_ns < disk.dk_bootprt.prt_ns) {
|
|
printf("EFI System Area < %llu sectors\n",
|
|
disk.dk_bootprt.prt_ns);
|
|
rslt = -1;
|
|
}
|
|
}
|
|
}
|
|
if (rslt == 0)
|
|
rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
|
|
|
|
if (rslt != 0)
|
|
memcpy(&gp, &oldgp, sizeof(gp));
|
|
|
|
return rslt;
|
|
}
|
|
|
|
int
|
|
GPT_init(const int how)
|
|
{
|
|
int rslt = 0;
|
|
|
|
if (how == GHANDGP)
|
|
rslt = init_gh();
|
|
if (rslt == 0)
|
|
rslt = init_gp(how);
|
|
|
|
return rslt;
|
|
}
|
|
|
|
void
|
|
GPT_zap_headers(void)
|
|
{
|
|
struct gpt_header legh;
|
|
|
|
if (DISK_readbytes(&legh, GPTSECTOR, sizeof(legh)))
|
|
return;
|
|
|
|
if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
|
|
memset(&legh, 0, sizeof(legh));
|
|
if (DISK_writebytes(&legh, GPTSECTOR, sizeof(legh)))
|
|
DPRINTF("Unable to zap GPT header @ sector %d",
|
|
GPTSECTOR);
|
|
}
|
|
|
|
if (DISK_readbytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
|
|
return;
|
|
|
|
if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
|
|
memset(&legh, 0, GPTMINHDRSIZE);
|
|
if (DISK_writebytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
|
|
DPRINTF("Unable to zap GPT header @ sector %llu",
|
|
DL_GETDSIZE(&dl) - 1);
|
|
}
|
|
}
|
|
|
|
int
|
|
GPT_write(void)
|
|
{
|
|
struct gpt_header legh;
|
|
struct gpt_partition *legp;
|
|
uint64_t altgh, altgp;
|
|
uint64_t gpbytes, gpsectors;
|
|
unsigned int pn;
|
|
int rslt = -1;
|
|
|
|
if (MBR_write(&gmbr))
|
|
return -1;
|
|
|
|
gpbytes = gh.gh_part_num * gh.gh_part_size;
|
|
gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
|
|
|
|
altgh = DL_GETDSIZE(&dl) - 1;
|
|
altgp = altgh - gpsectors;
|
|
|
|
legh.gh_sig = htole64(GPTSIGNATURE);
|
|
legh.gh_rev = htole32(GPTREVISION);
|
|
legh.gh_size = htole32(GPTMINHDRSIZE);
|
|
legh.gh_rsvd = 0;
|
|
legh.gh_lba_self = htole64(GPTSECTOR);
|
|
legh.gh_lba_alt = htole64(altgh);
|
|
legh.gh_lba_start = htole64(gh.gh_lba_start);
|
|
legh.gh_lba_end = htole64(gh.gh_lba_end);
|
|
uuid_enc_le(&legh.gh_guid, &gh.gh_guid);
|
|
legh.gh_part_lba = htole64(GPTSECTOR + 1);
|
|
legh.gh_part_num = htole32(gh.gh_part_num);
|
|
legh.gh_part_size = htole32(GPTMINPARTSIZE);
|
|
|
|
legp = calloc(1, gpbytes);
|
|
if (legp == NULL)
|
|
err(1, "legp");
|
|
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
uuid_enc_le(&legp[pn].gp_type, &gp[pn].gp_type);
|
|
uuid_enc_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
|
|
legp[pn].gp_lba_start = htole64(gp[pn].gp_lba_start);
|
|
legp[pn].gp_lba_end = htole64(gp[pn].gp_lba_end);
|
|
legp[pn].gp_attrs = htole64(gp[pn].gp_attrs);
|
|
memcpy(legp[pn].gp_name, gp[pn].gp_name,
|
|
sizeof(legp[pn].gp_name));
|
|
}
|
|
legh.gh_part_csum = htole32(crc32((unsigned char *)legp, gpbytes));
|
|
legh.gh_csum = 0;
|
|
legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
|
|
|
|
if (DISK_writebytes(&legh, GPTSECTOR, gh.gh_size) ||
|
|
DISK_writebytes(legp, GPTSECTOR + 1, gpbytes))
|
|
goto done;
|
|
|
|
legh.gh_lba_self = htole64(altgh);
|
|
legh.gh_lba_alt = htole64(GPTSECTOR);
|
|
legh.gh_part_lba = htole64(altgp);
|
|
legh.gh_csum = 0;
|
|
legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
|
|
|
|
if (DISK_writebytes(&legh, altgh, gh.gh_size) ||
|
|
DISK_writebytes(&gp, altgp, gpbytes))
|
|
goto done;
|
|
|
|
/* Refresh in-kernel disklabel from the updated disk information. */
|
|
if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
|
|
warn("DIOCRLDINFO");
|
|
rslt = 0;
|
|
|
|
done:
|
|
free(legp);
|
|
return rslt;
|
|
}
|
|
|
|
int
|
|
gp_lba_start_cmp(const void *e1, const void *e2)
|
|
{
|
|
struct gpt_partition *p1 = *(struct gpt_partition **)e1;
|
|
struct gpt_partition *p2 = *(struct gpt_partition **)e2;
|
|
uint64_t o1;
|
|
uint64_t o2;
|
|
|
|
o1 = p1->gp_lba_start;
|
|
o2 = p2->gp_lba_start;
|
|
|
|
if (o1 < o2)
|
|
return -1;
|
|
else if (o1 > o2)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
const struct gpt_partition * const *
|
|
sort_gpt(void)
|
|
{
|
|
static const struct gpt_partition *sgp[NGPTPARTITIONS+2];
|
|
unsigned int i, pn;
|
|
|
|
memset(sgp, 0, sizeof(sgp));
|
|
|
|
i = 0;
|
|
for (pn = 0; pn < gh.gh_part_num; pn++) {
|
|
if (gp[pn].gp_lba_start >= gh.gh_lba_start)
|
|
sgp[i++] = &gp[pn];
|
|
}
|
|
|
|
if (i > 1) {
|
|
if (mergesort(sgp, i, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
|
|
printf("unable to sort gpt by lba start\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return sgp;
|
|
}
|
|
|
|
int
|
|
lba_free(uint64_t *start, uint64_t *end)
|
|
{
|
|
const struct gpt_partition * const *sgp;
|
|
uint64_t bs, bigbs, nextbs, ns;
|
|
unsigned int i;
|
|
|
|
sgp = sort_gpt();
|
|
if (sgp == NULL)
|
|
return -1;
|
|
|
|
bs = gh.gh_lba_start;
|
|
ns = gh.gh_lba_end - bs + 1;
|
|
|
|
if (sgp[0] != NULL) {
|
|
bigbs = bs;
|
|
ns = 0;
|
|
for (i = 0; sgp[i] != NULL; i++) {
|
|
nextbs = sgp[i]->gp_lba_start;
|
|
if (bs < nextbs && ns < nextbs - bs) {
|
|
ns = nextbs - bs;
|
|
bigbs = bs;
|
|
}
|
|
bs = sgp[i]->gp_lba_end + 1;
|
|
}
|
|
nextbs = gh.gh_lba_end + 1;
|
|
if (bs < nextbs && ns < nextbs - bs) {
|
|
ns = nextbs - bs;
|
|
bigbs = bs;
|
|
}
|
|
bs = bigbs;
|
|
}
|
|
|
|
if (ns == 0)
|
|
return -1;
|
|
|
|
if (start != NULL)
|
|
*start = bs;
|
|
if (end != NULL)
|
|
*end = bs + ns - 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
GPT_get_lba_start(const unsigned int pn)
|
|
{
|
|
uint64_t bs;
|
|
unsigned int i;
|
|
int rslt;
|
|
|
|
bs = gh.gh_lba_start;
|
|
|
|
if (gp[pn].gp_lba_start >= bs) {
|
|
bs = gp[pn].gp_lba_start;
|
|
} else {
|
|
rslt = lba_free(&bs, NULL);
|
|
if (rslt == -1) {
|
|
printf("no space for partition %u\n", pn);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end);
|
|
for (i = 0; i < gh.gh_part_num; i++) {
|
|
if (i == pn)
|
|
continue;
|
|
if (bs >= gp[i].gp_lba_start && bs <= gp[i].gp_lba_end) {
|
|
printf("partition %u can't start inside partition %u\n",
|
|
pn, i);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
gp[pn].gp_lba_start = bs;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
GPT_get_lba_end(const unsigned int pn)
|
|
{
|
|
const struct gpt_partition * const *sgp;
|
|
uint64_t bs, nextbs, ns;
|
|
unsigned int i;
|
|
|
|
sgp = sort_gpt();
|
|
if (sgp == NULL)
|
|
return -1;
|
|
|
|
bs = gp[pn].gp_lba_start;
|
|
ns = gh.gh_lba_end - bs + 1;
|
|
for (i = 0; sgp[i] != NULL; i++) {
|
|
nextbs = sgp[i]->gp_lba_start;
|
|
if (nextbs > bs) {
|
|
ns = nextbs - bs;
|
|
break;
|
|
}
|
|
}
|
|
ns = getuint64("Partition size", ns, 1, ns);
|
|
|
|
gp[pn].gp_lba_end = bs + ns - 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
GPT_get_name(const unsigned int pn)
|
|
{
|
|
char name[GPTPARTNAMESIZE + 1];
|
|
|
|
printf("Partition name: [%s] ", name_to_string(pn));
|
|
string_from_line(name, sizeof(name), UNTRIMMED);
|
|
|
|
switch (strlen(name)) {
|
|
case 0:
|
|
break;
|
|
case GPTPARTNAMESIZE:
|
|
printf("partition name must be < %d characters\n",
|
|
GPTPARTNAMESIZE);
|
|
return -1;
|
|
default:
|
|
string_to_name(pn, name);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Adapted from Hacker's Delight crc32b().
|
|
*
|
|
* To quote http://www.hackersdelight.org/permissions.htm :
|
|
*
|
|
* "You are free to use, copy, and distribute any of the code on
|
|
* this web site, whether modified by you or not. You need not give
|
|
* attribution. This includes the algorithms (some of which appear
|
|
* in Hacker's Delight), the Hacker's Assistant, and any code submitted
|
|
* by readers. Submitters implicitly agree to this."
|
|
*/
|
|
uint32_t
|
|
crc32(const u_char *buf, const uint32_t size)
|
|
{
|
|
int j;
|
|
uint32_t i, byte, crc, mask;
|
|
|
|
crc = 0xFFFFFFFF;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
byte = buf[i]; /* Get next byte. */
|
|
crc = crc ^ byte;
|
|
for (j = 7; j >= 0; j--) { /* Do eight times. */
|
|
mask = -(crc & 1);
|
|
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
|
}
|
|
}
|
|
|
|
return ~crc;
|
|
}
|