version 1.28, 2014/08/10 18:27:15 |
version 1.28.2.2, 2018/08/13 16:12:12 |
|
|
* CRC32 code derived from work by Gary S. Brown. |
* CRC32 code derived from work by Gary S. Brown. |
*/ |
*/ |
|
|
|
#if HAVE_NBTOOL_CONFIG_H |
|
#include "nbtool_config.h" |
|
#endif |
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
#ifdef __FBSDID |
#ifdef __FBSDID |
__FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $"); |
__FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $"); |
Line 36 __RCSID("$NetBSD$"); |
|
Line 40 __RCSID("$NetBSD$"); |
|
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/disk.h> |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
#include <sys/bootblock.h> |
#include <sys/bootblock.h> |
Line 46 __RCSID("$NetBSD$"); |
|
Line 49 __RCSID("$NetBSD$"); |
|
#include <fcntl.h> |
#include <fcntl.h> |
#include <paths.h> |
#include <paths.h> |
#include <stddef.h> |
#include <stddef.h> |
|
#include <stdarg.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <util.h> |
|
#include <ctype.h> |
#include <ctype.h> |
#include <prop/proplib.h> |
|
#include <sys/drvctlio.h> |
|
|
|
#include "map.h" |
#include "map.h" |
#include "gpt.h" |
#include "gpt.h" |
|
#include "gpt_private.h" |
char device_path[MAXPATHLEN]; |
|
const char *device_arg; |
|
char *device_name; |
|
|
|
off_t mediasz; |
|
|
|
u_int parts; |
|
u_int secsz; |
|
|
|
int readonly, verbose; |
|
|
|
static uint32_t crc32_tab[] = { |
static uint32_t crc32_tab[] = { |
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, |
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, |
Line 130 crc32(const void *buf, size_t size) |
|
Line 121 crc32(const void *buf, size_t size) |
|
return crc ^ ~0U; |
return crc ^ ~0U; |
} |
} |
|
|
uint8_t * |
/* |
utf16_to_utf8(uint16_t *s16) |
* Produce a NUL-terminated utf-8 string from the non-NUL-terminated |
|
* utf16 string. |
|
*/ |
|
void |
|
utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len) |
{ |
{ |
static uint8_t *s8 = NULL; |
size_t s8idx, s16idx; |
static size_t s8len = 0; |
|
size_t s8idx, s16idx, s16len; |
|
uint32_t utfchar; |
uint32_t utfchar; |
unsigned int c; |
unsigned int c; |
|
|
s16len = 0; |
for (s16idx = 0; s16idx < s16len; s16idx++) |
while (s16[s16len++] != 0) |
if (s16[s16idx] == 0) |
; |
break; |
if (s8len < s16len * 3) { |
|
if (s8 != NULL) |
s16len = s16idx; |
free(s8); |
|
s8len = s16len * 3; |
|
s8 = calloc(s16len, 3); |
|
} |
|
s8idx = s16idx = 0; |
s8idx = s16idx = 0; |
while (s16idx < s16len) { |
while (s16idx < s16len) { |
utfchar = le16toh(s16[s16idx++]); |
utfchar = le16toh(s16[s16idx++]); |
Line 159 utf16_to_utf8(uint16_t *s16) |
|
Line 148 utf16_to_utf8(uint16_t *s16) |
|
s16idx++; |
s16idx++; |
} |
} |
if (utfchar < 0x80) { |
if (utfchar < 0x80) { |
s8[s8idx++] = utfchar; |
if (s8idx + 1 >= s8len) |
|
break; |
|
s8[s8idx++] = (uint8_t)utfchar; |
} else if (utfchar < 0x800) { |
} else if (utfchar < 0x800) { |
s8[s8idx++] = 0xc0 | (utfchar >> 6); |
if (s8idx + 2 >= s8len) |
s8[s8idx++] = 0x80 | (utfchar & 0x3f); |
break; |
|
s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6)); |
|
s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f)); |
} else if (utfchar < 0x10000) { |
} else if (utfchar < 0x10000) { |
s8[s8idx++] = 0xe0 | (utfchar >> 12); |
if (s8idx + 3 >= s8len) |
s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f); |
break; |
s8[s8idx++] = 0x80 | (utfchar & 0x3f); |
s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12)); |
|
s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f)); |
|
s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f)); |
} else if (utfchar < 0x200000) { |
} else if (utfchar < 0x200000) { |
s8[s8idx++] = 0xf0 | (utfchar >> 18); |
if (s8idx + 4 >= s8len) |
s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f); |
break; |
s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f); |
s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18)); |
s8[s8idx++] = 0x80 | (utfchar & 0x3f); |
s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f)); |
|
s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f)); |
|
s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f)); |
} |
} |
} |
} |
return (s8); |
s8[s8idx] = 0; |
} |
} |
|
|
|
/* |
|
* Produce a non-NUL-terminated utf-16 string from the NUL-terminated |
|
* utf8 string. |
|
*/ |
void |
void |
utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) |
utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) |
{ |
{ |
Line 220 utf8_to_utf16(const uint8_t *s8, uint16_ |
|
Line 221 utf8_to_utf16(const uint8_t *s8, uint16_ |
|
utfchar = (utfchar << 6) + (c & 0x3f); |
utfchar = (utfchar << 6) + (c & 0x3f); |
utfbytes--; |
utfbytes--; |
} else if (utfbytes == 0) |
} else if (utfbytes == 0) |
utfbytes = -1; |
utfbytes = (u_int)~0; |
} |
} |
if (utfbytes == 0) { |
if (utfbytes == 0) { |
if (utfchar >= 0x10000 && s16idx + 2 >= s16len) |
if (utfchar >= 0x10000 && s16idx + 2 >= s16len) |
utfchar = 0xfffd; |
utfchar = 0xfffd; |
if (utfchar >= 0x10000) { |
if (utfchar >= 0x10000) { |
s16[s16idx++] = |
s16[s16idx++] = htole16((uint16_t) |
htole16(0xd800 | ((utfchar>>10)-0x40)); |
(0xd800 | ((utfchar>>10) - 0x40))); |
s16[s16idx++] = |
s16[s16idx++] = htole16((uint16_t) |
htole16(0xdc00 | (utfchar & 0x3ff)); |
(0xdc00 | (utfchar & 0x3ff))); |
} else |
} else |
s16[s16idx++] = htole16(utfchar); |
s16[s16idx++] = htole16((uint16_t)utfchar); |
if (s16idx == s16len) { |
if (s16idx == s16len) { |
s16[--s16idx] = 0; |
|
return; |
return; |
} |
} |
} |
} |
} while (c != 0); |
} while (c != 0); |
} |
|
|
|
int |
|
parse_uuid(const char *s, uuid_t *uuid) |
|
{ |
|
uint32_t status; |
|
|
|
uuid_from_string(s, uuid, &status); |
while (s16idx < s16len) |
if (status == uuid_s_ok) |
s16[s16idx++] = 0; |
return (0); |
|
|
|
switch (*s) { |
|
case 'b': |
|
if (strcmp(s, "bios") == 0) { |
|
static const uuid_t bios = GPT_ENT_TYPE_BIOS; |
|
*uuid = bios; |
|
return (0); |
|
} |
|
break; |
|
case 'c': |
|
if (strcmp(s, "ccd") == 0) { |
|
static const uuid_t ccd = GPT_ENT_TYPE_NETBSD_CCD; |
|
*uuid = ccd; |
|
return (0); |
|
} else if (strcmp(s, "cgd") == 0) { |
|
static const uuid_t cgd = GPT_ENT_TYPE_NETBSD_CGD; |
|
*uuid = cgd; |
|
return (0); |
|
} |
|
break; |
|
case 'e': |
|
if (strcmp(s, "efi") == 0) { |
|
static const uuid_t efi = GPT_ENT_TYPE_EFI; |
|
*uuid = efi; |
|
return (0); |
|
} |
|
break; |
|
case 'f': |
|
if (strcmp(s, "ffs") == 0) { |
|
static const uuid_t nb_ffs = GPT_ENT_TYPE_NETBSD_FFS; |
|
*uuid = nb_ffs; |
|
return (0); |
|
} |
|
break; |
|
case 'h': |
|
if (strcmp(s, "hfs") == 0) { |
|
static const uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS; |
|
*uuid = hfs; |
|
return (0); |
|
} |
|
break; |
|
case 'l': |
|
if (strcmp(s, "lfs") == 0) { |
|
static const uuid_t lfs = GPT_ENT_TYPE_NETBSD_LFS; |
|
*uuid = lfs; |
|
return (0); |
|
} else if (strcmp(s, "linux") == 0) { |
|
static const uuid_t lnx = GPT_ENT_TYPE_LINUX_DATA; |
|
*uuid = lnx; |
|
return (0); |
|
} |
|
break; |
|
case 'r': |
|
if (strcmp(s, "raid") == 0) { |
|
static const uuid_t raid = GPT_ENT_TYPE_NETBSD_RAIDFRAME; |
|
*uuid = raid; |
|
return (0); |
|
} |
|
break; |
|
case 's': |
|
if (strcmp(s, "swap") == 0) { |
|
static const uuid_t sw = GPT_ENT_TYPE_NETBSD_SWAP; |
|
*uuid = sw; |
|
return (0); |
|
} |
|
break; |
|
case 'u': |
|
if (strcmp(s, "ufs") == 0) { |
|
static const uuid_t ufs = GPT_ENT_TYPE_NETBSD_FFS; |
|
*uuid = ufs; |
|
return (0); |
|
} |
|
break; |
|
case 'w': |
|
if (strcmp(s, "windows") == 0) { |
|
static const uuid_t win = GPT_ENT_TYPE_MS_BASIC_DATA; |
|
*uuid = win; |
|
return (0); |
|
} |
|
break; |
|
} |
|
return (EINVAL); |
|
} |
} |
|
|
void* |
void * |
gpt_read(int fd, off_t lba, size_t count) |
gpt_read(gpt_t gpt, off_t lba, size_t count) |
{ |
{ |
off_t ofs; |
off_t ofs; |
void *buf; |
void *buf; |
|
|
count *= secsz; |
count *= gpt->secsz; |
buf = malloc(count); |
buf = malloc(count); |
if (buf == NULL) |
if (buf == NULL) |
return (NULL); |
return NULL; |
|
|
ofs = lba * secsz; |
ofs = lba * gpt->secsz; |
if (lseek(fd, ofs, SEEK_SET) == ofs && |
if (lseek(gpt->fd, ofs, SEEK_SET) == ofs && |
read(fd, buf, count) == (ssize_t)count) |
read(gpt->fd, buf, count) == (ssize_t)count) |
return (buf); |
return buf; |
|
|
free(buf); |
free(buf); |
return (NULL); |
return NULL; |
} |
} |
|
|
int |
int |
gpt_write(int fd, map_t *map) |
gpt_write(gpt_t gpt, map_t map) |
{ |
{ |
off_t ofs; |
off_t ofs; |
size_t count; |
size_t count; |
|
|
count = map->map_size * secsz; |
count = (size_t)(map->map_size * gpt->secsz); |
ofs = map->map_start * secsz; |
ofs = map->map_start * gpt->secsz; |
if (lseek(fd, ofs, SEEK_SET) == ofs && |
if (lseek(gpt->fd, ofs, SEEK_SET) != ofs || |
write(fd, map->map_data, count) == (ssize_t)count) |
write(gpt->fd, map->map_data, count) != (ssize_t)count) |
return (0); |
return -1; |
return (-1); |
gpt->flags |= GPT_MODIFIED; |
|
return 0; |
} |
} |
|
|
static int |
static int |
gpt_mbr(int fd, off_t lba) |
gpt_mbr(gpt_t gpt, off_t lba) |
{ |
{ |
struct mbr *mbr; |
struct mbr *mbr; |
map_t *m, *p; |
map_t m, p; |
off_t size, start; |
off_t size, start; |
unsigned int i, pmbr; |
unsigned int i, pmbr; |
|
|
mbr = gpt_read(fd, lba, 1); |
mbr = gpt_read(gpt, lba, 1); |
if (mbr == NULL) |
if (mbr == NULL) { |
return (-1); |
gpt_warn(gpt, "Read failed"); |
|
return -1; |
|
} |
|
|
if (mbr->mbr_sig != htole16(MBR_SIG)) { |
if (mbr->mbr_sig != htole16(MBR_SIG)) { |
if (verbose) |
if (gpt->verbose) |
warnx("%s: MBR not found at sector %llu", device_name, |
gpt_msg(gpt, |
(long long)lba); |
"MBR not found at sector %ju", (uintmax_t)lba); |
free(mbr); |
free(mbr); |
return (0); |
return 0; |
} |
} |
|
|
/* |
/* |
Line 402 gpt_mbr(int fd, off_t lba) |
|
Line 316 gpt_mbr(int fd, off_t lba) |
|
} |
} |
if (pmbr && i == 4 && lba == 0) { |
if (pmbr && i == 4 && lba == 0) { |
if (pmbr != 1) |
if (pmbr != 1) |
warnx("%s: Suspicious PMBR at sector %llu", |
gpt_warnx(gpt, "Suspicious PMBR at sector %ju", |
device_name, (long long)lba); |
(uintmax_t)lba); |
else if (verbose > 1) |
else if (gpt->verbose > 1) |
warnx("%s: PMBR at sector %llu", device_name, |
gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba); |
(long long)lba); |
p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1); |
p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr); |
goto out; |
return ((p == NULL) ? -1 : 0); |
|
} |
} |
if (pmbr) |
if (pmbr) |
warnx("%s: Suspicious MBR at sector %llu", device_name, |
gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba); |
(long long)lba); |
else if (gpt->verbose > 1) |
else if (verbose > 1) |
gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba); |
warnx("%s: MBR at sector %llu", device_name, (long long)lba); |
|
|
|
p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr); |
p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1); |
if (p == NULL) |
if (p == NULL) |
return (-1); |
goto out; |
|
|
for (i = 0; i < 4; i++) { |
for (i = 0; i < 4; i++) { |
if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED || |
if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED || |
mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR) |
mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR) |
Line 428 gpt_mbr(int fd, off_t lba) |
|
Line 341 gpt_mbr(int fd, off_t lba) |
|
size = le16toh(mbr->mbr_part[i].part_size_hi); |
size = le16toh(mbr->mbr_part[i].part_size_hi); |
size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); |
size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); |
if (start == 0 && size == 0) { |
if (start == 0 && size == 0) { |
warnx("%s: Malformed MBR at sector %llu", device_name, |
gpt_warnx(gpt, "Malformed MBR at sector %ju", |
(long long)lba); |
(uintmax_t)lba); |
continue; |
continue; |
} |
} |
/* start is relative to the offset of the MBR itself. */ |
/* start is relative to the offset of the MBR itself. */ |
start += lba; |
start += lba; |
if (verbose > 2) |
if (gpt->verbose > 2) |
warnx("%s: MBR part: type=%d, start=%llu, size=%llu", |
gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, " |
device_name, mbr->mbr_part[i].part_typ, |
"size=%ju", mbr->mbr_part[i].part_flag, |
(long long)start, (long long)size); |
mbr->mbr_part[i].part_typ, |
|
(uintmax_t)start, (uintmax_t)size); |
if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) { |
if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) { |
m = map_add(start, size, MAP_TYPE_MBR_PART, p); |
m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0); |
if (m == NULL) |
if (m == NULL) |
return (-1); |
return -1; |
m->map_index = i + 1; |
m->map_index = i + 1; |
} else { |
} else { |
if (gpt_mbr(fd, start) == -1) |
if (gpt_mbr(gpt, start) == -1) |
return (-1); |
return -1; |
} |
} |
} |
} |
return (0); |
return 0; |
} |
out: |
|
if (p == NULL) { |
static int |
free(mbr); |
drvctl(const char *name, u_int *sector_size, off_t *media_size) |
|
{ |
|
prop_dictionary_t command_dict, args_dict, results_dict, data_dict, |
|
disk_info, geometry; |
|
prop_string_t string; |
|
prop_number_t number; |
|
int dfd, res; |
|
char *dname, *p; |
|
|
|
if ((dfd = open("/dev/drvctl", O_RDONLY)) == -1) { |
|
warn("%s: /dev/drvctl", __func__); |
|
return -1; |
|
} |
|
|
|
command_dict = prop_dictionary_create(); |
|
args_dict = prop_dictionary_create(); |
|
|
|
string = prop_string_create_cstring_nocopy("get-properties"); |
|
prop_dictionary_set(command_dict, "drvctl-command", string); |
|
prop_object_release(string); |
|
|
|
if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) { |
|
(void)close(dfd); |
|
return -1; |
|
} |
|
for (p = dname; *p; p++) |
|
continue; |
|
for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0') |
|
continue; |
|
|
|
string = prop_string_create_cstring(dname); |
|
free(dname); |
|
prop_dictionary_set(args_dict, "device-name", string); |
|
prop_object_release(string); |
|
|
|
prop_dictionary_set(command_dict, "drvctl-arguments", args_dict); |
|
prop_object_release(args_dict); |
|
|
|
res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND, |
|
&results_dict); |
|
(void)close(dfd); |
|
prop_object_release(command_dict); |
|
if (res) { |
|
warn("%s: prop_dictionary_sendrecv_ioctl", __func__); |
|
errno = res; |
|
return -1; |
return -1; |
} |
} |
|
|
number = prop_dictionary_get(results_dict, "drvctl-error"); |
|
if ((errno = prop_number_integer_value(number)) != 0) |
|
return -1; |
|
|
|
data_dict = prop_dictionary_get(results_dict, "drvctl-result-data"); |
|
if (data_dict == NULL) |
|
goto out; |
|
|
|
disk_info = prop_dictionary_get(data_dict, "disk-info"); |
|
if (disk_info == NULL) |
|
goto out; |
|
|
|
geometry = prop_dictionary_get(disk_info, "geometry"); |
|
if (geometry == NULL) |
|
goto out; |
|
|
|
number = prop_dictionary_get(geometry, "sector-size"); |
|
if (number == NULL) |
|
goto out; |
|
|
|
*sector_size = prop_number_integer_value(number); |
|
|
|
number = prop_dictionary_get(geometry, "sectors-per-unit"); |
|
if (number == NULL) |
|
goto out; |
|
|
|
*media_size = prop_number_integer_value(number) * *sector_size; |
|
|
|
return 0; |
return 0; |
out: |
|
errno = EINVAL; |
|
return -1; |
|
} |
} |
|
|
static int |
int |
gpt_gpt(int fd, off_t lba, int found) |
gpt_gpt(gpt_t gpt, off_t lba, int found) |
{ |
{ |
uuid_t type; |
|
off_t size; |
off_t size; |
struct gpt_ent *ent; |
struct gpt_ent *ent; |
struct gpt_hdr *hdr; |
struct gpt_hdr *hdr; |
char *p, *s; |
char *p; |
map_t *m; |
map_t m; |
size_t blocks, tblsz; |
size_t blocks, tblsz; |
unsigned int i; |
unsigned int i; |
uint32_t crc; |
uint32_t crc; |
|
|
hdr = gpt_read(fd, lba, 1); |
hdr = gpt_read(gpt, lba, 1); |
if (hdr == NULL) |
if (hdr == NULL) |
return (-1); |
return -1; |
|
|
if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig))) |
if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig))) |
goto fail_hdr; |
goto fail_hdr; |
Line 557 gpt_gpt(int fd, off_t lba, int found) |
|
Line 393 gpt_gpt(int fd, off_t lba, int found) |
|
crc = le32toh(hdr->hdr_crc_self); |
crc = le32toh(hdr->hdr_crc_self); |
hdr->hdr_crc_self = 0; |
hdr->hdr_crc_self = 0; |
if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) { |
if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) { |
if (verbose) |
if (gpt->verbose) |
warnx("%s: Bad CRC in GPT header at sector %llu", |
gpt_msg(gpt, "Bad CRC in GPT header at sector %ju", |
device_name, (long long)lba); |
(uintmax_t)lba); |
goto fail_hdr; |
goto fail_hdr; |
} |
} |
|
|
tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz); |
tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz); |
blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0); |
blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0); |
|
|
/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */ |
/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */ |
p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks); |
p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks); |
if (p == NULL) { |
if (p == NULL) { |
if (found) { |
if (found) { |
if (verbose) |
if (gpt->verbose) |
warn("%s: Cannot read LBA table at sector %llu", |
gpt_msg(gpt, |
device_name, (unsigned long long) |
"Cannot read LBA table at sector %ju", |
le64toh(hdr->hdr_lba_table)); |
(uintmax_t)le64toh(hdr->hdr_lba_table)); |
return (-1); |
return -1; |
} |
} |
goto fail_hdr; |
goto fail_hdr; |
} |
} |
|
|
if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) { |
if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) { |
if (verbose) |
if (gpt->verbose) |
warnx("%s: Bad CRC in GPT table at sector %llu", |
gpt_msg(gpt, "Bad CRC in GPT table at sector %ju", |
device_name, |
(uintmax_t)le64toh(hdr->hdr_lba_table)); |
(long long)le64toh(hdr->hdr_lba_table)); |
|
goto fail_ent; |
goto fail_ent; |
} |
} |
|
|
if (verbose > 1) |
if (gpt->verbose > 1) |
warnx("%s: %s GPT at sector %llu", device_name, |
gpt_msg(gpt, "%s GPT at sector %ju", |
(lba == 1) ? "Pri" : "Sec", (long long)lba); |
(lba == 1) ? "Pri" : "Sec", (uintmax_t)lba); |
|
|
m = map_add(lba, 1, (lba == 1) |
m = map_add(gpt, lba, 1, (lba == 1) |
? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr); |
? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1); |
if (m == NULL) |
if (m == NULL) |
return (-1); |
return (-1); |
|
|
m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1) |
m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), |
? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p); |
(off_t)blocks, |
|
lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1); |
if (m == NULL) |
if (m == NULL) |
return (-1); |
return (-1); |
|
|
Line 606 gpt_gpt(int fd, off_t lba, int found) |
|
Line 442 gpt_gpt(int fd, off_t lba, int found) |
|
|
|
for (i = 0; i < le32toh(hdr->hdr_entries); i++) { |
for (i = 0; i < le32toh(hdr->hdr_entries); i++) { |
ent = (void*)(p + i * le32toh(hdr->hdr_entsz)); |
ent = (void*)(p + i * le32toh(hdr->hdr_entsz)); |
if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL)) |
if (gpt_uuid_is_nil(ent->ent_type)) |
continue; |
continue; |
|
|
size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) + |
size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) - |
1LL; |
le64toh((uint64_t)ent->ent_lba_start) + 1LL); |
if (verbose > 2) { |
if (gpt->verbose > 2) { |
le_uuid_dec(&ent->ent_type, &type); |
char buf[128]; |
uuid_to_string(&type, &s, NULL); |
gpt_uuid_snprintf(buf, sizeof(buf), "%s", |
warnx( |
ent->ent_type); |
"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s, |
gpt_msg(gpt, "GPT partition: type=%s, start=%ju, " |
(long long)le64toh(ent->ent_lba_start), |
"size=%ju", buf, |
(long long)size); |
(uintmax_t)le64toh(ent->ent_lba_start), |
free(s); |
(uintmax_t)size); |
} |
} |
m = map_add(le64toh(ent->ent_lba_start), size, |
m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start), |
MAP_TYPE_GPT_PART, ent); |
size, MAP_TYPE_GPT_PART, ent, 0); |
if (m == NULL) |
if (m == NULL) |
return (-1); |
return (-1); |
m->map_index = i + 1; |
m->map_index = i + 1; |
Line 636 gpt_gpt(int fd, off_t lba, int found) |
|
Line 472 gpt_gpt(int fd, off_t lba, int found) |
|
return (0); |
return (0); |
} |
} |
|
|
int |
gpt_t |
gpt_open(const char *dev) |
gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz, |
|
time_t timestamp) |
{ |
{ |
struct stat sb; |
int mode, found; |
int fd, mode, found; |
off_t devsz; |
|
gpt_t gpt; |
|
|
mode = readonly ? O_RDONLY : O_RDWR|O_EXCL; |
|
|
|
device_arg = dev; |
if ((gpt = calloc(1, sizeof(*gpt))) == NULL) { |
fd = opendisk(dev, mode, device_path, sizeof(device_path), 0); |
if (!(flags & GPT_QUIET)) |
if (fd == -1) |
warn("Cannot allocate `%s'", dev); |
return -1; |
return NULL; |
if (strncmp(device_path, _PATH_DEV, strlen(_PATH_DEV)) == 0) |
} |
device_name = device_path + strlen(_PATH_DEV); |
gpt->flags = flags; |
else |
gpt->verbose = verbose; |
device_name = device_path; |
gpt->mediasz = mediasz; |
|
gpt->secsz = secsz; |
|
gpt->timestamp = timestamp; |
|
|
|
mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL; |
|
|
|
gpt->fd = opendisk(dev, mode, gpt->device_name, |
|
sizeof(gpt->device_name), 0); |
|
if (gpt->fd == -1) { |
|
strlcpy(gpt->device_name, dev, sizeof(gpt->device_name)); |
|
gpt_warn(gpt, "Cannot open"); |
|
goto close; |
|
} |
|
|
if (fstat(fd, &sb) == -1) |
if (fstat(gpt->fd, &gpt->sb) == -1) { |
|
gpt_warn(gpt, "Cannot stat"); |
goto close; |
goto close; |
|
} |
|
|
if ((sb.st_mode & S_IFMT) != S_IFREG) { |
if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) { |
|
if (gpt->secsz == 0) { |
#ifdef DIOCGSECTORSIZE |
#ifdef DIOCGSECTORSIZE |
if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 || |
if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) { |
ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1) |
gpt_warn(gpt, "Cannot get sector size"); |
goto close; |
goto close; |
|
} |
|
#endif |
|
if (gpt->secsz == 0) { |
|
gpt_warnx(gpt, "Sector size can't be 0"); |
|
goto close; |
|
} |
|
} |
|
if (gpt->mediasz == 0) { |
|
#ifdef DIOCGMEDIASIZE |
|
if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) { |
|
gpt_warn(gpt, "Cannot get media size"); |
|
goto close; |
|
} |
#endif |
#endif |
if (drvctl(device_name, &secsz, &mediasz) == -1) |
if (gpt->mediasz == 0) { |
goto close; |
gpt_warnx(gpt, "Media size can't be 0"); |
|
goto close; |
|
} |
|
} |
} else { |
} else { |
secsz = 512; /* Fixed size for files. */ |
gpt->flags |= GPT_FILE; |
if (sb.st_size % secsz) { |
if (gpt->secsz == 0) |
errno = EINVAL; |
gpt->secsz = 512; /* Fixed size for files. */ |
goto close; |
if (gpt->mediasz == 0) { |
|
if (gpt->sb.st_size % gpt->secsz) { |
|
errno = EINVAL; |
|
goto close; |
|
} |
|
gpt->mediasz = gpt->sb.st_size; |
} |
} |
mediasz = sb.st_size; |
gpt->flags |= GPT_NOSYNC; |
} |
} |
|
|
/* |
/* |
Line 679 gpt_open(const char *dev) |
|
Line 552 gpt_open(const char *dev) |
|
* user data. Let's catch this extreme border case here so that |
* user data. Let's catch this extreme border case here so that |
* we don't have to worry about it later. |
* we don't have to worry about it later. |
*/ |
*/ |
if (mediasz / secsz < 6) { |
devsz = gpt->mediasz / gpt->secsz; |
errno = ENODEV; |
if (devsz < 6) { |
|
gpt_warnx(gpt, "Need 6 sectors, we have %ju", |
|
(uintmax_t)devsz); |
goto close; |
goto close; |
} |
} |
|
|
if (verbose) |
if (gpt->verbose) { |
warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu", |
gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju", |
device_name, (long long)mediasz, secsz, |
(uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz); |
(long long)(mediasz / secsz)); |
} |
|
|
map_init(mediasz / secsz); |
if (map_init(gpt, devsz) == -1) |
|
goto close; |
|
|
if (gpt_mbr(fd, 0LL) == -1) |
if (gpt_mbr(gpt, 0LL) == -1) |
goto close; |
goto close; |
if ((found = gpt_gpt(fd, 1LL, 1)) == -1) |
if ((found = gpt_gpt(gpt, 1LL, 1)) == -1) |
goto close; |
goto close; |
if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1) |
if (gpt_gpt(gpt, devsz - 1LL, found) == -1) |
goto close; |
goto close; |
|
|
return (fd); |
return gpt; |
|
|
close: |
close: |
close(fd); |
if (gpt->fd != -1) |
return (-1); |
close(gpt->fd); |
|
free(gpt); |
|
return NULL; |
} |
} |
|
|
void |
void |
gpt_close(int fd) |
gpt_close(gpt_t gpt) |
{ |
{ |
/* XXX post processing? */ |
|
close(fd); |
if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC)) |
|
goto out; |
|
|
|
if (!(gpt->flags & GPT_NOSYNC)) { |
|
#ifdef DIOCMWEDGES |
|
int bits; |
|
if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1) |
|
gpt_warn(gpt, "Can't update wedge information"); |
|
else |
|
goto out; |
|
#endif |
|
} |
|
if (!(gpt->flags & GPT_FILE)) |
|
gpt_msg(gpt, "You need to run \"dkctl %s makewedges\"" |
|
" for the changes to take effect\n", gpt->device_name); |
|
|
|
out: |
|
close(gpt->fd); |
} |
} |
|
|
static struct { |
__printflike(2, 0) |
int (*fptr)(int, char *[]); |
static void |
const char *name; |
gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e) |
} cmdsw[] = { |
{ |
{ cmd_add, "add" }, |
if (gpt && (gpt->flags & GPT_QUIET)) |
{ cmd_backup, "backup" }, |
return; |
{ cmd_biosboot, "biosboot" }, |
fprintf(stderr, "%s: ", getprogname()); |
{ cmd_create, "create" }, |
if (gpt) |
{ cmd_destroy, "destroy" }, |
fprintf(stderr, "%s: ", gpt->device_name); |
{ NULL, "help" }, |
vfprintf(stderr, fmt, ap); |
{ cmd_label, "label" }, |
if (e) |
{ cmd_migrate, "migrate" }, |
fprintf(stderr, " (%s)\n", e); |
{ cmd_recover, "recover" }, |
else |
{ cmd_remove, "remove" }, |
fputc('\n', stderr); |
{ NULL, "rename" }, |
} |
{ cmd_resize, "resize" }, |
|
{ cmd_restore, "restore" }, |
|
{ cmd_set, "set" }, |
|
{ cmd_show, "show" }, |
|
{ cmd_unset, "unset" }, |
|
{ NULL, "verify" }, |
|
{ NULL, NULL } |
|
}; |
|
|
|
__dead static void |
void |
usage(void) |
gpt_warnx(gpt_t gpt, const char *fmt, ...) |
{ |
{ |
extern const char addmsg1[], addmsg2[], backupmsg[], biosbootmsg[]; |
va_list ap; |
extern const char createmsg[], destroymsg[], labelmsg1[], labelmsg2[]; |
|
extern const char labelmsg3[], migratemsg[], recovermsg[], removemsg1[]; |
va_start(ap, fmt); |
extern const char removemsg2[], resizemsg[], restoremsg[], setmsg[]; |
gpt_vwarnx(gpt, fmt, ap, NULL); |
extern const char showmsg[], unsetmsg[]; |
va_end(ap); |
|
|
fprintf(stderr, |
|
"usage: %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %*s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n" |
|
" %s %s\n", |
|
getprogname(), addmsg1, |
|
getprogname(), addmsg2, |
|
getprogname(), backupmsg, |
|
getprogname(), biosbootmsg, |
|
getprogname(), createmsg, |
|
getprogname(), destroymsg, |
|
getprogname(), labelmsg1, |
|
getprogname(), labelmsg2, |
|
(int)strlen(getprogname()), "", labelmsg3, |
|
getprogname(), migratemsg, |
|
getprogname(), recovermsg, |
|
getprogname(), removemsg1, |
|
getprogname(), removemsg2, |
|
getprogname(), resizemsg, |
|
getprogname(), restoremsg, |
|
getprogname(), setmsg, |
|
getprogname(), showmsg, |
|
getprogname(), unsetmsg); |
|
exit(1); |
|
} |
} |
|
|
static void |
void |
prefix(const char *cmd) |
gpt_warn(gpt_t gpt, const char *fmt, ...) |
{ |
{ |
char *pfx; |
va_list ap; |
const char *prg; |
|
|
va_start(ap, fmt); |
|
gpt_vwarnx(gpt, fmt, ap, strerror(errno)); |
|
va_end(ap); |
|
} |
|
|
prg = getprogname(); |
void |
pfx = malloc(strlen(prg) + strlen(cmd) + 2); |
gpt_msg(gpt_t gpt, const char *fmt, ...) |
/* Don't bother failing. It's not important */ |
{ |
if (pfx == NULL) |
va_list ap; |
|
|
|
if (gpt && (gpt->flags & GPT_QUIET)) |
return; |
return; |
|
if (gpt) |
|
printf("%s: ", gpt->device_name); |
|
va_start(ap, fmt); |
|
vprintf(fmt, ap); |
|
va_end(ap); |
|
printf("\n"); |
|
} |
|
|
sprintf(pfx, "%s %s", prg, cmd); |
struct gpt_hdr * |
setprogname(pfx); |
gpt_hdr(gpt_t gpt) |
|
{ |
|
gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR); |
|
if (gpt->gpt == NULL) { |
|
gpt_warnx(gpt, "No primary GPT header; run create or recover"); |
|
return NULL; |
|
} |
|
|
|
gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR); |
|
if (gpt->tpg == NULL) { |
|
gpt_warnx(gpt, "No secondary GPT header; run recover"); |
|
return NULL; |
|
} |
|
|
|
gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL); |
|
gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL); |
|
if (gpt->tbl == NULL || gpt->lbt == NULL) { |
|
gpt_warnx(gpt, "Corrupt maps, run recover"); |
|
return NULL; |
|
} |
|
|
|
return gpt->gpt->map_data; |
} |
} |
|
|
int |
int |
main(int argc, char *argv[]) |
gpt_write_crc(gpt_t gpt, map_t map, map_t tbl) |
{ |
{ |
char *cmd, *p; |
struct gpt_hdr *hdr = map->map_data; |
int ch, i; |
|
|
|
/* Get the generic options */ |
hdr->hdr_crc_table = htole32(crc32(tbl->map_data, |
while ((ch = getopt(argc, argv, "p:rv")) != -1) { |
le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); |
switch(ch) { |
hdr->hdr_crc_self = 0; |
case 'p': |
hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); |
if (parts > 0) |
|
usage(); |
if (gpt_write(gpt, map) == -1) { |
parts = strtoul(optarg, &p, 10); |
gpt_warn(gpt, "Error writing crc map"); |
if (*p != 0 || parts < 1) |
return -1; |
usage(); |
} |
break; |
|
case 'r': |
if (gpt_write(gpt, tbl) == -1) { |
readonly = 1; |
gpt_warn(gpt, "Error writing crc table"); |
break; |
return -1; |
case 'v': |
} |
verbose++; |
|
break; |
return 0; |
default: |
} |
usage(); |
|
|
int |
|
gpt_write_primary(gpt_t gpt) |
|
{ |
|
return gpt_write_crc(gpt, gpt->gpt, gpt->tbl); |
|
} |
|
|
|
|
|
int |
|
gpt_write_backup(gpt_t gpt) |
|
{ |
|
return gpt_write_crc(gpt, gpt->tpg, gpt->lbt); |
|
} |
|
|
|
void |
|
gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active) |
|
{ |
|
part->part_flag = active ? 0x80 : 0; |
|
part->part_shd = 0x00; |
|
part->part_ssect = 0x02; |
|
part->part_scyl = 0x00; |
|
part->part_typ = MBR_PTYPE_PMBR; |
|
part->part_ehd = 0xfe; |
|
part->part_esect = 0xff; |
|
part->part_ecyl = 0xff; |
|
part->part_start_lo = htole16(1); |
|
if (last > 0xffffffff) { |
|
part->part_size_lo = htole16(0xffff); |
|
part->part_size_hi = htole16(0xffff); |
|
} else { |
|
part->part_size_lo = htole16((uint16_t)last); |
|
part->part_size_hi = htole16((uint16_t)(last >> 16)); |
|
} |
|
} |
|
|
|
struct gpt_ent * |
|
gpt_ent(map_t map, map_t tbl, unsigned int i) |
|
{ |
|
struct gpt_hdr *hdr = map->map_data; |
|
return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz)); |
|
} |
|
|
|
struct gpt_ent * |
|
gpt_ent_primary(gpt_t gpt, unsigned int i) |
|
{ |
|
return gpt_ent(gpt->gpt, gpt->tbl, i); |
|
} |
|
|
|
struct gpt_ent * |
|
gpt_ent_backup(gpt_t gpt, unsigned int i) |
|
{ |
|
return gpt_ent(gpt->tpg, gpt->lbt, i); |
|
} |
|
|
|
int |
|
gpt_usage(const char *prefix, const struct gpt_cmd *cmd) |
|
{ |
|
const char **a = cmd->help; |
|
size_t hlen = cmd->hlen; |
|
size_t i; |
|
|
|
if (prefix == NULL) { |
|
const char *pname = getprogname(); |
|
const char *d1, *d2, *d = " <device>"; |
|
int len = (int)strlen(pname); |
|
if (strcmp(pname, "gpt") == 0) { |
|
d1 = ""; |
|
d2 = d; |
|
} else { |
|
d2 = ""; |
|
d1 = d; |
|
} |
|
fprintf(stderr, "Usage: %s%s %s %s%s\n", pname, |
|
d1, cmd->name, a[0], d2); |
|
for (i = 1; i < hlen; i++) { |
|
fprintf(stderr, |
|
" %*s%s %s %s%s\n", len, "", |
|
d1, cmd->name, a[i], d2); |
|
} |
|
} else { |
|
for (i = 0; i < hlen; i++) |
|
fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]); |
|
} |
|
return -1; |
|
} |
|
|
|
off_t |
|
gpt_last(gpt_t gpt) |
|
{ |
|
return gpt->mediasz / gpt->secsz - 1LL; |
|
} |
|
|
|
off_t |
|
gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only) |
|
{ |
|
off_t blocks; |
|
map_t map; |
|
struct gpt_hdr *hdr; |
|
struct gpt_ent *ent; |
|
unsigned int i; |
|
void *p; |
|
|
|
if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL || |
|
map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) { |
|
gpt_warnx(gpt, "Device already contains a GPT, " |
|
"destroy it first"); |
|
return -1; |
|
} |
|
|
|
/* Get the amount of free space after the MBR */ |
|
blocks = map_free(gpt, 1LL, 0LL); |
|
if (blocks == 0LL) { |
|
gpt_warnx(gpt, "No room for the GPT header"); |
|
return -1; |
|
} |
|
|
|
/* Don't create more than parts entries. */ |
|
if ((uint64_t)(blocks - 1) * gpt->secsz > |
|
parts * sizeof(struct gpt_ent)) { |
|
blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz); |
|
if ((parts * sizeof(struct gpt_ent)) % gpt->secsz) |
|
blocks++; |
|
blocks++; /* Don't forget the header itself */ |
|
} |
|
|
|
/* Never cross the median of the device. */ |
|
if ((blocks + 1LL) > ((last + 1LL) >> 1)) |
|
blocks = ((last + 1LL) >> 1) - 1LL; |
|
|
|
/* |
|
* Get the amount of free space at the end of the device and |
|
* calculate the size for the GPT structures. |
|
*/ |
|
map = map_last(gpt); |
|
if (map->map_type != MAP_TYPE_UNUSED) { |
|
gpt_warnx(gpt, "No room for the backup header"); |
|
return -1; |
|
} |
|
|
|
if (map->map_size < blocks) |
|
blocks = map->map_size; |
|
if (blocks == 1LL) { |
|
gpt_warnx(gpt, "No room for the GPT table"); |
|
return -1; |
|
} |
|
|
|
blocks--; /* Number of blocks in the GPT table. */ |
|
|
|
if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1) |
|
return -1; |
|
|
|
if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) { |
|
gpt_warnx(gpt, "Can't allocate the primary GPT table"); |
|
return -1; |
|
} |
|
if ((gpt->tbl = map_add(gpt, 2LL, blocks, |
|
MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) { |
|
free(p); |
|
gpt_warnx(gpt, "Can't add the primary GPT table"); |
|
return -1; |
|
} |
|
|
|
hdr = gpt->gpt->map_data; |
|
memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); |
|
|
|
/* |
|
* XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus |
|
* contains padding we must not include in the size. |
|
*/ |
|
hdr->hdr_revision = htole32(GPT_HDR_REVISION); |
|
hdr->hdr_size = htole32(GPT_HDR_SIZE); |
|
hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start); |
|
hdr->hdr_lba_alt = htole64((uint64_t)last); |
|
hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks)); |
|
hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL)); |
|
if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1) |
|
return -1; |
|
hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start)); |
|
hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) / |
|
sizeof(struct gpt_ent))); |
|
if (le32toh(hdr->hdr_entries) > parts) |
|
hdr->hdr_entries = htole32(parts); |
|
hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); |
|
|
|
ent = gpt->tbl->map_data; |
|
for (i = 0; i < le32toh(hdr->hdr_entries); i++) { |
|
if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1) |
|
return -1; |
|
} |
|
|
|
/* |
|
* Create backup GPT if the user didn't suppress it. |
|
*/ |
|
if (primary_only) |
|
return last; |
|
|
|
if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1) |
|
return -1; |
|
|
|
if ((gpt->lbt = map_add(gpt, last - blocks, blocks, |
|
MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) { |
|
gpt_warnx(gpt, "Can't add the secondary GPT table"); |
|
return -1; |
|
} |
|
|
|
memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz); |
|
|
|
hdr = gpt->tpg->map_data; |
|
hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start); |
|
hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start); |
|
hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start); |
|
return last; |
|
} |
|
|
|
static int |
|
gpt_size_get(gpt_t gpt, off_t *size) |
|
{ |
|
off_t sectors; |
|
int64_t human_num; |
|
char *p; |
|
|
|
if (*size > 0) |
|
return -1; |
|
sectors = strtoll(optarg, &p, 10); |
|
if (sectors < 1) |
|
return -1; |
|
if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) { |
|
*size = sectors * gpt->secsz; |
|
return 0; |
|
} |
|
if ((*p == 'b' || *p == 'B') && p[1] == '\0') { |
|
*size = sectors; |
|
return 0; |
|
} |
|
if (dehumanize_number(optarg, &human_num) < 0) |
|
return -1; |
|
*size = human_num; |
|
return 0; |
|
} |
|
|
|
int |
|
gpt_human_get(gpt_t gpt, off_t *human) |
|
{ |
|
int64_t human_num; |
|
|
|
if (*human > 0) { |
|
gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human, |
|
optarg); |
|
return -1; |
|
} |
|
if (dehumanize_number(optarg, &human_num) < 0) { |
|
gpt_warn(gpt, "Bad number `%s'", optarg); |
|
return -1; |
|
} |
|
*human = human_num; |
|
if (*human < 1) { |
|
gpt_warn(gpt, "Number `%s' < 1", optarg); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
int |
|
gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch) |
|
{ |
|
switch (ch) { |
|
case 'a': |
|
if (find->all > 0) { |
|
gpt_warn(gpt, "-a is already set"); |
|
return -1; |
|
} |
|
find->all = 1; |
|
break; |
|
case 'b': |
|
if (gpt_human_get(gpt, &find->block) == -1) |
|
return -1; |
|
break; |
|
case 'i': |
|
if (gpt_uint_get(gpt, &find->entry) == -1) |
|
return -1; |
|
break; |
|
case 'L': |
|
if (gpt_name_get(gpt, &find->label) == -1) |
|
return -1; |
|
break; |
|
case 's': |
|
if (gpt_size_get(gpt, &find->size) == -1) |
|
return -1; |
|
break; |
|
case 't': |
|
if (!gpt_uuid_is_nil(find->type)) |
|
return -1; |
|
if (gpt_uuid_parse(optarg, find->type) != 0) |
|
return -1; |
|
break; |
|
default: |
|
gpt_warn(gpt, "Unknown find option `%c'", ch); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
int |
|
gpt_change_ent(gpt_t gpt, const struct gpt_find *find, |
|
void (*cfn)(struct gpt_ent *, void *), void *v) |
|
{ |
|
map_t m; |
|
struct gpt_hdr *hdr; |
|
struct gpt_ent *ent; |
|
unsigned int i; |
|
uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1]; |
|
|
|
if (!find->all ^ |
|
(find->block > 0 || find->entry > 0 || find->label != NULL |
|
|| find->size > 0 || !gpt_uuid_is_nil(find->type))) |
|
return -1; |
|
|
|
if ((hdr = gpt_hdr(gpt)) == NULL) |
|
return -1; |
|
|
|
/* Relabel all matching entries in the map. */ |
|
for (m = map_first(gpt); m != NULL; m = m->map_next) { |
|
if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) |
|
continue; |
|
if (find->entry > 0 && find->entry != m->map_index) |
|
continue; |
|
if (find->block > 0 && find->block != m->map_start) |
|
continue; |
|
if (find->size > 0 && find->size != m->map_size) |
|
continue; |
|
|
|
i = m->map_index - 1; |
|
|
|
ent = gpt_ent_primary(gpt, i); |
|
if (find->label != NULL) { |
|
utf16_to_utf8(ent->ent_name, |
|
__arraycount(ent->ent_name), |
|
utfbuf, __arraycount(utfbuf)); |
|
if (strcmp((char *)find->label, (char *)utfbuf) == 0) |
|
continue; |
|
} |
|
|
|
if (!gpt_uuid_is_nil(find->type) && |
|
!gpt_uuid_equal(find->type, ent->ent_type)) |
|
continue; |
|
|
|
/* Change the primary entry. */ |
|
(*cfn)(ent, v); |
|
|
|
if (gpt_write_primary(gpt) == -1) |
|
return -1; |
|
|
|
ent = gpt_ent_backup(gpt, i); |
|
/* Change the secondary entry. */ |
|
(*cfn)(ent, v); |
|
|
|
if (gpt_write_backup(gpt) == -1) |
|
return -1; |
|
|
|
gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg); |
|
} |
|
return 0; |
|
} |
|
|
|
int |
|
gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch) |
|
{ |
|
switch (ch) { |
|
case 'a': |
|
if (gpt_human_get(gpt, alignment) == -1) |
|
return -1; |
|
return 0; |
|
case 'i': |
|
if (gpt_uint_get(gpt, entry) == -1) |
|
return -1; |
|
return 0; |
|
case 's': |
|
if (gpt_size_get(gpt, size) == -1) |
|
return -1; |
|
return 0; |
|
default: |
|
gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch); |
|
return -1; |
|
} |
|
} |
|
|
|
off_t |
|
gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size) |
|
{ |
|
if (entry == 0) { |
|
gpt_warnx(gpt, "Entry not specified"); |
|
return -1; |
|
} |
|
if (alignment % gpt->secsz != 0) { |
|
gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of " |
|
"sector size (%#x)", (uintmax_t)alignment, gpt->secsz); |
|
return -1; |
|
} |
|
|
|
if (size % gpt->secsz != 0) { |
|
gpt_warnx(gpt, "Size (%#jx) must be a multiple of " |
|
"sector size (%#x)", (uintmax_t)size, gpt->secsz); |
|
return -1; |
|
} |
|
if (size > 0) |
|
return size / gpt->secsz; |
|
return 0; |
|
} |
|
|
|
static const struct nvd { |
|
const char *name; |
|
uint64_t mask; |
|
const char *description; |
|
} gpt_attr[] = { |
|
{ |
|
"biosboot", |
|
GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE, |
|
"Legacy BIOS boot partition", |
|
}, |
|
{ |
|
"bootme", |
|
GPT_ENT_ATTR_BOOTME, |
|
"Bootable partition", |
|
}, |
|
{ |
|
"bootfailed", |
|
GPT_ENT_ATTR_BOOTFAILED, |
|
"Partition that marked bootonce failed to boot", |
|
}, |
|
{ |
|
"bootonce", |
|
GPT_ENT_ATTR_BOOTONCE, |
|
"Attempt to boot this partition only once", |
|
}, |
|
{ |
|
"noblockio", |
|
GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL, |
|
"UEFI won't recognize file system for block I/O", |
|
}, |
|
{ |
|
"required", |
|
GPT_ENT_ATTR_REQUIRED_PARTITION, |
|
"Partition required for platform to function", |
|
}, |
|
}; |
|
|
|
int |
|
gpt_attr_get(gpt_t gpt, uint64_t *attributes) |
|
{ |
|
size_t i; |
|
int rv = 0; |
|
char *ptr; |
|
|
|
*attributes = 0; |
|
|
|
for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) { |
|
for (i = 0; i < __arraycount(gpt_attr); i++) |
|
if (strcmp(gpt_attr[i].name, ptr) == 0) |
|
break; |
|
if (i == __arraycount(gpt_attr)) { |
|
gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr); |
|
rv = -1; |
|
} else |
|
*attributes |= gpt_attr[i].mask; |
|
} |
|
return rv; |
|
} |
|
|
|
void |
|
gpt_attr_help(const char *prefix) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < __arraycount(gpt_attr); i++) |
|
printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name, |
|
gpt_attr[i].description); |
|
} |
|
|
|
const char * |
|
gpt_attr_list(char *buf, size_t len, uint64_t attributes) |
|
{ |
|
size_t i; |
|
strlcpy(buf, "", len); |
|
|
|
for (i = 0; i < __arraycount(gpt_attr); i++) |
|
if (attributes & gpt_attr[i].mask) { |
|
strlcat(buf, buf[0] ? ", " : "", len); |
|
strlcat(buf, gpt_attr[i].name, len); |
} |
} |
|
return buf; |
|
} |
|
|
|
int |
|
gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr) |
|
{ |
|
struct gpt_hdr *hdr; |
|
struct gpt_ent *ent; |
|
unsigned int i; |
|
|
|
if (entry == 0 || (set == 0 && clr == 0)) { |
|
gpt_warnx(gpt, "Nothing to set"); |
|
return -1; |
} |
} |
if (!parts) |
|
parts = 128; |
|
|
|
if (argc == optind) |
if ((hdr = gpt_hdr(gpt)) == NULL) |
usage(); |
return -1; |
|
|
cmd = argv[optind++]; |
if (entry > le32toh(hdr->hdr_entries)) { |
for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++); |
gpt_warnx(gpt, "Index %u out of range (%u max)", |
|
entry, le32toh(hdr->hdr_entries)); |
|
return -1; |
|
} |
|
|
if (cmdsw[i].fptr == NULL) |
i = entry - 1; |
errx(1, "unknown command: %s", cmd); |
ent = gpt_ent_primary(gpt, i); |
|
if (gpt_uuid_is_nil(ent->ent_type)) { |
|
gpt_warnx(gpt, "Entry at index %u is unused", entry); |
|
return -1; |
|
} |
|
|
prefix(cmd); |
ent->ent_attr &= ~clr; |
return ((*cmdsw[i].fptr)(argc, argv)); |
ent->ent_attr |= set; |
|
|
|
if (gpt_write_primary(gpt) == -1) |
|
return -1; |
|
|
|
ent = gpt_ent_backup(gpt, i); |
|
ent->ent_attr &= ~clr; |
|
ent->ent_attr |= set; |
|
|
|
if (gpt_write_backup(gpt) == -1) |
|
return -1; |
|
gpt_msg(gpt, "Partition %d attributes updated", entry); |
|
return 0; |
|
} |
|
|
|
int |
|
gpt_uint_get(gpt_t gpt, u_int *entry) |
|
{ |
|
char *p; |
|
if (*entry > 0) |
|
return -1; |
|
*entry = (u_int)strtoul(optarg, &p, 10); |
|
if (*p != 0 || *entry < 1) { |
|
gpt_warn(gpt, "Bad number `%s'", optarg); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
int |
|
gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid) |
|
{ |
|
if (!gpt_uuid_is_nil(*uuid)) |
|
return -1; |
|
if (gpt_uuid_parse(optarg, *uuid) != 0) { |
|
gpt_warn(gpt, "Can't parse uuid"); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
int |
|
gpt_name_get(gpt_t gpt, void *v) |
|
{ |
|
char **name = v; |
|
if (*name != NULL) |
|
return -1; |
|
*name = strdup(optarg); |
|
if (*name == NULL) { |
|
gpt_warn(gpt, "Can't copy string"); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
void |
|
gpt_show_num(const char *prompt, uintmax_t num) |
|
{ |
|
#ifdef HN_AUTOSCALE |
|
char human_num[5]; |
|
if (humanize_number(human_num, 5, (int64_t)num , |
|
"", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0) |
|
human_num[0] = '\0'; |
|
#endif |
|
printf("%s: %ju", prompt, num); |
|
#ifdef HN_AUTOSCALE |
|
if (human_num[0] != '\0') |
|
printf(" (%s)", human_num); |
|
#endif |
|
printf("\n"); |
|
} |
|
|
|
int |
|
gpt_add_hdr(gpt_t gpt, int type, off_t loc) |
|
{ |
|
void *p; |
|
map_t *t; |
|
const char *msg; |
|
|
|
switch (type) { |
|
case MAP_TYPE_PRI_GPT_HDR: |
|
t = &gpt->gpt; |
|
msg = "primary"; |
|
break; |
|
case MAP_TYPE_SEC_GPT_HDR: |
|
t = &gpt->tpg; |
|
msg = "secondary"; |
|
break; |
|
default: |
|
gpt_warnx(gpt, "Unknown GPT header type %d", type); |
|
return -1; |
|
} |
|
|
|
if ((p = calloc(1, gpt->secsz)) == NULL) { |
|
gpt_warn(gpt, "Error allocating %s GPT header", msg); |
|
return -1; |
|
} |
|
|
|
*t = map_add(gpt, loc, 1LL, type, p, 1); |
|
if (*t == NULL) { |
|
gpt_warn(gpt, "Error adding %s GPT header", msg); |
|
free(p); |
|
return -1; |
|
} |
|
return 0; |
} |
} |