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