1*5aa145f6Smartin /* $NetBSD: gpt.c,v 1.32 2024/03/24 17:29:58 martin Exp $ */
24103857bSmartin
34103857bSmartin /*
44103857bSmartin * Copyright 2018 The NetBSD Foundation, Inc.
54103857bSmartin * All rights reserved.
64103857bSmartin *
74103857bSmartin * Redistribution and use in source and binary forms, with or without
84103857bSmartin * modification, are permitted provided that the following conditions
94103857bSmartin * are met:
104103857bSmartin * 1. Redistributions of source code must retain the above copyright
114103857bSmartin * notice, this list of conditions and the following disclaimer.
124103857bSmartin * 2. Redistributions in binary form must reproduce the above copyright
134103857bSmartin * notice, this list of conditions and the following disclaimer in the
144103857bSmartin * documentation and/or other materials provided with the distribution.
154103857bSmartin *
164103857bSmartin * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
174103857bSmartin * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184103857bSmartin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194103857bSmartin * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
204103857bSmartin * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
214103857bSmartin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224103857bSmartin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
234103857bSmartin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244103857bSmartin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
254103857bSmartin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
264103857bSmartin * THE POSSIBILITY OF SUCH DAMAGE.
274103857bSmartin *
284103857bSmartin */
294103857bSmartin
304103857bSmartin #include "defs.h"
314103857bSmartin #include "mbr.h"
324103857bSmartin #include "md.h"
334103857bSmartin #include "gpt_uuid.h"
344103857bSmartin #include <assert.h>
359238070cSmartin #include <errno.h>
36f77b58b1Smartin #include <err.h>
374103857bSmartin #include <paths.h>
384103857bSmartin #include <sys/param.h>
394103857bSmartin #include <sys/ioctl.h>
404103857bSmartin #include <util.h>
41f77b58b1Smartin #include <uuid.h>
424103857bSmartin
434103857bSmartin bool gpt_parts_check(void); /* check for needed binaries */
444103857bSmartin
454103857bSmartin
464103857bSmartin /*************** GPT ************************************************/
474103857bSmartin /* a GPT based disk_partitions interface */
484103857bSmartin
494103857bSmartin #define GUID_STR_LEN 40
50f77b58b1Smartin #define GPT_PTYPE_ALLOC 32 /* initial type array allocation, should be >
51f77b58b1Smartin * gpt type -l | wc -l */
52fade124bSmartin #define GPT_DEV_LEN DISKNAMESIZE /* dkNN */
534103857bSmartin
5486906049Smartin #define GPT_PARTS_PER_SEC 4 /* a 512 byte sector holds 4 entries */
554103857bSmartin #define GPT_DEFAULT_MAX_PARTS 128
564103857bSmartin
574103857bSmartin /* a usable label will be short, so we can get away with an arbitrary limit */
584103857bSmartin #define GPT_LABEL_LEN 96
594103857bSmartin
604103857bSmartin #define GPT_ATTR_BIOSBOOT 1
614103857bSmartin #define GPT_ATTR_BOOTME 2
624103857bSmartin #define GPT_ATTR_BOOTONCE 4
634103857bSmartin #define GPT_ATTR_BOOTFAILED 8
644103857bSmartin #define GPT_ATTR_NOBLOCKIO 16
654103857bSmartin #define GPT_ATTR_REQUIRED 32
664103857bSmartin
674103857bSmartin /* when we don't care for BIOS or UEFI boot, use the combined boot flags */
684103857bSmartin #define GPT_ATTR_BOOT (GPT_ATTR_BIOSBOOT|GPT_ATTR_BOOTME)
694103857bSmartin
704103857bSmartin struct gpt_attr_desc {
714103857bSmartin const char *name;
724103857bSmartin uint flag;
734103857bSmartin };
744103857bSmartin static const struct gpt_attr_desc gpt_avail_attrs[] = {
754103857bSmartin { "biosboot", GPT_ATTR_BIOSBOOT },
764103857bSmartin { "bootme", GPT_ATTR_BOOTME },
774103857bSmartin { "bootonce", GPT_ATTR_BOOTONCE },
784103857bSmartin { "bootfailed", GPT_ATTR_BOOTFAILED },
794103857bSmartin { "noblockio", GPT_ATTR_NOBLOCKIO },
804103857bSmartin { "required", GPT_ATTR_REQUIRED },
814103857bSmartin { NULL, 0 }
824103857bSmartin };
834103857bSmartin
844103857bSmartin struct gpt_ptype_desc {
854103857bSmartin struct part_type_desc gent;
864103857bSmartin char tid[GUID_STR_LEN];
874103857bSmartin uint fsflags, default_fs_type;
884103857bSmartin };
894103857bSmartin
904103857bSmartin static const
914103857bSmartin struct {
924103857bSmartin const char *name;
934103857bSmartin uint fstype;
944103857bSmartin enum part_type ptype;
954103857bSmartin uint fsflags;
964103857bSmartin } gpt_fs_types[] = {
974103857bSmartin { .name = "ffs", .fstype = FS_BSDFFS, .ptype = PT_root,
984103857bSmartin .fsflags = GLM_LIKELY_FFS },
994103857bSmartin { .name = "swap", .fstype = FS_SWAP, .ptype = PT_swap },
1004103857bSmartin { .name = "windows", .fstype = FS_MSDOS, .ptype = PT_FAT,
1014103857bSmartin .fsflags = GLM_MAYBE_FAT32|GLM_MAYBE_NTFS },
1024103857bSmartin { .name = "windows", .fstype = FS_NTFS, .ptype = PT_FAT,
1034103857bSmartin .fsflags = GLM_MAYBE_FAT32|GLM_MAYBE_NTFS },
1044103857bSmartin { .name = "efi", .fstype = FS_MSDOS, .ptype = PT_EFI_SYSTEM,
1054103857bSmartin .fsflags = GLM_MAYBE_FAT32 },
10678fe337eSmartin { .name = "efi", .fstype = FS_EFI_SP, .ptype = PT_EFI_SYSTEM,
10778fe337eSmartin .fsflags = GLM_MAYBE_FAT32 },
1084103857bSmartin { .name = "bios", .fstype = FS_MSDOS, .ptype = PT_FAT,
1094103857bSmartin .fsflags = GLM_MAYBE_FAT32 },
1104103857bSmartin { .name = "lfs", .fstype = FS_BSDLFS, .ptype = PT_root },
1114103857bSmartin { .name = "linux-data", .fstype = FS_EX2FS, .ptype = PT_root },
1124103857bSmartin { .name = "apple", .fstype = FS_HFS, .ptype = PT_unknown },
113abce8cb3Smartin { .name = "ccd", .fstype = FS_CCD, .ptype = PT_root },
114abce8cb3Smartin { .name = "cgd", .fstype = FS_CGD, .ptype = PT_root },
1154103857bSmartin { .name = "raid", .fstype = FS_RAID, .ptype = PT_root },
1164103857bSmartin { .name = "vmcore", .fstype = FS_VMKCORE, .ptype = PT_unknown },
1174103857bSmartin { .name = "vmfs", .fstype = FS_VMFS, .ptype = PT_unknown },
1183d593cdbSmartin { .name = "vmresered", .fstype = FS_VMWRESV, .ptype = PT_unknown },
119103d2d1eSmartin { .name = "zfs", .fstype = FS_ZFS, .ptype = PT_root },
1204103857bSmartin };
1214103857bSmartin
122f77b58b1Smartin static size_t gpt_ptype_cnt = 0, gpt_ptype_alloc = 0;
123f77b58b1Smartin static struct gpt_ptype_desc *gpt_ptype_descs = NULL;
124f77b58b1Smartin
125f77b58b1Smartin /* "well" known types with special handling */
126f77b58b1Smartin static const struct part_type_desc *gpt_native_root;
1274103857bSmartin
1284103857bSmartin /* similar to struct gpt_ent, but matching our needs */
1294103857bSmartin struct gpt_part_entry {
1304103857bSmartin const struct gpt_ptype_desc *gp_type;
1314103857bSmartin char gp_id[GUID_STR_LEN]; /* partition guid as string */
1324103857bSmartin daddr_t gp_start, gp_size;
1334103857bSmartin uint gp_attr; /* various attribute bits */
1344103857bSmartin char gp_label[GPT_LABEL_LEN]; /* user defined label */
1354103857bSmartin char gp_dev_name[GPT_DEV_LEN]; /* name of wedge */
1364103857bSmartin const char *last_mounted; /* last mounted if known */
137cb429b90Smartin uint fs_type, fs_sub_type, /* FS_* and maybe sub type */
138cb429b90Smartin fs_opt1, fs_opt2, fs_opt3; /* transient file system options */
1394103857bSmartin uint gp_flags;
1404103857bSmartin #define GPEF_ON_DISK 1 /* This entry exists on-disk */
1414103857bSmartin #define GPEF_MODIFIED 2 /* this entry has been changed */
1424103857bSmartin #define GPEF_WEDGE 4 /* wedge for this exists */
1434103857bSmartin #define GPEF_RESIZED 8 /* size has changed */
144d9c70220Smartin #define GPEF_TARGET 16 /* marked install target */
1454103857bSmartin struct gpt_part_entry *gp_next;
1464103857bSmartin };
1474103857bSmartin
1484103857bSmartin static const struct gpt_ptype_desc *gpt_find_native_type(
1494103857bSmartin const struct part_type_desc *gent);
1504103857bSmartin static const struct gpt_ptype_desc *gpt_find_guid_type(const char*);
1514103857bSmartin static bool
1524103857bSmartin gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info,
1534103857bSmartin const char **err_msg);
1544103857bSmartin
1554103857bSmartin const struct disk_partitioning_scheme gpt_parts;
1564103857bSmartin struct gpt_disk_partitions {
1574103857bSmartin struct disk_partitions dp;
1584103857bSmartin /*
1594103857bSmartin * We keep a list of our current valid partitions, pointed
1604103857bSmartin * to by "partitions".
1614103857bSmartin * dp.num_part is the number of entries in "partitions".
1624103857bSmartin * When partitions that have a representation on disk already
1634103857bSmartin * are deleted, we move them to the "obsolete" list so we
1644103857bSmartin * can issue the proper commands to remove it when writing back.
1654103857bSmartin */
1664103857bSmartin struct gpt_part_entry *partitions, /* current partitions */
1674103857bSmartin *obsolete; /* deleted partitions */
1684103857bSmartin size_t max_num_parts; /* how many entries max? */
1694103857bSmartin size_t prologue, epilogue; /* number of sectors res. */
1704103857bSmartin bool has_gpt; /* disk already has a GPT */
1714103857bSmartin };
1724103857bSmartin
1734103857bSmartin /*
1744103857bSmartin * Init global variables from MD details
1754103857bSmartin */
1764103857bSmartin static void
gpt_md_init(bool is_boot_disk,size_t * max_parts,size_t * head,size_t * tail)1774103857bSmartin gpt_md_init(bool is_boot_disk, size_t *max_parts, size_t *head, size_t *tail)
1784103857bSmartin {
1794103857bSmartin size_t num;
1804103857bSmartin
1814103857bSmartin if (is_boot_disk) {
1824103857bSmartin #ifdef MD_GPT_INITIAL_SIZE
1834103857bSmartin #if MD_GPT_INITIAL_SIZE < 2*512
1844103857bSmartin #error impossible small GPT prologue
1854103857bSmartin #endif
1864103857bSmartin num = ((MD_GPT_INITIAL_SIZE-(2*512))/512)*GPT_PARTS_PER_SEC;
1874103857bSmartin #else
1884103857bSmartin num = GPT_DEFAULT_MAX_PARTS;
1894103857bSmartin #endif
1904103857bSmartin } else {
1914103857bSmartin num = GPT_DEFAULT_MAX_PARTS;
1924103857bSmartin }
1934103857bSmartin *max_parts = num;
1944103857bSmartin *head = 2 + num/GPT_PARTS_PER_SEC;
1954103857bSmartin *tail = 1 + num/GPT_PARTS_PER_SEC;
1964103857bSmartin }
1974103857bSmartin
1984103857bSmartin /*
1994103857bSmartin * Parse a part of "gpt show" output into a struct gpt_part_entry.
2004103857bSmartin * Output is from "show -a" format if details = false, otherwise
2014103857bSmartin * from details for a specific partition (show -i or show -b)
2024103857bSmartin */
2034103857bSmartin static void
gpt_add_info(struct gpt_part_entry * part,const char * tag,char * val,bool details)2044103857bSmartin gpt_add_info(struct gpt_part_entry *part, const char *tag, char *val,
2054103857bSmartin bool details)
2064103857bSmartin {
2074103857bSmartin char *s, *e;
2084103857bSmartin
2094103857bSmartin if (details && strcmp(tag, "Start:") == 0) {
2104103857bSmartin part->gp_start = strtouq(val, NULL, 10);
2114103857bSmartin } else if (details && strcmp(tag, "Size:") == 0) {
2124103857bSmartin part->gp_size = strtouq(val, NULL, 10);
2134103857bSmartin } else if (details && strcmp(tag, "Type:") == 0) {
2144103857bSmartin s = strchr(val, '(');
2154103857bSmartin if (!s)
2164103857bSmartin return;
2174103857bSmartin e = strchr(s, ')');
2184103857bSmartin if (!e)
2194103857bSmartin return;
2204103857bSmartin *e = 0;
2214103857bSmartin part->gp_type = gpt_find_guid_type(s+1);
2224103857bSmartin } else if (strcmp(tag, "TypeID:") == 0) {
2234103857bSmartin part->gp_type = gpt_find_guid_type(val);
2244103857bSmartin } else if (strcmp(tag, "GUID:") == 0) {
2254103857bSmartin strlcpy(part->gp_id, val, sizeof(part->gp_id));
2264103857bSmartin } else if (strcmp(tag, "Label:") == 0) {
2274103857bSmartin strlcpy(part->gp_label, val, sizeof(part->gp_label));
2284103857bSmartin } else if (strcmp(tag, "Attributes:") == 0) {
2294103857bSmartin char *n;
2304103857bSmartin
2314103857bSmartin while ((n = strsep(&val, ", ")) != NULL) {
2324103857bSmartin if (*n == 0)
2334103857bSmartin continue;
2344103857bSmartin for (const struct gpt_attr_desc *p = gpt_avail_attrs;
2354103857bSmartin p->name != NULL; p++) {
2364103857bSmartin if (strcmp(p->name, n) == 0)
2374103857bSmartin part->gp_attr |= p->flag;
2384103857bSmartin }
2394103857bSmartin }
2404103857bSmartin }
2414103857bSmartin }
2424103857bSmartin
2439b4c571aSmartin /*
2449b4c571aSmartin * Find the partition matching this wedge info and record that we
2459b4c571aSmartin * have a wedge already.
2469b4c571aSmartin */
2479b4c571aSmartin static void
update_part_from_wedge_info(struct gpt_disk_partitions * parts,const struct dkwedge_info * dkw)2489b4c571aSmartin update_part_from_wedge_info(struct gpt_disk_partitions *parts,
2499b4c571aSmartin const struct dkwedge_info *dkw)
2509b4c571aSmartin {
2519b4c571aSmartin for (struct gpt_part_entry *p = parts->partitions; p != NULL;
2529b4c571aSmartin p = p->gp_next) {
2539b4c571aSmartin if (p->gp_start != dkw->dkw_offset ||
2549b4c571aSmartin (uint64_t)p->gp_size != dkw->dkw_size)
2559b4c571aSmartin continue;
2569b4c571aSmartin p->gp_flags |= GPEF_WEDGE;
2579b4c571aSmartin strlcpy(p->gp_dev_name, dkw->dkw_devname,
2589b4c571aSmartin sizeof p->gp_dev_name);
2599b4c571aSmartin return;
2609b4c571aSmartin }
2619b4c571aSmartin }
2629b4c571aSmartin
2634103857bSmartin static struct disk_partitions *
gpt_read_from_disk(const char * dev,daddr_t start,daddr_t len,size_t bps,const struct disk_partitioning_scheme * scheme)26486906049Smartin gpt_read_from_disk(const char *dev, daddr_t start, daddr_t len, size_t bps,
265b04f4d62Smartin const struct disk_partitioning_scheme *scheme)
2664103857bSmartin {
2674103857bSmartin char diskpath[MAXPATHLEN];
2684103857bSmartin int fd;
2699b4c571aSmartin struct dkwedge_info *dkw;
2709b4c571aSmartin struct dkwedge_list dkwl;
2719b4c571aSmartin size_t bufsize, dk;
2724103857bSmartin
2734103857bSmartin assert(start == 0);
2744103857bSmartin assert(have_gpt);
2754103857bSmartin
2764103857bSmartin if (run_program(RUN_SILENT | RUN_ERROR_OK,
2774103857bSmartin "gpt -rq header %s", dev) != 0)
2784103857bSmartin return NULL;
2794103857bSmartin
2804103857bSmartin /* read the partitions */
2814103857bSmartin int i;
2824103857bSmartin unsigned int p_index;
2834103857bSmartin daddr_t p_start = 0, p_size = 0, avail_start = 0, avail_size = 0,
2844103857bSmartin disk_size = 0;
2854103857bSmartin char *textbuf, *t, *tt, p_type[STRSIZE];
2864103857bSmartin static const char regpart_prefix[] = "GPT part - ";
2874103857bSmartin struct gpt_disk_partitions *parts;
2884103857bSmartin struct gpt_part_entry *last = NULL, *add_to = NULL;
289d9c70220Smartin const struct gpt_ptype_desc *native_root
290d9c70220Smartin = gpt_find_native_type(gpt_native_root);
291d9c70220Smartin bool have_target = false;
2924103857bSmartin
2934103857bSmartin if (collect(T_OUTPUT, &textbuf, "gpt -r show -a %s 2>/dev/null", dev)
2944103857bSmartin < 1)
2954103857bSmartin return NULL;
2964103857bSmartin
2974103857bSmartin /* parse output and create our list */
2984103857bSmartin parts = calloc(1, sizeof(*parts));
2994103857bSmartin if (parts == NULL)
3004103857bSmartin return NULL;
3014103857bSmartin
3024103857bSmartin (void)strtok(textbuf, "\n"); /* ignore first line */
3034103857bSmartin while ((t = strtok(NULL, "\n")) != NULL) {
3044103857bSmartin i = 0; p_start = 0; p_size = 0; p_index = 0;
3054103857bSmartin p_type[0] = 0;
3064103857bSmartin while ((tt = strsep(&t, " \t")) != NULL) {
3074103857bSmartin if (strlen(tt) == 0)
3084103857bSmartin continue;
3094103857bSmartin if (i == 0) {
3104103857bSmartin if (add_to != NULL)
3114103857bSmartin gpt_add_info(add_to, tt, t, false);
3124103857bSmartin p_start = strtouq(tt, NULL, 10);
3134103857bSmartin if (p_start == 0 && add_to != NULL)
3144103857bSmartin break;
3154103857bSmartin else
3164103857bSmartin add_to = NULL;
3174103857bSmartin }
3184103857bSmartin if (i == 1)
3194103857bSmartin p_size = strtouq(tt, NULL, 10);
3204103857bSmartin if (i == 2)
3214103857bSmartin p_index = strtouq(tt, NULL, 10);
3224103857bSmartin if (i > 2 || (i == 2 && p_index == 0)) {
3234103857bSmartin if (p_type[0])
3244103857bSmartin strlcat(p_type, " ", STRSIZE);
3254103857bSmartin strlcat(p_type, tt, STRSIZE);
3264103857bSmartin }
3274103857bSmartin i++;
3284103857bSmartin }
3294103857bSmartin
3304103857bSmartin if (p_start == 0 || p_size == 0)
3314103857bSmartin continue;
3324103857bSmartin else if (strcmp(p_type, "Pri GPT table") == 0) {
3334103857bSmartin avail_start = p_start + p_size;
3344103857bSmartin parts->prologue = avail_start;
3354103857bSmartin parts->epilogue = p_size + 1;
3364103857bSmartin parts->max_num_parts = p_size * GPT_PARTS_PER_SEC;
3374103857bSmartin } else if (strcmp(p_type, "Sec GPT table") == 0)
3384103857bSmartin avail_size = p_start - avail_start;
3394103857bSmartin else if(strcmp(p_type, "Sec GPT header") == 0)
3404103857bSmartin disk_size = p_start + p_size;
3414103857bSmartin else if (p_index == 0 && strlen(p_type) > 0)
3424103857bSmartin /* Utilitary entry (PMBR, etc) */
3434103857bSmartin continue;
3444103857bSmartin else if (p_index == 0) {
3454103857bSmartin /* Free space */
3464103857bSmartin continue;
3474103857bSmartin } else {
3484103857bSmartin /* Usual partition */
3494103857bSmartin tt = p_type;
3504103857bSmartin if (strncmp(tt, regpart_prefix,
3514103857bSmartin strlen(regpart_prefix)) == 0)
3524103857bSmartin tt += strlen(regpart_prefix);
3534103857bSmartin
3544103857bSmartin /* Add to our linked list */
3554103857bSmartin struct gpt_part_entry *np = calloc(1, sizeof(*np));
3564103857bSmartin if (np == NULL)
3574103857bSmartin break;
3584103857bSmartin
3594103857bSmartin strlcpy(np->gp_label, tt, sizeof(np->gp_label));
3604103857bSmartin np->gp_start = p_start;
3614103857bSmartin np->gp_size = p_size;
3624103857bSmartin np->gp_flags |= GPEF_ON_DISK;
363d9c70220Smartin if (!have_target && native_root != NULL &&
364d9c70220Smartin strcmp(np->gp_id, native_root->tid) == 0) {
365d9c70220Smartin have_target = true;
366d9c70220Smartin np->gp_flags |= GPEF_TARGET;
367d9c70220Smartin }
3684103857bSmartin
3694103857bSmartin if (last == NULL)
3704103857bSmartin parts->partitions = np;
3714103857bSmartin else
3724103857bSmartin last->gp_next = np;
3734103857bSmartin last = np;
3744103857bSmartin add_to = np;
3754103857bSmartin parts->dp.num_part++;
3764103857bSmartin }
3774103857bSmartin }
3784103857bSmartin free(textbuf);
3794103857bSmartin
3801c492288Smartin /* If the GPT was not complete (e.g. truncated image), barf */
3811c492288Smartin if (disk_size <= 0) {
3821c492288Smartin free(parts);
3831c492288Smartin return NULL;
3841c492288Smartin }
3851c492288Smartin
386b04f4d62Smartin parts->dp.pscheme = scheme;
387f77b58b1Smartin parts->dp.disk = strdup(dev);
3884103857bSmartin parts->dp.disk_start = start;
3894103857bSmartin parts->dp.disk_size = disk_size;
3904103857bSmartin parts->dp.free_space = avail_size;
39186906049Smartin parts->dp.bytes_per_sector = bps;
3924103857bSmartin parts->has_gpt = true;
3934103857bSmartin
3944103857bSmartin fd = opendisk(parts->dp.disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
3954103857bSmartin for (struct gpt_part_entry *p = parts->partitions; p != NULL;
3964103857bSmartin p = p->gp_next) {
3974103857bSmartin #ifdef DEFAULT_UFS2
3984103857bSmartin bool fs_is_default = false;
3994103857bSmartin #endif
4004103857bSmartin
4015b07b744Smartin if (p->gp_type != NULL) {
4025b07b744Smartin
4034103857bSmartin if (p->gp_type->fsflags != 0) {
4045b07b744Smartin const char *lm = get_last_mounted(fd,
4055b07b744Smartin p->gp_start, &p->fs_type,
4065b07b744Smartin &p->fs_sub_type, p->gp_type->fsflags);
4074103857bSmartin if (lm != NULL && *lm != 0) {
40862056b84Smartin char *path = strdup(lm);
40962056b84Smartin canonicalize_last_mounted(path);
41062056b84Smartin p->last_mounted = path;
4114103857bSmartin } else {
4125b07b744Smartin p->fs_type = p->gp_type->
4135b07b744Smartin default_fs_type;
4144103857bSmartin #ifdef DEFAULT_UFS2
4154103857bSmartin fs_is_default = true;
4164103857bSmartin #endif
4174103857bSmartin }
4184103857bSmartin } else {
4194103857bSmartin p->fs_type = p->gp_type->default_fs_type;
4204103857bSmartin #ifdef DEFAULT_UFS2
4214103857bSmartin fs_is_default = true;
4224103857bSmartin #endif
4234103857bSmartin }
4244103857bSmartin #ifdef DEFAULT_UFS2
4254103857bSmartin if (fs_is_default && p->fs_type == FS_BSDFFS)
4264103857bSmartin p->fs_sub_type = 2;
4274103857bSmartin #endif
4285b07b744Smartin }
4294103857bSmartin
4304103857bSmartin parts->dp.free_space -= p->gp_size;
4314103857bSmartin }
4329b4c571aSmartin
4339b4c571aSmartin /*
4349b4c571aSmartin * Check if we have any (matching/auto-configured) wedges already
4359b4c571aSmartin */
4369b4c571aSmartin dkw = NULL;
4379b4c571aSmartin dkwl.dkwl_buf = dkw;
4389b4c571aSmartin dkwl.dkwl_bufsize = 0;
4399b4c571aSmartin if (ioctl(fd, DIOCLWEDGES, &dkwl) == 0) {
4409b4c571aSmartin /* do not even try to deal with any races at this point */
4419b4c571aSmartin bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
4429b4c571aSmartin dkw = malloc(bufsize);
4439b4c571aSmartin dkwl.dkwl_buf = dkw;
4449b4c571aSmartin dkwl.dkwl_bufsize = bufsize;
4459b4c571aSmartin if (dkw != NULL && ioctl(fd, DIOCLWEDGES, &dkwl) == 0) {
4469b4c571aSmartin for (dk = 0; dk < dkwl.dkwl_ncopied; dk++)
4479b4c571aSmartin update_part_from_wedge_info(parts, &dkw[dk]);
4489b4c571aSmartin }
4499b4c571aSmartin free(dkw);
4509b4c571aSmartin }
4519b4c571aSmartin
4524103857bSmartin close(fd);
4534103857bSmartin
4544103857bSmartin return &parts->dp;
4554103857bSmartin }
4564103857bSmartin
457ead2350fSmartin static size_t
gpt_cyl_size(const struct disk_partitions * arg)458ead2350fSmartin gpt_cyl_size(const struct disk_partitions *arg)
459ead2350fSmartin {
460ead2350fSmartin return MEG / 512;
461ead2350fSmartin }
462ead2350fSmartin
4634103857bSmartin static struct disk_partitions *
gpt_create_new(const char * disk,daddr_t start,daddr_t len,bool is_boot_drive,struct disk_partitions * parent)46486906049Smartin gpt_create_new(const char *disk, daddr_t start, daddr_t len,
465abce8cb3Smartin bool is_boot_drive, struct disk_partitions *parent)
4664103857bSmartin {
4674103857bSmartin struct gpt_disk_partitions *parts;
46886906049Smartin struct disk_geom geo;
4694103857bSmartin
4704103857bSmartin if (start != 0) {
4714103857bSmartin assert(0);
4724103857bSmartin return NULL;
4734103857bSmartin }
4744103857bSmartin
47586906049Smartin if (!get_disk_geom(disk, &geo))
47686906049Smartin return NULL;
47786906049Smartin
4787e7989f7Smartin parts = calloc(1, sizeof(*parts));
4794103857bSmartin if (!parts)
4804103857bSmartin return NULL;
4814103857bSmartin
4824103857bSmartin parts->dp.pscheme = &gpt_parts;
483f77b58b1Smartin parts->dp.disk = strdup(disk);
4844103857bSmartin
4854103857bSmartin gpt_md_init(is_boot_drive, &parts->max_num_parts, &parts->prologue,
4864103857bSmartin &parts->epilogue);
4874103857bSmartin
4884103857bSmartin parts->dp.disk_start = start;
4894103857bSmartin parts->dp.disk_size = len;
49086906049Smartin parts->dp.bytes_per_sector = geo.dg_secsize;
4914103857bSmartin parts->dp.free_space = len - start - parts->prologue - parts->epilogue;
4924103857bSmartin parts->has_gpt = false;
4934103857bSmartin
4944103857bSmartin return &parts->dp;
4954103857bSmartin }
4964103857bSmartin
4974103857bSmartin static bool
gpt_get_part_info(const struct disk_partitions * arg,part_id id,struct disk_part_info * info)4984103857bSmartin gpt_get_part_info(const struct disk_partitions *arg, part_id id,
4994103857bSmartin struct disk_part_info *info)
5004103857bSmartin {
5015b07b744Smartin static const struct part_type_desc gpt_unknown_type =
5025b07b744Smartin { .generic_ptype = PT_undef,
5035b07b744Smartin .short_desc = "<unknown>" };
5044103857bSmartin const struct gpt_disk_partitions *parts =
5054103857bSmartin (const struct gpt_disk_partitions*)arg;
5064103857bSmartin const struct gpt_part_entry *p = parts->partitions;
5074103857bSmartin part_id no;
5084103857bSmartin
5094103857bSmartin for (no = 0; p != NULL && no < id; no++)
5104103857bSmartin p = p->gp_next;
5114103857bSmartin
5124103857bSmartin if (no != id || p == NULL)
5134103857bSmartin return false;
5144103857bSmartin
5154103857bSmartin memset(info, 0, sizeof(*info));
5164103857bSmartin info->start = p->gp_start;
5174103857bSmartin info->size = p->gp_size;
5184103857bSmartin if (p->gp_type)
5194103857bSmartin info->nat_type = &p->gp_type->gent;
5205b07b744Smartin else
5215b07b744Smartin info->nat_type = &gpt_unknown_type;
5224103857bSmartin info->last_mounted = p->last_mounted;
5234103857bSmartin info->fs_type = p->fs_type;
5244103857bSmartin info->fs_sub_type = p->fs_sub_type;
525cb429b90Smartin info->fs_opt1 = p->fs_opt1;
526cb429b90Smartin info->fs_opt2 = p->fs_opt2;
527cb429b90Smartin info->fs_opt3 = p->fs_opt3;
528d9c70220Smartin if (p->gp_flags & GPEF_TARGET)
529d9c70220Smartin info->flags |= PTI_INSTALL_TARGET;
5304103857bSmartin
5314103857bSmartin return true;
5324103857bSmartin }
5334103857bSmartin
5344103857bSmartin static bool
gpt_get_part_attr_str(const struct disk_partitions * arg,part_id id,char * str,size_t avail_space)5354103857bSmartin gpt_get_part_attr_str(const struct disk_partitions *arg, part_id id,
5364103857bSmartin char *str, size_t avail_space)
5374103857bSmartin {
5384103857bSmartin const struct gpt_disk_partitions *parts =
5394103857bSmartin (const struct gpt_disk_partitions*)arg;
5404103857bSmartin const struct gpt_part_entry *p = parts->partitions;
5414103857bSmartin part_id no;
5424103857bSmartin static const char *flags = NULL;
5434103857bSmartin
5444103857bSmartin for (no = 0; p != NULL && no < id; no++)
5454103857bSmartin p = p->gp_next;
5464103857bSmartin
5474103857bSmartin if (no != id || p == NULL)
5484103857bSmartin return false;
5494103857bSmartin
5504103857bSmartin if (flags == NULL)
5514103857bSmartin flags = msg_string(MSG_gpt_flags);
5524103857bSmartin
5534103857bSmartin if (avail_space < 2)
5544103857bSmartin return false;
5554103857bSmartin
5564103857bSmartin if (p->gp_attr & GPT_ATTR_BOOT)
5574103857bSmartin *str++ = flags[0];
5584103857bSmartin *str = 0;
5594103857bSmartin
5604103857bSmartin return true;
5614103857bSmartin }
5624103857bSmartin
5634103857bSmartin /*
5644103857bSmartin * Find insert position and check for duplicates.
5654103857bSmartin * If all goes well, insert the new "entry" in the "list".
5664103857bSmartin * If there are collisions, report "no free space".
5674103857bSmartin * We keep all lists sorted by start sector number,
5684103857bSmartin */
5694103857bSmartin static bool
gpt_insert_part_into_list(struct gpt_disk_partitions * parts,struct gpt_part_entry ** list,struct gpt_part_entry * entry,const char ** err_msg,part_id * new_id)5704103857bSmartin gpt_insert_part_into_list(struct gpt_disk_partitions *parts,
5714103857bSmartin struct gpt_part_entry **list,
572*5aa145f6Smartin struct gpt_part_entry *entry, const char **err_msg, part_id *new_id)
5734103857bSmartin {
5744103857bSmartin struct gpt_part_entry *p, *last;
575*5aa145f6Smartin part_id pno;
5764103857bSmartin
5774103857bSmartin /* find the first entry past the new one (if any) */
578*5aa145f6Smartin for (pno = 0, last = NULL, p = *list; p != NULL;
579*5aa145f6Smartin last = p, p = p->gp_next, pno++) {
5804103857bSmartin if (p->gp_start > entry->gp_start)
5814103857bSmartin break;
5824103857bSmartin }
5834103857bSmartin
5844103857bSmartin /* check if last partition overlaps with new one */
5854103857bSmartin if (last) {
5864103857bSmartin if (last->gp_start + last->gp_size > entry->gp_start) {
5874103857bSmartin if (err_msg)
5884103857bSmartin *err_msg = msg_string(MSG_No_free_space);
5894103857bSmartin return false;
5904103857bSmartin }
5914103857bSmartin }
5924103857bSmartin
5934103857bSmartin if (p == NULL) {
5944103857bSmartin entry->gp_next = NULL;
5954103857bSmartin if (last != NULL) {
5964103857bSmartin last->gp_next = entry;
5974103857bSmartin }
5984103857bSmartin } else {
5994103857bSmartin /* check if new entry overlaps with next */
6004103857bSmartin if (entry->gp_start + entry->gp_size > p->gp_start) {
6014103857bSmartin if (err_msg)
6024103857bSmartin *err_msg = msg_string(MSG_No_free_space);
6034103857bSmartin return false;
6044103857bSmartin }
6054103857bSmartin
6064103857bSmartin entry->gp_next = p;
6074103857bSmartin if (last != NULL)
6084103857bSmartin last->gp_next = entry;
6094103857bSmartin else
6104103857bSmartin *list = entry;
6114103857bSmartin }
6124103857bSmartin if (*list == NULL)
6134103857bSmartin *list = entry;
614*5aa145f6Smartin if (new_id != NULL)
615*5aa145f6Smartin *new_id = pno;
6164103857bSmartin return true;
6174103857bSmartin }
6184103857bSmartin
6194103857bSmartin static bool
gpt_set_part_info(struct disk_partitions * arg,part_id id,const struct disk_part_info * info,const char ** err_msg)6204103857bSmartin gpt_set_part_info(struct disk_partitions *arg, part_id id,
6214103857bSmartin const struct disk_part_info *info, const char **err_msg)
6224103857bSmartin {
6234103857bSmartin struct gpt_disk_partitions *parts =
6244103857bSmartin (struct gpt_disk_partitions*)arg;
6254103857bSmartin struct gpt_part_entry *p = parts->partitions, *n;
6264103857bSmartin part_id no;
6274103857bSmartin daddr_t lendiff;
6284103857bSmartin
6294103857bSmartin for (no = 0; p != NULL && no < id; no++)
6304103857bSmartin p = p->gp_next;
6314103857bSmartin
6324103857bSmartin if (no != id || p == NULL)
6334103857bSmartin return false;
6344103857bSmartin
635d9c70220Smartin /* update target mark - we can only have one */
636c4c8db15Smartin if (info->flags & PTI_INSTALL_TARGET) {
637d9c70220Smartin p->gp_flags |= GPEF_TARGET;
638d9c70220Smartin for (n = parts->partitions; n != NULL; n = n->gp_next)
639d9c70220Smartin if (n != p)
640d9c70220Smartin n->gp_flags &= ~GPEF_TARGET;
641c4c8db15Smartin } else {
642c4c8db15Smartin p->gp_flags &= ~GPEF_TARGET;
643c4c8db15Smartin }
644d9c70220Smartin
6454103857bSmartin if ((p->gp_flags & GPEF_ON_DISK)) {
6464103857bSmartin if (info->start != p->gp_start) {
6474103857bSmartin /* partition moved, we need to delete and re-add */
6484103857bSmartin n = calloc(1, sizeof(*n));
6494103857bSmartin if (n == NULL) {
6504103857bSmartin if (err_msg)
6514103857bSmartin *err_msg = err_outofmem;
6524103857bSmartin return false;
6534103857bSmartin }
6544103857bSmartin *n = *p;
6554103857bSmartin p->gp_flags &= ~GPEF_ON_DISK;
6564103857bSmartin if (!gpt_insert_part_into_list(parts, &parts->obsolete,
657*5aa145f6Smartin n, err_msg, NULL))
6584103857bSmartin return false;
6594103857bSmartin } else if (info->size != p->gp_size) {
6604103857bSmartin p->gp_flags |= GPEF_RESIZED;
6614103857bSmartin }
6624103857bSmartin }
6634103857bSmartin
6644103857bSmartin p->gp_flags |= GPEF_MODIFIED;
6654103857bSmartin
6664103857bSmartin lendiff = info->size - p->gp_size;
6674103857bSmartin parts->dp.free_space -= lendiff;
6684103857bSmartin return gpt_info_to_part(p, info, err_msg);
6694103857bSmartin }
6704103857bSmartin
6714103857bSmartin static size_t
gpt_get_free_spaces_internal(const struct gpt_disk_partitions * parts,struct disk_part_free_space * result,size_t max_num_result,daddr_t min_space_size,daddr_t align,daddr_t start,daddr_t ignore)6724103857bSmartin gpt_get_free_spaces_internal(const struct gpt_disk_partitions *parts,
6734103857bSmartin struct disk_part_free_space *result, size_t max_num_result,
6744103857bSmartin daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
6754103857bSmartin {
6764103857bSmartin size_t cnt = 0;
6774103857bSmartin daddr_t s, e, from, size, end_of_disk;
6784103857bSmartin struct gpt_part_entry *p;
6794103857bSmartin
6804103857bSmartin if (align > 1)
6814103857bSmartin start = max(roundup(start, align), align);
6824103857bSmartin if (start < 0 || start < (daddr_t)parts->prologue)
6834103857bSmartin start = parts->prologue;
6844103857bSmartin if (parts->dp.disk_start != 0 && parts->dp.disk_start > start)
6854103857bSmartin start = parts->dp.disk_start;
6864103857bSmartin if (min_space_size < 1)
6874103857bSmartin min_space_size = 1;
6884103857bSmartin end_of_disk = parts->dp.disk_start + parts->dp.disk_size
6894103857bSmartin - parts->epilogue;
6904103857bSmartin from = start;
6914103857bSmartin while (from < end_of_disk && cnt < max_num_result) {
6924103857bSmartin again:
6934103857bSmartin size = parts->dp.disk_start + parts->dp.disk_size - from;
6944103857bSmartin start = from;
6954103857bSmartin if (start + size > end_of_disk)
6964103857bSmartin size = end_of_disk - start;
6974103857bSmartin for (p = parts->partitions; p != NULL; p = p->gp_next) {
6984103857bSmartin s = p->gp_start;
6994103857bSmartin e = p->gp_size + s;
7004103857bSmartin if (s == ignore)
7014103857bSmartin continue;
7024103857bSmartin if (e < from)
7034103857bSmartin continue;
7044103857bSmartin if (s <= from && e > from) {
7054103857bSmartin if (e - 1 >= end_of_disk)
7064103857bSmartin return cnt;
7074103857bSmartin from = e + 1;
7084103857bSmartin if (align > 1) {
7094103857bSmartin from = max(roundup(from, align), align);
7104103857bSmartin if (from >= end_of_disk) {
7114103857bSmartin size = 0;
7124103857bSmartin break;
7134103857bSmartin }
7144103857bSmartin }
7154103857bSmartin goto again;
7164103857bSmartin }
7174103857bSmartin if (s > from && s - from < size) {
7184103857bSmartin size = s - from;
7194103857bSmartin }
7204103857bSmartin }
7214103857bSmartin if (size >= min_space_size) {
7224103857bSmartin result->start = start;
7234103857bSmartin result->size = size;
7244103857bSmartin result++;
7254103857bSmartin cnt++;
7264103857bSmartin }
7274103857bSmartin from += size + 1;
7284103857bSmartin if (align > 1)
7294103857bSmartin from = max(roundup(from, align), align);
7304103857bSmartin }
7314103857bSmartin
7324103857bSmartin return cnt;
7334103857bSmartin }
7344103857bSmartin
7354103857bSmartin static daddr_t
gpt_max_free_space_at(const struct disk_partitions * arg,daddr_t start)7364103857bSmartin gpt_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
7374103857bSmartin {
7384103857bSmartin const struct gpt_disk_partitions *parts =
7394103857bSmartin (const struct gpt_disk_partitions*)arg;
7404103857bSmartin struct disk_part_free_space space;
7414103857bSmartin
7424103857bSmartin if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 0,
7434103857bSmartin start, start) == 1)
7444103857bSmartin return space.size;
7454103857bSmartin
7464103857bSmartin return 0;
7474103857bSmartin }
7484103857bSmartin
7494103857bSmartin static size_t
gpt_get_free_spaces(const struct disk_partitions * arg,struct disk_part_free_space * result,size_t max_num_result,daddr_t min_space_size,daddr_t align,daddr_t start,daddr_t ignore)7504103857bSmartin gpt_get_free_spaces(const struct disk_partitions *arg,
7514103857bSmartin struct disk_part_free_space *result, size_t max_num_result,
7524103857bSmartin daddr_t min_space_size, daddr_t align, daddr_t start,
7534103857bSmartin daddr_t ignore)
7544103857bSmartin {
7554103857bSmartin const struct gpt_disk_partitions *parts =
7564103857bSmartin (const struct gpt_disk_partitions*)arg;
7574103857bSmartin
7584103857bSmartin return gpt_get_free_spaces_internal(parts, result,
7594103857bSmartin max_num_result, min_space_size, align, start, ignore);
7604103857bSmartin }
7614103857bSmartin
7624103857bSmartin static void
gpt_match_ptype(const char * name,struct gpt_ptype_desc * t)7634103857bSmartin gpt_match_ptype(const char *name, struct gpt_ptype_desc *t)
7644103857bSmartin {
7654103857bSmartin size_t i;
7664103857bSmartin
7674103857bSmartin for (i = 0; i < __arraycount(gpt_fs_types); i++) {
7684103857bSmartin if (strcmp(name, gpt_fs_types[i].name) == 0) {
7694103857bSmartin t->gent.generic_ptype = gpt_fs_types[i].ptype;
7704103857bSmartin t->fsflags = gpt_fs_types[i].fsflags;
7714103857bSmartin t->default_fs_type = gpt_fs_types[i].fstype;
772f77b58b1Smartin
773f77b58b1Smartin /* recongnize special entries */
774f77b58b1Smartin if (gpt_native_root == NULL && i == 0)
775f77b58b1Smartin gpt_native_root = &t->gent;
776f77b58b1Smartin
7774103857bSmartin return;
7784103857bSmartin }
7794103857bSmartin }
7804103857bSmartin
7814103857bSmartin t->gent.generic_ptype = PT_unknown;
7824103857bSmartin t->fsflags = 0;
7834103857bSmartin t->default_fs_type = FS_BSDFFS;
7844103857bSmartin }
7854103857bSmartin
7864103857bSmartin static void
gpt_internal_add_ptype(const char * uid,const char * name,const char * desc)7874103857bSmartin gpt_internal_add_ptype(const char *uid, const char *name, const char *desc)
7884103857bSmartin {
789f77b58b1Smartin if (gpt_ptype_cnt >= gpt_ptype_alloc) {
790f77b58b1Smartin gpt_ptype_alloc = gpt_ptype_alloc ? 2*gpt_ptype_alloc
791f77b58b1Smartin : GPT_PTYPE_ALLOC;
792f77b58b1Smartin struct gpt_ptype_desc *nptypes = realloc(gpt_ptype_descs,
793f77b58b1Smartin gpt_ptype_alloc*sizeof(*gpt_ptype_descs));
794f77b58b1Smartin if (nptypes == 0)
795f77b58b1Smartin errx(EXIT_FAILURE, "out of memory");
796f77b58b1Smartin gpt_ptype_descs = nptypes;
797f77b58b1Smartin }
798f77b58b1Smartin
7994103857bSmartin strlcpy(gpt_ptype_descs[gpt_ptype_cnt].tid, uid,
8004103857bSmartin sizeof(gpt_ptype_descs[gpt_ptype_cnt].tid));
801f77b58b1Smartin gpt_ptype_descs[gpt_ptype_cnt].gent.short_desc = strdup(name);
802f77b58b1Smartin gpt_ptype_descs[gpt_ptype_cnt].gent.description = strdup(desc);
8034103857bSmartin gpt_match_ptype(name, &gpt_ptype_descs[gpt_ptype_cnt]);
8044103857bSmartin gpt_ptype_cnt++;
8054103857bSmartin }
8064103857bSmartin
8074103857bSmartin static void
gpt_init_ptypes(void)8084103857bSmartin gpt_init_ptypes(void)
8094103857bSmartin {
8104103857bSmartin if (gpt_ptype_cnt == 0)
8114103857bSmartin gpt_uuid_query(gpt_internal_add_ptype);
8124103857bSmartin }
8134103857bSmartin
814f77b58b1Smartin static void
gpt_cleanup(void)815f77b58b1Smartin gpt_cleanup(void)
816f77b58b1Smartin {
817f77b58b1Smartin /* free all of gpt_ptype_descs */
818f77b58b1Smartin for (size_t i = 0; i < gpt_ptype_cnt; i++) {
819f77b58b1Smartin free(__UNCONST(gpt_ptype_descs[i].gent.short_desc));
820f77b58b1Smartin free(__UNCONST(gpt_ptype_descs[i].gent.description));
821f77b58b1Smartin }
822f77b58b1Smartin free(gpt_ptype_descs);
823f77b58b1Smartin gpt_ptype_descs = NULL;
824f77b58b1Smartin gpt_ptype_cnt = gpt_ptype_alloc = 0;
825f77b58b1Smartin }
826f77b58b1Smartin
8274103857bSmartin static size_t
gpt_type_count(void)8284103857bSmartin gpt_type_count(void)
8294103857bSmartin {
8304103857bSmartin if (gpt_ptype_cnt == 0)
8314103857bSmartin gpt_init_ptypes();
8324103857bSmartin
8334103857bSmartin return gpt_ptype_cnt;
8344103857bSmartin }
8354103857bSmartin
8364103857bSmartin static const struct part_type_desc *
gpt_get_ptype(size_t ndx)8374103857bSmartin gpt_get_ptype(size_t ndx)
8384103857bSmartin {
8394103857bSmartin if (gpt_ptype_cnt == 0)
8404103857bSmartin gpt_init_ptypes();
8414103857bSmartin
8424103857bSmartin if (ndx >= gpt_ptype_cnt)
8434103857bSmartin return NULL;
8444103857bSmartin
8454103857bSmartin return &gpt_ptype_descs[ndx].gent;
8464103857bSmartin }
8474103857bSmartin
8484103857bSmartin static const struct part_type_desc *
gpt_get_generic_type(enum part_type gent)8494103857bSmartin gpt_get_generic_type(enum part_type gent)
8504103857bSmartin {
8514103857bSmartin if (gpt_ptype_cnt == 0)
8524103857bSmartin gpt_init_ptypes();
8534103857bSmartin
854f77b58b1Smartin if (gent == PT_root)
855f77b58b1Smartin return gpt_native_root;
856f77b58b1Smartin if (gent == PT_unknown)
857f77b58b1Smartin return NULL;
858f77b58b1Smartin
8594103857bSmartin for (size_t i = 0; i < gpt_ptype_cnt; i++)
8604103857bSmartin if (gpt_ptype_descs[i].gent.generic_ptype == gent)
8614103857bSmartin return &gpt_ptype_descs[i].gent;
8624103857bSmartin
8634103857bSmartin return NULL;
8644103857bSmartin }
8654103857bSmartin
8664103857bSmartin static const struct gpt_ptype_desc *
gpt_find_native_type(const struct part_type_desc * gent)8674103857bSmartin gpt_find_native_type(const struct part_type_desc *gent)
8684103857bSmartin {
8694103857bSmartin if (gpt_ptype_cnt == 0)
8704103857bSmartin gpt_init_ptypes();
8714103857bSmartin
8724103857bSmartin if (gent == NULL)
8734103857bSmartin return NULL;
8744103857bSmartin
8754103857bSmartin for (size_t i = 0; i < gpt_ptype_cnt; i++)
8764103857bSmartin if (gent == &gpt_ptype_descs[i].gent)
8774103857bSmartin return &gpt_ptype_descs[i];
8784103857bSmartin
8794103857bSmartin gent = gpt_get_generic_type(gent->generic_ptype);
8804103857bSmartin if (gent == NULL)
8814103857bSmartin return NULL;
8824103857bSmartin
8834103857bSmartin /* this can not recurse deeper than once, we would not have found a
8844103857bSmartin * generic type a few lines above if it would. */
8854103857bSmartin return gpt_find_native_type(gent);
8864103857bSmartin }
8874103857bSmartin
8884103857bSmartin static const struct gpt_ptype_desc *
gpt_find_guid_type(const char * uid)8894103857bSmartin gpt_find_guid_type(const char *uid)
8904103857bSmartin {
8914103857bSmartin if (gpt_ptype_cnt == 0)
8924103857bSmartin gpt_init_ptypes();
8934103857bSmartin
8944103857bSmartin if (uid == NULL || uid[0] == 0)
8954103857bSmartin return NULL;
8964103857bSmartin
8974103857bSmartin for (size_t i = 0; i < gpt_ptype_cnt; i++)
8984103857bSmartin if (strcmp(gpt_ptype_descs[i].tid, uid) == 0)
8994103857bSmartin return &gpt_ptype_descs[i];
9004103857bSmartin
9014103857bSmartin return NULL;
9024103857bSmartin }
9034103857bSmartin
9044103857bSmartin static const struct part_type_desc *
gpt_find_type(const char * desc)9054103857bSmartin gpt_find_type(const char *desc)
9064103857bSmartin {
9074103857bSmartin if (gpt_ptype_cnt == 0)
9084103857bSmartin gpt_init_ptypes();
9094103857bSmartin
9104103857bSmartin if (desc == NULL || desc[0] == 0)
9114103857bSmartin return NULL;
9124103857bSmartin
9134103857bSmartin for (size_t i = 0; i < gpt_ptype_cnt; i++)
9144103857bSmartin if (strcmp(gpt_ptype_descs[i].gent.short_desc, desc) == 0)
9154103857bSmartin return &gpt_ptype_descs[i].gent;
9164103857bSmartin
9174103857bSmartin return NULL;
9184103857bSmartin }
9194103857bSmartin
9204103857bSmartin static const struct part_type_desc *
gpt_get_fs_part_type(enum part_type pt,unsigned fstype,unsigned fs_sub_type)921194b0d85Smartin gpt_get_fs_part_type(enum part_type pt, unsigned fstype, unsigned fs_sub_type)
9224103857bSmartin {
9234103857bSmartin size_t i;
9244103857bSmartin
925abce8cb3Smartin /* Try with complete match (including part_type) first */
926194b0d85Smartin for (i = 0; i < __arraycount(gpt_fs_types); i++)
927194b0d85Smartin if (fstype == gpt_fs_types[i].fstype &&
928194b0d85Smartin pt == gpt_fs_types[i].ptype)
929194b0d85Smartin return gpt_find_type(gpt_fs_types[i].name);
930194b0d85Smartin
931194b0d85Smartin /* If that did not work, ignore part_type */
9324103857bSmartin for (i = 0; i < __arraycount(gpt_fs_types); i++)
9334103857bSmartin if (fstype == gpt_fs_types[i].fstype)
9344103857bSmartin return gpt_find_type(gpt_fs_types[i].name);
9354103857bSmartin
936f77b58b1Smartin return NULL;
937f77b58b1Smartin }
938f77b58b1Smartin
939abce8cb3Smartin static bool
gpt_get_default_fstype(const struct part_type_desc * nat_type,unsigned * fstype,unsigned * fs_sub_type)940abce8cb3Smartin gpt_get_default_fstype(const struct part_type_desc *nat_type,
941abce8cb3Smartin unsigned *fstype, unsigned *fs_sub_type)
942abce8cb3Smartin {
943abce8cb3Smartin const struct gpt_ptype_desc *gtype;
944abce8cb3Smartin
945abce8cb3Smartin gtype = gpt_find_native_type(nat_type);
946abce8cb3Smartin if (gtype == NULL)
947abce8cb3Smartin return false;
948abce8cb3Smartin
949abce8cb3Smartin *fstype = gtype->default_fs_type;
950abce8cb3Smartin #ifdef DEFAULT_UFS2
951abce8cb3Smartin if (gtype->default_fs_type == FS_BSDFFS)
952abce8cb3Smartin *fs_sub_type = 2;
953abce8cb3Smartin else
954abce8cb3Smartin #endif
955abce8cb3Smartin *fs_sub_type = 0;
956abce8cb3Smartin return true;
957abce8cb3Smartin }
958abce8cb3Smartin
959f77b58b1Smartin static const struct part_type_desc *
gpt_get_uuid_part_type(const uuid_t * id)960f77b58b1Smartin gpt_get_uuid_part_type(const uuid_t *id)
961f77b58b1Smartin {
962f77b58b1Smartin char str[GUID_STR_LEN], desc[GUID_STR_LEN + MENUSTRSIZE];
963f77b58b1Smartin const struct gpt_ptype_desc *t;
964f77b58b1Smartin char *guid = NULL;
965f77b58b1Smartin uint32_t err;
966f77b58b1Smartin
967f77b58b1Smartin uuid_to_string(id, &guid, &err);
968f77b58b1Smartin strlcpy(str, err == uuid_s_ok ? guid : "-", sizeof str);
969f77b58b1Smartin free(guid);
970f77b58b1Smartin
971f77b58b1Smartin t = gpt_find_guid_type(str);
972f77b58b1Smartin if (t == NULL) {
973f77b58b1Smartin snprintf(desc, sizeof desc, "%s (%s)",
974f77b58b1Smartin msg_string(MSG_custom_type), str);
975f77b58b1Smartin gpt_internal_add_ptype(str, str, desc);
976f77b58b1Smartin t = gpt_find_guid_type(str);
977f77b58b1Smartin assert(t != NULL);
978f77b58b1Smartin }
979f77b58b1Smartin return &t->gent;
980f77b58b1Smartin }
981f77b58b1Smartin
982f77b58b1Smartin static const struct part_type_desc *
gpt_create_custom_part_type(const char * custom,const char ** err_msg)983f77b58b1Smartin gpt_create_custom_part_type(const char *custom, const char **err_msg)
984f77b58b1Smartin {
985f77b58b1Smartin uuid_t id;
986f77b58b1Smartin uint32_t err;
987f77b58b1Smartin
988f77b58b1Smartin uuid_from_string(custom, &id, &err);
989f77b58b1Smartin if (err_msg != NULL &&
990f77b58b1Smartin (err == uuid_s_invalid_string_uuid || err == uuid_s_bad_version)) {
991f77b58b1Smartin *err_msg = MSG_invalid_guid;
992f77b58b1Smartin return NULL;
993f77b58b1Smartin }
994f77b58b1Smartin if (err != uuid_s_ok)
995f77b58b1Smartin return NULL;
996f77b58b1Smartin
997f77b58b1Smartin return gpt_get_uuid_part_type(&id);
998f77b58b1Smartin }
999f77b58b1Smartin
1000f77b58b1Smartin static const struct part_type_desc *
gpt_create_unknown_part_type(void)1001f77b58b1Smartin gpt_create_unknown_part_type(void)
1002f77b58b1Smartin {
1003f77b58b1Smartin uuid_t id;
1004f77b58b1Smartin uint32_t err;
1005f77b58b1Smartin
1006f77b58b1Smartin uuid_create(&id, &err);
1007f77b58b1Smartin if (err != uuid_s_ok)
1008f77b58b1Smartin return NULL;
1009f77b58b1Smartin
1010f77b58b1Smartin return gpt_get_uuid_part_type(&id);
10114103857bSmartin }
10124103857bSmartin
10134103857bSmartin static daddr_t
gpt_get_part_alignment(const struct disk_partitions * parts)10144103857bSmartin gpt_get_part_alignment(const struct disk_partitions *parts)
10154103857bSmartin {
10164103857bSmartin
10174103857bSmartin assert(parts->disk_size > 0);
10184103857bSmartin if (parts->disk_size < 0)
10194103857bSmartin return 1;
10204103857bSmartin
1021d3789543Sandvar /* Use 1MB offset/alignment for large (>128GB) disks */
10224103857bSmartin if (parts->disk_size > HUGE_DISK_SIZE)
10234103857bSmartin return 2048;
10244103857bSmartin else if (parts->disk_size > TINY_DISK_SIZE)
10254103857bSmartin return 64;
10264103857bSmartin else
10274103857bSmartin return 4;
10284103857bSmartin }
10294103857bSmartin
10304103857bSmartin static bool
gpt_can_add_partition(const struct disk_partitions * arg)10314103857bSmartin gpt_can_add_partition(const struct disk_partitions *arg)
10324103857bSmartin {
10334103857bSmartin const struct gpt_disk_partitions *parts =
10344103857bSmartin (const struct gpt_disk_partitions*)arg;
10354103857bSmartin struct disk_part_free_space space;
10364103857bSmartin daddr_t align;
10374103857bSmartin
10384103857bSmartin if (parts->dp.num_part >= parts->max_num_parts)
10394103857bSmartin return false;
10404103857bSmartin
10414103857bSmartin align = gpt_get_part_alignment(arg);
10424103857bSmartin if (parts->dp.free_space <= align)
10434103857bSmartin return false;
10444103857bSmartin
10454103857bSmartin if (gpt_get_free_spaces_internal(parts, &space, 1, align, align,
10464103857bSmartin 0, -1) < 1)
10474103857bSmartin return false;
10484103857bSmartin
10494103857bSmartin return true;
10504103857bSmartin }
10514103857bSmartin
10524103857bSmartin static bool
gpt_info_to_part(struct gpt_part_entry * p,const struct disk_part_info * info,const char ** err_msg)10534103857bSmartin gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info,
10544103857bSmartin const char **err_msg)
10554103857bSmartin {
10564103857bSmartin p->gp_type = gpt_find_native_type(info->nat_type);
10574103857bSmartin p->gp_start = info->start;
10584103857bSmartin p->gp_size = info->size;
10594103857bSmartin if (info->last_mounted != NULL && info->last_mounted !=
10604103857bSmartin p->last_mounted) {
10614103857bSmartin free(__UNCONST(p->last_mounted));
10624103857bSmartin p->last_mounted = strdup(info->last_mounted);
10634103857bSmartin }
10644103857bSmartin p->fs_type = info->fs_type;
10654103857bSmartin p->fs_sub_type = info->fs_sub_type;
1066cb429b90Smartin p->fs_opt1 = info->fs_opt1;
1067cb429b90Smartin p->fs_opt2 = info->fs_opt2;
1068cb429b90Smartin p->fs_opt3 = info->fs_opt3;
10694103857bSmartin
10704103857bSmartin return true;
10714103857bSmartin }
10724103857bSmartin
10734103857bSmartin static part_id
gpt_add_part(struct disk_partitions * arg,const struct disk_part_info * info,const char ** err_msg)10744103857bSmartin gpt_add_part(struct disk_partitions *arg,
10754103857bSmartin const struct disk_part_info *info, const char **err_msg)
10764103857bSmartin {
10774103857bSmartin struct gpt_disk_partitions *parts =
10784103857bSmartin (struct gpt_disk_partitions*)arg;
10794103857bSmartin struct disk_part_free_space space;
10804103857bSmartin struct disk_part_info data = *info;
1081c4c8db15Smartin struct gpt_part_entry *p, *n;
1082*5aa145f6Smartin part_id pno;
10834103857bSmartin bool ok;
10844103857bSmartin
10854103857bSmartin if (err_msg != NULL)
10864103857bSmartin *err_msg = NULL;
10874103857bSmartin
10884103857bSmartin if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 1,
10894103857bSmartin info->start, -1) < 1) {
10904103857bSmartin if (err_msg)
10914103857bSmartin *err_msg = msg_string(MSG_No_free_space);
10924103857bSmartin return NO_PART;
10934103857bSmartin }
10944103857bSmartin if (parts->dp.num_part >= parts->max_num_parts) {
10954103857bSmartin if (err_msg)
10964103857bSmartin *err_msg = msg_string(MSG_err_too_many_partitions);
10974103857bSmartin return NO_PART;
10984103857bSmartin }
10994103857bSmartin
11004103857bSmartin if (data.size > space.size)
11014103857bSmartin data.size = space.size;
11024103857bSmartin
11034103857bSmartin p = calloc(1, sizeof(*p));
11044103857bSmartin if (p == NULL) {
11054103857bSmartin if (err_msg != NULL)
11064103857bSmartin *err_msg = INTERNAL_ERROR;
11074103857bSmartin return NO_PART;
11084103857bSmartin }
11094103857bSmartin if (!gpt_info_to_part(p, &data, err_msg)) {
11104103857bSmartin free(p);
11114103857bSmartin return NO_PART;
11124103857bSmartin }
11134103857bSmartin p->gp_flags |= GPEF_MODIFIED;
1114*5aa145f6Smartin ok = gpt_insert_part_into_list(parts, &parts->partitions, p,
1115*5aa145f6Smartin err_msg, &pno);
11164103857bSmartin if (ok) {
1117c4c8db15Smartin if (info->flags & PTI_INSTALL_TARGET) {
1118c4c8db15Smartin /* update target mark - we can only have one */
1119c4c8db15Smartin p->gp_flags |= GPEF_TARGET;
1120c4c8db15Smartin for (n = parts->partitions; n != NULL; n = n->gp_next)
1121c4c8db15Smartin if (n != p)
1122c4c8db15Smartin n->gp_flags &= ~GPEF_TARGET;
1123c4c8db15Smartin }
1124c4c8db15Smartin
11254103857bSmartin parts->dp.num_part++;
11264103857bSmartin parts->dp.free_space -= p->gp_size;
1127*5aa145f6Smartin return pno;
11284103857bSmartin } else {
11294103857bSmartin free(p);
11304103857bSmartin return NO_PART;
11314103857bSmartin }
11324103857bSmartin }
11334103857bSmartin
11344103857bSmartin static bool
gpt_delete_partition(struct disk_partitions * arg,part_id id,const char ** err_msg)11354103857bSmartin gpt_delete_partition(struct disk_partitions *arg, part_id id,
11364103857bSmartin const char **err_msg)
11374103857bSmartin {
11384103857bSmartin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
11394103857bSmartin struct gpt_part_entry *p, *last = NULL;
11404103857bSmartin part_id i;
11414103857bSmartin bool res;
11424103857bSmartin
11434103857bSmartin if (parts->dp.num_part == 0)
11444103857bSmartin return false;
11454103857bSmartin
11464103857bSmartin for (i = 0, p = parts->partitions;
11474103857bSmartin i != id && i < parts->dp.num_part && p != NULL;
11484103857bSmartin i++, p = p->gp_next)
11494103857bSmartin last = p;
11504103857bSmartin
11514103857bSmartin if (p == NULL) {
11524103857bSmartin if (err_msg)
11534103857bSmartin *err_msg = INTERNAL_ERROR;
11544103857bSmartin return false;
11554103857bSmartin }
11564103857bSmartin
11574103857bSmartin if (last == NULL)
11584103857bSmartin parts->partitions = p->gp_next;
11594103857bSmartin else
11604103857bSmartin last->gp_next = p->gp_next;
11614103857bSmartin
11624103857bSmartin res = true;
11634103857bSmartin if (p->gp_flags & GPEF_ON_DISK) {
11644103857bSmartin if (!gpt_insert_part_into_list(parts, &parts->obsolete,
1165*5aa145f6Smartin p, err_msg, NULL))
11664103857bSmartin res = false;
11674103857bSmartin } else {
11684103857bSmartin free(p);
11694103857bSmartin }
11704103857bSmartin
11714103857bSmartin if (res) {
11724103857bSmartin parts->dp.num_part--;
11734103857bSmartin parts->dp.free_space += p->gp_size;
11744103857bSmartin }
11754103857bSmartin
11764103857bSmartin return res;
11774103857bSmartin }
11784103857bSmartin
11794103857bSmartin static bool
gpt_delete_all_partitions(struct disk_partitions * arg)11804103857bSmartin gpt_delete_all_partitions(struct disk_partitions *arg)
11814103857bSmartin {
11824103857bSmartin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
11834103857bSmartin
11844103857bSmartin while (parts->dp.num_part > 0) {
11854103857bSmartin if (!gpt_delete_partition(&parts->dp, 0, NULL))
11864103857bSmartin return false;
11874103857bSmartin }
11884103857bSmartin
11894103857bSmartin return true;
11904103857bSmartin }
11914103857bSmartin
11924103857bSmartin static bool
gpt_read_part(const char * disk,daddr_t start,struct gpt_part_entry * p)11934103857bSmartin gpt_read_part(const char *disk, daddr_t start, struct gpt_part_entry *p)
11944103857bSmartin {
11954103857bSmartin char *textbuf, *t, *tt;
11964103857bSmartin static const char expected_hdr[] = "Details for index ";
11974103857bSmartin
11984103857bSmartin /* run gpt show for this partition */
11994103857bSmartin if (collect(T_OUTPUT, &textbuf,
12004103857bSmartin "gpt -r show -b %" PRIu64 " %s 2>/dev/null", start, disk) < 1)
12014103857bSmartin return false;
12024103857bSmartin
12034103857bSmartin /*
12044103857bSmartin * gpt show should respond with single partition details, but will
12054103857bSmartin * fall back to "show -a" output if something is wrong
12064103857bSmartin */
12074103857bSmartin t = strtok(textbuf, "\n"); /* first line is special */
12084103857bSmartin if (strncmp(t, expected_hdr, sizeof(expected_hdr)-1) != 0) {
12094103857bSmartin free(textbuf);
12104103857bSmartin return false;
12114103857bSmartin }
12124103857bSmartin
12134103857bSmartin /* parse output into "old" */
12144103857bSmartin while ((t = strtok(NULL, "\n")) != NULL) {
12154103857bSmartin tt = strsep(&t, " \t");
12164103857bSmartin if (strlen(tt) == 0)
12174103857bSmartin continue;
12184103857bSmartin gpt_add_info(p, tt, t, true);
12194103857bSmartin }
12204103857bSmartin free(textbuf);
12214103857bSmartin
12224103857bSmartin return true;
12234103857bSmartin }
12244103857bSmartin
12254103857bSmartin static bool
gpt_apply_attr(const char * disk,const char * cmd,off_t start,uint todo)12264103857bSmartin gpt_apply_attr(const char *disk, const char *cmd, off_t start, uint todo)
12274103857bSmartin {
12284103857bSmartin size_t i;
12294103857bSmartin char attr_str[STRSIZE];
12304103857bSmartin
12314103857bSmartin if (todo == 0)
12324103857bSmartin return true;
12334103857bSmartin
12344103857bSmartin strcpy(attr_str, "-a ");
12354103857bSmartin for (i = 0; todo != 0; i++) {
12364103857bSmartin if (!(gpt_avail_attrs[i].flag & todo))
12374103857bSmartin continue;
12384103857bSmartin todo &= ~gpt_avail_attrs[i].flag;
12394103857bSmartin if (attr_str[0])
12404103857bSmartin strlcat(attr_str, ",",
12414103857bSmartin sizeof(attr_str));
12424103857bSmartin strlcat(attr_str,
12434103857bSmartin gpt_avail_attrs[i].name,
12444103857bSmartin sizeof(attr_str));
12454103857bSmartin }
12464103857bSmartin if (run_program(RUN_SILENT,
12474103857bSmartin "gpt %s %s -b %" PRIu64 " %s", cmd, attr_str, start, disk) != 0)
12484103857bSmartin return false;
12494103857bSmartin return true;
12504103857bSmartin }
12514103857bSmartin
12524103857bSmartin /*
12534103857bSmartin * Modify an existing on-disk partition.
12544103857bSmartin * Start and size can not be changed here, caller needs to deal
12554103857bSmartin * with that kind of changes upfront.
12564103857bSmartin */
12574103857bSmartin static bool
gpt_modify_part(const char * disk,struct gpt_part_entry * p)12584103857bSmartin gpt_modify_part(const char *disk, struct gpt_part_entry *p)
12594103857bSmartin {
12604103857bSmartin struct gpt_part_entry old;
12614103857bSmartin uint todo_set, todo_unset;
12624103857bSmartin
12634103857bSmartin /*
12644103857bSmartin * Query current on-disk state
12654103857bSmartin */
12664103857bSmartin memset(&old, 0, sizeof old);
12674103857bSmartin if (!gpt_read_part(disk, p->gp_start, &old))
12684103857bSmartin return false;
12694103857bSmartin
12704103857bSmartin /* Reject unsupported changes */
12714103857bSmartin if (old.gp_start != p->gp_start || old.gp_size != p->gp_size)
12724103857bSmartin return false;
12734103857bSmartin
12744103857bSmartin /*
12754103857bSmartin * GUID should never change, but the internal copy
12764103857bSmartin * may not yet know it.
12774103857bSmartin */
12784103857bSmartin strcpy(p->gp_id, old.gp_id);
12794103857bSmartin
12804103857bSmartin /* Check type */
12814103857bSmartin if (p->gp_type != old.gp_type) {
12824103857bSmartin if (run_program(RUN_SILENT,
1283852192b2Smartin "gpt type -b %" PRIu64 " -T %s %s",
12844103857bSmartin p->gp_start, p->gp_type->tid, disk) != 0)
12854103857bSmartin return false;
12864103857bSmartin }
12874103857bSmartin
12884103857bSmartin /* Check label */
12894103857bSmartin if (strcmp(p->gp_label, old.gp_label) != 0) {
12904103857bSmartin if (run_program(RUN_SILENT,
12916f0d7230Smartin "gpt label -b %" PRIu64 " -l \'%s\' %s",
12924103857bSmartin p->gp_start, p->gp_label, disk) != 0)
12934103857bSmartin return false;
12944103857bSmartin }
12954103857bSmartin
12964103857bSmartin /* Check attributes */
12974103857bSmartin if (p->gp_attr != old.gp_attr) {
12984103857bSmartin if (p->gp_attr == 0) {
12994103857bSmartin if (run_program(RUN_SILENT,
13004103857bSmartin "gpt set -N -b %" PRIu64 " %s",
13014103857bSmartin p->gp_start, disk) != 0)
13024103857bSmartin return false;
13034103857bSmartin } else {
13044103857bSmartin todo_set = (p->gp_attr ^ old.gp_attr) & p->gp_attr;
13054103857bSmartin todo_unset = (p->gp_attr ^ old.gp_attr) & old.gp_attr;
13064103857bSmartin if (!gpt_apply_attr(disk, "unset", p->gp_start,
13074103857bSmartin todo_unset))
13084103857bSmartin return false;
13094103857bSmartin if (!gpt_apply_attr(disk, "set", p->gp_start,
13104103857bSmartin todo_set))
13114103857bSmartin return false;
13124103857bSmartin }
13134103857bSmartin }
13144103857bSmartin
13154103857bSmartin return true;
13164103857bSmartin }
13174103857bSmartin
13184103857bSmartin /*
13194103857bSmartin * verbatim copy from sys/dev/dkwedge/dkwedge_bsdlabel.c:
13204103857bSmartin * map FS_* to wedge strings
13214103857bSmartin */
13224103857bSmartin static const char *
bsdlabel_fstype_to_str(uint8_t fstype)13234103857bSmartin bsdlabel_fstype_to_str(uint8_t fstype)
13244103857bSmartin {
13254103857bSmartin const char *str;
13264103857bSmartin
13274103857bSmartin /*
13284103857bSmartin * For each type known to FSTYPE_DEFN (from <sys/disklabel.h>),
13294103857bSmartin * a suitable case branch will convert the type number to a string.
13304103857bSmartin */
13314103857bSmartin switch (fstype) {
13324103857bSmartin #define FSTYPE_TO_STR_CASE(tag, number, name, fsck, mount) \
13334103857bSmartin case __CONCAT(FS_,tag): str = __CONCAT(DKW_PTYPE_,tag); break;
13344103857bSmartin FSTYPE_DEFN(FSTYPE_TO_STR_CASE)
13354103857bSmartin #undef FSTYPE_TO_STR_CASE
13364103857bSmartin default: str = NULL; break;
13374103857bSmartin }
13384103857bSmartin
13394103857bSmartin return (str);
13404103857bSmartin }
13414103857bSmartin
13429238070cSmartin /*
13439238070cSmartin * diskfd is an open file descriptor for a disk we had trouble with
13449238070cSmartin * creating some new wedges.
13459238070cSmartin * Go through all wedges actually on that disk, check if we have a
13469238070cSmartin * record for them and remove all others.
13479238070cSmartin * This should sync our internal model of partitions with the real state.
13489238070cSmartin */
13499238070cSmartin static void
gpt_sanitize(int diskfd,const struct gpt_disk_partitions * parts,struct gpt_part_entry * ignore)13509238070cSmartin gpt_sanitize(int diskfd, const struct gpt_disk_partitions *parts,
13519238070cSmartin struct gpt_part_entry *ignore)
13529238070cSmartin {
13539238070cSmartin struct dkwedge_info *dkw, delw;
13549238070cSmartin struct dkwedge_list dkwl;
13559238070cSmartin size_t bufsize;
13569238070cSmartin u_int i;
13579238070cSmartin
13589238070cSmartin dkw = NULL;
13599238070cSmartin dkwl.dkwl_buf = dkw;
13609238070cSmartin dkwl.dkwl_bufsize = 0;
13619238070cSmartin
13629238070cSmartin /* get a list of all wedges */
13639238070cSmartin for (;;) {
13649238070cSmartin if (ioctl(diskfd, DIOCLWEDGES, &dkwl) == -1)
13659238070cSmartin return;
13669238070cSmartin if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied)
13679238070cSmartin break;
13689238070cSmartin bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
13699238070cSmartin if (dkwl.dkwl_bufsize < bufsize) {
13709238070cSmartin dkw = realloc(dkwl.dkwl_buf, bufsize);
13719238070cSmartin if (dkw == NULL)
13729238070cSmartin return;
13739238070cSmartin dkwl.dkwl_buf = dkw;
13749238070cSmartin dkwl.dkwl_bufsize = bufsize;
13759238070cSmartin }
13769238070cSmartin }
13779238070cSmartin
13789238070cSmartin /* try to remove all the ones we do not know about */
13799238070cSmartin for (i = 0; i < dkwl.dkwl_nwedges; i++) {
13809238070cSmartin bool found = false;
13819238070cSmartin const char *devname = dkw[i].dkw_devname;
13829238070cSmartin
13839238070cSmartin for (struct gpt_part_entry *pe = parts->partitions;
13849238070cSmartin pe != NULL; pe = pe->gp_next) {
13859238070cSmartin if (pe == ignore)
13869238070cSmartin continue;
13879238070cSmartin if ((pe->gp_flags & GPEF_WEDGE) &&
13889238070cSmartin strcmp(pe->gp_dev_name, devname) == 0) {
13899238070cSmartin found = true;
13909238070cSmartin break;
13919238070cSmartin }
13929238070cSmartin }
13939238070cSmartin if (found)
13949238070cSmartin continue;
13959238070cSmartin memset(&delw, 0, sizeof(delw));
1396f15fffe7Smartin strlcpy(delw.dkw_devname, devname, sizeof(delw.dkw_devname));
13979238070cSmartin (void)ioctl(diskfd, DIOCDWEDGE, &delw);
13989238070cSmartin }
13999238070cSmartin
14009238070cSmartin /* cleanup */
14019238070cSmartin free(dkw);
14029238070cSmartin }
14039238070cSmartin
14044103857bSmartin static bool
gpt_add_wedge(const char * disk,struct gpt_part_entry * p,const struct gpt_disk_partitions * parts)14059238070cSmartin gpt_add_wedge(const char *disk, struct gpt_part_entry *p,
14069238070cSmartin const struct gpt_disk_partitions *parts)
14074103857bSmartin {
14084103857bSmartin struct dkwedge_info dkw;
14094103857bSmartin const char *tname;
14104103857bSmartin char diskpath[MAXPATHLEN];
14114103857bSmartin int fd;
14124103857bSmartin
14134103857bSmartin memset(&dkw, 0, sizeof(dkw));
14144103857bSmartin tname = bsdlabel_fstype_to_str(p->fs_type);
14154103857bSmartin if (tname)
14164103857bSmartin strlcpy(dkw.dkw_ptype, tname, sizeof(dkw.dkw_ptype));
14174103857bSmartin
14184103857bSmartin strlcpy((char*)&dkw.dkw_wname, p->gp_id, sizeof(dkw.dkw_wname));
14194103857bSmartin dkw.dkw_offset = p->gp_start;
14204103857bSmartin dkw.dkw_size = p->gp_size;
1421abce8cb3Smartin if (dkw.dkw_wname[0] == 0) {
1422abce8cb3Smartin if (p->gp_label[0] != 0)
1423abce8cb3Smartin strlcpy((char*)&dkw.dkw_wname,
1424abce8cb3Smartin p->gp_label, sizeof(dkw.dkw_wname));
1425abce8cb3Smartin }
1426abce8cb3Smartin if (dkw.dkw_wname[0] == 0) {
1427abce8cb3Smartin snprintf((char*)dkw.dkw_wname, sizeof dkw.dkw_wname,
1428abce8cb3Smartin "%s_%" PRIi64 "@%" PRIi64, disk, p->gp_size, p->gp_start);
1429abce8cb3Smartin }
14304103857bSmartin
14314103857bSmartin fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
14324103857bSmartin if (fd < 0)
14334103857bSmartin return false;
14344103857bSmartin if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
14359238070cSmartin if (errno == EINVAL) {
14369238070cSmartin /* sanitize existing wedges and try again */
14379238070cSmartin gpt_sanitize(fd, parts, p);
14389238070cSmartin if (ioctl(fd, DIOCAWEDGE, &dkw) == 0)
14399238070cSmartin goto ok;
14409238070cSmartin }
14414103857bSmartin close(fd);
14424103857bSmartin return false;
14434103857bSmartin }
14449238070cSmartin ok:
14454103857bSmartin close(fd);
14464103857bSmartin
14474103857bSmartin strlcpy(p->gp_dev_name, dkw.dkw_devname, sizeof(p->gp_dev_name));
14484103857bSmartin p->gp_flags |= GPEF_WEDGE;
14494103857bSmartin return true;
14504103857bSmartin }
14514103857bSmartin
14526f0d7230Smartin static void
escape_spaces(char * dest,const char * src)14536f0d7230Smartin escape_spaces(char *dest, const char *src)
14546f0d7230Smartin {
14556f0d7230Smartin unsigned char c;
14566f0d7230Smartin
14576f0d7230Smartin while (*src) {
14586f0d7230Smartin c = *src++;
14596f0d7230Smartin if (isspace(c) || c == '\\')
14606f0d7230Smartin *dest++ = '\\';
14616f0d7230Smartin *dest++ = c;
14626f0d7230Smartin }
14636f0d7230Smartin *dest = 0;
14646f0d7230Smartin }
14656f0d7230Smartin
14664103857bSmartin static bool
gpt_get_part_device(const struct disk_partitions * arg,part_id id,char * devname,size_t max_devname_len,int * part,enum dev_name_usage usage,bool with_path,bool life)14674103857bSmartin gpt_get_part_device(const struct disk_partitions *arg,
14684103857bSmartin part_id id, char *devname, size_t max_devname_len, int *part,
1469abce8cb3Smartin enum dev_name_usage usage, bool with_path, bool life)
14704103857bSmartin {
14714103857bSmartin const struct gpt_disk_partitions *parts =
14724103857bSmartin (const struct gpt_disk_partitions*)arg;
14734103857bSmartin struct gpt_part_entry *p = parts->partitions;
14746f0d7230Smartin char tmpname[GPT_LABEL_LEN*2];
14754103857bSmartin part_id no;
14764103857bSmartin
14774103857bSmartin
14784103857bSmartin for (no = 0; p != NULL && no < id; no++)
14794103857bSmartin p = p->gp_next;
14804103857bSmartin
14814103857bSmartin if (no != id || p == NULL)
14824103857bSmartin return false;
14834103857bSmartin
14844103857bSmartin if (part)
14854103857bSmartin *part = -1;
14864103857bSmartin
1487abce8cb3Smartin if (usage == logical_name && p->gp_label[0] == 0 && p->gp_id[0] == 0)
1488abce8cb3Smartin usage = plain_name;
1489abce8cb3Smartin if (usage == plain_name || usage == raw_dev_name)
1490abce8cb3Smartin life = true;
14912f5e5e5dSmartin if (!(p->gp_flags & GPEF_WEDGE) && life &&
14929238070cSmartin !gpt_add_wedge(arg->disk, p, parts))
14932f5e5e5dSmartin return false;
14944103857bSmartin
14954103857bSmartin switch (usage) {
14964103857bSmartin case logical_name:
14976f0d7230Smartin if (p->gp_label[0] != 0) {
14986f0d7230Smartin escape_spaces(tmpname, p->gp_label);
14994103857bSmartin snprintf(devname, max_devname_len,
15006f0d7230Smartin "NAME=%s", tmpname);
15016f0d7230Smartin } else {
15024103857bSmartin snprintf(devname, max_devname_len,
15034103857bSmartin "NAME=%s", p->gp_id);
15046f0d7230Smartin }
15054103857bSmartin break;
15064103857bSmartin case plain_name:
15074103857bSmartin assert(p->gp_flags & GPEF_WEDGE);
15084103857bSmartin if (with_path)
15094103857bSmartin snprintf(devname, max_devname_len, _PATH_DEV "%s",
15104103857bSmartin p->gp_dev_name);
15114103857bSmartin else
15124103857bSmartin strlcpy(devname, p->gp_dev_name, max_devname_len);
15134103857bSmartin break;
15144103857bSmartin case raw_dev_name:
15154103857bSmartin assert(p->gp_flags & GPEF_WEDGE);
15164103857bSmartin if (with_path)
15174103857bSmartin snprintf(devname, max_devname_len, _PATH_DEV "r%s",
15184103857bSmartin p->gp_dev_name);
15194103857bSmartin else
15204103857bSmartin snprintf(devname, max_devname_len, "r%s",
15214103857bSmartin p->gp_dev_name);
15224103857bSmartin break;
15234103857bSmartin default:
15244103857bSmartin return false;
15254103857bSmartin }
15264103857bSmartin
15274103857bSmartin return true;
15284103857bSmartin }
15294103857bSmartin
15304103857bSmartin static bool
gpt_write_to_disk(struct disk_partitions * arg)15314103857bSmartin gpt_write_to_disk(struct disk_partitions *arg)
15324103857bSmartin {
15334103857bSmartin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
15344103857bSmartin struct gpt_part_entry *p, *n;
15356f0d7230Smartin char label_arg[sizeof(p->gp_label) + 10];
15364103857bSmartin char diskpath[MAXPATHLEN];
15374103857bSmartin int fd, bits = 0;
15384103857bSmartin bool root_is_new = false, efi_is_new = false;
15394103857bSmartin part_id root_id = NO_PART, efi_id = NO_PART, pno;
15404103857bSmartin
15414103857bSmartin /*
15424103857bSmartin * Remove all wedges on this disk - they may become invalid and we
15434103857bSmartin * have no easy way to associate them with the partitioning data.
15444103857bSmartin * Instead we will explicitly request creation of wedges on demand
15454103857bSmartin * later.
15464103857bSmartin */
15474103857bSmartin fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0);
15484103857bSmartin if (fd < 0)
15494103857bSmartin return false;
15504103857bSmartin if (ioctl(fd, DIOCRMWEDGES, &bits) == -1)
15514103857bSmartin return false;
15524103857bSmartin close(fd);
15534103857bSmartin
15544103857bSmartin /*
1555f77b58b1Smartin * Collect first root and efi partition (if available), clear
1556f77b58b1Smartin * "have wedge" flags.
15574103857bSmartin */
15584103857bSmartin for (pno = 0, p = parts->partitions; p != NULL; p = p->gp_next, pno++) {
1559f77b58b1Smartin p->gp_flags &= ~GPEF_WEDGE;
15605b07b744Smartin if (root_id == NO_PART && p->gp_type != NULL) {
15614103857bSmartin if (p->gp_type->gent.generic_ptype == PT_root &&
1562d9c70220Smartin (p->gp_flags & GPEF_TARGET)) {
15634103857bSmartin root_id = pno;
15644103857bSmartin root_is_new = !(p->gp_flags & GPEF_ON_DISK);
15654103857bSmartin } else if (efi_id == NO_PART &&
15664103857bSmartin p->gp_type->gent.generic_ptype == PT_EFI_SYSTEM) {
15674103857bSmartin efi_id = pno;
15684103857bSmartin efi_is_new = !(p->gp_flags & GPEF_ON_DISK);
15694103857bSmartin }
15704103857bSmartin }
15714103857bSmartin }
15724103857bSmartin
15734103857bSmartin /*
15744103857bSmartin * If no GPT on disk yet, create it.
15754103857bSmartin */
15764103857bSmartin if (!parts->has_gpt) {
15774103857bSmartin char limit[30];
15784103857bSmartin
15794103857bSmartin if (parts->max_num_parts > 0)
15804103857bSmartin sprintf(limit, "-p %zu", parts->max_num_parts);
15814103857bSmartin else
15824103857bSmartin limit[0] = 0;
15834103857bSmartin if (run_program(RUN_SILENT, "gpt create %s %s",
15844103857bSmartin limit, parts->dp.disk))
15854103857bSmartin return false;
15864103857bSmartin parts->has_gpt = true;
15874103857bSmartin }
15884103857bSmartin
15894103857bSmartin /*
15904103857bSmartin * Delete all old partitions
15914103857bSmartin */
15924103857bSmartin for (p = parts->obsolete; p != NULL; p = n) {
15934103857bSmartin run_program(RUN_SILENT, "gpt -n remove -b %" PRIu64 " %s",
15944103857bSmartin p->gp_start, arg->disk);
15954103857bSmartin n = p->gp_next;
15964103857bSmartin free(p);
15974103857bSmartin }
15984103857bSmartin parts->obsolete = NULL;
15994103857bSmartin
16004103857bSmartin /*
16014103857bSmartin * Modify existing but changed partitions
16024103857bSmartin */
16034103857bSmartin for (p = parts->partitions; p != NULL; p = p->gp_next) {
16044103857bSmartin if (!(p->gp_flags & GPEF_ON_DISK))
16054103857bSmartin continue;
16064103857bSmartin
16074103857bSmartin if (p->gp_flags & GPEF_RESIZED) {
16084103857bSmartin run_program(RUN_SILENT,
16094103857bSmartin "gpt -n resize -b %" PRIu64 " -s %" PRIu64 "s %s",
16104103857bSmartin p->gp_start, p->gp_size, arg->disk);
16114103857bSmartin p->gp_flags &= ~GPEF_RESIZED;
16124103857bSmartin }
16134103857bSmartin
16144103857bSmartin if (!(p->gp_flags & GPEF_MODIFIED))
16154103857bSmartin continue;
16164103857bSmartin
16174103857bSmartin if (!gpt_modify_part(parts->dp.disk, p))
16184103857bSmartin return false;
16194103857bSmartin }
16204103857bSmartin
16214103857bSmartin /*
16224103857bSmartin * Add new partitions
16234103857bSmartin */
16244103857bSmartin for (p = parts->partitions; p != NULL; p = p->gp_next) {
16254103857bSmartin if (p->gp_flags & GPEF_ON_DISK)
16264103857bSmartin continue;
16274103857bSmartin if (!(p->gp_flags & GPEF_MODIFIED))
16284103857bSmartin continue;
16294103857bSmartin
16304103857bSmartin if (p->gp_label[0] == 0)
16314103857bSmartin label_arg[0] = 0;
16324103857bSmartin else
16336f0d7230Smartin sprintf(label_arg, "-l \'%s\'", p->gp_label);
16344103857bSmartin
16355b07b744Smartin if (p->gp_type != NULL)
16364103857bSmartin run_program(RUN_SILENT,
16375b07b744Smartin "gpt -n add -b %" PRIu64 " -s %" PRIu64
16385b07b744Smartin "s -t %s %s %s",
16394103857bSmartin p->gp_start, p->gp_size, p->gp_type->tid,
16404103857bSmartin label_arg, arg->disk);
16415b07b744Smartin else
16425b07b744Smartin run_program(RUN_SILENT,
16435b07b744Smartin "gpt -n add -b %" PRIu64 " -s %" PRIu64
16445b07b744Smartin "s %s %s",
16455b07b744Smartin p->gp_start, p->gp_size, label_arg, arg->disk);
16464103857bSmartin gpt_apply_attr(arg->disk, "set", p->gp_start, p->gp_attr);
16474103857bSmartin gpt_read_part(arg->disk, p->gp_start, p);
16484103857bSmartin p->gp_flags |= GPEF_ON_DISK;
16494103857bSmartin }
16504103857bSmartin
16514103857bSmartin /*
16524103857bSmartin * Additional MD bootloader magic...
16534103857bSmartin */
16544103857bSmartin if (!md_gpt_post_write(&parts->dp, root_id, root_is_new, efi_id,
16554103857bSmartin efi_is_new))
16564103857bSmartin return false;
16574103857bSmartin
16584103857bSmartin return true;
16594103857bSmartin }
16604103857bSmartin
1661a7267d53Smartin static part_id
gpt_find_by_name(struct disk_partitions * arg,const char * name)1662a7267d53Smartin gpt_find_by_name(struct disk_partitions *arg, const char *name)
1663a7267d53Smartin {
1664a7267d53Smartin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
1665a7267d53Smartin struct gpt_part_entry *p;
1666a7267d53Smartin part_id pno;
1667a7267d53Smartin
1668a7267d53Smartin for (pno = 0, p = parts->partitions; p != NULL;
1669a7267d53Smartin p = p->gp_next, pno++) {
1670a7267d53Smartin if (strcmp(p->gp_label, name) == 0)
1671a7267d53Smartin return pno;
1672a7267d53Smartin if (strcmp(p->gp_id, name) == 0)
1673a7267d53Smartin return pno;
1674a7267d53Smartin }
1675a7267d53Smartin
1676a7267d53Smartin return NO_PART;
1677a7267d53Smartin }
1678a7267d53Smartin
16794103857bSmartin bool
gpt_parts_check(void)16804103857bSmartin gpt_parts_check(void)
16814103857bSmartin {
16824103857bSmartin
16834103857bSmartin check_available_binaries();
16844103857bSmartin
16854103857bSmartin return have_gpt && have_dk;
16864103857bSmartin }
16874103857bSmartin
16884103857bSmartin static void
gpt_free(struct disk_partitions * arg)16894103857bSmartin gpt_free(struct disk_partitions *arg)
16904103857bSmartin {
16914103857bSmartin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg;
16924103857bSmartin struct gpt_part_entry *p, *n;
16934103857bSmartin
16944103857bSmartin assert(parts != NULL);
16954103857bSmartin for (p = parts->partitions; p != NULL; p = n) {
16968bb96d39Smartin if (p->gp_flags & GPEF_WEDGE)
16978bb96d39Smartin register_post_umount_delwedge(parts->dp.disk,
16988bb96d39Smartin p->gp_dev_name);
16994103857bSmartin free(__UNCONST(p->last_mounted));
17004103857bSmartin n = p->gp_next;
17014103857bSmartin free(p);
17024103857bSmartin }
1703f77b58b1Smartin free(__UNCONST(parts->dp.disk));
17044103857bSmartin free(parts);
17054103857bSmartin }
17064103857bSmartin
1707957b5cd6Smartin static void
gpt_destroy_part_scheme(struct disk_partitions * arg)1708957b5cd6Smartin gpt_destroy_part_scheme(struct disk_partitions *arg)
1709957b5cd6Smartin {
1710957b5cd6Smartin
1711957b5cd6Smartin run_program(RUN_SILENT, "gpt destroy %s", arg->disk);
1712957b5cd6Smartin gpt_free(arg);
1713957b5cd6Smartin }
1714957b5cd6Smartin
17154103857bSmartin static bool
gpt_custom_attribute_writable(const struct disk_partitions * arg,part_id ptn,size_t attr_no)17164103857bSmartin gpt_custom_attribute_writable(const struct disk_partitions *arg,
17174103857bSmartin part_id ptn, size_t attr_no)
17184103857bSmartin {
17194103857bSmartin const struct gpt_disk_partitions *parts =
17204103857bSmartin (const struct gpt_disk_partitions*)arg;
17214103857bSmartin size_t i;
17224103857bSmartin struct gpt_part_entry *p;
17234103857bSmartin
17244103857bSmartin if (attr_no >= arg->pscheme->custom_attribute_count)
17254103857bSmartin return false;
17264103857bSmartin
17274103857bSmartin const msg label = arg->pscheme->custom_attributes[attr_no].label;
17284103857bSmartin
17294103857bSmartin /* we can not edit the uuid attribute */
17304103857bSmartin if (label == MSG_ptn_uuid)
17314103857bSmartin return false;
17324103857bSmartin
17334103857bSmartin /* the label is always editable */
17344103857bSmartin if (label == MSG_ptn_label)
17354103857bSmartin return true;
17364103857bSmartin
17374103857bSmartin /* the GPT type is read only */
17384103857bSmartin if (label == MSG_ptn_gpt_type)
17394103857bSmartin return false;
17404103857bSmartin
17414103857bSmartin /* BOOTME makes no sense on swap partitions */
17424103857bSmartin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
17434103857bSmartin if (i == ptn)
17444103857bSmartin break;
17454103857bSmartin
17464103857bSmartin if (p == NULL)
17474103857bSmartin return false;
17484103857bSmartin
17495b07b744Smartin if (p->fs_type == FS_SWAP ||
17505b07b744Smartin (p->gp_type != NULL && p->gp_type->gent.generic_ptype == PT_swap))
17514103857bSmartin return false;
17524103857bSmartin
17534103857bSmartin return true;
17544103857bSmartin }
17554103857bSmartin
17564c36c108Smartin static const char *
gpt_get_label_str(const struct disk_partitions * arg,part_id ptn)17574c36c108Smartin gpt_get_label_str(const struct disk_partitions *arg, part_id ptn)
17584c36c108Smartin {
17594c36c108Smartin const struct gpt_disk_partitions *parts =
17604c36c108Smartin (const struct gpt_disk_partitions*)arg;
17614c36c108Smartin size_t i;
17624c36c108Smartin struct gpt_part_entry *p;
17634c36c108Smartin
17644c36c108Smartin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
17654c36c108Smartin if (i == ptn)
17664c36c108Smartin break;
17674c36c108Smartin
17684c36c108Smartin if (p == NULL)
17694c36c108Smartin return NULL;
17704c36c108Smartin
17714c36c108Smartin if (p->gp_label[0] != 0)
17724c36c108Smartin return p->gp_label;
17734c36c108Smartin return p->gp_id;
17744c36c108Smartin }
17754c36c108Smartin
17764103857bSmartin static bool
gpt_format_custom_attribute(const struct disk_partitions * arg,part_id ptn,size_t attr_no,const struct disk_part_info * info,char * out,size_t out_space)17774103857bSmartin gpt_format_custom_attribute(const struct disk_partitions *arg,
17784103857bSmartin part_id ptn, size_t attr_no, const struct disk_part_info *info,
17794103857bSmartin char *out, size_t out_space)
17804103857bSmartin {
17814103857bSmartin const struct gpt_disk_partitions *parts =
17824103857bSmartin (const struct gpt_disk_partitions*)arg;
17834103857bSmartin size_t i;
17844103857bSmartin struct gpt_part_entry *p, data;
17854103857bSmartin
17864103857bSmartin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
17874103857bSmartin if (i == ptn)
17884103857bSmartin break;
17894103857bSmartin
17904103857bSmartin if (p == NULL)
17914103857bSmartin return false;
17924103857bSmartin
17934103857bSmartin if (attr_no >= parts->dp.pscheme->custom_attribute_count)
17944103857bSmartin return false;
17954103857bSmartin
17964103857bSmartin const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
17974103857bSmartin
17984103857bSmartin if (info != NULL) {
17994103857bSmartin data = *p;
18004103857bSmartin gpt_info_to_part(&data, info, NULL);
18014103857bSmartin p = &data;
18024103857bSmartin }
18034103857bSmartin
18044103857bSmartin if (label == MSG_ptn_label)
18054103857bSmartin strlcpy(out, p->gp_label, out_space);
18064103857bSmartin else if (label == MSG_ptn_uuid)
18074103857bSmartin strlcpy(out, p->gp_id, out_space);
18085b07b744Smartin else if (label == MSG_ptn_gpt_type) {
18095b07b744Smartin if (p->gp_type != NULL)
18104103857bSmartin strlcpy(out, p->gp_type->gent.description, out_space);
18115b07b744Smartin else if (out_space > 1)
18125b07b744Smartin out[0] = 0;
18135b07b744Smartin } else if (label == MSG_ptn_boot)
18144103857bSmartin strlcpy(out, msg_string(p->gp_attr & GPT_ATTR_BOOT ?
18154103857bSmartin MSG_Yes : MSG_No), out_space);
18164103857bSmartin else
18174103857bSmartin return false;
18184103857bSmartin
18194103857bSmartin return true;
18204103857bSmartin }
18214103857bSmartin
18224103857bSmartin static bool
gpt_custom_attribute_toggle(struct disk_partitions * arg,part_id ptn,size_t attr_no)18234103857bSmartin gpt_custom_attribute_toggle(struct disk_partitions *arg,
18244103857bSmartin part_id ptn, size_t attr_no)
18254103857bSmartin {
18264103857bSmartin const struct gpt_disk_partitions *parts =
18274103857bSmartin (const struct gpt_disk_partitions*)arg;
18284103857bSmartin size_t i;
18294103857bSmartin struct gpt_part_entry *p;
18304103857bSmartin
18314103857bSmartin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
18324103857bSmartin if (i == ptn)
18334103857bSmartin break;
18344103857bSmartin
18354103857bSmartin if (p == NULL)
18364103857bSmartin return false;
18374103857bSmartin
18384103857bSmartin if (attr_no >= parts->dp.pscheme->custom_attribute_count)
18394103857bSmartin return false;
18404103857bSmartin
18414103857bSmartin const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
18424103857bSmartin if (label != MSG_ptn_boot)
18434103857bSmartin return false;
18444103857bSmartin
18454103857bSmartin if (p->gp_attr & GPT_ATTR_BOOT) {
18464103857bSmartin p->gp_attr &= ~GPT_ATTR_BOOT;
18474103857bSmartin } else {
18484103857bSmartin for (i = 0, p = parts->partitions; p != NULL;
18494103857bSmartin i++, p = p->gp_next)
18504103857bSmartin if (i == ptn)
18514103857bSmartin p->gp_attr |= GPT_ATTR_BOOT;
18524103857bSmartin else
18534103857bSmartin p->gp_attr &= ~GPT_ATTR_BOOT;
18544103857bSmartin }
18554103857bSmartin return true;
18564103857bSmartin }
18574103857bSmartin
18584103857bSmartin static bool
gpt_custom_attribute_set_str(struct disk_partitions * arg,part_id ptn,size_t attr_no,const char * new_val)18594103857bSmartin gpt_custom_attribute_set_str(struct disk_partitions *arg,
18604103857bSmartin part_id ptn, size_t attr_no, const char *new_val)
18614103857bSmartin {
18624103857bSmartin const struct gpt_disk_partitions *parts =
18634103857bSmartin (const struct gpt_disk_partitions*)arg;
18644103857bSmartin size_t i;
18654103857bSmartin struct gpt_part_entry *p;
18664103857bSmartin
18674103857bSmartin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next)
18684103857bSmartin if (i == ptn)
18694103857bSmartin break;
18704103857bSmartin
18714103857bSmartin if (p == NULL)
18724103857bSmartin return false;
18734103857bSmartin
18744103857bSmartin if (attr_no >= parts->dp.pscheme->custom_attribute_count)
18754103857bSmartin return false;
18764103857bSmartin
18774103857bSmartin const msg label = parts->dp.pscheme->custom_attributes[attr_no].label;
18784103857bSmartin
18794103857bSmartin if (label != MSG_ptn_label)
18804103857bSmartin return false;
18814103857bSmartin
18824103857bSmartin strlcpy(p->gp_label, new_val, sizeof(p->gp_label));
18834103857bSmartin return true;
18844103857bSmartin }
18854103857bSmartin
18864103857bSmartin static bool
gpt_have_boot_support(const char * disk)18874103857bSmartin gpt_have_boot_support(const char *disk)
18884103857bSmartin {
18894103857bSmartin #ifdef HAVE_GPT_BOOT
18904103857bSmartin return true;
18914103857bSmartin #else
18924103857bSmartin return false;
18934103857bSmartin #endif
18944103857bSmartin }
18954103857bSmartin
18964103857bSmartin const struct disk_part_custom_attribute gpt_custom_attrs[] = {
18974103857bSmartin { .label = MSG_ptn_label, .type = pet_str },
18984103857bSmartin { .label = MSG_ptn_uuid, .type = pet_str },
18994103857bSmartin { .label = MSG_ptn_gpt_type, .type = pet_str },
19004103857bSmartin { .label = MSG_ptn_boot, .type = pet_bool },
19014103857bSmartin };
19024103857bSmartin
19034103857bSmartin const struct disk_partitioning_scheme
19044103857bSmartin gpt_parts = {
19054103857bSmartin .name = MSG_parttype_gpt,
19064103857bSmartin .short_name = MSG_parttype_gpt_short,
19074103857bSmartin .part_flag_desc = MSG_gpt_flag_desc,
19084103857bSmartin .custom_attribute_count = __arraycount(gpt_custom_attrs),
19094103857bSmartin .custom_attributes = gpt_custom_attrs,
19104103857bSmartin .get_part_types_count = gpt_type_count,
19114103857bSmartin .get_part_type = gpt_get_ptype,
19124103857bSmartin .get_generic_part_type = gpt_get_generic_type,
19134103857bSmartin .get_fs_part_type = gpt_get_fs_part_type,
1914abce8cb3Smartin .get_default_fstype = gpt_get_default_fstype,
1915f77b58b1Smartin .create_custom_part_type = gpt_create_custom_part_type,
1916f77b58b1Smartin .create_unknown_part_type = gpt_create_unknown_part_type,
19174103857bSmartin .get_part_alignment = gpt_get_part_alignment,
19184103857bSmartin .read_from_disk = gpt_read_from_disk,
1919ead2350fSmartin .get_cylinder_size = gpt_cyl_size,
19204103857bSmartin .create_new_for_disk = gpt_create_new,
19214103857bSmartin .have_boot_support = gpt_have_boot_support,
1922a7267d53Smartin .find_by_name = gpt_find_by_name,
19234103857bSmartin .can_add_partition = gpt_can_add_partition,
19244103857bSmartin .custom_attribute_writable = gpt_custom_attribute_writable,
19254103857bSmartin .format_custom_attribute = gpt_format_custom_attribute,
19264103857bSmartin .custom_attribute_toggle = gpt_custom_attribute_toggle,
19274103857bSmartin .custom_attribute_set_str = gpt_custom_attribute_set_str,
19284c36c108Smartin .other_partition_identifier = gpt_get_label_str,
19294103857bSmartin .get_part_device = gpt_get_part_device,
19304103857bSmartin .max_free_space_at = gpt_max_free_space_at,
19314103857bSmartin .get_free_spaces = gpt_get_free_spaces,
1932f77b58b1Smartin .adapt_foreign_part_info = generic_adapt_foreign_part_info,
19334103857bSmartin .get_part_info = gpt_get_part_info,
19344103857bSmartin .get_part_attr_str = gpt_get_part_attr_str,
19354103857bSmartin .set_part_info = gpt_set_part_info,
19364103857bSmartin .add_partition = gpt_add_part,
19374103857bSmartin .delete_all_partitions = gpt_delete_all_partitions,
19384103857bSmartin .delete_partition = gpt_delete_partition,
19394103857bSmartin .write_to_disk = gpt_write_to_disk,
19404103857bSmartin .free = gpt_free,
1941957b5cd6Smartin .destroy_part_scheme = gpt_destroy_part_scheme,
1942f77b58b1Smartin .cleanup = gpt_cleanup,
19434103857bSmartin };
1944