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