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