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.form.value_without_ok = true;
657 conf.title = "Edit Partition";
658 choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items);
659
660 if (choice == BSDDIALOG_CANCEL)
661 goto endedit;
662
663 /* If this is the root partition, check that this fs is bootable */
664 if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme,
665 items[0].value)) {
666 char message[512];
667
668 snprintf(message, sizeof(message),
669 "This file system (%s) is not bootable "
670 "on this system. Are you sure you want to proceed?",
671 items[0].value);
672 conf.button.default_cancel = true;
673 conf.title = "Warning";
674 choice = bsddialog_yesno(&conf, message, 0, 0);
675 conf.button.default_cancel = false;
676 if (choice == BSDDIALOG_CANCEL)
677 goto editpart;
678 }
679
680 /* Check if the label has a / in it */
681 if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) {
682 conf.title = "Error";
683 bsddialog_msgbox(&conf, "Label contains a /, which is not an "
684 "allowed character.", 0, 0);
685 goto editpart;
686 }
687
688 r = gctl_get_handle();
689 gctl_ro_param(r, "class", -1, "PART");
690 gctl_ro_param(r, "arg0", -1, geom->lg_name);
691 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
692 gctl_ro_param(r, "verb", -1, "modify");
693 gctl_ro_param(r, "index", sizeof(idx), &idx);
694 if (items[3].value != NULL && (hadlabel || items[3].value[0] != '\0'))
695 gctl_ro_param(r, "label", -1, items[3].value);
696 gctl_ro_param(r, "type", -1, items[0].value);
697 errstr = gctl_issue(r);
698 if (errstr != NULL && errstr[0] != '\0') {
699 gpart_show_error("Error", NULL, errstr);
700 gctl_free(r);
701 goto editpart;
702 }
703 gctl_free(r);
704
705 newfs = newfs_command(items[0].value, 1);
706 set_default_part_metadata(pp->lg_name, scheme, items[0].value,
707 items[2].value, (strcmp(oldtype, items[0].value) != 0) ?
708 newfs : NULL);
709 free(newfs);
710
711 endedit:
712 if (strcmp(oldtype, items[0].value) != 0 && cp != NULL)
713 gpart_destroy(cp->lg_geom);
714 if (strcmp(oldtype, items[0].value) != 0 && strcmp(items[0].value,
715 "freebsd") == 0)
716 gpart_partition(pp->lg_name, "BSD");
717
718 for (i = 0; i < nitems(items); i++)
719 if (items[i].value != NULL)
720 free(items[i].value);
721 }
722
723 void
set_default_part_metadata(const char * name,const char * scheme,const char * type,const char * mountpoint,const char * newfs)724 set_default_part_metadata(const char *name, const char *scheme,
725 const char *type, const char *mountpoint, const char *newfs)
726 {
727 struct partition_metadata *md;
728 char *zpool_name = NULL;
729 const char *default_bootmount = NULL;
730 int i;
731
732 /* Set part metadata */
733 md = get_part_metadata(name, 1);
734
735 if (newfs) {
736 if (md->newfs != NULL) {
737 free(md->newfs);
738 md->newfs = NULL;
739 }
740
741 if (newfs != NULL && newfs[0] != '\0') {
742 if (strcmp("freebsd-zfs", type) == 0) {
743 zpool_name = strdup((strlen(mountpoint) == 1) ?
744 "root" : &mountpoint[1]);
745 for (i = 0; zpool_name[i] != 0; i++)
746 if (!isalnum(zpool_name[i]))
747 zpool_name[i] = '_';
748 asprintf(&md->newfs, "%s %s /dev/%s", newfs,
749 zpool_name, name);
750 } else {
751 asprintf(&md->newfs, "%s /dev/%s", newfs, name);
752 }
753 }
754 }
755
756 if (strcmp(type, "freebsd-swap") == 0)
757 mountpoint = "none";
758 if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) {
759 if (default_bootmount == NULL)
760 md->bootcode = 1;
761 else if (mountpoint == NULL || strlen(mountpoint) == 0)
762 mountpoint = default_bootmount;
763 }
764
765 if (mountpoint == NULL || mountpoint[0] == '\0') {
766 if (md->fstab != NULL) {
767 free(md->fstab->fs_spec);
768 free(md->fstab->fs_file);
769 free(md->fstab->fs_vfstype);
770 free(md->fstab->fs_mntops);
771 free(md->fstab->fs_type);
772 free(md->fstab);
773 md->fstab = NULL;
774 }
775 } else {
776 if (md->fstab == NULL) {
777 md->fstab = malloc(sizeof(struct fstab));
778 } else {
779 free(md->fstab->fs_spec);
780 free(md->fstab->fs_file);
781 free(md->fstab->fs_vfstype);
782 free(md->fstab->fs_mntops);
783 free(md->fstab->fs_type);
784 }
785 if (strcmp("freebsd-zfs", type) == 0) {
786 md->fstab->fs_spec = strdup(zpool_name);
787 } else {
788 asprintf(&md->fstab->fs_spec, "/dev/%s", name);
789 }
790 md->fstab->fs_file = strdup(mountpoint);
791 /* Get VFS from text after freebsd-, if possible */
792 if (strncmp("freebsd-", type, 8) == 0)
793 md->fstab->fs_vfstype = strdup(&type[8]);
794 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0
795 || strcmp("ms-basic-data", type) == 0)
796 md->fstab->fs_vfstype = strdup("msdosfs");
797 else
798 md->fstab->fs_vfstype = strdup(type); /* Guess */
799 if (strcmp(type, "freebsd-swap") == 0) {
800 md->fstab->fs_type = strdup(FSTAB_SW);
801 md->fstab->fs_freq = 0;
802 md->fstab->fs_passno = 0;
803 } else if (strcmp(type, "freebsd-zfs") == 0) {
804 md->fstab->fs_type = strdup(FSTAB_RW);
805 md->fstab->fs_freq = 0;
806 md->fstab->fs_passno = 0;
807 } else {
808 md->fstab->fs_type = strdup(FSTAB_RW);
809 if (strcmp(mountpoint, "/") == 0) {
810 md->fstab->fs_freq = 1;
811 md->fstab->fs_passno = 1;
812 } else {
813 md->fstab->fs_freq = 2;
814 md->fstab->fs_passno = 2;
815 }
816 }
817 md->fstab->fs_mntops = strdup(md->fstab->fs_type);
818 }
819
820 if (zpool_name != NULL)
821 free(zpool_name);
822 }
823
824 static
part_compare(const void * xa,const void * xb)825 int part_compare(const void *xa, const void *xb)
826 {
827 struct gprovider **a = (struct gprovider **)xa;
828 struct gprovider **b = (struct gprovider **)xb;
829 intmax_t astart, bstart;
830 struct gconfig *gc;
831
832 astart = bstart = 0;
833 LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
834 if (strcmp(gc->lg_name, "start") == 0) {
835 astart = strtoimax(gc->lg_val, NULL, 0);
836 break;
837 }
838 LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
839 if (strcmp(gc->lg_name, "start") == 0) {
840 bstart = strtoimax(gc->lg_val, NULL, 0);
841 break;
842 }
843
844 if (astart < bstart)
845 return -1;
846 else if (astart > bstart)
847 return 1;
848 else
849 return 0;
850 }
851
852 intmax_t
gpart_max_free(struct ggeom * geom,intmax_t * npartstart)853 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
854 {
855 struct gconfig *gc;
856 struct gprovider *pp, **providers;
857 intmax_t sectorsize, stripesize, offset;
858 intmax_t lastend;
859 intmax_t start, end;
860 intmax_t maxsize, maxstart;
861 intmax_t partstart, partend;
862 int i, nparts;
863
864 /* Now get the maximum free size and free start */
865 start = end = 0;
866 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
867 if (strcmp(gc->lg_name, "first") == 0)
868 start = strtoimax(gc->lg_val, NULL, 0);
869 if (strcmp(gc->lg_name, "last") == 0)
870 end = strtoimax(gc->lg_val, NULL, 0);
871 }
872
873 i = nparts = 0;
874 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
875 nparts++;
876 providers = calloc(nparts, sizeof(providers[0]));
877 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
878 providers[i++] = pp;
879 qsort(providers, nparts, sizeof(providers[0]), part_compare);
880
881 lastend = start - 1;
882 maxsize = 0;
883 for (i = 0; i < nparts; i++) {
884 pp = providers[i];
885
886 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
887 if (strcmp(gc->lg_name, "start") == 0)
888 partstart = strtoimax(gc->lg_val, NULL, 0);
889 if (strcmp(gc->lg_name, "end") == 0)
890 partend = strtoimax(gc->lg_val, NULL, 0);
891 }
892
893 if (partstart - lastend > maxsize) {
894 maxsize = partstart - lastend - 1;
895 maxstart = lastend + 1;
896 }
897
898 lastend = partend;
899 }
900
901 if (end - lastend > maxsize) {
902 maxsize = end - lastend;
903 maxstart = lastend + 1;
904 }
905
906 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
907
908 /*
909 * Round the start and size of the largest available space up to
910 * the nearest multiple of the adjusted stripe size.
911 *
912 * The adjusted stripe size is the least common multiple of the
913 * actual stripe size, or the sector size if no stripe size was
914 * reported, and 4096. The reason for this is that contemporary
915 * disks often have 4096-byte physical sectors but report 512
916 * bytes instead for compatibility with older / broken operating
917 * systems and BIOSes. For the same reasons, virtualized storage
918 * may also report a 512-byte stripe size, or none at all.
919 */
920 sectorsize = pp->lg_sectorsize;
921 if ((stripesize = pp->lg_stripesize) == 0)
922 stripesize = sectorsize;
923 while (stripesize % 4096 != 0)
924 stripesize *= 2;
925 if ((offset = maxstart * sectorsize % stripesize) != 0) {
926 offset = (stripesize - offset) / sectorsize;
927 maxstart += offset;
928 maxsize -= offset;
929 }
930
931 if (npartstart != NULL)
932 *npartstart = maxstart;
933
934 return (maxsize);
935 }
936
937 static size_t
add_boot_partition(struct ggeom * geom,struct gprovider * pp,const char * scheme,int interactive)938 add_boot_partition(struct ggeom *geom, struct gprovider *pp,
939 const char *scheme, int interactive)
940 {
941 struct gconfig *gc;
942 struct gprovider *ppi;
943 int choice;
944 struct bsddialog_conf conf;
945
946 /* Check for existing freebsd-boot partition */
947 LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) {
948 struct partition_metadata *md;
949 const char *bootmount = NULL;
950
951 LIST_FOREACH(gc, &ppi->lg_config, lg_config)
952 if (strcmp(gc->lg_name, "type") == 0)
953 break;
954 if (gc == NULL)
955 continue;
956 if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0)
957 continue;
958
959 /*
960 * If the boot partition is not mountable and needs partcode,
961 * but doesn't have it, it doesn't satisfy our requirements.
962 */
963 md = get_part_metadata(ppi->lg_name, 0);
964 if (bootmount == NULL && (md == NULL || !md->bootcode))
965 continue;
966
967 /* If it is mountable, but mounted somewhere else, remount */
968 if (bootmount != NULL && md != NULL && md->fstab != NULL
969 && strlen(md->fstab->fs_file) > 0
970 && strcmp(md->fstab->fs_file, bootmount) != 0)
971 continue;
972
973 /* If it is mountable, but mountpoint is not set, mount it */
974 if (bootmount != NULL && md == NULL)
975 set_default_part_metadata(ppi->lg_name, scheme,
976 gc->lg_val, bootmount, NULL);
977
978 /* Looks good at this point, no added data needed */
979 return (0);
980 }
981
982 if (interactive) {
983 bsddialog_initconf(&conf);
984 conf.title = "Boot Partition";
985 choice = bsddialog_yesno(&conf,
986 "This partition scheme requires a boot partition "
987 "for the disk to be bootable. Would you like to "
988 "make one now?", 0, 0);
989 } else {
990 choice = BSDDIALOG_YES;
991 }
992
993 if (choice == BSDDIALOG_YES) {
994 struct partition_metadata *md;
995 const char *bootmount = NULL;
996 char *bootpartname = NULL;
997 char sizestr[7];
998
999 humanize_number(sizestr, 7,
1000 bootpart_size(scheme), "B", HN_AUTOSCALE,
1001 HN_NOSPACE | HN_DECIMAL);
1002
1003 gpart_create(pp, bootpart_type(scheme, &bootmount),
1004 sizestr, bootmount, &bootpartname, 0);
1005
1006 if (bootpartname == NULL) /* Error reported to user already */
1007 return 0;
1008
1009 /* If the part is not mountable, make sure newfs isn't set */
1010 if (bootmount == NULL) {
1011 md = get_part_metadata(bootpartname, 0);
1012 if (md != NULL && md->newfs != NULL) {
1013 free(md->newfs);
1014 md->newfs = NULL;
1015 }
1016 }
1017
1018 free(bootpartname);
1019
1020 return (bootpart_size(scheme));
1021 }
1022
1023 return (0);
1024 }
1025
1026 void
gpart_create(struct gprovider * pp,const char * default_type,const char * default_size,const char * default_mountpoint,char ** partname,int interactive)1027 gpart_create(struct gprovider *pp, const char *default_type,
1028 const char *default_size, const char *default_mountpoint,
1029 char **partname, int interactive)
1030 {
1031 struct gctl_req *r;
1032 struct gconfig *gc;
1033 struct gconsumer *cp;
1034 struct ggeom *geom;
1035 const char *errstr, *scheme;
1036 char sizestr[32], startstr[32], output[64], *newpartname;
1037 char *newfs, options_fstype[64];
1038 intmax_t maxsize, size, sector, firstfree, stripe;
1039 uint64_t bytes;
1040 int nitems, choice, junk;
1041 unsigned i;
1042 bool init_allocated;
1043 struct bsddialog_conf conf;
1044
1045 struct bsddialog_formitem items[] = {
1046 {"Type:", 1, 1, "freebsd-ufs", 1, 12, 12, 15, NULL, 0,
1047 "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
1048 "freebsd-swap)"},
1049 {"Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0,
1050 "Partition size. Append K, M, G for kilobytes, "
1051 "megabytes or gigabytes."},
1052 {"Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0,
1053 "Path at which to mount partition (blank for "
1054 "swap, / for root filesystem)"},
1055 {"Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0,
1056 "Partition name. Not all partition schemes support this."},
1057 };
1058
1059 bsddialog_initconf(&conf);
1060
1061 if (partname != NULL)
1062 *partname = NULL;
1063
1064 /* Record sector and stripe sizes */
1065 sector = pp->lg_sectorsize;
1066 stripe = pp->lg_stripesize;
1067
1068 /*
1069 * Find the PART geom we are manipulating. This may be a consumer of
1070 * this provider, or its parent. Check the consumer case first.
1071 */
1072 geom = NULL;
1073 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1074 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1075 geom = cp->lg_geom;
1076 break;
1077 }
1078
1079 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
1080 geom = pp->lg_geom;
1081
1082 /* Now get the partition scheme */
1083 scheme = NULL;
1084 if (geom != NULL) {
1085 LIST_FOREACH(gc, &geom->lg_config, lg_config)
1086 if (strcmp(gc->lg_name, "scheme") == 0)
1087 scheme = gc->lg_val;
1088 }
1089
1090 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
1091 if (gpart_partition(pp->lg_name, NULL) == 0) {
1092 bsddialog_msgbox(&conf,
1093 "The partition table has been successfully created."
1094 " Please press Create again to create partitions.",
1095 0, 0);
1096 }
1097
1098 return;
1099 }
1100
1101 /*
1102 * If we still don't have a geom, either the user has
1103 * canceled partitioning or there has been an error which has already
1104 * been displayed, so bail.
1105 */
1106 if (geom == NULL)
1107 return;
1108
1109 maxsize = size = gpart_max_free(geom, &firstfree);
1110 if (size <= 0) {
1111 conf .title = "Error";
1112 bsddialog_msgbox(&conf, "No free space left on device.", 0, 0);
1113 return;
1114 }
1115
1116 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
1117 HN_NOSPACE | HN_DECIMAL);
1118 items[1].init = sizestr;
1119
1120 /* Special-case the MBR default type for nested partitions */
1121 if (strcmp(scheme, "MBR") == 0) {
1122 items[0].init = "freebsd";
1123 items[0].bottomdesc = "Filesystem type (e.g. freebsd, fat32)";
1124 }
1125
1126 nitems = scheme_supports_labels(scheme) ? 4 : 3;
1127
1128 if (default_type != NULL)
1129 items[0].init = (char *)default_type;
1130 if (default_size != NULL)
1131 items[1].init = (char *)default_size;
1132 if (default_mountpoint != NULL)
1133 items[2].init = (char *)default_mountpoint;
1134
1135 /* Default options */
1136 strncpy(options_fstype, items[0].init,
1137 sizeof(options_fstype));
1138 newfs = newfs_command(options_fstype, 1);
1139
1140 init_allocated = false;
1141 addpartform:
1142 if (interactive) {
1143 conf.button.with_extra = true;
1144 conf.button.extra_label = "Options";
1145 conf.form.value_without_ok = true;
1146 conf.title = "Add Partition";
1147 choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items);
1148 conf.button.with_extra = false;
1149 conf.button.extra_label = NULL;
1150 conf.form.value_without_ok = false;
1151 switch (choice) {
1152 case BSDDIALOG_OK:
1153 break;
1154 case BSDDIALOG_CANCEL:
1155 return;
1156 case BSDDIALOG_EXTRA: /* Options */
1157 free(newfs);
1158 strncpy(options_fstype, items[0].value,
1159 sizeof(options_fstype));
1160 newfs = newfs_command(options_fstype, 0);
1161 for (i = 0; i < nitems(items); i++) {
1162 if (init_allocated)
1163 free((char*)items[i].init);
1164 items[i].init = items[i].value;
1165 }
1166 init_allocated = true;
1167 goto addpartform;
1168 }
1169 } else { /* auto partitioning */
1170 items[0].value = strdup(items[0].init);
1171 items[1].value = strdup(items[1].init);
1172 items[2].value = strdup(items[2].init);
1173 if (nitems > 3)
1174 items[3].value = strdup(items[3].init);
1175 }
1176
1177 /*
1178 * If the user changed the fs type after specifying options, undo
1179 * their choices in favor of the new filesystem's defaults.
1180 */
1181 if (strcmp(options_fstype, items[0].value) != 0) {
1182 free(newfs);
1183 strncpy(options_fstype, items[0].value, sizeof(options_fstype));
1184 newfs = newfs_command(options_fstype, 1);
1185 }
1186
1187 size = maxsize;
1188 if (strlen(items[1].value) > 0) {
1189 if (expand_number(items[1].value, &bytes) != 0) {
1190 char error[512];
1191
1192 snprintf(error, sizeof(error), "Invalid size: %s\n",
1193 strerror(errno));
1194 conf.title = "Error";
1195 bsddialog_msgbox(&conf, error, 0, 0);
1196 goto addpartform;
1197 }
1198 size = MIN((intmax_t)(bytes/sector), maxsize);
1199 }
1200
1201 /* Check if the label has a / in it */
1202 if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) {
1203 conf.title = "Error";
1204 bsddialog_msgbox(&conf, "Label contains a /, which is not an "
1205 "allowed character.", 0, 0);
1206 goto addpartform;
1207 }
1208
1209 /* Warn if no mountpoint set */
1210 if (strcmp(items[0].value, "freebsd-ufs") == 0 &&
1211 items[2].value[0] != '/') {
1212 choice = 0;
1213 if (interactive) {
1214 conf.button.default_cancel = true;
1215 conf.title = "Warning";
1216 choice = bsddialog_yesno(&conf,
1217 "This partition does not have a valid mountpoint "
1218 "(for the partition from which you intend to boot the "
1219 "operating system, the mountpoint should be /). Are you "
1220 "sure you want to continue?"
1221 , 0, 0);
1222 conf.button.default_cancel = false;
1223 }
1224 if (choice == BSDDIALOG_CANCEL)
1225 goto addpartform;
1226 }
1227
1228 /*
1229 * Error if this scheme needs nested partitions, this is one, and
1230 * a mountpoint was set.
1231 */
1232 if (strcmp(items[0].value, "freebsd") == 0 &&
1233 strlen(items[2].value) > 0) {
1234 conf.title = "Error";
1235 bsddialog_msgbox(&conf, "Partitions of type \"freebsd\" are "
1236 "nested BSD-type partition schemes and cannot have "
1237 "mountpoints. After creating one, select it and press "
1238 "Create again to add the actual file systems.", 0, 0);
1239 goto addpartform;
1240 }
1241
1242 /* If this is the root partition, check that this scheme is bootable */
1243 if (strcmp(items[2].value, "/") == 0 && !is_scheme_bootable(scheme)) {
1244 char message[512];
1245
1246 snprintf(message, sizeof(message),
1247 "This partition scheme (%s) is not bootable "
1248 "on this platform. Are you sure you want to proceed?",
1249 scheme);
1250 conf.button.default_cancel = true;
1251 conf.title = "Warning";
1252 choice = bsddialog_yesno(&conf, message, 0, 0);
1253 conf.button.default_cancel = false;
1254 if (choice == BSDDIALOG_CANCEL)
1255 goto addpartform;
1256 }
1257
1258 /* If this is the root partition, check that this fs is bootable */
1259 if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme,
1260 items[0].value)) {
1261 char message[512];
1262
1263 snprintf(message, sizeof(message),
1264 "This file system (%s) is not bootable "
1265 "on this system. Are you sure you want to proceed?",
1266 items[0].value);
1267 conf.button.default_cancel = true;
1268 conf.title = "Warning";
1269 choice = bsddialog_yesno(&conf, message, 0, 0);
1270 conf.button.default_cancel = false;
1271 if (choice == BSDDIALOG_CANCEL)
1272 goto addpartform;
1273 }
1274
1275 /*
1276 * If this is the root partition, and we need a boot partition, ask
1277 * the user to add one.
1278 */
1279
1280 if ((strcmp(items[0].value, "freebsd") == 0 ||
1281 strcmp(items[2].value, "/") == 0) && bootpart_size(scheme) > 0) {
1282 size_t bytes = add_boot_partition(geom, pp, scheme,
1283 interactive);
1284
1285 /* Now adjust the part we are really adding forward */
1286 if (bytes > 0) {
1287 firstfree += bytes / sector;
1288 size -= (bytes + stripe)/sector;
1289 if (stripe > 0 && (firstfree*sector % stripe) != 0)
1290 firstfree += (stripe - ((firstfree*sector) %
1291 stripe)) / sector;
1292 }
1293 }
1294
1295 output[0] = '\0';
1296
1297 r = gctl_get_handle();
1298 gctl_ro_param(r, "class", -1, "PART");
1299 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1300 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1301 gctl_ro_param(r, "verb", -1, "add");
1302
1303 gctl_ro_param(r, "type", -1, items[0].value);
1304 snprintf(sizestr, sizeof(sizestr), "%jd", size);
1305 gctl_ro_param(r, "size", -1, sizestr);
1306 snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1307 gctl_ro_param(r, "start", -1, startstr);
1308 if (items[3].value != NULL && items[3].value[0] != '\0')
1309 gctl_ro_param(r, "label", -1, items[3].value);
1310 gctl_add_param(r, "output", sizeof(output), output,
1311 GCTL_PARAM_WR | GCTL_PARAM_ASCII);
1312 errstr = gctl_issue(r);
1313 if (errstr != NULL && errstr[0] != '\0') {
1314 gpart_show_error("Error", NULL, errstr);
1315 gctl_free(r);
1316 goto addpartform;
1317 }
1318 newpartname = strtok(output, " ");
1319 gctl_free(r);
1320
1321 /*
1322 * Try to destroy any geom that gpart picked up already here from
1323 * dirty blocks.
1324 */
1325 r = gctl_get_handle();
1326 gctl_ro_param(r, "class", -1, "PART");
1327 gctl_ro_param(r, "arg0", -1, newpartname);
1328 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1329 junk = 1;
1330 gctl_ro_param(r, "force", sizeof(junk), &junk);
1331 gctl_ro_param(r, "verb", -1, "destroy");
1332 gctl_issue(r); /* Error usually expected and non-fatal */
1333 gctl_free(r);
1334
1335
1336 if (strcmp(items[0].value, "freebsd") == 0)
1337 gpart_partition(newpartname, "BSD");
1338 else
1339 set_default_part_metadata(newpartname, scheme,
1340 items[0].value, items[2].value, newfs);
1341 free(newfs);
1342
1343 for (i = 0; i < nitems(items); i++) {
1344 if (items[i].value != NULL) {
1345 free(items[i].value);
1346 if (init_allocated && items[i].init != NULL)
1347 free((char*)items[i].init);
1348 }
1349 }
1350
1351 if (partname != NULL)
1352 *partname = strdup(newpartname);
1353 }
1354
1355 void
gpart_delete(struct gprovider * pp)1356 gpart_delete(struct gprovider *pp)
1357 {
1358 struct gconfig *gc;
1359 struct ggeom *geom;
1360 struct gconsumer *cp;
1361 struct gctl_req *r;
1362 const char *errstr;
1363 intmax_t idx;
1364 int is_partition;
1365 struct bsddialog_conf conf;
1366
1367 /* Is it a partition? */
1368 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1369
1370 /* Find out if this is the root of a gpart geom */
1371 geom = NULL;
1372 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1373 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1374 geom = cp->lg_geom;
1375 break;
1376 }
1377
1378 /* If so, destroy all children */
1379 if (geom != NULL) {
1380 gpart_destroy(geom);
1381
1382 /* If this is a partition, revert it, so it can be deleted */
1383 if (is_partition) {
1384 r = gctl_get_handle();
1385 gctl_ro_param(r, "class", -1, "PART");
1386 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1387 gctl_ro_param(r, "verb", -1, "undo");
1388 gctl_issue(r); /* Ignore non-fatal errors */
1389 gctl_free(r);
1390 }
1391 }
1392
1393 /*
1394 * If this is not a partition, see if that is a problem, complain if
1395 * necessary, and return always, since we need not do anything further,
1396 * error or no.
1397 */
1398 if (!is_partition) {
1399 if (geom == NULL) {
1400 bsddialog_initconf(&conf);
1401 conf.title = "Error";
1402 bsddialog_msgbox(&conf,
1403 "Only partitions can be deleted.", 0, 0);
1404 }
1405 return;
1406 }
1407
1408 r = gctl_get_handle();
1409 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1410 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1411 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1412 gctl_ro_param(r, "verb", -1, "delete");
1413
1414 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1415 if (strcmp(gc->lg_name, "index") == 0) {
1416 idx = atoi(gc->lg_val);
1417 gctl_ro_param(r, "index", sizeof(idx), &idx);
1418 break;
1419 }
1420 }
1421
1422 errstr = gctl_issue(r);
1423 if (errstr != NULL && errstr[0] != '\0') {
1424 gpart_show_error("Error", NULL, errstr);
1425 gctl_free(r);
1426 return;
1427 }
1428
1429 gctl_free(r);
1430
1431 delete_part_metadata(pp->lg_name);
1432 }
1433
1434 void
gpart_revert_all(struct gmesh * mesh)1435 gpart_revert_all(struct gmesh *mesh)
1436 {
1437 struct gclass *classp;
1438 struct gconfig *gc;
1439 struct ggeom *gp;
1440 struct gctl_req *r;
1441 const char *modified;
1442 struct bsddialog_conf conf;
1443
1444 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1445 if (strcmp(classp->lg_name, "PART") == 0)
1446 break;
1447 }
1448
1449 if (strcmp(classp->lg_name, "PART") != 0) {
1450 bsddialog_initconf(&conf);
1451 conf.title = "Error";
1452 bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
1453 return;
1454 }
1455
1456 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1457 modified = "true"; /* XXX: If we don't know (kernel too old),
1458 * assume there are modifications. */
1459 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1460 if (strcmp(gc->lg_name, "modified") == 0) {
1461 modified = gc->lg_val;
1462 break;
1463 }
1464 }
1465
1466 if (strcmp(modified, "false") == 0)
1467 continue;
1468
1469 r = gctl_get_handle();
1470 gctl_ro_param(r, "class", -1, "PART");
1471 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1472 gctl_ro_param(r, "verb", -1, "undo");
1473
1474 gctl_issue(r);
1475 gctl_free(r);
1476 }
1477 }
1478
1479 void
gpart_commit(struct gmesh * mesh)1480 gpart_commit(struct gmesh *mesh)
1481 {
1482 struct partition_metadata *md;
1483 struct gclass *classp;
1484 struct ggeom *gp;
1485 struct gconfig *gc;
1486 struct gconsumer *cp;
1487 struct gprovider *pp;
1488 struct gctl_req *r;
1489 const char *errstr;
1490 const char *modified;
1491 const char *rootfs;
1492 struct bsddialog_conf conf;
1493
1494 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1495 if (strcmp(classp->lg_name, "PART") == 0)
1496 break;
1497 }
1498
1499 /* Figure out what filesystem / uses */
1500 rootfs = "ufs"; /* Assume ufs if nothing else present */
1501 TAILQ_FOREACH(md, &part_metadata, metadata) {
1502 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1503 rootfs = md->fstab->fs_vfstype;
1504 break;
1505 }
1506 }
1507
1508 if (strcmp(classp->lg_name, "PART") != 0) {
1509 bsddialog_initconf(&conf);
1510 conf.title = "Error";
1511 bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
1512 return;
1513 }
1514
1515 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1516 modified = "true"; /* XXX: If we don't know (kernel too old),
1517 * assume there are modifications. */
1518 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1519 if (strcmp(gc->lg_name, "modified") == 0) {
1520 modified = gc->lg_val;
1521 break;
1522 }
1523 }
1524
1525 if (strcmp(modified, "false") == 0)
1526 continue;
1527
1528 /* Add bootcode if necessary, before the commit */
1529 md = get_part_metadata(gp->lg_name, 0);
1530 if (md != NULL && md->bootcode)
1531 gpart_bootcode(gp);
1532
1533 /* Now install partcode on its partitions, if necessary */
1534 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1535 md = get_part_metadata(pp->lg_name, 0);
1536 if (md == NULL || !md->bootcode)
1537 continue;
1538
1539 /* Mark this partition active if that's required */
1540 gpart_activate(pp);
1541
1542 /* Check if the partition has sub-partitions */
1543 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1544 if (strcmp(cp->lg_geom->lg_class->lg_name,
1545 "PART") == 0)
1546 break;
1547
1548 if (cp == NULL) /* No sub-partitions */
1549 gpart_partcode(pp, rootfs);
1550 }
1551
1552 r = gctl_get_handle();
1553 gctl_ro_param(r, "class", -1, "PART");
1554 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1555 gctl_ro_param(r, "verb", -1, "commit");
1556
1557 errstr = gctl_issue(r);
1558 if (errstr != NULL && errstr[0] != '\0')
1559 gpart_show_error("Error", NULL, errstr);
1560 gctl_free(r);
1561 }
1562 }
1563
1564