1 1.32 martin /* $NetBSD: gpt.c,v 1.32 2024/03/24 17:29:58 martin Exp $ */ 2 1.1 martin 3 1.1 martin /* 4 1.1 martin * Copyright 2018 The NetBSD Foundation, Inc. 5 1.1 martin * All rights reserved. 6 1.1 martin * 7 1.1 martin * Redistribution and use in source and binary forms, with or without 8 1.1 martin * modification, are permitted provided that the following conditions 9 1.1 martin * are met: 10 1.1 martin * 1. Redistributions of source code must retain the above copyright 11 1.1 martin * notice, this list of conditions and the following disclaimer. 12 1.1 martin * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 martin * notice, this list of conditions and the following disclaimer in the 14 1.1 martin * documentation and/or other materials provided with the distribution. 15 1.1 martin * 16 1.1 martin * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 17 1.1 martin * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 martin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 martin * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 20 1.1 martin * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 martin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 martin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 martin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 martin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 martin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 1.1 martin * THE POSSIBILITY OF SUCH DAMAGE. 27 1.1 martin * 28 1.1 martin */ 29 1.1 martin 30 1.1 martin #include "defs.h" 31 1.1 martin #include "mbr.h" 32 1.1 martin #include "md.h" 33 1.1 martin #include "gpt_uuid.h" 34 1.1 martin #include <assert.h> 35 1.25 martin #include <errno.h> 36 1.12 martin #include <err.h> 37 1.1 martin #include <paths.h> 38 1.1 martin #include <sys/param.h> 39 1.1 martin #include <sys/ioctl.h> 40 1.1 martin #include <util.h> 41 1.12 martin #include <uuid.h> 42 1.1 martin 43 1.1 martin bool gpt_parts_check(void); /* check for needed binaries */ 44 1.1 martin 45 1.1 martin 46 1.1 martin /*************** GPT ************************************************/ 47 1.1 martin /* a GPT based disk_partitions interface */ 48 1.1 martin 49 1.1 martin #define GUID_STR_LEN 40 50 1.12 martin #define GPT_PTYPE_ALLOC 32 /* initial type array allocation, should be > 51 1.12 martin * gpt type -l | wc -l */ 52 1.28 martin #define GPT_DEV_LEN DISKNAMESIZE /* dkNN */ 53 1.1 martin 54 1.16 martin #define GPT_PARTS_PER_SEC 4 /* a 512 byte sector holds 4 entries */ 55 1.1 martin #define GPT_DEFAULT_MAX_PARTS 128 56 1.1 martin 57 1.1 martin /* a usable label will be short, so we can get away with an arbitrary limit */ 58 1.1 martin #define GPT_LABEL_LEN 96 59 1.1 martin 60 1.1 martin #define GPT_ATTR_BIOSBOOT 1 61 1.1 martin #define GPT_ATTR_BOOTME 2 62 1.1 martin #define GPT_ATTR_BOOTONCE 4 63 1.1 martin #define GPT_ATTR_BOOTFAILED 8 64 1.1 martin #define GPT_ATTR_NOBLOCKIO 16 65 1.1 martin #define GPT_ATTR_REQUIRED 32 66 1.1 martin 67 1.1 martin /* when we don't care for BIOS or UEFI boot, use the combined boot flags */ 68 1.1 martin #define GPT_ATTR_BOOT (GPT_ATTR_BIOSBOOT|GPT_ATTR_BOOTME) 69 1.1 martin 70 1.1 martin struct gpt_attr_desc { 71 1.1 martin const char *name; 72 1.1 martin uint flag; 73 1.1 martin }; 74 1.1 martin static const struct gpt_attr_desc gpt_avail_attrs[] = { 75 1.1 martin { "biosboot", GPT_ATTR_BIOSBOOT }, 76 1.1 martin { "bootme", GPT_ATTR_BOOTME }, 77 1.1 martin { "bootonce", GPT_ATTR_BOOTONCE }, 78 1.1 martin { "bootfailed", GPT_ATTR_BOOTFAILED }, 79 1.1 martin { "noblockio", GPT_ATTR_NOBLOCKIO }, 80 1.1 martin { "required", GPT_ATTR_REQUIRED }, 81 1.1 martin { NULL, 0 } 82 1.1 martin }; 83 1.1 martin 84 1.1 martin struct gpt_ptype_desc { 85 1.1 martin struct part_type_desc gent; 86 1.1 martin char tid[GUID_STR_LEN]; 87 1.1 martin uint fsflags, default_fs_type; 88 1.1 martin }; 89 1.1 martin 90 1.1 martin static const 91 1.1 martin struct { 92 1.1 martin const char *name; 93 1.1 martin uint fstype; 94 1.1 martin enum part_type ptype; 95 1.23 rillig uint fsflags; 96 1.1 martin } gpt_fs_types[] = { 97 1.1 martin { .name = "ffs", .fstype = FS_BSDFFS, .ptype = PT_root, 98 1.1 martin .fsflags = GLM_LIKELY_FFS }, 99 1.1 martin { .name = "swap", .fstype = FS_SWAP, .ptype = PT_swap }, 100 1.1 martin { .name = "windows", .fstype = FS_MSDOS, .ptype = PT_FAT, 101 1.1 martin .fsflags = GLM_MAYBE_FAT32|GLM_MAYBE_NTFS }, 102 1.1 martin { .name = "windows", .fstype = FS_NTFS, .ptype = PT_FAT, 103 1.1 martin .fsflags = GLM_MAYBE_FAT32|GLM_MAYBE_NTFS }, 104 1.1 martin { .name = "efi", .fstype = FS_MSDOS, .ptype = PT_EFI_SYSTEM, 105 1.1 martin .fsflags = GLM_MAYBE_FAT32 }, 106 1.29 martin { .name = "efi", .fstype = FS_EFI_SP, .ptype = PT_EFI_SYSTEM, 107 1.29 martin .fsflags = GLM_MAYBE_FAT32 }, 108 1.1 martin { .name = "bios", .fstype = FS_MSDOS, .ptype = PT_FAT, 109 1.1 martin .fsflags = GLM_MAYBE_FAT32 }, 110 1.1 martin { .name = "lfs", .fstype = FS_BSDLFS, .ptype = PT_root }, 111 1.1 martin { .name = "linux-data", .fstype = FS_EX2FS, .ptype = PT_root }, 112 1.1 martin { .name = "apple", .fstype = FS_HFS, .ptype = PT_unknown }, 113 1.14 martin { .name = "ccd", .fstype = FS_CCD, .ptype = PT_root }, 114 1.14 martin { .name = "cgd", .fstype = FS_CGD, .ptype = PT_root }, 115 1.1 martin { .name = "raid", .fstype = FS_RAID, .ptype = PT_root }, 116 1.1 martin { .name = "vmcore", .fstype = FS_VMKCORE, .ptype = PT_unknown }, 117 1.1 martin { .name = "vmfs", .fstype = FS_VMFS, .ptype = PT_unknown }, 118 1.17 martin { .name = "vmresered", .fstype = FS_VMWRESV, .ptype = PT_unknown }, 119 1.18 martin { .name = "zfs", .fstype = FS_ZFS, .ptype = PT_root }, 120 1.1 martin }; 121 1.1 martin 122 1.12 martin static size_t gpt_ptype_cnt = 0, gpt_ptype_alloc = 0; 123 1.12 martin static struct gpt_ptype_desc *gpt_ptype_descs = NULL; 124 1.12 martin 125 1.12 martin /* "well" known types with special handling */ 126 1.12 martin static const struct part_type_desc *gpt_native_root; 127 1.1 martin 128 1.1 martin /* similar to struct gpt_ent, but matching our needs */ 129 1.1 martin struct gpt_part_entry { 130 1.1 martin const struct gpt_ptype_desc *gp_type; 131 1.1 martin char gp_id[GUID_STR_LEN]; /* partition guid as string */ 132 1.1 martin daddr_t gp_start, gp_size; 133 1.1 martin uint gp_attr; /* various attribute bits */ 134 1.1 martin char gp_label[GPT_LABEL_LEN]; /* user defined label */ 135 1.1 martin char gp_dev_name[GPT_DEV_LEN]; /* name of wedge */ 136 1.1 martin const char *last_mounted; /* last mounted if known */ 137 1.21 martin uint fs_type, fs_sub_type, /* FS_* and maybe sub type */ 138 1.21 martin fs_opt1, fs_opt2, fs_opt3; /* transient file system options */ 139 1.23 rillig uint gp_flags; 140 1.1 martin #define GPEF_ON_DISK 1 /* This entry exists on-disk */ 141 1.1 martin #define GPEF_MODIFIED 2 /* this entry has been changed */ 142 1.1 martin #define GPEF_WEDGE 4 /* wedge for this exists */ 143 1.1 martin #define GPEF_RESIZED 8 /* size has changed */ 144 1.19 martin #define GPEF_TARGET 16 /* marked install target */ 145 1.1 martin struct gpt_part_entry *gp_next; 146 1.1 martin }; 147 1.1 martin 148 1.1 martin static const struct gpt_ptype_desc *gpt_find_native_type( 149 1.1 martin const struct part_type_desc *gent); 150 1.1 martin static const struct gpt_ptype_desc *gpt_find_guid_type(const char*); 151 1.1 martin static bool 152 1.1 martin gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info, 153 1.1 martin const char **err_msg); 154 1.1 martin 155 1.1 martin const struct disk_partitioning_scheme gpt_parts; 156 1.1 martin struct gpt_disk_partitions { 157 1.1 martin struct disk_partitions dp; 158 1.1 martin /* 159 1.1 martin * We keep a list of our current valid partitions, pointed 160 1.1 martin * to by "partitions". 161 1.1 martin * dp.num_part is the number of entries in "partitions". 162 1.1 martin * When partitions that have a representation on disk already 163 1.1 martin * are deleted, we move them to the "obsolete" list so we 164 1.1 martin * can issue the proper commands to remove it when writing back. 165 1.1 martin */ 166 1.1 martin struct gpt_part_entry *partitions, /* current partitions */ 167 1.1 martin *obsolete; /* deleted partitions */ 168 1.1 martin size_t max_num_parts; /* how many entries max? */ 169 1.1 martin size_t prologue, epilogue; /* number of sectors res. */ 170 1.1 martin bool has_gpt; /* disk already has a GPT */ 171 1.1 martin }; 172 1.1 martin 173 1.1 martin /* 174 1.1 martin * Init global variables from MD details 175 1.1 martin */ 176 1.1 martin static void 177 1.1 martin gpt_md_init(bool is_boot_disk, size_t *max_parts, size_t *head, size_t *tail) 178 1.1 martin { 179 1.1 martin size_t num; 180 1.1 martin 181 1.1 martin if (is_boot_disk) { 182 1.1 martin #ifdef MD_GPT_INITIAL_SIZE 183 1.1 martin #if MD_GPT_INITIAL_SIZE < 2*512 184 1.1 martin #error impossible small GPT prologue 185 1.1 martin #endif 186 1.1 martin num = ((MD_GPT_INITIAL_SIZE-(2*512))/512)*GPT_PARTS_PER_SEC; 187 1.1 martin #else 188 1.1 martin num = GPT_DEFAULT_MAX_PARTS; 189 1.1 martin #endif 190 1.1 martin } else { 191 1.1 martin num = GPT_DEFAULT_MAX_PARTS; 192 1.1 martin } 193 1.1 martin *max_parts = num; 194 1.1 martin *head = 2 + num/GPT_PARTS_PER_SEC; 195 1.1 martin *tail = 1 + num/GPT_PARTS_PER_SEC; 196 1.1 martin } 197 1.1 martin 198 1.1 martin /* 199 1.1 martin * Parse a part of "gpt show" output into a struct gpt_part_entry. 200 1.1 martin * Output is from "show -a" format if details = false, otherwise 201 1.1 martin * from details for a specific partition (show -i or show -b) 202 1.1 martin */ 203 1.1 martin static void 204 1.1 martin gpt_add_info(struct gpt_part_entry *part, const char *tag, char *val, 205 1.1 martin bool details) 206 1.1 martin { 207 1.1 martin char *s, *e; 208 1.1 martin 209 1.1 martin if (details && strcmp(tag, "Start:") == 0) { 210 1.1 martin part->gp_start = strtouq(val, NULL, 10); 211 1.1 martin } else if (details && strcmp(tag, "Size:") == 0) { 212 1.1 martin part->gp_size = strtouq(val, NULL, 10); 213 1.1 martin } else if (details && strcmp(tag, "Type:") == 0) { 214 1.1 martin s = strchr(val, '('); 215 1.1 martin if (!s) 216 1.1 martin return; 217 1.1 martin e = strchr(s, ')'); 218 1.1 martin if (!e) 219 1.1 martin return; 220 1.1 martin *e = 0; 221 1.1 martin part->gp_type = gpt_find_guid_type(s+1); 222 1.1 martin } else if (strcmp(tag, "TypeID:") == 0) { 223 1.1 martin part->gp_type = gpt_find_guid_type(val); 224 1.1 martin } else if (strcmp(tag, "GUID:") == 0) { 225 1.1 martin strlcpy(part->gp_id, val, sizeof(part->gp_id)); 226 1.1 martin } else if (strcmp(tag, "Label:") == 0) { 227 1.7 martin strlcpy(part->gp_label, val, sizeof(part->gp_label)); 228 1.1 martin } else if (strcmp(tag, "Attributes:") == 0) { 229 1.1 martin char *n; 230 1.1 martin 231 1.1 martin while ((n = strsep(&val, ", ")) != NULL) { 232 1.1 martin if (*n == 0) 233 1.1 martin continue; 234 1.1 martin for (const struct gpt_attr_desc *p = gpt_avail_attrs; 235 1.1 martin p->name != NULL; p++) { 236 1.1 martin if (strcmp(p->name, n) == 0) 237 1.1 martin part->gp_attr |= p->flag; 238 1.1 martin } 239 1.1 martin } 240 1.1 martin } 241 1.1 martin } 242 1.1 martin 243 1.8 martin /* 244 1.8 martin * Find the partition matching this wedge info and record that we 245 1.8 martin * have a wedge already. 246 1.8 martin */ 247 1.8 martin static void 248 1.8 martin update_part_from_wedge_info(struct gpt_disk_partitions *parts, 249 1.8 martin const struct dkwedge_info *dkw) 250 1.8 martin { 251 1.8 martin for (struct gpt_part_entry *p = parts->partitions; p != NULL; 252 1.8 martin p = p->gp_next) { 253 1.8 martin if (p->gp_start != dkw->dkw_offset || 254 1.8 martin (uint64_t)p->gp_size != dkw->dkw_size) 255 1.8 martin continue; 256 1.8 martin p->gp_flags |= GPEF_WEDGE; 257 1.8 martin strlcpy(p->gp_dev_name, dkw->dkw_devname, 258 1.8 martin sizeof p->gp_dev_name); 259 1.8 martin return; 260 1.8 martin } 261 1.8 martin } 262 1.8 martin 263 1.1 martin static struct disk_partitions * 264 1.16 martin gpt_read_from_disk(const char *dev, daddr_t start, daddr_t len, size_t bps, 265 1.10 martin const struct disk_partitioning_scheme *scheme) 266 1.1 martin { 267 1.1 martin char diskpath[MAXPATHLEN]; 268 1.1 martin int fd; 269 1.8 martin struct dkwedge_info *dkw; 270 1.8 martin struct dkwedge_list dkwl; 271 1.8 martin size_t bufsize, dk; 272 1.1 martin 273 1.1 martin assert(start == 0); 274 1.1 martin assert(have_gpt); 275 1.1 martin 276 1.1 martin if (run_program(RUN_SILENT | RUN_ERROR_OK, 277 1.1 martin "gpt -rq header %s", dev) != 0) 278 1.1 martin return NULL; 279 1.1 martin 280 1.1 martin /* read the partitions */ 281 1.1 martin int i; 282 1.1 martin unsigned int p_index; 283 1.1 martin daddr_t p_start = 0, p_size = 0, avail_start = 0, avail_size = 0, 284 1.1 martin disk_size = 0; 285 1.1 martin char *textbuf, *t, *tt, p_type[STRSIZE]; 286 1.1 martin static const char regpart_prefix[] = "GPT part - "; 287 1.1 martin struct gpt_disk_partitions *parts; 288 1.1 martin struct gpt_part_entry *last = NULL, *add_to = NULL; 289 1.19 martin const struct gpt_ptype_desc *native_root 290 1.19 martin = gpt_find_native_type(gpt_native_root); 291 1.19 martin bool have_target = false; 292 1.1 martin 293 1.1 martin if (collect(T_OUTPUT, &textbuf, "gpt -r show -a %s 2>/dev/null", dev) 294 1.1 martin < 1) 295 1.1 martin return NULL; 296 1.1 martin 297 1.1 martin /* parse output and create our list */ 298 1.1 martin parts = calloc(1, sizeof(*parts)); 299 1.1 martin if (parts == NULL) 300 1.1 martin return NULL; 301 1.1 martin 302 1.1 martin (void)strtok(textbuf, "\n"); /* ignore first line */ 303 1.1 martin while ((t = strtok(NULL, "\n")) != NULL) { 304 1.1 martin i = 0; p_start = 0; p_size = 0; p_index = 0; 305 1.23 rillig p_type[0] = 0; 306 1.1 martin while ((tt = strsep(&t, " \t")) != NULL) { 307 1.1 martin if (strlen(tt) == 0) 308 1.1 martin continue; 309 1.1 martin if (i == 0) { 310 1.1 martin if (add_to != NULL) 311 1.1 martin gpt_add_info(add_to, tt, t, false); 312 1.1 martin p_start = strtouq(tt, NULL, 10); 313 1.1 martin if (p_start == 0 && add_to != NULL) 314 1.1 martin break; 315 1.1 martin else 316 1.1 martin add_to = NULL; 317 1.1 martin } 318 1.1 martin if (i == 1) 319 1.1 martin p_size = strtouq(tt, NULL, 10); 320 1.1 martin if (i == 2) 321 1.1 martin p_index = strtouq(tt, NULL, 10); 322 1.1 martin if (i > 2 || (i == 2 && p_index == 0)) { 323 1.1 martin if (p_type[0]) 324 1.1 martin strlcat(p_type, " ", STRSIZE); 325 1.1 martin strlcat(p_type, tt, STRSIZE); 326 1.1 martin } 327 1.1 martin i++; 328 1.1 martin } 329 1.1 martin 330 1.1 martin if (p_start == 0 || p_size == 0) 331 1.1 martin continue; 332 1.1 martin else if (strcmp(p_type, "Pri GPT table") == 0) { 333 1.1 martin avail_start = p_start + p_size; 334 1.1 martin parts->prologue = avail_start; 335 1.1 martin parts->epilogue = p_size + 1; 336 1.1 martin parts->max_num_parts = p_size * GPT_PARTS_PER_SEC; 337 1.1 martin } else if (strcmp(p_type, "Sec GPT table") == 0) 338 1.1 martin avail_size = p_start - avail_start; 339 1.1 martin else if(strcmp(p_type, "Sec GPT header") == 0) 340 1.1 martin disk_size = p_start + p_size; 341 1.1 martin else if (p_index == 0 && strlen(p_type) > 0) 342 1.1 martin /* Utilitary entry (PMBR, etc) */ 343 1.1 martin continue; 344 1.1 martin else if (p_index == 0) { 345 1.1 martin /* Free space */ 346 1.1 martin continue; 347 1.1 martin } else { 348 1.1 martin /* Usual partition */ 349 1.1 martin tt = p_type; 350 1.1 martin if (strncmp(tt, regpart_prefix, 351 1.1 martin strlen(regpart_prefix)) == 0) 352 1.1 martin tt += strlen(regpart_prefix); 353 1.1 martin 354 1.1 martin /* Add to our linked list */ 355 1.1 martin struct gpt_part_entry *np = calloc(1, sizeof(*np)); 356 1.1 martin if (np == NULL) 357 1.1 martin break; 358 1.1 martin 359 1.1 martin strlcpy(np->gp_label, tt, sizeof(np->gp_label)); 360 1.1 martin np->gp_start = p_start; 361 1.1 martin np->gp_size = p_size; 362 1.1 martin np->gp_flags |= GPEF_ON_DISK; 363 1.19 martin if (!have_target && native_root != NULL && 364 1.19 martin strcmp(np->gp_id, native_root->tid) == 0) { 365 1.19 martin have_target = true; 366 1.19 martin np->gp_flags |= GPEF_TARGET; 367 1.19 martin } 368 1.1 martin 369 1.1 martin if (last == NULL) 370 1.1 martin parts->partitions = np; 371 1.1 martin else 372 1.1 martin last->gp_next = np; 373 1.1 martin last = np; 374 1.1 martin add_to = np; 375 1.1 martin parts->dp.num_part++; 376 1.1 martin } 377 1.1 martin } 378 1.1 martin free(textbuf); 379 1.1 martin 380 1.2 martin /* If the GPT was not complete (e.g. truncated image), barf */ 381 1.2 martin if (disk_size <= 0) { 382 1.2 martin free(parts); 383 1.2 martin return NULL; 384 1.2 martin } 385 1.2 martin 386 1.10 martin parts->dp.pscheme = scheme; 387 1.12 martin parts->dp.disk = strdup(dev); 388 1.1 martin parts->dp.disk_start = start; 389 1.1 martin parts->dp.disk_size = disk_size; 390 1.1 martin parts->dp.free_space = avail_size; 391 1.16 martin parts->dp.bytes_per_sector = bps; 392 1.1 martin parts->has_gpt = true; 393 1.1 martin 394 1.1 martin fd = opendisk(parts->dp.disk, O_RDONLY, diskpath, sizeof(diskpath), 0); 395 1.1 martin for (struct gpt_part_entry *p = parts->partitions; p != NULL; 396 1.1 martin p = p->gp_next) { 397 1.1 martin #ifdef DEFAULT_UFS2 398 1.1 martin bool fs_is_default = false; 399 1.1 martin #endif 400 1.1 martin 401 1.5 martin if (p->gp_type != NULL) { 402 1.5 martin 403 1.5 martin if (p->gp_type->fsflags != 0) { 404 1.5 martin const char *lm = get_last_mounted(fd, 405 1.5 martin p->gp_start, &p->fs_type, 406 1.5 martin &p->fs_sub_type, p->gp_type->fsflags); 407 1.5 martin if (lm != NULL && *lm != 0) { 408 1.5 martin char *path = strdup(lm); 409 1.5 martin canonicalize_last_mounted(path); 410 1.5 martin p->last_mounted = path; 411 1.5 martin } else { 412 1.5 martin p->fs_type = p->gp_type-> 413 1.5 martin default_fs_type; 414 1.5 martin #ifdef DEFAULT_UFS2 415 1.5 martin fs_is_default = true; 416 1.5 martin #endif 417 1.5 martin } 418 1.1 martin } else { 419 1.1 martin p->fs_type = p->gp_type->default_fs_type; 420 1.1 martin #ifdef DEFAULT_UFS2 421 1.1 martin fs_is_default = true; 422 1.1 martin #endif 423 1.1 martin } 424 1.1 martin #ifdef DEFAULT_UFS2 425 1.5 martin if (fs_is_default && p->fs_type == FS_BSDFFS) 426 1.5 martin p->fs_sub_type = 2; 427 1.1 martin #endif 428 1.1 martin } 429 1.1 martin 430 1.1 martin parts->dp.free_space -= p->gp_size; 431 1.1 martin } 432 1.8 martin 433 1.8 martin /* 434 1.8 martin * Check if we have any (matching/auto-configured) wedges already 435 1.8 martin */ 436 1.8 martin dkw = NULL; 437 1.8 martin dkwl.dkwl_buf = dkw; 438 1.8 martin dkwl.dkwl_bufsize = 0; 439 1.8 martin if (ioctl(fd, DIOCLWEDGES, &dkwl) == 0) { 440 1.8 martin /* do not even try to deal with any races at this point */ 441 1.8 martin bufsize = dkwl.dkwl_nwedges * sizeof(*dkw); 442 1.8 martin dkw = malloc(bufsize); 443 1.8 martin dkwl.dkwl_buf = dkw; 444 1.8 martin dkwl.dkwl_bufsize = bufsize; 445 1.8 martin if (dkw != NULL && ioctl(fd, DIOCLWEDGES, &dkwl) == 0) { 446 1.8 martin for (dk = 0; dk < dkwl.dkwl_ncopied; dk++) 447 1.8 martin update_part_from_wedge_info(parts, &dkw[dk]); 448 1.8 martin } 449 1.8 martin free(dkw); 450 1.8 martin } 451 1.8 martin 452 1.1 martin close(fd); 453 1.1 martin 454 1.1 martin return &parts->dp; 455 1.1 martin } 456 1.1 martin 457 1.15 martin static size_t 458 1.15 martin gpt_cyl_size(const struct disk_partitions *arg) 459 1.15 martin { 460 1.15 martin return MEG / 512; 461 1.15 martin } 462 1.15 martin 463 1.1 martin static struct disk_partitions * 464 1.16 martin gpt_create_new(const char *disk, daddr_t start, daddr_t len, 465 1.14 martin bool is_boot_drive, struct disk_partitions *parent) 466 1.1 martin { 467 1.1 martin struct gpt_disk_partitions *parts; 468 1.16 martin struct disk_geom geo; 469 1.1 martin 470 1.1 martin if (start != 0) { 471 1.1 martin assert(0); 472 1.1 martin return NULL; 473 1.1 martin } 474 1.1 martin 475 1.16 martin if (!get_disk_geom(disk, &geo)) 476 1.16 martin return NULL; 477 1.16 martin 478 1.3 martin parts = calloc(1, sizeof(*parts)); 479 1.1 martin if (!parts) 480 1.1 martin return NULL; 481 1.1 martin 482 1.1 martin parts->dp.pscheme = &gpt_parts; 483 1.12 martin parts->dp.disk = strdup(disk); 484 1.1 martin 485 1.1 martin gpt_md_init(is_boot_drive, &parts->max_num_parts, &parts->prologue, 486 1.1 martin &parts->epilogue); 487 1.1 martin 488 1.1 martin parts->dp.disk_start = start; 489 1.1 martin parts->dp.disk_size = len; 490 1.16 martin parts->dp.bytes_per_sector = geo.dg_secsize; 491 1.1 martin parts->dp.free_space = len - start - parts->prologue - parts->epilogue; 492 1.1 martin parts->has_gpt = false; 493 1.1 martin 494 1.1 martin return &parts->dp; 495 1.1 martin } 496 1.1 martin 497 1.1 martin static bool 498 1.1 martin gpt_get_part_info(const struct disk_partitions *arg, part_id id, 499 1.1 martin struct disk_part_info *info) 500 1.1 martin { 501 1.5 martin static const struct part_type_desc gpt_unknown_type = 502 1.5 martin { .generic_ptype = PT_undef, 503 1.5 martin .short_desc = "<unknown>" }; 504 1.1 martin const struct gpt_disk_partitions *parts = 505 1.1 martin (const struct gpt_disk_partitions*)arg; 506 1.1 martin const struct gpt_part_entry *p = parts->partitions; 507 1.1 martin part_id no; 508 1.1 martin 509 1.1 martin for (no = 0; p != NULL && no < id; no++) 510 1.1 martin p = p->gp_next; 511 1.1 martin 512 1.1 martin if (no != id || p == NULL) 513 1.1 martin return false; 514 1.1 martin 515 1.1 martin memset(info, 0, sizeof(*info)); 516 1.1 martin info->start = p->gp_start; 517 1.1 martin info->size = p->gp_size; 518 1.1 martin if (p->gp_type) 519 1.1 martin info->nat_type = &p->gp_type->gent; 520 1.5 martin else 521 1.5 martin info->nat_type = &gpt_unknown_type; 522 1.1 martin info->last_mounted = p->last_mounted; 523 1.1 martin info->fs_type = p->fs_type; 524 1.1 martin info->fs_sub_type = p->fs_sub_type; 525 1.21 martin info->fs_opt1 = p->fs_opt1; 526 1.21 martin info->fs_opt2 = p->fs_opt2; 527 1.21 martin info->fs_opt3 = p->fs_opt3; 528 1.19 martin if (p->gp_flags & GPEF_TARGET) 529 1.19 martin info->flags |= PTI_INSTALL_TARGET; 530 1.1 martin 531 1.1 martin return true; 532 1.1 martin } 533 1.1 martin 534 1.1 martin static bool 535 1.1 martin gpt_get_part_attr_str(const struct disk_partitions *arg, part_id id, 536 1.1 martin char *str, size_t avail_space) 537 1.1 martin { 538 1.1 martin const struct gpt_disk_partitions *parts = 539 1.1 martin (const struct gpt_disk_partitions*)arg; 540 1.1 martin const struct gpt_part_entry *p = parts->partitions; 541 1.1 martin part_id no; 542 1.1 martin static const char *flags = NULL; 543 1.1 martin 544 1.1 martin for (no = 0; p != NULL && no < id; no++) 545 1.1 martin p = p->gp_next; 546 1.1 martin 547 1.1 martin if (no != id || p == NULL) 548 1.1 martin return false; 549 1.1 martin 550 1.1 martin if (flags == NULL) 551 1.1 martin flags = msg_string(MSG_gpt_flags); 552 1.1 martin 553 1.1 martin if (avail_space < 2) 554 1.1 martin return false; 555 1.1 martin 556 1.1 martin if (p->gp_attr & GPT_ATTR_BOOT) 557 1.1 martin *str++ = flags[0]; 558 1.1 martin *str = 0; 559 1.1 martin 560 1.1 martin return true; 561 1.1 martin } 562 1.1 martin 563 1.1 martin /* 564 1.1 martin * Find insert position and check for duplicates. 565 1.1 martin * If all goes well, insert the new "entry" in the "list". 566 1.1 martin * If there are collisions, report "no free space". 567 1.1 martin * We keep all lists sorted by start sector number, 568 1.1 martin */ 569 1.1 martin static bool 570 1.1 martin gpt_insert_part_into_list(struct gpt_disk_partitions *parts, 571 1.1 martin struct gpt_part_entry **list, 572 1.32 martin struct gpt_part_entry *entry, const char **err_msg, part_id *new_id) 573 1.1 martin { 574 1.1 martin struct gpt_part_entry *p, *last; 575 1.32 martin part_id pno; 576 1.1 martin 577 1.1 martin /* find the first entry past the new one (if any) */ 578 1.32 martin for (pno = 0, last = NULL, p = *list; p != NULL; 579 1.32 martin last = p, p = p->gp_next, pno++) { 580 1.1 martin if (p->gp_start > entry->gp_start) 581 1.1 martin break; 582 1.1 martin } 583 1.1 martin 584 1.1 martin /* check if last partition overlaps with new one */ 585 1.1 martin if (last) { 586 1.1 martin if (last->gp_start + last->gp_size > entry->gp_start) { 587 1.1 martin if (err_msg) 588 1.1 martin *err_msg = msg_string(MSG_No_free_space); 589 1.1 martin return false; 590 1.1 martin } 591 1.1 martin } 592 1.1 martin 593 1.1 martin if (p == NULL) { 594 1.1 martin entry->gp_next = NULL; 595 1.1 martin if (last != NULL) { 596 1.1 martin last->gp_next = entry; 597 1.1 martin } 598 1.1 martin } else { 599 1.1 martin /* check if new entry overlaps with next */ 600 1.1 martin if (entry->gp_start + entry->gp_size > p->gp_start) { 601 1.1 martin if (err_msg) 602 1.1 martin *err_msg = msg_string(MSG_No_free_space); 603 1.1 martin return false; 604 1.1 martin } 605 1.1 martin 606 1.1 martin entry->gp_next = p; 607 1.1 martin if (last != NULL) 608 1.1 martin last->gp_next = entry; 609 1.1 martin else 610 1.1 martin *list = entry; 611 1.1 martin } 612 1.1 martin if (*list == NULL) 613 1.1 martin *list = entry; 614 1.32 martin if (new_id != NULL) 615 1.32 martin *new_id = pno; 616 1.1 martin return true; 617 1.1 martin } 618 1.1 martin 619 1.1 martin static bool 620 1.1 martin gpt_set_part_info(struct disk_partitions *arg, part_id id, 621 1.1 martin const struct disk_part_info *info, const char **err_msg) 622 1.1 martin { 623 1.1 martin struct gpt_disk_partitions *parts = 624 1.1 martin (struct gpt_disk_partitions*)arg; 625 1.1 martin struct gpt_part_entry *p = parts->partitions, *n; 626 1.1 martin part_id no; 627 1.1 martin daddr_t lendiff; 628 1.1 martin 629 1.1 martin for (no = 0; p != NULL && no < id; no++) 630 1.1 martin p = p->gp_next; 631 1.1 martin 632 1.1 martin if (no != id || p == NULL) 633 1.1 martin return false; 634 1.1 martin 635 1.19 martin /* update target mark - we can only have one */ 636 1.30 martin if (info->flags & PTI_INSTALL_TARGET) { 637 1.19 martin p->gp_flags |= GPEF_TARGET; 638 1.19 martin for (n = parts->partitions; n != NULL; n = n->gp_next) 639 1.19 martin if (n != p) 640 1.19 martin n->gp_flags &= ~GPEF_TARGET; 641 1.30 martin } else { 642 1.30 martin p->gp_flags &= ~GPEF_TARGET; 643 1.30 martin } 644 1.19 martin 645 1.1 martin if ((p->gp_flags & GPEF_ON_DISK)) { 646 1.1 martin if (info->start != p->gp_start) { 647 1.1 martin /* partition moved, we need to delete and re-add */ 648 1.1 martin n = calloc(1, sizeof(*n)); 649 1.1 martin if (n == NULL) { 650 1.1 martin if (err_msg) 651 1.1 martin *err_msg = err_outofmem; 652 1.1 martin return false; 653 1.1 martin } 654 1.1 martin *n = *p; 655 1.1 martin p->gp_flags &= ~GPEF_ON_DISK; 656 1.1 martin if (!gpt_insert_part_into_list(parts, &parts->obsolete, 657 1.32 martin n, err_msg, NULL)) 658 1.1 martin return false; 659 1.1 martin } else if (info->size != p->gp_size) { 660 1.1 martin p->gp_flags |= GPEF_RESIZED; 661 1.1 martin } 662 1.1 martin } 663 1.1 martin 664 1.1 martin p->gp_flags |= GPEF_MODIFIED; 665 1.1 martin 666 1.1 martin lendiff = info->size - p->gp_size; 667 1.1 martin parts->dp.free_space -= lendiff; 668 1.1 martin return gpt_info_to_part(p, info, err_msg); 669 1.1 martin } 670 1.1 martin 671 1.1 martin static size_t 672 1.1 martin gpt_get_free_spaces_internal(const struct gpt_disk_partitions *parts, 673 1.1 martin struct disk_part_free_space *result, size_t max_num_result, 674 1.1 martin daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 675 1.1 martin { 676 1.1 martin size_t cnt = 0; 677 1.1 martin daddr_t s, e, from, size, end_of_disk; 678 1.1 martin struct gpt_part_entry *p; 679 1.1 martin 680 1.1 martin if (align > 1) 681 1.1 martin start = max(roundup(start, align), align); 682 1.1 martin if (start < 0 || start < (daddr_t)parts->prologue) 683 1.1 martin start = parts->prologue; 684 1.1 martin if (parts->dp.disk_start != 0 && parts->dp.disk_start > start) 685 1.1 martin start = parts->dp.disk_start; 686 1.1 martin if (min_space_size < 1) 687 1.1 martin min_space_size = 1; 688 1.1 martin end_of_disk = parts->dp.disk_start + parts->dp.disk_size 689 1.1 martin - parts->epilogue; 690 1.1 martin from = start; 691 1.1 martin while (from < end_of_disk && cnt < max_num_result) { 692 1.1 martin again: 693 1.1 martin size = parts->dp.disk_start + parts->dp.disk_size - from; 694 1.1 martin start = from; 695 1.1 martin if (start + size > end_of_disk) 696 1.1 martin size = end_of_disk - start; 697 1.1 martin for (p = parts->partitions; p != NULL; p = p->gp_next) { 698 1.1 martin s = p->gp_start; 699 1.1 martin e = p->gp_size + s; 700 1.1 martin if (s == ignore) 701 1.1 martin continue; 702 1.1 martin if (e < from) 703 1.1 martin continue; 704 1.1 martin if (s <= from && e > from) { 705 1.1 martin if (e - 1 >= end_of_disk) 706 1.1 martin return cnt; 707 1.1 martin from = e + 1; 708 1.1 martin if (align > 1) { 709 1.1 martin from = max(roundup(from, align), align); 710 1.1 martin if (from >= end_of_disk) { 711 1.1 martin size = 0; 712 1.1 martin break; 713 1.1 martin } 714 1.1 martin } 715 1.1 martin goto again; 716 1.1 martin } 717 1.1 martin if (s > from && s - from < size) { 718 1.1 martin size = s - from; 719 1.1 martin } 720 1.1 martin } 721 1.1 martin if (size >= min_space_size) { 722 1.1 martin result->start = start; 723 1.1 martin result->size = size; 724 1.1 martin result++; 725 1.1 martin cnt++; 726 1.1 martin } 727 1.1 martin from += size + 1; 728 1.1 martin if (align > 1) 729 1.1 martin from = max(roundup(from, align), align); 730 1.1 martin } 731 1.1 martin 732 1.1 martin return cnt; 733 1.1 martin } 734 1.1 martin 735 1.1 martin static daddr_t 736 1.1 martin gpt_max_free_space_at(const struct disk_partitions *arg, daddr_t start) 737 1.1 martin { 738 1.1 martin const struct gpt_disk_partitions *parts = 739 1.1 martin (const struct gpt_disk_partitions*)arg; 740 1.1 martin struct disk_part_free_space space; 741 1.1 martin 742 1.1 martin if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 0, 743 1.1 martin start, start) == 1) 744 1.1 martin return space.size; 745 1.1 martin 746 1.1 martin return 0; 747 1.1 martin } 748 1.1 martin 749 1.1 martin static size_t 750 1.1 martin gpt_get_free_spaces(const struct disk_partitions *arg, 751 1.1 martin struct disk_part_free_space *result, size_t max_num_result, 752 1.1 martin daddr_t min_space_size, daddr_t align, daddr_t start, 753 1.1 martin daddr_t ignore) 754 1.1 martin { 755 1.1 martin const struct gpt_disk_partitions *parts = 756 1.1 martin (const struct gpt_disk_partitions*)arg; 757 1.1 martin 758 1.1 martin return gpt_get_free_spaces_internal(parts, result, 759 1.1 martin max_num_result, min_space_size, align, start, ignore); 760 1.1 martin } 761 1.1 martin 762 1.1 martin static void 763 1.1 martin gpt_match_ptype(const char *name, struct gpt_ptype_desc *t) 764 1.1 martin { 765 1.1 martin size_t i; 766 1.1 martin 767 1.1 martin for (i = 0; i < __arraycount(gpt_fs_types); i++) { 768 1.1 martin if (strcmp(name, gpt_fs_types[i].name) == 0) { 769 1.1 martin t->gent.generic_ptype = gpt_fs_types[i].ptype; 770 1.1 martin t->fsflags = gpt_fs_types[i].fsflags; 771 1.1 martin t->default_fs_type = gpt_fs_types[i].fstype; 772 1.12 martin 773 1.12 martin /* recongnize special entries */ 774 1.12 martin if (gpt_native_root == NULL && i == 0) 775 1.12 martin gpt_native_root = &t->gent; 776 1.12 martin 777 1.1 martin return; 778 1.1 martin } 779 1.1 martin } 780 1.1 martin 781 1.1 martin t->gent.generic_ptype = PT_unknown; 782 1.1 martin t->fsflags = 0; 783 1.1 martin t->default_fs_type = FS_BSDFFS; 784 1.1 martin } 785 1.1 martin 786 1.1 martin static void 787 1.1 martin gpt_internal_add_ptype(const char *uid, const char *name, const char *desc) 788 1.1 martin { 789 1.12 martin if (gpt_ptype_cnt >= gpt_ptype_alloc) { 790 1.12 martin gpt_ptype_alloc = gpt_ptype_alloc ? 2*gpt_ptype_alloc 791 1.12 martin : GPT_PTYPE_ALLOC; 792 1.12 martin struct gpt_ptype_desc *nptypes = realloc(gpt_ptype_descs, 793 1.12 martin gpt_ptype_alloc*sizeof(*gpt_ptype_descs)); 794 1.12 martin if (nptypes == 0) 795 1.12 martin errx(EXIT_FAILURE, "out of memory"); 796 1.12 martin gpt_ptype_descs = nptypes; 797 1.12 martin } 798 1.12 martin 799 1.1 martin strlcpy(gpt_ptype_descs[gpt_ptype_cnt].tid, uid, 800 1.1 martin sizeof(gpt_ptype_descs[gpt_ptype_cnt].tid)); 801 1.12 martin gpt_ptype_descs[gpt_ptype_cnt].gent.short_desc = strdup(name); 802 1.12 martin gpt_ptype_descs[gpt_ptype_cnt].gent.description = strdup(desc); 803 1.1 martin gpt_match_ptype(name, &gpt_ptype_descs[gpt_ptype_cnt]); 804 1.1 martin gpt_ptype_cnt++; 805 1.1 martin } 806 1.1 martin 807 1.1 martin static void 808 1.1 martin gpt_init_ptypes(void) 809 1.1 martin { 810 1.1 martin if (gpt_ptype_cnt == 0) 811 1.1 martin gpt_uuid_query(gpt_internal_add_ptype); 812 1.1 martin } 813 1.1 martin 814 1.12 martin static void 815 1.12 martin gpt_cleanup(void) 816 1.12 martin { 817 1.12 martin /* free all of gpt_ptype_descs */ 818 1.12 martin for (size_t i = 0; i < gpt_ptype_cnt; i++) { 819 1.12 martin free(__UNCONST(gpt_ptype_descs[i].gent.short_desc)); 820 1.12 martin free(__UNCONST(gpt_ptype_descs[i].gent.description)); 821 1.12 martin } 822 1.12 martin free(gpt_ptype_descs); 823 1.12 martin gpt_ptype_descs = NULL; 824 1.12 martin gpt_ptype_cnt = gpt_ptype_alloc = 0; 825 1.12 martin } 826 1.12 martin 827 1.1 martin static size_t 828 1.1 martin gpt_type_count(void) 829 1.1 martin { 830 1.1 martin if (gpt_ptype_cnt == 0) 831 1.1 martin gpt_init_ptypes(); 832 1.1 martin 833 1.1 martin return gpt_ptype_cnt; 834 1.1 martin } 835 1.1 martin 836 1.1 martin static const struct part_type_desc * 837 1.1 martin gpt_get_ptype(size_t ndx) 838 1.1 martin { 839 1.1 martin if (gpt_ptype_cnt == 0) 840 1.1 martin gpt_init_ptypes(); 841 1.1 martin 842 1.1 martin if (ndx >= gpt_ptype_cnt) 843 1.1 martin return NULL; 844 1.1 martin 845 1.1 martin return &gpt_ptype_descs[ndx].gent; 846 1.1 martin } 847 1.1 martin 848 1.1 martin static const struct part_type_desc * 849 1.1 martin gpt_get_generic_type(enum part_type gent) 850 1.1 martin { 851 1.1 martin if (gpt_ptype_cnt == 0) 852 1.1 martin gpt_init_ptypes(); 853 1.1 martin 854 1.12 martin if (gent == PT_root) 855 1.12 martin return gpt_native_root; 856 1.12 martin if (gent == PT_unknown) 857 1.12 martin return NULL; 858 1.12 martin 859 1.1 martin for (size_t i = 0; i < gpt_ptype_cnt; i++) 860 1.1 martin if (gpt_ptype_descs[i].gent.generic_ptype == gent) 861 1.1 martin return &gpt_ptype_descs[i].gent; 862 1.1 martin 863 1.1 martin return NULL; 864 1.1 martin } 865 1.1 martin 866 1.1 martin static const struct gpt_ptype_desc * 867 1.1 martin gpt_find_native_type(const struct part_type_desc *gent) 868 1.1 martin { 869 1.1 martin if (gpt_ptype_cnt == 0) 870 1.1 martin gpt_init_ptypes(); 871 1.1 martin 872 1.1 martin if (gent == NULL) 873 1.1 martin return NULL; 874 1.1 martin 875 1.1 martin for (size_t i = 0; i < gpt_ptype_cnt; i++) 876 1.1 martin if (gent == &gpt_ptype_descs[i].gent) 877 1.1 martin return &gpt_ptype_descs[i]; 878 1.1 martin 879 1.1 martin gent = gpt_get_generic_type(gent->generic_ptype); 880 1.1 martin if (gent == NULL) 881 1.1 martin return NULL; 882 1.1 martin 883 1.1 martin /* this can not recurse deeper than once, we would not have found a 884 1.1 martin * generic type a few lines above if it would. */ 885 1.1 martin return gpt_find_native_type(gent); 886 1.1 martin } 887 1.1 martin 888 1.1 martin static const struct gpt_ptype_desc * 889 1.1 martin gpt_find_guid_type(const char *uid) 890 1.1 martin { 891 1.1 martin if (gpt_ptype_cnt == 0) 892 1.1 martin gpt_init_ptypes(); 893 1.1 martin 894 1.1 martin if (uid == NULL || uid[0] == 0) 895 1.1 martin return NULL; 896 1.1 martin 897 1.1 martin for (size_t i = 0; i < gpt_ptype_cnt; i++) 898 1.1 martin if (strcmp(gpt_ptype_descs[i].tid, uid) == 0) 899 1.1 martin return &gpt_ptype_descs[i]; 900 1.1 martin 901 1.1 martin return NULL; 902 1.1 martin } 903 1.1 martin 904 1.1 martin static const struct part_type_desc * 905 1.1 martin gpt_find_type(const char *desc) 906 1.1 martin { 907 1.1 martin if (gpt_ptype_cnt == 0) 908 1.1 martin gpt_init_ptypes(); 909 1.1 martin 910 1.1 martin if (desc == NULL || desc[0] == 0) 911 1.1 martin return NULL; 912 1.1 martin 913 1.1 martin for (size_t i = 0; i < gpt_ptype_cnt; i++) 914 1.1 martin if (strcmp(gpt_ptype_descs[i].gent.short_desc, desc) == 0) 915 1.1 martin return &gpt_ptype_descs[i].gent; 916 1.1 martin 917 1.1 martin return NULL; 918 1.1 martin } 919 1.1 martin 920 1.1 martin static const struct part_type_desc * 921 1.13 martin gpt_get_fs_part_type(enum part_type pt, unsigned fstype, unsigned fs_sub_type) 922 1.1 martin { 923 1.1 martin size_t i; 924 1.1 martin 925 1.14 martin /* Try with complete match (including part_type) first */ 926 1.13 martin for (i = 0; i < __arraycount(gpt_fs_types); i++) 927 1.13 martin if (fstype == gpt_fs_types[i].fstype && 928 1.13 martin pt == gpt_fs_types[i].ptype) 929 1.13 martin return gpt_find_type(gpt_fs_types[i].name); 930 1.13 martin 931 1.13 martin /* If that did not work, ignore part_type */ 932 1.1 martin for (i = 0; i < __arraycount(gpt_fs_types); i++) 933 1.1 martin if (fstype == gpt_fs_types[i].fstype) 934 1.1 martin return gpt_find_type(gpt_fs_types[i].name); 935 1.1 martin 936 1.12 martin return NULL; 937 1.12 martin } 938 1.12 martin 939 1.14 martin static bool 940 1.14 martin gpt_get_default_fstype(const struct part_type_desc *nat_type, 941 1.14 martin unsigned *fstype, unsigned *fs_sub_type) 942 1.14 martin { 943 1.14 martin const struct gpt_ptype_desc *gtype; 944 1.14 martin 945 1.14 martin gtype = gpt_find_native_type(nat_type); 946 1.14 martin if (gtype == NULL) 947 1.14 martin return false; 948 1.14 martin 949 1.14 martin *fstype = gtype->default_fs_type; 950 1.14 martin #ifdef DEFAULT_UFS2 951 1.14 martin if (gtype->default_fs_type == FS_BSDFFS) 952 1.14 martin *fs_sub_type = 2; 953 1.14 martin else 954 1.14 martin #endif 955 1.14 martin *fs_sub_type = 0; 956 1.14 martin return true; 957 1.14 martin } 958 1.14 martin 959 1.12 martin static const struct part_type_desc * 960 1.12 martin gpt_get_uuid_part_type(const uuid_t *id) 961 1.12 martin { 962 1.12 martin char str[GUID_STR_LEN], desc[GUID_STR_LEN + MENUSTRSIZE]; 963 1.12 martin const struct gpt_ptype_desc *t; 964 1.12 martin char *guid = NULL; 965 1.12 martin uint32_t err; 966 1.12 martin 967 1.12 martin uuid_to_string(id, &guid, &err); 968 1.12 martin strlcpy(str, err == uuid_s_ok ? guid : "-", sizeof str); 969 1.12 martin free(guid); 970 1.12 martin 971 1.12 martin t = gpt_find_guid_type(str); 972 1.12 martin if (t == NULL) { 973 1.12 martin snprintf(desc, sizeof desc, "%s (%s)", 974 1.12 martin msg_string(MSG_custom_type), str); 975 1.12 martin gpt_internal_add_ptype(str, str, desc); 976 1.12 martin t = gpt_find_guid_type(str); 977 1.12 martin assert(t != NULL); 978 1.12 martin } 979 1.12 martin return &t->gent; 980 1.12 martin } 981 1.12 martin 982 1.12 martin static const struct part_type_desc * 983 1.12 martin gpt_create_custom_part_type(const char *custom, const char **err_msg) 984 1.12 martin { 985 1.12 martin uuid_t id; 986 1.12 martin uint32_t err; 987 1.12 martin 988 1.12 martin uuid_from_string(custom, &id, &err); 989 1.12 martin if (err_msg != NULL && 990 1.12 martin (err == uuid_s_invalid_string_uuid || err == uuid_s_bad_version)) { 991 1.12 martin *err_msg = MSG_invalid_guid; 992 1.12 martin return NULL; 993 1.12 martin } 994 1.12 martin if (err != uuid_s_ok) 995 1.12 martin return NULL; 996 1.12 martin 997 1.12 martin return gpt_get_uuid_part_type(&id); 998 1.12 martin } 999 1.12 martin 1000 1.12 martin static const struct part_type_desc * 1001 1.12 martin gpt_create_unknown_part_type(void) 1002 1.12 martin { 1003 1.12 martin uuid_t id; 1004 1.12 martin uint32_t err; 1005 1.12 martin 1006 1.12 martin uuid_create(&id, &err); 1007 1.12 martin if (err != uuid_s_ok) 1008 1.12 martin return NULL; 1009 1.12 martin 1010 1.12 martin return gpt_get_uuid_part_type(&id); 1011 1.1 martin } 1012 1.1 martin 1013 1.1 martin static daddr_t 1014 1.1 martin gpt_get_part_alignment(const struct disk_partitions *parts) 1015 1.1 martin { 1016 1.1 martin 1017 1.1 martin assert(parts->disk_size > 0); 1018 1.1 martin if (parts->disk_size < 0) 1019 1.1 martin return 1; 1020 1.1 martin 1021 1.31 andvar /* Use 1MB offset/alignment for large (>128GB) disks */ 1022 1.1 martin if (parts->disk_size > HUGE_DISK_SIZE) 1023 1.1 martin return 2048; 1024 1.1 martin else if (parts->disk_size > TINY_DISK_SIZE) 1025 1.1 martin return 64; 1026 1.1 martin else 1027 1.1 martin return 4; 1028 1.1 martin } 1029 1.1 martin 1030 1.1 martin static bool 1031 1.1 martin gpt_can_add_partition(const struct disk_partitions *arg) 1032 1.1 martin { 1033 1.1 martin const struct gpt_disk_partitions *parts = 1034 1.1 martin (const struct gpt_disk_partitions*)arg; 1035 1.1 martin struct disk_part_free_space space; 1036 1.1 martin daddr_t align; 1037 1.1 martin 1038 1.1 martin if (parts->dp.num_part >= parts->max_num_parts) 1039 1.1 martin return false; 1040 1.1 martin 1041 1.1 martin align = gpt_get_part_alignment(arg); 1042 1.1 martin if (parts->dp.free_space <= align) 1043 1.1 martin return false; 1044 1.1 martin 1045 1.1 martin if (gpt_get_free_spaces_internal(parts, &space, 1, align, align, 1046 1.1 martin 0, -1) < 1) 1047 1.1 martin return false; 1048 1.1 martin 1049 1.1 martin return true; 1050 1.1 martin } 1051 1.1 martin 1052 1.1 martin static bool 1053 1.1 martin gpt_info_to_part(struct gpt_part_entry *p, const struct disk_part_info *info, 1054 1.1 martin const char **err_msg) 1055 1.1 martin { 1056 1.1 martin p->gp_type = gpt_find_native_type(info->nat_type); 1057 1.1 martin p->gp_start = info->start; 1058 1.1 martin p->gp_size = info->size; 1059 1.1 martin if (info->last_mounted != NULL && info->last_mounted != 1060 1.1 martin p->last_mounted) { 1061 1.1 martin free(__UNCONST(p->last_mounted)); 1062 1.1 martin p->last_mounted = strdup(info->last_mounted); 1063 1.1 martin } 1064 1.1 martin p->fs_type = info->fs_type; 1065 1.1 martin p->fs_sub_type = info->fs_sub_type; 1066 1.21 martin p->fs_opt1 = info->fs_opt1; 1067 1.21 martin p->fs_opt2 = info->fs_opt2; 1068 1.21 martin p->fs_opt3 = info->fs_opt3; 1069 1.23 rillig 1070 1.1 martin return true; 1071 1.1 martin } 1072 1.1 martin 1073 1.1 martin static part_id 1074 1.1 martin gpt_add_part(struct disk_partitions *arg, 1075 1.1 martin const struct disk_part_info *info, const char **err_msg) 1076 1.1 martin { 1077 1.1 martin struct gpt_disk_partitions *parts = 1078 1.1 martin (struct gpt_disk_partitions*)arg; 1079 1.1 martin struct disk_part_free_space space; 1080 1.1 martin struct disk_part_info data = *info; 1081 1.30 martin struct gpt_part_entry *p, *n; 1082 1.32 martin part_id pno; 1083 1.1 martin bool ok; 1084 1.1 martin 1085 1.1 martin if (err_msg != NULL) 1086 1.1 martin *err_msg = NULL; 1087 1.1 martin 1088 1.1 martin if (gpt_get_free_spaces_internal(parts, &space, 1, 1, 1, 1089 1.1 martin info->start, -1) < 1) { 1090 1.1 martin if (err_msg) 1091 1.1 martin *err_msg = msg_string(MSG_No_free_space); 1092 1.1 martin return NO_PART; 1093 1.1 martin } 1094 1.1 martin if (parts->dp.num_part >= parts->max_num_parts) { 1095 1.1 martin if (err_msg) 1096 1.1 martin *err_msg = msg_string(MSG_err_too_many_partitions); 1097 1.1 martin return NO_PART; 1098 1.1 martin } 1099 1.1 martin 1100 1.1 martin if (data.size > space.size) 1101 1.1 martin data.size = space.size; 1102 1.1 martin 1103 1.1 martin p = calloc(1, sizeof(*p)); 1104 1.1 martin if (p == NULL) { 1105 1.1 martin if (err_msg != NULL) 1106 1.1 martin *err_msg = INTERNAL_ERROR; 1107 1.1 martin return NO_PART; 1108 1.1 martin } 1109 1.1 martin if (!gpt_info_to_part(p, &data, err_msg)) { 1110 1.1 martin free(p); 1111 1.1 martin return NO_PART; 1112 1.1 martin } 1113 1.1 martin p->gp_flags |= GPEF_MODIFIED; 1114 1.32 martin ok = gpt_insert_part_into_list(parts, &parts->partitions, p, 1115 1.32 martin err_msg, &pno); 1116 1.1 martin if (ok) { 1117 1.30 martin if (info->flags & PTI_INSTALL_TARGET) { 1118 1.30 martin /* update target mark - we can only have one */ 1119 1.30 martin p->gp_flags |= GPEF_TARGET; 1120 1.30 martin for (n = parts->partitions; n != NULL; n = n->gp_next) 1121 1.30 martin if (n != p) 1122 1.30 martin n->gp_flags &= ~GPEF_TARGET; 1123 1.30 martin } 1124 1.30 martin 1125 1.1 martin parts->dp.num_part++; 1126 1.1 martin parts->dp.free_space -= p->gp_size; 1127 1.32 martin return pno; 1128 1.1 martin } else { 1129 1.1 martin free(p); 1130 1.1 martin return NO_PART; 1131 1.1 martin } 1132 1.1 martin } 1133 1.1 martin 1134 1.1 martin static bool 1135 1.1 martin gpt_delete_partition(struct disk_partitions *arg, part_id id, 1136 1.1 martin const char **err_msg) 1137 1.1 martin { 1138 1.1 martin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg; 1139 1.1 martin struct gpt_part_entry *p, *last = NULL; 1140 1.1 martin part_id i; 1141 1.1 martin bool res; 1142 1.1 martin 1143 1.1 martin if (parts->dp.num_part == 0) 1144 1.1 martin return false; 1145 1.1 martin 1146 1.1 martin for (i = 0, p = parts->partitions; 1147 1.1 martin i != id && i < parts->dp.num_part && p != NULL; 1148 1.1 martin i++, p = p->gp_next) 1149 1.1 martin last = p; 1150 1.1 martin 1151 1.1 martin if (p == NULL) { 1152 1.1 martin if (err_msg) 1153 1.1 martin *err_msg = INTERNAL_ERROR; 1154 1.1 martin return false; 1155 1.1 martin } 1156 1.1 martin 1157 1.1 martin if (last == NULL) 1158 1.1 martin parts->partitions = p->gp_next; 1159 1.1 martin else 1160 1.1 martin last->gp_next = p->gp_next; 1161 1.1 martin 1162 1.1 martin res = true; 1163 1.1 martin if (p->gp_flags & GPEF_ON_DISK) { 1164 1.1 martin if (!gpt_insert_part_into_list(parts, &parts->obsolete, 1165 1.32 martin p, err_msg, NULL)) 1166 1.1 martin res = false; 1167 1.1 martin } else { 1168 1.1 martin free(p); 1169 1.1 martin } 1170 1.1 martin 1171 1.1 martin if (res) { 1172 1.1 martin parts->dp.num_part--; 1173 1.1 martin parts->dp.free_space += p->gp_size; 1174 1.1 martin } 1175 1.1 martin 1176 1.1 martin return res; 1177 1.1 martin } 1178 1.1 martin 1179 1.1 martin static bool 1180 1.1 martin gpt_delete_all_partitions(struct disk_partitions *arg) 1181 1.1 martin { 1182 1.1 martin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg; 1183 1.1 martin 1184 1.1 martin while (parts->dp.num_part > 0) { 1185 1.1 martin if (!gpt_delete_partition(&parts->dp, 0, NULL)) 1186 1.1 martin return false; 1187 1.1 martin } 1188 1.1 martin 1189 1.1 martin return true; 1190 1.1 martin } 1191 1.1 martin 1192 1.1 martin static bool 1193 1.1 martin gpt_read_part(const char *disk, daddr_t start, struct gpt_part_entry *p) 1194 1.1 martin { 1195 1.1 martin char *textbuf, *t, *tt; 1196 1.1 martin static const char expected_hdr[] = "Details for index "; 1197 1.1 martin 1198 1.1 martin /* run gpt show for this partition */ 1199 1.1 martin if (collect(T_OUTPUT, &textbuf, 1200 1.1 martin "gpt -r show -b %" PRIu64 " %s 2>/dev/null", start, disk) < 1) 1201 1.1 martin return false; 1202 1.1 martin 1203 1.1 martin /* 1204 1.1 martin * gpt show should respond with single partition details, but will 1205 1.1 martin * fall back to "show -a" output if something is wrong 1206 1.1 martin */ 1207 1.1 martin t = strtok(textbuf, "\n"); /* first line is special */ 1208 1.1 martin if (strncmp(t, expected_hdr, sizeof(expected_hdr)-1) != 0) { 1209 1.1 martin free(textbuf); 1210 1.1 martin return false; 1211 1.1 martin } 1212 1.1 martin 1213 1.1 martin /* parse output into "old" */ 1214 1.1 martin while ((t = strtok(NULL, "\n")) != NULL) { 1215 1.1 martin tt = strsep(&t, " \t"); 1216 1.1 martin if (strlen(tt) == 0) 1217 1.1 martin continue; 1218 1.1 martin gpt_add_info(p, tt, t, true); 1219 1.1 martin } 1220 1.1 martin free(textbuf); 1221 1.1 martin 1222 1.1 martin return true; 1223 1.1 martin } 1224 1.1 martin 1225 1.1 martin static bool 1226 1.1 martin gpt_apply_attr(const char *disk, const char *cmd, off_t start, uint todo) 1227 1.1 martin { 1228 1.1 martin size_t i; 1229 1.1 martin char attr_str[STRSIZE]; 1230 1.1 martin 1231 1.1 martin if (todo == 0) 1232 1.1 martin return true; 1233 1.1 martin 1234 1.1 martin strcpy(attr_str, "-a "); 1235 1.1 martin for (i = 0; todo != 0; i++) { 1236 1.1 martin if (!(gpt_avail_attrs[i].flag & todo)) 1237 1.1 martin continue; 1238 1.1 martin todo &= ~gpt_avail_attrs[i].flag; 1239 1.1 martin if (attr_str[0]) 1240 1.1 martin strlcat(attr_str, ",", 1241 1.1 martin sizeof(attr_str)); 1242 1.1 martin strlcat(attr_str, 1243 1.1 martin gpt_avail_attrs[i].name, 1244 1.1 martin sizeof(attr_str)); 1245 1.1 martin } 1246 1.1 martin if (run_program(RUN_SILENT, 1247 1.1 martin "gpt %s %s -b %" PRIu64 " %s", cmd, attr_str, start, disk) != 0) 1248 1.1 martin return false; 1249 1.1 martin return true; 1250 1.1 martin } 1251 1.1 martin 1252 1.1 martin /* 1253 1.1 martin * Modify an existing on-disk partition. 1254 1.1 martin * Start and size can not be changed here, caller needs to deal 1255 1.1 martin * with that kind of changes upfront. 1256 1.1 martin */ 1257 1.1 martin static bool 1258 1.1 martin gpt_modify_part(const char *disk, struct gpt_part_entry *p) 1259 1.1 martin { 1260 1.1 martin struct gpt_part_entry old; 1261 1.1 martin uint todo_set, todo_unset; 1262 1.1 martin 1263 1.1 martin /* 1264 1.1 martin * Query current on-disk state 1265 1.1 martin */ 1266 1.1 martin memset(&old, 0, sizeof old); 1267 1.1 martin if (!gpt_read_part(disk, p->gp_start, &old)) 1268 1.1 martin return false; 1269 1.1 martin 1270 1.1 martin /* Reject unsupported changes */ 1271 1.1 martin if (old.gp_start != p->gp_start || old.gp_size != p->gp_size) 1272 1.1 martin return false; 1273 1.1 martin 1274 1.1 martin /* 1275 1.1 martin * GUID should never change, but the internal copy 1276 1.1 martin * may not yet know it. 1277 1.1 martin */ 1278 1.1 martin strcpy(p->gp_id, old.gp_id); 1279 1.1 martin 1280 1.1 martin /* Check type */ 1281 1.1 martin if (p->gp_type != old.gp_type) { 1282 1.1 martin if (run_program(RUN_SILENT, 1283 1.22 martin "gpt type -b %" PRIu64 " -T %s %s", 1284 1.1 martin p->gp_start, p->gp_type->tid, disk) != 0) 1285 1.1 martin return false; 1286 1.1 martin } 1287 1.1 martin 1288 1.1 martin /* Check label */ 1289 1.1 martin if (strcmp(p->gp_label, old.gp_label) != 0) { 1290 1.1 martin if (run_program(RUN_SILENT, 1291 1.11 martin "gpt label -b %" PRIu64 " -l \'%s\' %s", 1292 1.1 martin p->gp_start, p->gp_label, disk) != 0) 1293 1.1 martin return false; 1294 1.1 martin } 1295 1.1 martin 1296 1.1 martin /* Check attributes */ 1297 1.1 martin if (p->gp_attr != old.gp_attr) { 1298 1.1 martin if (p->gp_attr == 0) { 1299 1.1 martin if (run_program(RUN_SILENT, 1300 1.1 martin "gpt set -N -b %" PRIu64 " %s", 1301 1.1 martin p->gp_start, disk) != 0) 1302 1.1 martin return false; 1303 1.1 martin } else { 1304 1.1 martin todo_set = (p->gp_attr ^ old.gp_attr) & p->gp_attr; 1305 1.1 martin todo_unset = (p->gp_attr ^ old.gp_attr) & old.gp_attr; 1306 1.1 martin if (!gpt_apply_attr(disk, "unset", p->gp_start, 1307 1.1 martin todo_unset)) 1308 1.1 martin return false; 1309 1.1 martin if (!gpt_apply_attr(disk, "set", p->gp_start, 1310 1.1 martin todo_set)) 1311 1.1 martin return false; 1312 1.1 martin } 1313 1.1 martin } 1314 1.1 martin 1315 1.1 martin return true; 1316 1.1 martin } 1317 1.1 martin 1318 1.1 martin /* 1319 1.1 martin * verbatim copy from sys/dev/dkwedge/dkwedge_bsdlabel.c: 1320 1.1 martin * map FS_* to wedge strings 1321 1.1 martin */ 1322 1.1 martin static const char * 1323 1.1 martin bsdlabel_fstype_to_str(uint8_t fstype) 1324 1.1 martin { 1325 1.1 martin const char *str; 1326 1.1 martin 1327 1.1 martin /* 1328 1.1 martin * For each type known to FSTYPE_DEFN (from <sys/disklabel.h>), 1329 1.1 martin * a suitable case branch will convert the type number to a string. 1330 1.1 martin */ 1331 1.1 martin switch (fstype) { 1332 1.1 martin #define FSTYPE_TO_STR_CASE(tag, number, name, fsck, mount) \ 1333 1.1 martin case __CONCAT(FS_,tag): str = __CONCAT(DKW_PTYPE_,tag); break; 1334 1.1 martin FSTYPE_DEFN(FSTYPE_TO_STR_CASE) 1335 1.1 martin #undef FSTYPE_TO_STR_CASE 1336 1.1 martin default: str = NULL; break; 1337 1.1 martin } 1338 1.1 martin 1339 1.1 martin return (str); 1340 1.1 martin } 1341 1.1 martin 1342 1.25 martin /* 1343 1.25 martin * diskfd is an open file descriptor for a disk we had trouble with 1344 1.25 martin * creating some new wedges. 1345 1.25 martin * Go through all wedges actually on that disk, check if we have a 1346 1.25 martin * record for them and remove all others. 1347 1.25 martin * This should sync our internal model of partitions with the real state. 1348 1.25 martin */ 1349 1.25 martin static void 1350 1.25 martin gpt_sanitize(int diskfd, const struct gpt_disk_partitions *parts, 1351 1.25 martin struct gpt_part_entry *ignore) 1352 1.25 martin { 1353 1.25 martin struct dkwedge_info *dkw, delw; 1354 1.25 martin struct dkwedge_list dkwl; 1355 1.25 martin size_t bufsize; 1356 1.25 martin u_int i; 1357 1.25 martin 1358 1.25 martin dkw = NULL; 1359 1.25 martin dkwl.dkwl_buf = dkw; 1360 1.25 martin dkwl.dkwl_bufsize = 0; 1361 1.25 martin 1362 1.25 martin /* get a list of all wedges */ 1363 1.25 martin for (;;) { 1364 1.25 martin if (ioctl(diskfd, DIOCLWEDGES, &dkwl) == -1) 1365 1.25 martin return; 1366 1.25 martin if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied) 1367 1.25 martin break; 1368 1.25 martin bufsize = dkwl.dkwl_nwedges * sizeof(*dkw); 1369 1.25 martin if (dkwl.dkwl_bufsize < bufsize) { 1370 1.25 martin dkw = realloc(dkwl.dkwl_buf, bufsize); 1371 1.25 martin if (dkw == NULL) 1372 1.25 martin return; 1373 1.25 martin dkwl.dkwl_buf = dkw; 1374 1.25 martin dkwl.dkwl_bufsize = bufsize; 1375 1.25 martin } 1376 1.25 martin } 1377 1.25 martin 1378 1.25 martin /* try to remove all the ones we do not know about */ 1379 1.25 martin for (i = 0; i < dkwl.dkwl_nwedges; i++) { 1380 1.25 martin bool found = false; 1381 1.25 martin const char *devname = dkw[i].dkw_devname; 1382 1.25 martin 1383 1.25 martin for (struct gpt_part_entry *pe = parts->partitions; 1384 1.25 martin pe != NULL; pe = pe->gp_next) { 1385 1.25 martin if (pe == ignore) 1386 1.25 martin continue; 1387 1.25 martin if ((pe->gp_flags & GPEF_WEDGE) && 1388 1.25 martin strcmp(pe->gp_dev_name, devname) == 0) { 1389 1.25 martin found = true; 1390 1.25 martin break; 1391 1.25 martin } 1392 1.25 martin } 1393 1.25 martin if (found) 1394 1.25 martin continue; 1395 1.25 martin memset(&delw, 0, sizeof(delw)); 1396 1.26 martin strlcpy(delw.dkw_devname, devname, sizeof(delw.dkw_devname)); 1397 1.25 martin (void)ioctl(diskfd, DIOCDWEDGE, &delw); 1398 1.25 martin } 1399 1.25 martin 1400 1.25 martin /* cleanup */ 1401 1.25 martin free(dkw); 1402 1.25 martin } 1403 1.25 martin 1404 1.1 martin static bool 1405 1.25 martin gpt_add_wedge(const char *disk, struct gpt_part_entry *p, 1406 1.25 martin const struct gpt_disk_partitions *parts) 1407 1.1 martin { 1408 1.1 martin struct dkwedge_info dkw; 1409 1.1 martin const char *tname; 1410 1.1 martin char diskpath[MAXPATHLEN]; 1411 1.1 martin int fd; 1412 1.1 martin 1413 1.1 martin memset(&dkw, 0, sizeof(dkw)); 1414 1.1 martin tname = bsdlabel_fstype_to_str(p->fs_type); 1415 1.1 martin if (tname) 1416 1.1 martin strlcpy(dkw.dkw_ptype, tname, sizeof(dkw.dkw_ptype)); 1417 1.1 martin 1418 1.1 martin strlcpy((char*)&dkw.dkw_wname, p->gp_id, sizeof(dkw.dkw_wname)); 1419 1.1 martin dkw.dkw_offset = p->gp_start; 1420 1.1 martin dkw.dkw_size = p->gp_size; 1421 1.14 martin if (dkw.dkw_wname[0] == 0) { 1422 1.14 martin if (p->gp_label[0] != 0) 1423 1.14 martin strlcpy((char*)&dkw.dkw_wname, 1424 1.14 martin p->gp_label, sizeof(dkw.dkw_wname)); 1425 1.14 martin } 1426 1.14 martin if (dkw.dkw_wname[0] == 0) { 1427 1.14 martin snprintf((char*)dkw.dkw_wname, sizeof dkw.dkw_wname, 1428 1.14 martin "%s_%" PRIi64 "@%" PRIi64, disk, p->gp_size, p->gp_start); 1429 1.14 martin } 1430 1.1 martin 1431 1.1 martin fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0); 1432 1.1 martin if (fd < 0) 1433 1.1 martin return false; 1434 1.1 martin if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) { 1435 1.25 martin if (errno == EINVAL) { 1436 1.25 martin /* sanitize existing wedges and try again */ 1437 1.25 martin gpt_sanitize(fd, parts, p); 1438 1.25 martin if (ioctl(fd, DIOCAWEDGE, &dkw) == 0) 1439 1.25 martin goto ok; 1440 1.25 martin } 1441 1.1 martin close(fd); 1442 1.1 martin return false; 1443 1.1 martin } 1444 1.25 martin ok: 1445 1.1 martin close(fd); 1446 1.1 martin 1447 1.1 martin strlcpy(p->gp_dev_name, dkw.dkw_devname, sizeof(p->gp_dev_name)); 1448 1.1 martin p->gp_flags |= GPEF_WEDGE; 1449 1.1 martin return true; 1450 1.1 martin } 1451 1.1 martin 1452 1.11 martin static void 1453 1.11 martin escape_spaces(char *dest, const char *src) 1454 1.11 martin { 1455 1.11 martin unsigned char c; 1456 1.11 martin 1457 1.11 martin while (*src) { 1458 1.11 martin c = *src++; 1459 1.11 martin if (isspace(c) || c == '\\') 1460 1.11 martin *dest++ = '\\'; 1461 1.11 martin *dest++ = c; 1462 1.11 martin } 1463 1.11 martin *dest = 0; 1464 1.11 martin } 1465 1.11 martin 1466 1.1 martin static bool 1467 1.1 martin gpt_get_part_device(const struct disk_partitions *arg, 1468 1.1 martin part_id id, char *devname, size_t max_devname_len, int *part, 1469 1.14 martin enum dev_name_usage usage, bool with_path, bool life) 1470 1.1 martin { 1471 1.1 martin const struct gpt_disk_partitions *parts = 1472 1.1 martin (const struct gpt_disk_partitions*)arg; 1473 1.1 martin struct gpt_part_entry *p = parts->partitions; 1474 1.11 martin char tmpname[GPT_LABEL_LEN*2]; 1475 1.1 martin part_id no; 1476 1.1 martin 1477 1.1 martin 1478 1.1 martin for (no = 0; p != NULL && no < id; no++) 1479 1.1 martin p = p->gp_next; 1480 1.1 martin 1481 1.1 martin if (no != id || p == NULL) 1482 1.1 martin return false; 1483 1.1 martin 1484 1.1 martin if (part) 1485 1.1 martin *part = -1; 1486 1.1 martin 1487 1.14 martin if (usage == logical_name && p->gp_label[0] == 0 && p->gp_id[0] == 0) 1488 1.14 martin usage = plain_name; 1489 1.14 martin if (usage == plain_name || usage == raw_dev_name) 1490 1.14 martin life = true; 1491 1.24 martin if (!(p->gp_flags & GPEF_WEDGE) && life && 1492 1.25 martin !gpt_add_wedge(arg->disk, p, parts)) 1493 1.24 martin return false; 1494 1.1 martin 1495 1.1 martin switch (usage) { 1496 1.1 martin case logical_name: 1497 1.11 martin if (p->gp_label[0] != 0) { 1498 1.11 martin escape_spaces(tmpname, p->gp_label); 1499 1.1 martin snprintf(devname, max_devname_len, 1500 1.11 martin "NAME=%s", tmpname); 1501 1.11 martin } else { 1502 1.1 martin snprintf(devname, max_devname_len, 1503 1.1 martin "NAME=%s", p->gp_id); 1504 1.11 martin } 1505 1.1 martin break; 1506 1.1 martin case plain_name: 1507 1.1 martin assert(p->gp_flags & GPEF_WEDGE); 1508 1.1 martin if (with_path) 1509 1.1 martin snprintf(devname, max_devname_len, _PATH_DEV "%s", 1510 1.1 martin p->gp_dev_name); 1511 1.1 martin else 1512 1.1 martin strlcpy(devname, p->gp_dev_name, max_devname_len); 1513 1.1 martin break; 1514 1.1 martin case raw_dev_name: 1515 1.1 martin assert(p->gp_flags & GPEF_WEDGE); 1516 1.1 martin if (with_path) 1517 1.1 martin snprintf(devname, max_devname_len, _PATH_DEV "r%s", 1518 1.1 martin p->gp_dev_name); 1519 1.1 martin else 1520 1.1 martin snprintf(devname, max_devname_len, "r%s", 1521 1.1 martin p->gp_dev_name); 1522 1.1 martin break; 1523 1.1 martin default: 1524 1.1 martin return false; 1525 1.1 martin } 1526 1.1 martin 1527 1.1 martin return true; 1528 1.1 martin } 1529 1.1 martin 1530 1.1 martin static bool 1531 1.1 martin gpt_write_to_disk(struct disk_partitions *arg) 1532 1.1 martin { 1533 1.1 martin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg; 1534 1.1 martin struct gpt_part_entry *p, *n; 1535 1.11 martin char label_arg[sizeof(p->gp_label) + 10]; 1536 1.1 martin char diskpath[MAXPATHLEN]; 1537 1.1 martin int fd, bits = 0; 1538 1.1 martin bool root_is_new = false, efi_is_new = false; 1539 1.1 martin part_id root_id = NO_PART, efi_id = NO_PART, pno; 1540 1.1 martin 1541 1.1 martin /* 1542 1.1 martin * Remove all wedges on this disk - they may become invalid and we 1543 1.1 martin * have no easy way to associate them with the partitioning data. 1544 1.1 martin * Instead we will explicitly request creation of wedges on demand 1545 1.1 martin * later. 1546 1.1 martin */ 1547 1.1 martin fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0); 1548 1.1 martin if (fd < 0) 1549 1.1 martin return false; 1550 1.1 martin if (ioctl(fd, DIOCRMWEDGES, &bits) == -1) 1551 1.1 martin return false; 1552 1.1 martin close(fd); 1553 1.1 martin 1554 1.1 martin /* 1555 1.12 martin * Collect first root and efi partition (if available), clear 1556 1.12 martin * "have wedge" flags. 1557 1.1 martin */ 1558 1.1 martin for (pno = 0, p = parts->partitions; p != NULL; p = p->gp_next, pno++) { 1559 1.12 martin p->gp_flags &= ~GPEF_WEDGE; 1560 1.5 martin if (root_id == NO_PART && p->gp_type != NULL) { 1561 1.1 martin if (p->gp_type->gent.generic_ptype == PT_root && 1562 1.19 martin (p->gp_flags & GPEF_TARGET)) { 1563 1.1 martin root_id = pno; 1564 1.1 martin root_is_new = !(p->gp_flags & GPEF_ON_DISK); 1565 1.1 martin } else if (efi_id == NO_PART && 1566 1.1 martin p->gp_type->gent.generic_ptype == PT_EFI_SYSTEM) { 1567 1.1 martin efi_id = pno; 1568 1.1 martin efi_is_new = !(p->gp_flags & GPEF_ON_DISK); 1569 1.1 martin } 1570 1.1 martin } 1571 1.1 martin } 1572 1.1 martin 1573 1.1 martin /* 1574 1.1 martin * If no GPT on disk yet, create it. 1575 1.1 martin */ 1576 1.1 martin if (!parts->has_gpt) { 1577 1.1 martin char limit[30]; 1578 1.1 martin 1579 1.1 martin if (parts->max_num_parts > 0) 1580 1.1 martin sprintf(limit, "-p %zu", parts->max_num_parts); 1581 1.1 martin else 1582 1.1 martin limit[0] = 0; 1583 1.1 martin if (run_program(RUN_SILENT, "gpt create %s %s", 1584 1.1 martin limit, parts->dp.disk)) 1585 1.1 martin return false; 1586 1.1 martin parts->has_gpt = true; 1587 1.1 martin } 1588 1.1 martin 1589 1.1 martin /* 1590 1.1 martin * Delete all old partitions 1591 1.1 martin */ 1592 1.1 martin for (p = parts->obsolete; p != NULL; p = n) { 1593 1.1 martin run_program(RUN_SILENT, "gpt -n remove -b %" PRIu64 " %s", 1594 1.1 martin p->gp_start, arg->disk); 1595 1.1 martin n = p->gp_next; 1596 1.1 martin free(p); 1597 1.1 martin } 1598 1.1 martin parts->obsolete = NULL; 1599 1.1 martin 1600 1.1 martin /* 1601 1.1 martin * Modify existing but changed partitions 1602 1.1 martin */ 1603 1.1 martin for (p = parts->partitions; p != NULL; p = p->gp_next) { 1604 1.1 martin if (!(p->gp_flags & GPEF_ON_DISK)) 1605 1.1 martin continue; 1606 1.1 martin 1607 1.1 martin if (p->gp_flags & GPEF_RESIZED) { 1608 1.1 martin run_program(RUN_SILENT, 1609 1.1 martin "gpt -n resize -b %" PRIu64 " -s %" PRIu64 "s %s", 1610 1.1 martin p->gp_start, p->gp_size, arg->disk); 1611 1.1 martin p->gp_flags &= ~GPEF_RESIZED; 1612 1.1 martin } 1613 1.1 martin 1614 1.1 martin if (!(p->gp_flags & GPEF_MODIFIED)) 1615 1.1 martin continue; 1616 1.1 martin 1617 1.1 martin if (!gpt_modify_part(parts->dp.disk, p)) 1618 1.1 martin return false; 1619 1.1 martin } 1620 1.1 martin 1621 1.1 martin /* 1622 1.1 martin * Add new partitions 1623 1.1 martin */ 1624 1.1 martin for (p = parts->partitions; p != NULL; p = p->gp_next) { 1625 1.1 martin if (p->gp_flags & GPEF_ON_DISK) 1626 1.1 martin continue; 1627 1.1 martin if (!(p->gp_flags & GPEF_MODIFIED)) 1628 1.1 martin continue; 1629 1.1 martin 1630 1.1 martin if (p->gp_label[0] == 0) 1631 1.1 martin label_arg[0] = 0; 1632 1.1 martin else 1633 1.11 martin sprintf(label_arg, "-l \'%s\'", p->gp_label); 1634 1.1 martin 1635 1.5 martin if (p->gp_type != NULL) 1636 1.5 martin run_program(RUN_SILENT, 1637 1.5 martin "gpt -n add -b %" PRIu64 " -s %" PRIu64 1638 1.5 martin "s -t %s %s %s", 1639 1.5 martin p->gp_start, p->gp_size, p->gp_type->tid, 1640 1.5 martin label_arg, arg->disk); 1641 1.5 martin else 1642 1.5 martin run_program(RUN_SILENT, 1643 1.5 martin "gpt -n add -b %" PRIu64 " -s %" PRIu64 1644 1.5 martin "s %s %s", 1645 1.5 martin p->gp_start, p->gp_size, label_arg, arg->disk); 1646 1.1 martin gpt_apply_attr(arg->disk, "set", p->gp_start, p->gp_attr); 1647 1.1 martin gpt_read_part(arg->disk, p->gp_start, p); 1648 1.1 martin p->gp_flags |= GPEF_ON_DISK; 1649 1.1 martin } 1650 1.1 martin 1651 1.1 martin /* 1652 1.1 martin * Additional MD bootloader magic... 1653 1.1 martin */ 1654 1.1 martin if (!md_gpt_post_write(&parts->dp, root_id, root_is_new, efi_id, 1655 1.1 martin efi_is_new)) 1656 1.1 martin return false; 1657 1.1 martin 1658 1.1 martin return true; 1659 1.1 martin } 1660 1.1 martin 1661 1.9 martin static part_id 1662 1.9 martin gpt_find_by_name(struct disk_partitions *arg, const char *name) 1663 1.9 martin { 1664 1.9 martin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg; 1665 1.9 martin struct gpt_part_entry *p; 1666 1.9 martin part_id pno; 1667 1.9 martin 1668 1.9 martin for (pno = 0, p = parts->partitions; p != NULL; 1669 1.9 martin p = p->gp_next, pno++) { 1670 1.9 martin if (strcmp(p->gp_label, name) == 0) 1671 1.9 martin return pno; 1672 1.9 martin if (strcmp(p->gp_id, name) == 0) 1673 1.9 martin return pno; 1674 1.9 martin } 1675 1.9 martin 1676 1.9 martin return NO_PART; 1677 1.9 martin } 1678 1.9 martin 1679 1.1 martin bool 1680 1.1 martin gpt_parts_check(void) 1681 1.1 martin { 1682 1.1 martin 1683 1.1 martin check_available_binaries(); 1684 1.1 martin 1685 1.1 martin return have_gpt && have_dk; 1686 1.1 martin } 1687 1.1 martin 1688 1.1 martin static void 1689 1.1 martin gpt_free(struct disk_partitions *arg) 1690 1.1 martin { 1691 1.1 martin struct gpt_disk_partitions *parts = (struct gpt_disk_partitions*)arg; 1692 1.1 martin struct gpt_part_entry *p, *n; 1693 1.1 martin 1694 1.1 martin assert(parts != NULL); 1695 1.1 martin for (p = parts->partitions; p != NULL; p = n) { 1696 1.27 martin if (p->gp_flags & GPEF_WEDGE) 1697 1.27 martin register_post_umount_delwedge(parts->dp.disk, 1698 1.27 martin p->gp_dev_name); 1699 1.1 martin free(__UNCONST(p->last_mounted)); 1700 1.1 martin n = p->gp_next; 1701 1.1 martin free(p); 1702 1.1 martin } 1703 1.12 martin free(__UNCONST(parts->dp.disk)); 1704 1.1 martin free(parts); 1705 1.1 martin } 1706 1.1 martin 1707 1.20 martin static void 1708 1.20 martin gpt_destroy_part_scheme(struct disk_partitions *arg) 1709 1.20 martin { 1710 1.20 martin 1711 1.20 martin run_program(RUN_SILENT, "gpt destroy %s", arg->disk); 1712 1.20 martin gpt_free(arg); 1713 1.20 martin } 1714 1.20 martin 1715 1.1 martin static bool 1716 1.1 martin gpt_custom_attribute_writable(const struct disk_partitions *arg, 1717 1.1 martin part_id ptn, size_t attr_no) 1718 1.1 martin { 1719 1.1 martin const struct gpt_disk_partitions *parts = 1720 1.1 martin (const struct gpt_disk_partitions*)arg; 1721 1.1 martin size_t i; 1722 1.1 martin struct gpt_part_entry *p; 1723 1.1 martin 1724 1.1 martin if (attr_no >= arg->pscheme->custom_attribute_count) 1725 1.1 martin return false; 1726 1.1 martin 1727 1.1 martin const msg label = arg->pscheme->custom_attributes[attr_no].label; 1728 1.1 martin 1729 1.1 martin /* we can not edit the uuid attribute */ 1730 1.1 martin if (label == MSG_ptn_uuid) 1731 1.1 martin return false; 1732 1.1 martin 1733 1.1 martin /* the label is always editable */ 1734 1.1 martin if (label == MSG_ptn_label) 1735 1.1 martin return true; 1736 1.1 martin 1737 1.1 martin /* the GPT type is read only */ 1738 1.1 martin if (label == MSG_ptn_gpt_type) 1739 1.1 martin return false; 1740 1.1 martin 1741 1.1 martin /* BOOTME makes no sense on swap partitions */ 1742 1.1 martin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next) 1743 1.1 martin if (i == ptn) 1744 1.1 martin break; 1745 1.1 martin 1746 1.1 martin if (p == NULL) 1747 1.1 martin return false; 1748 1.1 martin 1749 1.5 martin if (p->fs_type == FS_SWAP || 1750 1.5 martin (p->gp_type != NULL && p->gp_type->gent.generic_ptype == PT_swap)) 1751 1.1 martin return false; 1752 1.1 martin 1753 1.1 martin return true; 1754 1.1 martin } 1755 1.1 martin 1756 1.6 martin static const char * 1757 1.6 martin gpt_get_label_str(const struct disk_partitions *arg, part_id ptn) 1758 1.6 martin { 1759 1.6 martin const struct gpt_disk_partitions *parts = 1760 1.6 martin (const struct gpt_disk_partitions*)arg; 1761 1.6 martin size_t i; 1762 1.6 martin struct gpt_part_entry *p; 1763 1.6 martin 1764 1.6 martin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next) 1765 1.6 martin if (i == ptn) 1766 1.6 martin break; 1767 1.6 martin 1768 1.6 martin if (p == NULL) 1769 1.6 martin return NULL; 1770 1.6 martin 1771 1.6 martin if (p->gp_label[0] != 0) 1772 1.6 martin return p->gp_label; 1773 1.6 martin return p->gp_id; 1774 1.6 martin } 1775 1.6 martin 1776 1.1 martin static bool 1777 1.1 martin gpt_format_custom_attribute(const struct disk_partitions *arg, 1778 1.1 martin part_id ptn, size_t attr_no, const struct disk_part_info *info, 1779 1.1 martin char *out, size_t out_space) 1780 1.1 martin { 1781 1.1 martin const struct gpt_disk_partitions *parts = 1782 1.1 martin (const struct gpt_disk_partitions*)arg; 1783 1.1 martin size_t i; 1784 1.1 martin struct gpt_part_entry *p, data; 1785 1.1 martin 1786 1.1 martin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next) 1787 1.1 martin if (i == ptn) 1788 1.1 martin break; 1789 1.1 martin 1790 1.1 martin if (p == NULL) 1791 1.1 martin return false; 1792 1.1 martin 1793 1.1 martin if (attr_no >= parts->dp.pscheme->custom_attribute_count) 1794 1.1 martin return false; 1795 1.1 martin 1796 1.1 martin const msg label = parts->dp.pscheme->custom_attributes[attr_no].label; 1797 1.1 martin 1798 1.1 martin if (info != NULL) { 1799 1.1 martin data = *p; 1800 1.1 martin gpt_info_to_part(&data, info, NULL); 1801 1.1 martin p = &data; 1802 1.1 martin } 1803 1.1 martin 1804 1.1 martin if (label == MSG_ptn_label) 1805 1.1 martin strlcpy(out, p->gp_label, out_space); 1806 1.1 martin else if (label == MSG_ptn_uuid) 1807 1.1 martin strlcpy(out, p->gp_id, out_space); 1808 1.5 martin else if (label == MSG_ptn_gpt_type) { 1809 1.5 martin if (p->gp_type != NULL) 1810 1.5 martin strlcpy(out, p->gp_type->gent.description, out_space); 1811 1.5 martin else if (out_space > 1) 1812 1.5 martin out[0] = 0; 1813 1.5 martin } else if (label == MSG_ptn_boot) 1814 1.1 martin strlcpy(out, msg_string(p->gp_attr & GPT_ATTR_BOOT ? 1815 1.1 martin MSG_Yes : MSG_No), out_space); 1816 1.1 martin else 1817 1.1 martin return false; 1818 1.1 martin 1819 1.1 martin return true; 1820 1.1 martin } 1821 1.1 martin 1822 1.1 martin static bool 1823 1.1 martin gpt_custom_attribute_toggle(struct disk_partitions *arg, 1824 1.1 martin part_id ptn, size_t attr_no) 1825 1.1 martin { 1826 1.1 martin const struct gpt_disk_partitions *parts = 1827 1.1 martin (const struct gpt_disk_partitions*)arg; 1828 1.1 martin size_t i; 1829 1.1 martin struct gpt_part_entry *p; 1830 1.1 martin 1831 1.1 martin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next) 1832 1.1 martin if (i == ptn) 1833 1.1 martin break; 1834 1.1 martin 1835 1.1 martin if (p == NULL) 1836 1.1 martin return false; 1837 1.1 martin 1838 1.1 martin if (attr_no >= parts->dp.pscheme->custom_attribute_count) 1839 1.1 martin return false; 1840 1.1 martin 1841 1.1 martin const msg label = parts->dp.pscheme->custom_attributes[attr_no].label; 1842 1.1 martin if (label != MSG_ptn_boot) 1843 1.1 martin return false; 1844 1.1 martin 1845 1.1 martin if (p->gp_attr & GPT_ATTR_BOOT) { 1846 1.1 martin p->gp_attr &= ~GPT_ATTR_BOOT; 1847 1.1 martin } else { 1848 1.1 martin for (i = 0, p = parts->partitions; p != NULL; 1849 1.1 martin i++, p = p->gp_next) 1850 1.1 martin if (i == ptn) 1851 1.1 martin p->gp_attr |= GPT_ATTR_BOOT; 1852 1.1 martin else 1853 1.1 martin p->gp_attr &= ~GPT_ATTR_BOOT; 1854 1.1 martin } 1855 1.1 martin return true; 1856 1.1 martin } 1857 1.1 martin 1858 1.1 martin static bool 1859 1.1 martin gpt_custom_attribute_set_str(struct disk_partitions *arg, 1860 1.1 martin part_id ptn, size_t attr_no, const char *new_val) 1861 1.1 martin { 1862 1.1 martin const struct gpt_disk_partitions *parts = 1863 1.1 martin (const struct gpt_disk_partitions*)arg; 1864 1.1 martin size_t i; 1865 1.1 martin struct gpt_part_entry *p; 1866 1.1 martin 1867 1.1 martin for (i = 0, p = parts->partitions; p != NULL; i++, p = p->gp_next) 1868 1.1 martin if (i == ptn) 1869 1.1 martin break; 1870 1.1 martin 1871 1.1 martin if (p == NULL) 1872 1.1 martin return false; 1873 1.1 martin 1874 1.1 martin if (attr_no >= parts->dp.pscheme->custom_attribute_count) 1875 1.1 martin return false; 1876 1.1 martin 1877 1.1 martin const msg label = parts->dp.pscheme->custom_attributes[attr_no].label; 1878 1.1 martin 1879 1.1 martin if (label != MSG_ptn_label) 1880 1.1 martin return false; 1881 1.1 martin 1882 1.1 martin strlcpy(p->gp_label, new_val, sizeof(p->gp_label)); 1883 1.1 martin return true; 1884 1.1 martin } 1885 1.1 martin 1886 1.1 martin static bool 1887 1.1 martin gpt_have_boot_support(const char *disk) 1888 1.1 martin { 1889 1.1 martin #ifdef HAVE_GPT_BOOT 1890 1.1 martin return true; 1891 1.1 martin #else 1892 1.1 martin return false; 1893 1.1 martin #endif 1894 1.1 martin } 1895 1.1 martin 1896 1.1 martin const struct disk_part_custom_attribute gpt_custom_attrs[] = { 1897 1.1 martin { .label = MSG_ptn_label, .type = pet_str }, 1898 1.1 martin { .label = MSG_ptn_uuid, .type = pet_str }, 1899 1.1 martin { .label = MSG_ptn_gpt_type, .type = pet_str }, 1900 1.1 martin { .label = MSG_ptn_boot, .type = pet_bool }, 1901 1.1 martin }; 1902 1.1 martin 1903 1.1 martin const struct disk_partitioning_scheme 1904 1.1 martin gpt_parts = { 1905 1.1 martin .name = MSG_parttype_gpt, 1906 1.1 martin .short_name = MSG_parttype_gpt_short, 1907 1.1 martin .part_flag_desc = MSG_gpt_flag_desc, 1908 1.1 martin .custom_attribute_count = __arraycount(gpt_custom_attrs), 1909 1.1 martin .custom_attributes = gpt_custom_attrs, 1910 1.1 martin .get_part_types_count = gpt_type_count, 1911 1.1 martin .get_part_type = gpt_get_ptype, 1912 1.1 martin .get_generic_part_type = gpt_get_generic_type, 1913 1.1 martin .get_fs_part_type = gpt_get_fs_part_type, 1914 1.14 martin .get_default_fstype = gpt_get_default_fstype, 1915 1.12 martin .create_custom_part_type = gpt_create_custom_part_type, 1916 1.12 martin .create_unknown_part_type = gpt_create_unknown_part_type, 1917 1.1 martin .get_part_alignment = gpt_get_part_alignment, 1918 1.1 martin .read_from_disk = gpt_read_from_disk, 1919 1.15 martin .get_cylinder_size = gpt_cyl_size, 1920 1.1 martin .create_new_for_disk = gpt_create_new, 1921 1.1 martin .have_boot_support = gpt_have_boot_support, 1922 1.9 martin .find_by_name = gpt_find_by_name, 1923 1.1 martin .can_add_partition = gpt_can_add_partition, 1924 1.1 martin .custom_attribute_writable = gpt_custom_attribute_writable, 1925 1.1 martin .format_custom_attribute = gpt_format_custom_attribute, 1926 1.1 martin .custom_attribute_toggle = gpt_custom_attribute_toggle, 1927 1.1 martin .custom_attribute_set_str = gpt_custom_attribute_set_str, 1928 1.6 martin .other_partition_identifier = gpt_get_label_str, 1929 1.1 martin .get_part_device = gpt_get_part_device, 1930 1.1 martin .max_free_space_at = gpt_max_free_space_at, 1931 1.1 martin .get_free_spaces = gpt_get_free_spaces, 1932 1.12 martin .adapt_foreign_part_info = generic_adapt_foreign_part_info, 1933 1.1 martin .get_part_info = gpt_get_part_info, 1934 1.1 martin .get_part_attr_str = gpt_get_part_attr_str, 1935 1.1 martin .set_part_info = gpt_set_part_info, 1936 1.1 martin .add_partition = gpt_add_part, 1937 1.1 martin .delete_all_partitions = gpt_delete_all_partitions, 1938 1.1 martin .delete_partition = gpt_delete_partition, 1939 1.1 martin .write_to_disk = gpt_write_to_disk, 1940 1.1 martin .free = gpt_free, 1941 1.20 martin .destroy_part_scheme = gpt_destroy_part_scheme, 1942 1.12 martin .cleanup = gpt_cleanup, 1943 1.1 martin }; 1944