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