1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2011 Nathan Whitehorn
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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * 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 THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <libutil.h>
34 #include <inttypes.h>
35
36 #include <libgeom.h>
37 #include <dialog.h>
38 #include <dlg_keys.h>
39
40 #include "partedit.h"
41
42 #define GPART_FLAGS "x" /* Do not commit changes by default */
43
44 static void
gpart_show_error(const char * title,const char * explanation,const char * errstr)45 gpart_show_error(const char *title, const char *explanation, const char *errstr)
46 {
47 char *errmsg;
48 char message[512];
49 int error;
50
51 if (explanation == NULL)
52 explanation = "";
53
54 error = strtol(errstr, &errmsg, 0);
55 if (errmsg != errstr) {
56 while (errmsg[0] == ' ')
57 errmsg++;
58 if (errmsg[0] != '\0')
59 snprintf(message, sizeof(message), "%s%s. %s",
60 explanation, strerror(error), errmsg);
61 else
62 snprintf(message, sizeof(message), "%s%s", explanation,
63 strerror(error));
64 } else {
65 snprintf(message, sizeof(message), "%s%s", explanation, errmsg);
66 }
67
68 dialog_msgbox(title, message, 0, 0, TRUE);
69 }
70
71 static int
scheme_supports_labels(const char * scheme)72 scheme_supports_labels(const char *scheme)
73 {
74 if (strcmp(scheme, "APM") == 0)
75 return (1);
76 if (strcmp(scheme, "GPT") == 0)
77 return (1);
78
79 return (0);
80 }
81
82 static char *
newfs_command(const char * fstype,int use_default)83 newfs_command(const char *fstype, int use_default)
84 {
85 FILE *fp;
86 char *buf;
87 size_t len;
88
89 fp = open_memstream(&buf, &len);
90
91 if (strcmp(fstype, "freebsd-ufs") == 0) {
92 int i;
93 DIALOG_LISTITEM items[] = {
94 {"UFS1", "UFS Version 1",
95 "Use version 1 of the UFS file system instead "
96 "of version 2 (not recommended)", 0 },
97 {"SU", "Softupdates",
98 "Enable softupdates (default)", 1 },
99 {"SUJ", "Softupdates journaling",
100 "Enable file system journaling (default - "
101 "turn off for SSDs)", 1 },
102 {"TRIM", "Enable SSD TRIM support",
103 "Enable TRIM support, useful on solid-state drives",
104 0 },
105 };
106
107 if (!use_default) {
108 int choice;
109 choice = dlg_checklist("UFS Options", "", 0, 0, 0,
110 nitems(items), items, NULL,
111 FLAG_CHECK, &i);
112 if (choice == 1) /* Cancel */
113 goto out;
114 }
115
116 fputs("newfs ", fp);
117 for (i = 0; i < (int)nitems(items); i++) {
118 if (items[i].state == 0)
119 continue;
120 if (strcmp(items[i].name, "UFS1") == 0)
121 fputs("-O1 ", fp);
122 else if (strcmp(items[i].name, "SU") == 0)
123 fputs("-U ", fp);
124 else if (strcmp(items[i].name, "SUJ") == 0)
125 fputs("-j ", fp);
126 else if (strcmp(items[i].name, "TRIM") == 0)
127 fputs("-t ", fp);
128 }
129 } else if (strcmp(fstype, "freebsd-zfs") == 0) {
130 int i;
131 DIALOG_LISTITEM items[] = {
132 {"fletcher4", "checksum algorithm: fletcher4",
133 "Use fletcher4 for data integrity checking. "
134 "(default)", 1 },
135 {"fletcher2", "checksum algorithm: fletcher2",
136 "Use fletcher2 for data integrity checking. "
137 "(not recommended)", 0 },
138 {"sha256", "checksum algorithm: sha256",
139 "Use sha256 for data integrity checking. "
140 "(not recommended)", 0 },
141 {"atime", "Update atimes for files",
142 "Disable atime update", 0 },
143 };
144
145 if (!use_default) {
146 int choice;
147 choice = dlg_checklist("ZFS Options", "", 0, 0, 0,
148 nitems(items), items, NULL,
149 FLAG_CHECK, &i);
150 if (choice == 1) /* Cancel */
151 goto out;
152 }
153
154 fputs("zpool create -f -m none ", fp);
155 if (getenv("BSDINSTALL_TMPBOOT") != NULL) {
156 char zfsboot_path[MAXPATHLEN];
157
158 snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs",
159 getenv("BSDINSTALL_TMPBOOT"));
160 mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP |
161 S_IROTH | S_IXOTH);
162 fprintf(fp, " -o cachefile=%s/zpool.cache ",
163 zfsboot_path);
164 }
165 for (i = 0; i < (int)nitems(items); i++) {
166 if (items[i].state == 0)
167 continue;
168 if (strcmp(items[i].name, "fletcher4") == 0)
169 fputs("-O checksum=fletcher4 ", fp);
170 else if (strcmp(items[i].name, "fletcher2") == 0)
171 fputs("-O checksum=fletcher2 ", fp);
172 else if (strcmp(items[i].name, "sha256") == 0)
173 fputs("-O checksum=sha256 ", fp);
174 else if (strcmp(items[i].name, "atime") == 0)
175 fputs("-O atime=off ", fp);
176 }
177 } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0 ||
178 strcmp(fstype, "ms-basic-data") == 0) {
179 int i;
180 DIALOG_LISTITEM items[] = {
181 {"FAT32", "FAT Type 32",
182 "Create a FAT32 filesystem (default)", 1 },
183 {"FAT16", "FAT Type 16",
184 "Create a FAT16 filesystem", 0 },
185 {"FAT12", "FAT Type 12",
186 "Create a FAT12 filesystem", 0 },
187 };
188
189 if (!use_default) {
190 int choice;
191 choice = dlg_checklist("FAT Options", "", 0, 0, 0,
192 nitems(items), items, NULL,
193 FLAG_RADIO, &i);
194 if (choice == 1) /* Cancel */
195 goto out;
196 }
197
198 fputs("newfs_msdos ", fp);
199 for (i = 0; i < (int)nitems(items); i++) {
200 if (items[i].state == 0)
201 continue;
202 if (strcmp(items[i].name, "FAT32") == 0)
203 fputs("-F 32 -c 1", fp);
204 else if (strcmp(items[i].name, "FAT16") == 0)
205 fputs("-F 16 ", fp);
206 else if (strcmp(items[i].name, "FAT12") == 0)
207 fputs("-F 12 ", fp);
208 }
209 } else {
210 if (!use_default)
211 dialog_msgbox("Error", "No configurable options exist "
212 "for this filesystem.", 0, 0, TRUE);
213 }
214
215 out:
216 fclose(fp);
217 return (buf);
218 }
219
220 const char *
choose_part_type(const char * def_scheme)221 choose_part_type(const char *def_scheme)
222 {
223 int cancel, choice;
224 const char *scheme = NULL;
225
226 DIALOG_LISTITEM items[] = {
227 {"APM", "Apple Partition Map",
228 "Bootable on PowerPC Apple Hardware", 0 },
229 {"BSD", "BSD Labels",
230 "Bootable on most x86 systems", 0 },
231 {"GPT", "GUID Partition Table",
232 "Bootable on most x86 systems and EFI aware ARM64", 0 },
233 {"MBR", "DOS Partitions",
234 "Bootable on most x86 systems", 0 },
235 };
236
237 parttypemenu:
238 dialog_vars.default_item = __DECONST(char *, def_scheme);
239 cancel = dlg_menu("Partition Scheme",
240 "Select a partition scheme for this volume:", 0, 0, 0,
241 nitems(items), items, &choice, NULL);
242 dialog_vars.default_item = NULL;
243
244 if (cancel)
245 return NULL;
246
247 if (!is_scheme_bootable(items[choice].name)) {
248 char message[512];
249
250 snprintf(message, sizeof(message),
251 "This partition scheme (%s) is not "
252 "bootable on this platform. Are you sure you want "
253 "to proceed?", items[choice].name);
254 dialog_vars.defaultno = TRUE;
255 cancel = dialog_yesno("Warning", message, 0, 0);
256 dialog_vars.defaultno = FALSE;
257 if (cancel) /* cancel */
258 goto parttypemenu;
259 }
260
261 scheme = items[choice].name;
262
263 return scheme;
264 }
265
266 int
gpart_partition(const char * lg_name,const char * scheme)267 gpart_partition(const char *lg_name, const char *scheme)
268 {
269 int cancel;
270 struct gctl_req *r;
271 const char *errstr;
272
273 schememenu:
274 if (scheme == NULL) {
275 scheme = choose_part_type(default_scheme());
276
277 if (scheme == NULL)
278 return (-1);
279
280 if (!is_scheme_bootable(scheme)) {
281 char message[512];
282
283 snprintf(message, sizeof(message),
284 "This partition scheme (%s) is not "
285 "bootable on this platform. Are you sure you want "
286 "to proceed?", scheme);
287 dialog_vars.defaultno = TRUE;
288 cancel = dialog_yesno("Warning", message, 0, 0);
289 dialog_vars.defaultno = FALSE;
290 if (cancel) { /* cancel */
291 /* Reset scheme so user can choose another */
292 scheme = NULL;
293 goto schememenu;
294 }
295 }
296 }
297
298 r = gctl_get_handle();
299 gctl_ro_param(r, "class", -1, "PART");
300 gctl_ro_param(r, "arg0", -1, lg_name);
301 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
302 gctl_ro_param(r, "scheme", -1, scheme);
303 gctl_ro_param(r, "verb", -1, "create");
304
305 errstr = gctl_issue(r);
306 if (errstr != NULL && errstr[0] != '\0') {
307 gpart_show_error("Error", NULL, errstr);
308 gctl_free(r);
309 scheme = NULL;
310 goto schememenu;
311 }
312 gctl_free(r);
313
314 if (bootcode_path(scheme) != NULL)
315 get_part_metadata(lg_name, 1)->bootcode = 1;
316 return (0);
317 }
318
319 static void
gpart_activate(struct gprovider * pp)320 gpart_activate(struct gprovider *pp)
321 {
322 struct gconfig *gc;
323 struct gctl_req *r;
324 const char *errstr, *scheme;
325 const char *attribute = NULL;
326 intmax_t idx;
327
328 /*
329 * Some partition schemes need this partition to be marked 'active'
330 * for it to be bootable.
331 */
332 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
333 if (strcmp(gc->lg_name, "scheme") == 0) {
334 scheme = gc->lg_val;
335 break;
336 }
337 }
338
339 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0)
340 attribute = "active";
341 else
342 return;
343
344 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
345 if (strcmp(gc->lg_name, "index") == 0) {
346 idx = atoi(gc->lg_val);
347 break;
348 }
349 }
350
351 r = gctl_get_handle();
352 gctl_ro_param(r, "class", -1, "PART");
353 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
354 gctl_ro_param(r, "verb", -1, "set");
355 gctl_ro_param(r, "attrib", -1, attribute);
356 gctl_ro_param(r, "index", sizeof(idx), &idx);
357
358 errstr = gctl_issue(r);
359 if (errstr != NULL && errstr[0] != '\0')
360 gpart_show_error("Error", "Error marking partition active:",
361 errstr);
362 gctl_free(r);
363 }
364
365 void
gpart_set_root(const char * lg_name,const char * attribute)366 gpart_set_root(const char *lg_name, const char *attribute)
367 {
368 struct gctl_req *r;
369 const char *errstr;
370
371 r = gctl_get_handle();
372 gctl_ro_param(r, "class", -1, "PART");
373 gctl_ro_param(r, "arg0", -1, lg_name);
374 gctl_ro_param(r, "flags", -1, "C");
375 gctl_ro_param(r, "verb", -1, "set");
376 gctl_ro_param(r, "attrib", -1, attribute);
377
378 errstr = gctl_issue(r);
379 if (errstr != NULL && errstr[0] != '\0')
380 gpart_show_error("Error", "Error setting parameter on disk:",
381 errstr);
382 gctl_free(r);
383 }
384
385 static void
gpart_bootcode(struct ggeom * gp)386 gpart_bootcode(struct ggeom *gp)
387 {
388 const char *bootcode;
389 struct gconfig *gc;
390 struct gctl_req *r;
391 const char *errstr, *scheme;
392 uint8_t *boot;
393 size_t bootsize, bytes;
394 int bootfd;
395
396 /*
397 * Write default bootcode to the newly partitioned disk, if that
398 * applies on this platform.
399 */
400 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
401 if (strcmp(gc->lg_name, "scheme") == 0) {
402 scheme = gc->lg_val;
403 break;
404 }
405 }
406
407 bootcode = bootcode_path(scheme);
408 if (bootcode == NULL)
409 return;
410
411 bootfd = open(bootcode, O_RDONLY);
412 if (bootfd < 0) {
413 dialog_msgbox("Bootcode Error", strerror(errno), 0, 0,
414 TRUE);
415 return;
416 }
417
418 bootsize = lseek(bootfd, 0, SEEK_END);
419 boot = malloc(bootsize);
420 lseek(bootfd, 0, SEEK_SET);
421 bytes = 0;
422 while (bytes < bootsize)
423 bytes += read(bootfd, boot + bytes, bootsize - bytes);
424 close(bootfd);
425
426 r = gctl_get_handle();
427 gctl_ro_param(r, "class", -1, "PART");
428 gctl_ro_param(r, "arg0", -1, gp->lg_name);
429 gctl_ro_param(r, "verb", -1, "bootcode");
430 gctl_ro_param(r, "bootcode", bootsize, boot);
431
432 errstr = gctl_issue(r);
433 if (errstr != NULL && errstr[0] != '\0')
434 gpart_show_error("Bootcode Error", NULL, errstr);
435 gctl_free(r);
436 free(boot);
437 }
438
439 static void
gpart_partcode(struct gprovider * pp,const char * fstype)440 gpart_partcode(struct gprovider *pp, const char *fstype)
441 {
442 struct gconfig *gc;
443 const char *scheme;
444 const char *indexstr;
445 char message[255], command[255];
446
447 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
448 if (strcmp(gc->lg_name, "scheme") == 0) {
449 scheme = gc->lg_val;
450 break;
451 }
452 }
453
454 /* Make sure this partition scheme needs partcode on this platform */
455 if (partcode_path(scheme, fstype) == NULL)
456 return;
457
458 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
459 if (strcmp(gc->lg_name, "index") == 0) {
460 indexstr = gc->lg_val;
461 break;
462 }
463 }
464
465 /* Shell out to gpart for partcode for now */
466 snprintf(command, sizeof(command), "gpart bootcode -p %s -i %s %s",
467 partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name);
468 if (system(command) != 0) {
469 snprintf(message, sizeof(message),
470 "Error installing partcode on partition %s",
471 pp->lg_name);
472 dialog_msgbox("Error", message, 0, 0, TRUE);
473 }
474 }
475
476 void
gpart_destroy(struct ggeom * lg_geom)477 gpart_destroy(struct ggeom *lg_geom)
478 {
479 struct gctl_req *r;
480 struct gprovider *pp;
481 const char *errstr;
482 int force = 1;
483
484 /* Delete all child metadata */
485 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider)
486 gpart_delete(pp);
487
488 /* Revert any local changes to get this geom into a pristine state */
489 r = gctl_get_handle();
490 gctl_ro_param(r, "class", -1, "PART");
491 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
492 gctl_ro_param(r, "verb", -1, "undo");
493 gctl_issue(r); /* Ignore errors -- these are non-fatal */
494 gctl_free(r);
495
496 /* Now destroy the geom itself */
497 r = gctl_get_handle();
498 gctl_ro_param(r, "class", -1, "PART");
499 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
500 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
501 gctl_ro_param(r, "force", sizeof(force), &force);
502 gctl_ro_param(r, "verb", -1, "destroy");
503 errstr = gctl_issue(r);
504 if (errstr != NULL && errstr[0] != '\0') {
505 /*
506 * Check if we reverted away the existence of the geom
507 * altogether. Show all other errors to the user.
508 */
509 if (strtol(errstr, NULL, 0) != EINVAL)
510 gpart_show_error("Error", NULL, errstr);
511 }
512 gctl_free(r);
513
514 /* And any metadata associated with the partition scheme itself */
515 delete_part_metadata(lg_geom->lg_name);
516 }
517
518 void
gpart_edit(struct gprovider * pp)519 gpart_edit(struct gprovider *pp)
520 {
521 struct gctl_req *r;
522 struct gconfig *gc;
523 struct gconsumer *cp;
524 struct ggeom *geom;
525 const char *errstr, *oldtype, *scheme;
526 struct partition_metadata *md;
527 char sizestr[32];
528 char *newfs;
529 intmax_t idx;
530 int hadlabel, choice, junk, nitems;
531 unsigned i;
532
533 DIALOG_FORMITEM items[] = {
534 {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0,
535 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
536 "freebsd-swap)", FALSE},
537 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0,
538 FALSE, "Partition size. Append K, M, G for kilobytes, "
539 "megabytes or gigabytes.", FALSE},
540 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
541 FALSE, "Path at which to mount this partition (leave blank "
542 "for swap, set to / for root filesystem)", FALSE},
543 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
544 "Partition name. Not all partition schemes support this.",
545 FALSE},
546 };
547
548 /*
549 * Find the PART geom we are manipulating. This may be a consumer of
550 * this provider, or its parent. Check the consumer case first.
551 */
552 geom = NULL;
553 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
554 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
555 /* Check for zombie geoms, treating them as blank */
556 scheme = NULL;
557 LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) {
558 if (strcmp(gc->lg_name, "scheme") == 0) {
559 scheme = gc->lg_val;
560 break;
561 }
562 }
563 if (scheme == NULL || strcmp(scheme, "(none)") == 0) {
564 gpart_partition(cp->lg_geom->lg_name, NULL);
565 return;
566 }
567
568 /* If this is a nested partition, edit as usual */
569 if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
570 break;
571
572 /* Destroy the geom and all sub-partitions */
573 gpart_destroy(cp->lg_geom);
574
575 /* Now re-partition and return */
576 gpart_partition(cp->lg_geom->lg_name, NULL);
577 return;
578 }
579
580 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
581 geom = pp->lg_geom;
582
583 if (geom == NULL) {
584 /* Disk not partitioned, so partition it */
585 gpart_partition(pp->lg_name, NULL);
586 return;
587 }
588
589 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
590 if (strcmp(gc->lg_name, "scheme") == 0) {
591 scheme = gc->lg_val;
592 break;
593 }
594 }
595
596 nitems = scheme_supports_labels(scheme) ? 4 : 3;
597
598 /* Edit editable parameters of a partition */
599 hadlabel = 0;
600 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
601 if (strcmp(gc->lg_name, "type") == 0) {
602 oldtype = gc->lg_val;
603 items[0].text = gc->lg_val;
604 }
605 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) {
606 hadlabel = 1;
607 items[3].text = gc->lg_val;
608 }
609 if (strcmp(gc->lg_name, "index") == 0)
610 idx = atoi(gc->lg_val);
611 }
612
613 TAILQ_FOREACH(md, &part_metadata, metadata) {
614 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) {
615 if (md->fstab != NULL)
616 items[2].text = md->fstab->fs_file;
617 break;
618 }
619 }
620
621 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE,
622 HN_NOSPACE | HN_DECIMAL);
623 items[1].text = sizestr;
624
625 editpart:
626 choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk);
627
628 if (choice) /* Cancel pressed */
629 goto endedit;
630
631 /* If this is the root partition, check that this fs is bootable */
632 if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme,
633 items[0].text)) {
634 char message[512];
635
636 snprintf(message, sizeof(message),
637 "This file system (%s) is not bootable "
638 "on this system. Are you sure you want to proceed?",
639 items[0].text);
640 dialog_vars.defaultno = TRUE;
641 choice = dialog_yesno("Warning", message, 0, 0);
642 dialog_vars.defaultno = FALSE;
643 if (choice == 1) /* cancel */
644 goto editpart;
645 }
646
647 /* Check if the label has a / in it */
648 if (strchr(items[3].text, '/') != NULL) {
649 dialog_msgbox("Error", "Label contains a /, which is not an "
650 "allowed character.", 0, 0, TRUE);
651 goto editpart;
652 }
653
654 r = gctl_get_handle();
655 gctl_ro_param(r, "class", -1, "PART");
656 gctl_ro_param(r, "arg0", -1, geom->lg_name);
657 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
658 gctl_ro_param(r, "verb", -1, "modify");
659 gctl_ro_param(r, "index", sizeof(idx), &idx);
660 if (hadlabel || items[3].text[0] != '\0')
661 gctl_ro_param(r, "label", -1, items[3].text);
662 gctl_ro_param(r, "type", -1, items[0].text);
663 errstr = gctl_issue(r);
664 if (errstr != NULL && errstr[0] != '\0') {
665 gpart_show_error("Error", NULL, errstr);
666 gctl_free(r);
667 goto editpart;
668 }
669 gctl_free(r);
670
671 newfs = newfs_command(items[0].text, 1);
672 set_default_part_metadata(pp->lg_name, scheme, items[0].text,
673 items[2].text, (strcmp(oldtype, items[0].text) != 0) ?
674 newfs : NULL);
675 free(newfs);
676
677 endedit:
678 if (strcmp(oldtype, items[0].text) != 0 && cp != NULL)
679 gpart_destroy(cp->lg_geom);
680 if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text,
681 "freebsd") == 0)
682 gpart_partition(pp->lg_name, "BSD");
683
684 for (i = 0; i < nitems(items); i++)
685 if (items[i].text_free)
686 free(items[i].text);
687 }
688
689 void
set_default_part_metadata(const char * name,const char * scheme,const char * type,const char * mountpoint,const char * newfs)690 set_default_part_metadata(const char *name, const char *scheme,
691 const char *type, const char *mountpoint, const char *newfs)
692 {
693 struct partition_metadata *md;
694 char *zpool_name = NULL;
695 const char *default_bootmount = NULL;
696 int i;
697
698 /* Set part metadata */
699 md = get_part_metadata(name, 1);
700
701 if (newfs) {
702 if (md->newfs != NULL) {
703 free(md->newfs);
704 md->newfs = NULL;
705 }
706
707 if (newfs != NULL && newfs[0] != '\0') {
708 if (strcmp("freebsd-zfs", type) == 0) {
709 zpool_name = strdup((strlen(mountpoint) == 1) ?
710 "root" : &mountpoint[1]);
711 for (i = 0; zpool_name[i] != 0; i++)
712 if (!isalnum(zpool_name[i]))
713 zpool_name[i] = '_';
714 asprintf(&md->newfs, "%s %s /dev/%s", newfs,
715 zpool_name, name);
716 } else {
717 asprintf(&md->newfs, "%s /dev/%s", newfs, name);
718 }
719 }
720 }
721
722 if (strcmp(type, "freebsd-swap") == 0)
723 mountpoint = "none";
724 if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) {
725 if (default_bootmount == NULL)
726 md->bootcode = 1;
727 else if (mountpoint == NULL || strlen(mountpoint) == 0)
728 mountpoint = default_bootmount;
729 }
730
731 if (mountpoint == NULL || mountpoint[0] == '\0') {
732 if (md->fstab != NULL) {
733 free(md->fstab->fs_spec);
734 free(md->fstab->fs_file);
735 free(md->fstab->fs_vfstype);
736 free(md->fstab->fs_mntops);
737 free(md->fstab->fs_type);
738 free(md->fstab);
739 md->fstab = NULL;
740 }
741 } else {
742 if (md->fstab == NULL) {
743 md->fstab = malloc(sizeof(struct fstab));
744 } else {
745 free(md->fstab->fs_spec);
746 free(md->fstab->fs_file);
747 free(md->fstab->fs_vfstype);
748 free(md->fstab->fs_mntops);
749 free(md->fstab->fs_type);
750 }
751 if (strcmp("freebsd-zfs", type) == 0) {
752 md->fstab->fs_spec = strdup(zpool_name);
753 } else {
754 asprintf(&md->fstab->fs_spec, "/dev/%s", name);
755 }
756 md->fstab->fs_file = strdup(mountpoint);
757 /* Get VFS from text after freebsd-, if possible */
758 if (strncmp("freebsd-", type, 8) == 0)
759 md->fstab->fs_vfstype = strdup(&type[8]);
760 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0
761 || strcmp("ms-basic-data", type) == 0)
762 md->fstab->fs_vfstype = strdup("msdosfs");
763 else
764 md->fstab->fs_vfstype = strdup(type); /* Guess */
765 if (strcmp(type, "freebsd-swap") == 0) {
766 md->fstab->fs_type = strdup(FSTAB_SW);
767 md->fstab->fs_freq = 0;
768 md->fstab->fs_passno = 0;
769 } else if (strcmp(type, "freebsd-zfs") == 0) {
770 md->fstab->fs_type = strdup(FSTAB_RW);
771 md->fstab->fs_freq = 0;
772 md->fstab->fs_passno = 0;
773 } else {
774 md->fstab->fs_type = strdup(FSTAB_RW);
775 if (strcmp(mountpoint, "/") == 0) {
776 md->fstab->fs_freq = 1;
777 md->fstab->fs_passno = 1;
778 } else {
779 md->fstab->fs_freq = 2;
780 md->fstab->fs_passno = 2;
781 }
782 }
783 md->fstab->fs_mntops = strdup(md->fstab->fs_type);
784 }
785
786 if (zpool_name != NULL)
787 free(zpool_name);
788 }
789
790 static
part_compare(const void * xa,const void * xb)791 int part_compare(const void *xa, const void *xb)
792 {
793 struct gprovider **a = (struct gprovider **)xa;
794 struct gprovider **b = (struct gprovider **)xb;
795 intmax_t astart, bstart;
796 struct gconfig *gc;
797
798 astart = bstart = 0;
799 LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
800 if (strcmp(gc->lg_name, "start") == 0) {
801 astart = strtoimax(gc->lg_val, NULL, 0);
802 break;
803 }
804 LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
805 if (strcmp(gc->lg_name, "start") == 0) {
806 bstart = strtoimax(gc->lg_val, NULL, 0);
807 break;
808 }
809
810 if (astart < bstart)
811 return -1;
812 else if (astart > bstart)
813 return 1;
814 else
815 return 0;
816 }
817
818 intmax_t
gpart_max_free(struct ggeom * geom,intmax_t * npartstart)819 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
820 {
821 struct gconfig *gc;
822 struct gprovider *pp, **providers;
823 intmax_t sectorsize, stripesize, offset;
824 intmax_t lastend;
825 intmax_t start, end;
826 intmax_t maxsize, maxstart;
827 intmax_t partstart, partend;
828 int i, nparts;
829
830 /* Now get the maximum free size and free start */
831 start = end = 0;
832 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
833 if (strcmp(gc->lg_name, "first") == 0)
834 start = strtoimax(gc->lg_val, NULL, 0);
835 if (strcmp(gc->lg_name, "last") == 0)
836 end = strtoimax(gc->lg_val, NULL, 0);
837 }
838
839 i = nparts = 0;
840 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
841 nparts++;
842 providers = calloc(nparts, sizeof(providers[0]));
843 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
844 providers[i++] = pp;
845 qsort(providers, nparts, sizeof(providers[0]), part_compare);
846
847 lastend = start - 1;
848 maxsize = 0;
849 for (i = 0; i < nparts; i++) {
850 pp = providers[i];
851
852 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
853 if (strcmp(gc->lg_name, "start") == 0)
854 partstart = strtoimax(gc->lg_val, NULL, 0);
855 if (strcmp(gc->lg_name, "end") == 0)
856 partend = strtoimax(gc->lg_val, NULL, 0);
857 }
858
859 if (partstart - lastend > maxsize) {
860 maxsize = partstart - lastend - 1;
861 maxstart = lastend + 1;
862 }
863
864 lastend = partend;
865 }
866
867 if (end - lastend > maxsize) {
868 maxsize = end - lastend;
869 maxstart = lastend + 1;
870 }
871
872 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
873
874 /*
875 * Round the start and size of the largest available space up to
876 * the nearest multiple of the adjusted stripe size.
877 *
878 * The adjusted stripe size is the least common multiple of the
879 * actual stripe size, or the sector size if no stripe size was
880 * reported, and 4096. The reason for this is that contemporary
881 * disks often have 4096-byte physical sectors but report 512
882 * bytes instead for compatibility with older / broken operating
883 * systems and BIOSes. For the same reasons, virtualized storage
884 * may also report a 512-byte stripe size, or none at all.
885 */
886 sectorsize = pp->lg_sectorsize;
887 if ((stripesize = pp->lg_stripesize) == 0)
888 stripesize = sectorsize;
889 while (stripesize % 4096 != 0)
890 stripesize *= 2;
891 if ((offset = maxstart * sectorsize % stripesize) != 0) {
892 offset = (stripesize - offset) / sectorsize;
893 maxstart += offset;
894 maxsize -= offset;
895 }
896
897 if (npartstart != NULL)
898 *npartstart = maxstart;
899
900 return (maxsize);
901 }
902
903 static size_t
add_boot_partition(struct ggeom * geom,struct gprovider * pp,const char * scheme,int interactive)904 add_boot_partition(struct ggeom *geom, struct gprovider *pp,
905 const char *scheme, int interactive)
906 {
907 struct gconfig *gc;
908 struct gprovider *ppi;
909 int choice;
910
911 /* Check for existing freebsd-boot partition */
912 LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) {
913 struct partition_metadata *md;
914 const char *bootmount = NULL;
915
916 LIST_FOREACH(gc, &ppi->lg_config, lg_config)
917 if (strcmp(gc->lg_name, "type") == 0)
918 break;
919 if (gc == NULL)
920 continue;
921 if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0)
922 continue;
923
924 /*
925 * If the boot partition is not mountable and needs partcode,
926 * but doesn't have it, it doesn't satisfy our requirements.
927 */
928 md = get_part_metadata(ppi->lg_name, 0);
929 if (bootmount == NULL && (md == NULL || !md->bootcode))
930 continue;
931
932 /* If it is mountable, but mounted somewhere else, remount */
933 if (bootmount != NULL && md != NULL && md->fstab != NULL
934 && strlen(md->fstab->fs_file) > 0
935 && strcmp(md->fstab->fs_file, bootmount) != 0)
936 continue;
937
938 /* If it is mountable, but mountpoint is not set, mount it */
939 if (bootmount != NULL && md == NULL)
940 set_default_part_metadata(ppi->lg_name, scheme,
941 gc->lg_val, bootmount, NULL);
942
943 /* Looks good at this point, no added data needed */
944 return (0);
945 }
946
947 if (interactive)
948 choice = dialog_yesno("Boot Partition",
949 "This partition scheme requires a boot partition "
950 "for the disk to be bootable. Would you like to "
951 "make one now?", 0, 0);
952 else
953 choice = 0;
954
955 if (choice == 0) { /* yes */
956 struct partition_metadata *md;
957 const char *bootmount = NULL;
958 char *bootpartname = NULL;
959 char sizestr[7];
960
961 humanize_number(sizestr, 7,
962 bootpart_size(scheme), "B", HN_AUTOSCALE,
963 HN_NOSPACE | HN_DECIMAL);
964
965 gpart_create(pp, bootpart_type(scheme, &bootmount),
966 sizestr, bootmount, &bootpartname, 0);
967
968 if (bootpartname == NULL) /* Error reported to user already */
969 return 0;
970
971 /* If the part is not mountable, make sure newfs isn't set */
972 if (bootmount == NULL) {
973 md = get_part_metadata(bootpartname, 0);
974 if (md != NULL && md->newfs != NULL) {
975 free(md->newfs);
976 md->newfs = NULL;
977 }
978 }
979
980 free(bootpartname);
981
982 return (bootpart_size(scheme));
983 }
984
985 return (0);
986 }
987
988 void
gpart_create(struct gprovider * pp,const char * default_type,const char * default_size,const char * default_mountpoint,char ** partname,int interactive)989 gpart_create(struct gprovider *pp, const char *default_type,
990 const char *default_size, const char *default_mountpoint,
991 char **partname, int interactive)
992 {
993 struct gctl_req *r;
994 struct gconfig *gc;
995 struct gconsumer *cp;
996 struct ggeom *geom;
997 const char *errstr, *scheme;
998 char sizestr[32], startstr[32], output[64], *newpartname;
999 char *newfs, options_fstype[64];
1000 intmax_t maxsize, size, sector, firstfree, stripe;
1001 uint64_t bytes;
1002 int nitems, choice, junk;
1003 unsigned i;
1004
1005 DIALOG_FORMITEM items[] = {
1006 {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0,
1007 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
1008 "freebsd-swap)", FALSE},
1009 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0,
1010 FALSE, "Partition size. Append K, M, G for kilobytes, "
1011 "megabytes or gigabytes.", FALSE},
1012 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
1013 FALSE, "Path at which to mount partition (blank for "
1014 "swap, / for root filesystem)", FALSE},
1015 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
1016 "Partition name. Not all partition schemes support this.",
1017 FALSE},
1018 };
1019
1020 if (partname != NULL)
1021 *partname = NULL;
1022
1023 /* Record sector and stripe sizes */
1024 sector = pp->lg_sectorsize;
1025 stripe = pp->lg_stripesize;
1026
1027 /*
1028 * Find the PART geom we are manipulating. This may be a consumer of
1029 * this provider, or its parent. Check the consumer case first.
1030 */
1031 geom = NULL;
1032 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1033 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1034 geom = cp->lg_geom;
1035 break;
1036 }
1037
1038 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
1039 geom = pp->lg_geom;
1040
1041 /* Now get the partition scheme */
1042 scheme = NULL;
1043 if (geom != NULL) {
1044 LIST_FOREACH(gc, &geom->lg_config, lg_config)
1045 if (strcmp(gc->lg_name, "scheme") == 0)
1046 scheme = gc->lg_val;
1047 }
1048
1049 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
1050 if (gpart_partition(pp->lg_name, NULL) == 0)
1051 dialog_msgbox("",
1052 "The partition table has been successfully created."
1053 " Please press Create again to create partitions.",
1054 0, 0, TRUE);
1055
1056 return;
1057 }
1058
1059 /*
1060 * If we still don't have a geom, either the user has
1061 * canceled partitioning or there has been an error which has already
1062 * been displayed, so bail.
1063 */
1064 if (geom == NULL)
1065 return;
1066
1067 maxsize = size = gpart_max_free(geom, &firstfree);
1068 if (size <= 0) {
1069 dialog_msgbox("Error", "No free space left on device.", 0, 0,
1070 TRUE);
1071 return;
1072 }
1073
1074 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
1075 HN_NOSPACE | HN_DECIMAL);
1076 items[1].text = sizestr;
1077
1078 /* Special-case the MBR default type for nested partitions */
1079 if (strcmp(scheme, "MBR") == 0) {
1080 items[0].text = "freebsd";
1081 items[0].help = "Filesystem type (e.g. freebsd, fat32)";
1082 }
1083
1084 nitems = scheme_supports_labels(scheme) ? 4 : 3;
1085
1086 if (default_type != NULL)
1087 items[0].text = (char *)default_type;
1088 if (default_size != NULL)
1089 items[1].text = (char *)default_size;
1090 if (default_mountpoint != NULL)
1091 items[2].text = (char *)default_mountpoint;
1092
1093 /* Default options */
1094 strncpy(options_fstype, items[0].text,
1095 sizeof(options_fstype));
1096 newfs = newfs_command(options_fstype, 1);
1097 addpartform:
1098 if (interactive) {
1099 dialog_vars.extra_label = "Options";
1100 dialog_vars.extra_button = TRUE;
1101 choice = dlg_form("Add Partition", "", 0, 0, 0, nitems,
1102 items, &junk);
1103 dialog_vars.extra_button = FALSE;
1104 switch (choice) {
1105 case 0: /* OK */
1106 break;
1107 case 1: /* Cancel */
1108 return;
1109 case 3: /* Options */
1110 free(newfs);
1111 strncpy(options_fstype, items[0].text,
1112 sizeof(options_fstype));
1113 newfs = newfs_command(options_fstype, 0);
1114 goto addpartform;
1115 }
1116 }
1117
1118 /*
1119 * If the user changed the fs type after specifying options, undo
1120 * their choices in favor of the new filesystem's defaults.
1121 */
1122 if (strcmp(options_fstype, items[0].text) != 0) {
1123 free(newfs);
1124 strncpy(options_fstype, items[0].text, sizeof(options_fstype));
1125 newfs = newfs_command(options_fstype, 1);
1126 }
1127
1128 size = maxsize;
1129 if (strlen(items[1].text) > 0) {
1130 if (expand_number(items[1].text, &bytes) != 0) {
1131 char error[512];
1132
1133 snprintf(error, sizeof(error), "Invalid size: %s\n",
1134 strerror(errno));
1135 dialog_msgbox("Error", error, 0, 0, TRUE);
1136 goto addpartform;
1137 }
1138 size = MIN((intmax_t)(bytes/sector), maxsize);
1139 }
1140
1141 /* Check if the label has a / in it */
1142 if (strchr(items[3].text, '/') != NULL) {
1143 dialog_msgbox("Error", "Label contains a /, which is not an "
1144 "allowed character.", 0, 0, TRUE);
1145 goto addpartform;
1146 }
1147
1148 /* Warn if no mountpoint set */
1149 if (strcmp(items[0].text, "freebsd-ufs") == 0 &&
1150 items[2].text[0] != '/') {
1151 choice = 0;
1152 if (interactive) {
1153 dialog_vars.defaultno = TRUE;
1154 choice = dialog_yesno("Warning",
1155 "This partition does not have a valid mountpoint "
1156 "(for the partition from which you intend to boot the "
1157 "operating system, the mountpoint should be /). Are you "
1158 "sure you want to continue?"
1159 , 0, 0);
1160 dialog_vars.defaultno = FALSE;
1161 }
1162 if (choice == 1) /* cancel */
1163 goto addpartform;
1164 }
1165
1166 /*
1167 * Error if this scheme needs nested partitions, this is one, and
1168 * a mountpoint was set.
1169 */
1170 if (strcmp(items[0].text, "freebsd") == 0 &&
1171 strlen(items[2].text) > 0) {
1172 dialog_msgbox("Error", "Partitions of type \"freebsd\" are "
1173 "nested BSD-type partition schemes and cannot have "
1174 "mountpoints. After creating one, select it and press "
1175 "Create again to add the actual file systems.", 0, 0, TRUE);
1176 goto addpartform;
1177 }
1178
1179 /* If this is the root partition, check that this scheme is bootable */
1180 if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) {
1181 char message[512];
1182
1183 snprintf(message, sizeof(message),
1184 "This partition scheme (%s) is not bootable "
1185 "on this platform. Are you sure you want to proceed?",
1186 scheme);
1187 dialog_vars.defaultno = TRUE;
1188 choice = dialog_yesno("Warning", message, 0, 0);
1189 dialog_vars.defaultno = FALSE;
1190 if (choice == 1) /* cancel */
1191 goto addpartform;
1192 }
1193
1194 /* If this is the root partition, check that this fs is bootable */
1195 if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme,
1196 items[0].text)) {
1197 char message[512];
1198
1199 snprintf(message, sizeof(message),
1200 "This file system (%s) is not bootable "
1201 "on this system. Are you sure you want to proceed?",
1202 items[0].text);
1203 dialog_vars.defaultno = TRUE;
1204 choice = dialog_yesno("Warning", message, 0, 0);
1205 dialog_vars.defaultno = FALSE;
1206 if (choice == 1) /* cancel */
1207 goto addpartform;
1208 }
1209
1210 /*
1211 * If this is the root partition, and we need a boot partition, ask
1212 * the user to add one.
1213 */
1214
1215 if ((strcmp(items[0].text, "freebsd") == 0 ||
1216 strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0) {
1217 size_t bytes = add_boot_partition(geom, pp, scheme,
1218 interactive);
1219
1220 /* Now adjust the part we are really adding forward */
1221 if (bytes > 0) {
1222 firstfree += bytes / sector;
1223 size -= (bytes + stripe)/sector;
1224 if (stripe > 0 && (firstfree*sector % stripe) != 0)
1225 firstfree += (stripe - ((firstfree*sector) %
1226 stripe)) / sector;
1227 }
1228 }
1229
1230 output[0] = '\0';
1231
1232 r = gctl_get_handle();
1233 gctl_ro_param(r, "class", -1, "PART");
1234 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1235 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1236 gctl_ro_param(r, "verb", -1, "add");
1237 gctl_ro_param(r, "type", -1, items[0].text);
1238 snprintf(sizestr, sizeof(sizestr), "%jd", size);
1239 gctl_ro_param(r, "size", -1, sizestr);
1240 snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1241 gctl_ro_param(r, "start", -1, startstr);
1242 if (items[3].text[0] != '\0')
1243 gctl_ro_param(r, "label", -1, items[3].text);
1244 gctl_add_param(r, "output", sizeof(output), output,
1245 GCTL_PARAM_WR | GCTL_PARAM_ASCII);
1246 errstr = gctl_issue(r);
1247 if (errstr != NULL && errstr[0] != '\0') {
1248 gpart_show_error("Error", NULL, errstr);
1249 gctl_free(r);
1250 goto addpartform;
1251 }
1252 newpartname = strtok(output, " ");
1253 gctl_free(r);
1254
1255 /*
1256 * Try to destroy any geom that gpart picked up already here from
1257 * dirty blocks.
1258 */
1259 r = gctl_get_handle();
1260 gctl_ro_param(r, "class", -1, "PART");
1261 gctl_ro_param(r, "arg0", -1, newpartname);
1262 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1263 junk = 1;
1264 gctl_ro_param(r, "force", sizeof(junk), &junk);
1265 gctl_ro_param(r, "verb", -1, "destroy");
1266 gctl_issue(r); /* Error usually expected and non-fatal */
1267 gctl_free(r);
1268
1269
1270 if (strcmp(items[0].text, "freebsd") == 0)
1271 gpart_partition(newpartname, "BSD");
1272 else
1273 set_default_part_metadata(newpartname, scheme,
1274 items[0].text, items[2].text, newfs);
1275 free(newfs);
1276
1277 for (i = 0; i < nitems(items); i++)
1278 if (items[i].text_free)
1279 free(items[i].text);
1280
1281 if (partname != NULL)
1282 *partname = strdup(newpartname);
1283 }
1284
1285 void
gpart_delete(struct gprovider * pp)1286 gpart_delete(struct gprovider *pp)
1287 {
1288 struct gconfig *gc;
1289 struct ggeom *geom;
1290 struct gconsumer *cp;
1291 struct gctl_req *r;
1292 const char *errstr;
1293 intmax_t idx;
1294 int is_partition;
1295
1296 /* Is it a partition? */
1297 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1298
1299 /* Find out if this is the root of a gpart geom */
1300 geom = NULL;
1301 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1302 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1303 geom = cp->lg_geom;
1304 break;
1305 }
1306
1307 /* If so, destroy all children */
1308 if (geom != NULL) {
1309 gpart_destroy(geom);
1310
1311 /* If this is a partition, revert it, so it can be deleted */
1312 if (is_partition) {
1313 r = gctl_get_handle();
1314 gctl_ro_param(r, "class", -1, "PART");
1315 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1316 gctl_ro_param(r, "verb", -1, "undo");
1317 gctl_issue(r); /* Ignore non-fatal errors */
1318 gctl_free(r);
1319 }
1320 }
1321
1322 /*
1323 * If this is not a partition, see if that is a problem, complain if
1324 * necessary, and return always, since we need not do anything further,
1325 * error or no.
1326 */
1327 if (!is_partition) {
1328 if (geom == NULL)
1329 dialog_msgbox("Error",
1330 "Only partitions can be deleted.", 0, 0, TRUE);
1331 return;
1332 }
1333
1334 r = gctl_get_handle();
1335 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1336 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1337 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1338 gctl_ro_param(r, "verb", -1, "delete");
1339
1340 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1341 if (strcmp(gc->lg_name, "index") == 0) {
1342 idx = atoi(gc->lg_val);
1343 gctl_ro_param(r, "index", sizeof(idx), &idx);
1344 break;
1345 }
1346 }
1347
1348 errstr = gctl_issue(r);
1349 if (errstr != NULL && errstr[0] != '\0') {
1350 gpart_show_error("Error", NULL, errstr);
1351 gctl_free(r);
1352 return;
1353 }
1354
1355 gctl_free(r);
1356
1357 delete_part_metadata(pp->lg_name);
1358 }
1359
1360 void
gpart_revert_all(struct gmesh * mesh)1361 gpart_revert_all(struct gmesh *mesh)
1362 {
1363 struct gclass *classp;
1364 struct gconfig *gc;
1365 struct ggeom *gp;
1366 struct gctl_req *r;
1367 const char *modified;
1368
1369 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1370 if (strcmp(classp->lg_name, "PART") == 0)
1371 break;
1372 }
1373
1374 if (strcmp(classp->lg_name, "PART") != 0) {
1375 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1376 return;
1377 }
1378
1379 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1380 modified = "true"; /* XXX: If we don't know (kernel too old),
1381 * assume there are modifications. */
1382 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1383 if (strcmp(gc->lg_name, "modified") == 0) {
1384 modified = gc->lg_val;
1385 break;
1386 }
1387 }
1388
1389 if (strcmp(modified, "false") == 0)
1390 continue;
1391
1392 r = gctl_get_handle();
1393 gctl_ro_param(r, "class", -1, "PART");
1394 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1395 gctl_ro_param(r, "verb", -1, "undo");
1396
1397 gctl_issue(r);
1398 gctl_free(r);
1399 }
1400 }
1401
1402 void
gpart_commit(struct gmesh * mesh)1403 gpart_commit(struct gmesh *mesh)
1404 {
1405 struct partition_metadata *md;
1406 struct gclass *classp;
1407 struct ggeom *gp;
1408 struct gconfig *gc;
1409 struct gconsumer *cp;
1410 struct gprovider *pp;
1411 struct gctl_req *r;
1412 const char *errstr;
1413 const char *modified;
1414 const char *rootfs;
1415
1416 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1417 if (strcmp(classp->lg_name, "PART") == 0)
1418 break;
1419 }
1420
1421 /* Figure out what filesystem / uses */
1422 rootfs = "ufs"; /* Assume ufs if nothing else present */
1423 TAILQ_FOREACH(md, &part_metadata, metadata) {
1424 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1425 rootfs = md->fstab->fs_vfstype;
1426 break;
1427 }
1428 }
1429
1430 if (strcmp(classp->lg_name, "PART") != 0) {
1431 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1432 return;
1433 }
1434
1435 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1436 modified = "true"; /* XXX: If we don't know (kernel too old),
1437 * assume there are modifications. */
1438 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1439 if (strcmp(gc->lg_name, "modified") == 0) {
1440 modified = gc->lg_val;
1441 break;
1442 }
1443 }
1444
1445 if (strcmp(modified, "false") == 0)
1446 continue;
1447
1448 /* Add bootcode if necessary, before the commit */
1449 md = get_part_metadata(gp->lg_name, 0);
1450 if (md != NULL && md->bootcode)
1451 gpart_bootcode(gp);
1452
1453 /* Now install partcode on its partitions, if necessary */
1454 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1455 md = get_part_metadata(pp->lg_name, 0);
1456 if (md == NULL || !md->bootcode)
1457 continue;
1458
1459 /* Mark this partition active if that's required */
1460 gpart_activate(pp);
1461
1462 /* Check if the partition has sub-partitions */
1463 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1464 if (strcmp(cp->lg_geom->lg_class->lg_name,
1465 "PART") == 0)
1466 break;
1467
1468 if (cp == NULL) /* No sub-partitions */
1469 gpart_partcode(pp, rootfs);
1470 }
1471
1472 r = gctl_get_handle();
1473 gctl_ro_param(r, "class", -1, "PART");
1474 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1475 gctl_ro_param(r, "verb", -1, "commit");
1476
1477 errstr = gctl_issue(r);
1478 if (errstr != NULL && errstr[0] != '\0')
1479 gpart_show_error("Error", NULL, errstr);
1480 gctl_free(r);
1481 }
1482 }
1483
1484