1218799Snwhitehorn/*- 2218799Snwhitehorn * Copyright (c) 2011 Nathan Whitehorn 3218799Snwhitehorn * All rights reserved. 4218799Snwhitehorn * 5218799Snwhitehorn * Redistribution and use in source and binary forms, with or without 6218799Snwhitehorn * modification, are permitted provided that the following conditions 7218799Snwhitehorn * are met: 8218799Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9218799Snwhitehorn * notice, this list of conditions and the following disclaimer. 10218799Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11218799Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12218799Snwhitehorn * documentation and/or other materials provided with the distribution. 13218799Snwhitehorn * 14218799Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15218799Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16218799Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17218799Snwhitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18218799Snwhitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19218799Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20218799Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21218799Snwhitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22218799Snwhitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23218799Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24218799Snwhitehorn * SUCH DAMAGE. 25218799Snwhitehorn * 26218799Snwhitehorn * $FreeBSD$ 27218799Snwhitehorn */ 28218799Snwhitehorn 29218799Snwhitehorn#include <sys/param.h> 30218799Snwhitehorn#include <errno.h> 31218799Snwhitehorn#include <libutil.h> 32218799Snwhitehorn#include <inttypes.h> 33218799Snwhitehorn 34218799Snwhitehorn#include <libgeom.h> 35218799Snwhitehorn#include <dialog.h> 36218799Snwhitehorn#include <dlg_keys.h> 37218799Snwhitehorn 38218799Snwhitehorn#include "partedit.h" 39218799Snwhitehorn 40218799Snwhitehorn#define GPART_FLAGS "x" /* Do not commit changes by default */ 41218799Snwhitehorn 42218799Snwhitehornstatic void 43218799Snwhitehorngpart_show_error(const char *title, const char *explanation, const char *errstr) 44218799Snwhitehorn{ 45218799Snwhitehorn char *errmsg; 46218799Snwhitehorn char message[512]; 47218799Snwhitehorn int error; 48218799Snwhitehorn 49218799Snwhitehorn if (explanation == NULL) 50218799Snwhitehorn explanation = ""; 51218799Snwhitehorn 52218799Snwhitehorn error = strtol(errstr, &errmsg, 0); 53218799Snwhitehorn if (errmsg != errstr) { 54218799Snwhitehorn while (errmsg[0] == ' ') 55218799Snwhitehorn errmsg++; 56218799Snwhitehorn if (errmsg[0] != '\0') 57218799Snwhitehorn sprintf(message, "%s%s. %s", explanation, 58218799Snwhitehorn strerror(error), errmsg); 59218799Snwhitehorn else 60218799Snwhitehorn sprintf(message, "%s%s", explanation, strerror(error)); 61218799Snwhitehorn } else { 62218799Snwhitehorn sprintf(message, "%s%s", explanation, errmsg); 63218799Snwhitehorn } 64218799Snwhitehorn 65218799Snwhitehorn dialog_msgbox(title, message, 0, 0, TRUE); 66218799Snwhitehorn} 67218799Snwhitehorn 68218853Snwhitehornstatic int 69218853Snwhitehornscheme_supports_labels(const char *scheme) 70218853Snwhitehorn{ 71218853Snwhitehorn if (strcmp(scheme, "APM") == 0) 72218853Snwhitehorn return (1); 73218853Snwhitehorn if (strcmp(scheme, "GPT") == 0) 74218853Snwhitehorn return (1); 75218853Snwhitehorn if (strcmp(scheme, "PC98") == 0) 76218853Snwhitehorn return (1); 77218853Snwhitehorn 78218853Snwhitehorn return (0); 79218853Snwhitehorn} 80218853Snwhitehorn 81219892Snwhitehornstatic void 82219892Snwhitehornnewfs_command(const char *fstype, char *command, int use_default) 83219892Snwhitehorn{ 84219892Snwhitehorn if (strcmp(fstype, "freebsd-ufs") == 0) { 85219892Snwhitehorn int i; 86219892Snwhitehorn DIALOG_LISTITEM items[] = { 87219892Snwhitehorn {"UFS1", "UFS Version 1", 88219892Snwhitehorn "Use version 1 of the UFS file system instead " 89219892Snwhitehorn "of version 2 (not recommended)", 0 }, 90219892Snwhitehorn {"SU", "Softupdates", 91219892Snwhitehorn "Enable softupdates (default)", 1 }, 92219892Snwhitehorn {"SUJ", "Softupdates journaling", 93219892Snwhitehorn "Enable file system journaling (default - " 94219892Snwhitehorn "turn off for SSDs)", 1 }, 95219892Snwhitehorn {"TRIM", "Enable SSD TRIM support", 96219892Snwhitehorn "Enable TRIM support, useful on solid-state drives", 97219892Snwhitehorn 0 }, 98219892Snwhitehorn }; 99219892Snwhitehorn 100219892Snwhitehorn if (!use_default) { 101219892Snwhitehorn int choice; 102219892Snwhitehorn choice = dlg_checklist("UFS Options", "", 0, 0, 0, 103219892Snwhitehorn sizeof(items)/sizeof(items[0]), items, NULL, 104219892Snwhitehorn FLAG_CHECK, &i); 105219892Snwhitehorn if (choice == 1) /* Cancel */ 106219892Snwhitehorn return; 107219892Snwhitehorn } 108219892Snwhitehorn 109219892Snwhitehorn strcpy(command, "newfs "); 110219892Snwhitehorn for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); i++) { 111219892Snwhitehorn if (items[i].state == 0) 112219892Snwhitehorn continue; 113219892Snwhitehorn if (strcmp(items[i].name, "UFS1") == 0) 114219892Snwhitehorn strcat(command, "-O1 "); 115219892Snwhitehorn else if (strcmp(items[i].name, "SU") == 0) 116219892Snwhitehorn strcat(command, "-U "); 117219892Snwhitehorn else if (strcmp(items[i].name, "SUJ") == 0) 118219892Snwhitehorn strcat(command, "-j "); 119219892Snwhitehorn else if (strcmp(items[i].name, "TRIM") == 0) 120219892Snwhitehorn strcat(command, "-t "); 121219892Snwhitehorn } 122219892Snwhitehorn } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0) { 123219892Snwhitehorn int i; 124219892Snwhitehorn DIALOG_LISTITEM items[] = { 125219892Snwhitehorn {"FAT32", "FAT Type 32", 126219892Snwhitehorn "Create a FAT32 filesystem (default)", 1 }, 127219892Snwhitehorn {"FAT16", "FAT Type 16", 128219892Snwhitehorn "Create a FAT16 filesystem", 0 }, 129219892Snwhitehorn {"FAT12", "FAT Type 12", 130219892Snwhitehorn "Create a FAT12 filesystem", 0 }, 131219892Snwhitehorn }; 132219892Snwhitehorn 133219892Snwhitehorn if (!use_default) { 134219892Snwhitehorn int choice; 135219892Snwhitehorn choice = dlg_checklist("FAT Options", "", 0, 0, 0, 136219892Snwhitehorn sizeof(items)/sizeof(items[0]), items, NULL, 137219892Snwhitehorn FLAG_RADIO, &i); 138219892Snwhitehorn if (choice == 1) /* Cancel */ 139219892Snwhitehorn return; 140219892Snwhitehorn } 141219892Snwhitehorn 142219892Snwhitehorn strcpy(command, "newfs_msdos "); 143219892Snwhitehorn for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); i++) { 144219892Snwhitehorn if (items[i].state == 0) 145219892Snwhitehorn continue; 146219892Snwhitehorn if (strcmp(items[i].name, "FAT32") == 0) 147219892Snwhitehorn strcat(command, "-F 32 "); 148219892Snwhitehorn else if (strcmp(items[i].name, "FAT16") == 0) 149219892Snwhitehorn strcat(command, "-F 16 "); 150248237Snwhitehorn else if (strcmp(items[i].name, "FAT12") == 0) 151219892Snwhitehorn strcat(command, "-F 12 "); 152219892Snwhitehorn } 153219892Snwhitehorn } else { 154219892Snwhitehorn if (!use_default) 155219892Snwhitehorn dialog_msgbox("Error", "No configurable options exist " 156219892Snwhitehorn "for this filesystem.", 0, 0, TRUE); 157219892Snwhitehorn command[0] = '\0'; 158219892Snwhitehorn } 159219892Snwhitehorn} 160219892Snwhitehorn 161218799Snwhitehornint 162218799Snwhitehorngpart_partition(const char *lg_name, const char *scheme) 163218799Snwhitehorn{ 164218799Snwhitehorn int cancel, choice; 165218799Snwhitehorn struct gctl_req *r; 166218799Snwhitehorn const char *errstr; 167218799Snwhitehorn 168218799Snwhitehorn DIALOG_LISTITEM items[] = { 169218799Snwhitehorn {"APM", "Apple Partition Map", 170218799Snwhitehorn "Bootable on PowerPC Apple Hardware", 0 }, 171218799Snwhitehorn {"BSD", "BSD Labels", 172218799Snwhitehorn "Bootable on most x86 systems", 0 }, 173218799Snwhitehorn {"GPT", "GUID Partition Table", 174218799Snwhitehorn "Bootable on most x86 systems", 0 }, 175218799Snwhitehorn {"MBR", "DOS Partitions", 176218799Snwhitehorn "Bootable on most x86 systems", 0 }, 177218799Snwhitehorn {"PC98", "NEC PC9801 Partition Table", 178218799Snwhitehorn "Bootable on NEC PC9801 systems", 0 }, 179218799Snwhitehorn {"VTOC8", "Sun VTOC8 Partition Table", 180218799Snwhitehorn "Bootable on Sun SPARC systems", 0 }, 181218799Snwhitehorn }; 182218799Snwhitehorn 183218799Snwhitehornschememenu: 184218799Snwhitehorn if (scheme == NULL) { 185218799Snwhitehorn dialog_vars.default_item = __DECONST(char *, default_scheme()); 186218799Snwhitehorn cancel = dlg_menu("Partition Scheme", 187218799Snwhitehorn "Select a partition scheme for this volume:", 0, 0, 0, 188218799Snwhitehorn sizeof(items) / sizeof(items[0]), items, &choice, NULL); 189218799Snwhitehorn dialog_vars.default_item = NULL; 190218799Snwhitehorn 191218799Snwhitehorn if (cancel) 192218799Snwhitehorn return (-1); 193218799Snwhitehorn 194218799Snwhitehorn if (!is_scheme_bootable(items[choice].name)) { 195218799Snwhitehorn char message[512]; 196218799Snwhitehorn sprintf(message, "This partition scheme (%s) is not " 197218799Snwhitehorn "bootable on this platform. Are you sure you want " 198218799Snwhitehorn "to proceed?", items[choice].name); 199218799Snwhitehorn dialog_vars.defaultno = TRUE; 200218799Snwhitehorn cancel = dialog_yesno("Warning", message, 0, 0); 201218799Snwhitehorn dialog_vars.defaultno = FALSE; 202218799Snwhitehorn if (cancel) /* cancel */ 203218799Snwhitehorn goto schememenu; 204218799Snwhitehorn } 205218799Snwhitehorn 206218799Snwhitehorn scheme = items[choice].name; 207218799Snwhitehorn } 208218799Snwhitehorn 209218799Snwhitehorn r = gctl_get_handle(); 210218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 211218799Snwhitehorn gctl_ro_param(r, "arg0", -1, lg_name); 212218799Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 213218799Snwhitehorn gctl_ro_param(r, "scheme", -1, scheme); 214218799Snwhitehorn gctl_ro_param(r, "verb", -1, "create"); 215218799Snwhitehorn 216218799Snwhitehorn errstr = gctl_issue(r); 217218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') { 218218799Snwhitehorn gpart_show_error("Error", NULL, errstr); 219218799Snwhitehorn gctl_free(r); 220218799Snwhitehorn scheme = NULL; 221218799Snwhitehorn goto schememenu; 222218799Snwhitehorn } 223218799Snwhitehorn gctl_free(r); 224218799Snwhitehorn 225218799Snwhitehorn if (bootcode_path(scheme) != NULL) 226218799Snwhitehorn get_part_metadata(lg_name, 1)->bootcode = 1; 227218799Snwhitehorn return (0); 228218799Snwhitehorn} 229218799Snwhitehorn 230218799Snwhitehornstatic void 231218799Snwhitehorngpart_activate(struct gprovider *pp) 232218799Snwhitehorn{ 233218799Snwhitehorn struct gconfig *gc; 234218799Snwhitehorn struct gctl_req *r; 235218799Snwhitehorn const char *errstr, *scheme; 236218799Snwhitehorn const char *attribute = NULL; 237218799Snwhitehorn intmax_t idx; 238218799Snwhitehorn 239218799Snwhitehorn /* 240218799Snwhitehorn * Some partition schemes need this partition to be marked 'active' 241218799Snwhitehorn * for it to be bootable. 242218799Snwhitehorn */ 243218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { 244218799Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) { 245218799Snwhitehorn scheme = gc->lg_val; 246218799Snwhitehorn break; 247218799Snwhitehorn } 248218799Snwhitehorn } 249218799Snwhitehorn 250218799Snwhitehorn if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0 || 251218799Snwhitehorn strcmp(scheme, "PC98") == 0) 252218799Snwhitehorn attribute = "active"; 253218799Snwhitehorn else 254218799Snwhitehorn return; 255218799Snwhitehorn 256218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) { 257218799Snwhitehorn if (strcmp(gc->lg_name, "index") == 0) { 258218799Snwhitehorn idx = atoi(gc->lg_val); 259218799Snwhitehorn break; 260218799Snwhitehorn } 261218799Snwhitehorn } 262218799Snwhitehorn 263218799Snwhitehorn r = gctl_get_handle(); 264218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 265218799Snwhitehorn gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 266218799Snwhitehorn gctl_ro_param(r, "verb", -1, "set"); 267218799Snwhitehorn gctl_ro_param(r, "attrib", -1, attribute); 268218799Snwhitehorn gctl_ro_param(r, "index", sizeof(idx), &idx); 269218799Snwhitehorn 270218799Snwhitehorn errstr = gctl_issue(r); 271218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') 272218799Snwhitehorn gpart_show_error("Error", "Error marking partition active:", 273218799Snwhitehorn errstr); 274218799Snwhitehorn gctl_free(r); 275218799Snwhitehorn} 276218799Snwhitehorn 277218799Snwhitehornstatic void 278218799Snwhitehorngpart_bootcode(struct ggeom *gp) 279218799Snwhitehorn{ 280218799Snwhitehorn const char *bootcode; 281218799Snwhitehorn struct gconfig *gc; 282218799Snwhitehorn struct gctl_req *r; 283218799Snwhitehorn const char *errstr, *scheme; 284218799Snwhitehorn uint8_t *boot; 285218799Snwhitehorn size_t bootsize, bytes; 286218799Snwhitehorn int bootfd; 287218799Snwhitehorn 288218799Snwhitehorn /* 289218799Snwhitehorn * Write default bootcode to the newly partitioned disk, if that 290218799Snwhitehorn * applies on this platform. 291218799Snwhitehorn */ 292218799Snwhitehorn LIST_FOREACH(gc, &gp->lg_config, lg_config) { 293218799Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) { 294218799Snwhitehorn scheme = gc->lg_val; 295218799Snwhitehorn break; 296218799Snwhitehorn } 297218799Snwhitehorn } 298218799Snwhitehorn 299218799Snwhitehorn bootcode = bootcode_path(scheme); 300218799Snwhitehorn if (bootcode == NULL) 301218799Snwhitehorn return; 302218799Snwhitehorn 303218799Snwhitehorn bootfd = open(bootcode, O_RDONLY); 304229688Skevlo if (bootfd < 0) { 305218799Snwhitehorn dialog_msgbox("Bootcode Error", strerror(errno), 0, 0, 306218799Snwhitehorn TRUE); 307218799Snwhitehorn return; 308218799Snwhitehorn } 309218799Snwhitehorn 310218799Snwhitehorn bootsize = lseek(bootfd, 0, SEEK_END); 311218799Snwhitehorn boot = malloc(bootsize); 312218799Snwhitehorn lseek(bootfd, 0, SEEK_SET); 313218799Snwhitehorn bytes = 0; 314218799Snwhitehorn while (bytes < bootsize) 315218799Snwhitehorn bytes += read(bootfd, boot + bytes, bootsize - bytes); 316218799Snwhitehorn close(bootfd); 317218799Snwhitehorn 318218799Snwhitehorn r = gctl_get_handle(); 319218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 320218799Snwhitehorn gctl_ro_param(r, "arg0", -1, gp->lg_name); 321218799Snwhitehorn gctl_ro_param(r, "verb", -1, "bootcode"); 322218799Snwhitehorn gctl_ro_param(r, "bootcode", bootsize, boot); 323218799Snwhitehorn 324218799Snwhitehorn errstr = gctl_issue(r); 325218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') 326218799Snwhitehorn gpart_show_error("Bootcode Error", NULL, errstr); 327218799Snwhitehorn gctl_free(r); 328218799Snwhitehorn free(boot); 329218799Snwhitehorn} 330218799Snwhitehorn 331218799Snwhitehornstatic void 332218799Snwhitehorngpart_partcode(struct gprovider *pp) 333218799Snwhitehorn{ 334218799Snwhitehorn struct gconfig *gc; 335218799Snwhitehorn const char *scheme; 336218799Snwhitehorn const char *indexstr; 337218799Snwhitehorn char message[255], command[255]; 338218799Snwhitehorn 339218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { 340218799Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) { 341218799Snwhitehorn scheme = gc->lg_val; 342218799Snwhitehorn break; 343218799Snwhitehorn } 344218799Snwhitehorn } 345218799Snwhitehorn 346218799Snwhitehorn /* Make sure this partition scheme needs partcode on this platform */ 347218799Snwhitehorn if (partcode_path(scheme) == NULL) 348218799Snwhitehorn return; 349218799Snwhitehorn 350218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) { 351218799Snwhitehorn if (strcmp(gc->lg_name, "index") == 0) { 352218799Snwhitehorn indexstr = gc->lg_val; 353218799Snwhitehorn break; 354218799Snwhitehorn } 355218799Snwhitehorn } 356218799Snwhitehorn 357218799Snwhitehorn /* Shell out to gpart for partcode for now */ 358218799Snwhitehorn sprintf(command, "gpart bootcode -p %s -i %s %s", 359218799Snwhitehorn partcode_path(scheme), indexstr, pp->lg_geom->lg_name); 360218799Snwhitehorn if (system(command) != 0) { 361218799Snwhitehorn sprintf(message, "Error installing partcode on partition %s", 362218799Snwhitehorn pp->lg_name); 363218799Snwhitehorn dialog_msgbox("Error", message, 0, 0, TRUE); 364218799Snwhitehorn } 365218799Snwhitehorn} 366218799Snwhitehorn 367218799Snwhitehornvoid 368226212Snwhitehorngpart_destroy(struct ggeom *lg_geom) 369218799Snwhitehorn{ 370226212Snwhitehorn struct gctl_req *r; 371218799Snwhitehorn struct gprovider *pp; 372218799Snwhitehorn const char *errstr; 373226212Snwhitehorn int force = 1; 374218799Snwhitehorn 375226212Snwhitehorn /* Delete all child metadata */ 376218799Snwhitehorn LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) 377218799Snwhitehorn gpart_delete(pp); 378218799Snwhitehorn 379226212Snwhitehorn /* Revert any local changes to get this geom into a pristine state */ 380226212Snwhitehorn r = gctl_get_handle(); 381226212Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 382226212Snwhitehorn gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 383226212Snwhitehorn gctl_ro_param(r, "verb", -1, "undo"); 384226212Snwhitehorn gctl_issue(r); /* Ignore errors -- these are non-fatal */ 385226212Snwhitehorn gctl_free(r); 386226212Snwhitehorn 387218799Snwhitehorn /* Now destroy the geom itself */ 388218799Snwhitehorn r = gctl_get_handle(); 389218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 390218799Snwhitehorn gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); 391218799Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 392226212Snwhitehorn gctl_ro_param(r, "force", sizeof(force), &force); 393218799Snwhitehorn gctl_ro_param(r, "verb", -1, "destroy"); 394218799Snwhitehorn errstr = gctl_issue(r); 395227222Snwhitehorn if (errstr != NULL && errstr[0] != '\0') { 396227222Snwhitehorn /* 397227222Snwhitehorn * Check if we reverted away the existence of the geom 398227222Snwhitehorn * altogether. Show all other errors to the user. 399227222Snwhitehorn */ 400227222Snwhitehorn if (strtol(errstr, NULL, 0) != EINVAL) 401227222Snwhitehorn gpart_show_error("Error", NULL, errstr); 402227222Snwhitehorn } 403218799Snwhitehorn gctl_free(r); 404218799Snwhitehorn 405218799Snwhitehorn /* And any metadata associated with the partition scheme itself */ 406218799Snwhitehorn delete_part_metadata(lg_geom->lg_name); 407218799Snwhitehorn} 408218799Snwhitehorn 409218799Snwhitehornvoid 410218799Snwhitehorngpart_edit(struct gprovider *pp) 411218799Snwhitehorn{ 412218799Snwhitehorn struct gctl_req *r; 413218799Snwhitehorn struct gconfig *gc; 414218799Snwhitehorn struct gconsumer *cp; 415218799Snwhitehorn struct ggeom *geom; 416218799Snwhitehorn const char *errstr, *oldtype, *scheme; 417218799Snwhitehorn struct partition_metadata *md; 418218799Snwhitehorn char sizestr[32]; 419219892Snwhitehorn char newfs[64]; 420218799Snwhitehorn intmax_t idx; 421218799Snwhitehorn int hadlabel, choice, junk, nitems; 422218799Snwhitehorn unsigned i; 423218799Snwhitehorn 424218799Snwhitehorn DIALOG_FORMITEM items[] = { 425218799Snwhitehorn {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0, 426218799Snwhitehorn FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)", 427218799Snwhitehorn FALSE}, 428218799Snwhitehorn {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0, 429218799Snwhitehorn FALSE, "Partition size. Append K, M, G for kilobytes, " 430218799Snwhitehorn "megabytes or gigabytes.", FALSE}, 431218799Snwhitehorn {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, 432218799Snwhitehorn FALSE, "Path at which to mount this partition (leave blank " 433218799Snwhitehorn "for swap, set to / for root filesystem)", FALSE}, 434218799Snwhitehorn {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, 435218799Snwhitehorn "Partition name. Not all partition schemes support this.", 436218799Snwhitehorn FALSE}, 437218799Snwhitehorn }; 438218799Snwhitehorn 439218799Snwhitehorn /* 440218799Snwhitehorn * Find the PART geom we are manipulating. This may be a consumer of 441218799Snwhitehorn * this provider, or its parent. Check the consumer case first. 442218799Snwhitehorn */ 443218799Snwhitehorn geom = NULL; 444218799Snwhitehorn LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 445218799Snwhitehorn if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 446226212Snwhitehorn /* Check for zombie geoms, treating them as blank */ 447226212Snwhitehorn scheme = NULL; 448226212Snwhitehorn LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) { 449226212Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) { 450226212Snwhitehorn scheme = gc->lg_val; 451226212Snwhitehorn break; 452226212Snwhitehorn } 453226212Snwhitehorn } 454226212Snwhitehorn if (scheme == NULL || strcmp(scheme, "(none)") == 0) { 455226212Snwhitehorn gpart_partition(cp->lg_geom->lg_name, NULL); 456218799Snwhitehorn return; 457226212Snwhitehorn } 458218799Snwhitehorn 459227222Snwhitehorn /* If this is a nested partition, edit as usual */ 460227222Snwhitehorn if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 461227222Snwhitehorn break; 462227222Snwhitehorn 463218799Snwhitehorn /* Destroy the geom and all sub-partitions */ 464226212Snwhitehorn gpart_destroy(cp->lg_geom); 465218799Snwhitehorn 466218799Snwhitehorn /* Now re-partition and return */ 467218799Snwhitehorn gpart_partition(cp->lg_geom->lg_name, NULL); 468218799Snwhitehorn return; 469218799Snwhitehorn } 470218799Snwhitehorn 471218799Snwhitehorn if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 472218799Snwhitehorn geom = pp->lg_geom; 473218799Snwhitehorn 474218799Snwhitehorn if (geom == NULL) { 475218799Snwhitehorn /* Disk not partitioned, so partition it */ 476225066Snwhitehorn gpart_partition(pp->lg_name, NULL); 477218799Snwhitehorn return; 478218799Snwhitehorn } 479218799Snwhitehorn 480218799Snwhitehorn LIST_FOREACH(gc, &geom->lg_config, lg_config) { 481218799Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) { 482218799Snwhitehorn scheme = gc->lg_val; 483218799Snwhitehorn break; 484218799Snwhitehorn } 485218799Snwhitehorn } 486218799Snwhitehorn 487218853Snwhitehorn nitems = scheme_supports_labels(scheme) ? 4 : 3; 488218799Snwhitehorn 489218799Snwhitehorn /* Edit editable parameters of a partition */ 490218799Snwhitehorn hadlabel = 0; 491218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) { 492218799Snwhitehorn if (strcmp(gc->lg_name, "type") == 0) { 493218799Snwhitehorn oldtype = gc->lg_val; 494218799Snwhitehorn items[0].text = gc->lg_val; 495218799Snwhitehorn } 496218799Snwhitehorn if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { 497218799Snwhitehorn hadlabel = 1; 498218799Snwhitehorn items[3].text = gc->lg_val; 499218799Snwhitehorn } 500218799Snwhitehorn if (strcmp(gc->lg_name, "index") == 0) 501218799Snwhitehorn idx = atoi(gc->lg_val); 502218799Snwhitehorn } 503218799Snwhitehorn 504218799Snwhitehorn TAILQ_FOREACH(md, &part_metadata, metadata) { 505218799Snwhitehorn if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { 506218799Snwhitehorn if (md->fstab != NULL) 507218799Snwhitehorn items[2].text = md->fstab->fs_file; 508218799Snwhitehorn break; 509218799Snwhitehorn } 510218799Snwhitehorn } 511218799Snwhitehorn 512218799Snwhitehorn humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, 513218799Snwhitehorn HN_NOSPACE | HN_DECIMAL); 514218799Snwhitehorn items[1].text = sizestr; 515218799Snwhitehorn 516218799Snwhitehorneditpart: 517218799Snwhitehorn choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk); 518218799Snwhitehorn 519218799Snwhitehorn if (choice) /* Cancel pressed */ 520227222Snwhitehorn goto endedit; 521218799Snwhitehorn 522218799Snwhitehorn /* Check if the label has a / in it */ 523218799Snwhitehorn if (strchr(items[3].text, '/') != NULL) { 524218799Snwhitehorn dialog_msgbox("Error", "Label contains a /, which is not an " 525218799Snwhitehorn "allowed character.", 0, 0, TRUE); 526218799Snwhitehorn goto editpart; 527218799Snwhitehorn } 528218799Snwhitehorn 529218799Snwhitehorn r = gctl_get_handle(); 530218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 531218799Snwhitehorn gctl_ro_param(r, "arg0", -1, geom->lg_name); 532218799Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 533218799Snwhitehorn gctl_ro_param(r, "verb", -1, "modify"); 534218799Snwhitehorn gctl_ro_param(r, "index", sizeof(idx), &idx); 535218799Snwhitehorn if (hadlabel || items[3].text[0] != '\0') 536218799Snwhitehorn gctl_ro_param(r, "label", -1, items[3].text); 537218799Snwhitehorn gctl_ro_param(r, "type", -1, items[0].text); 538218799Snwhitehorn errstr = gctl_issue(r); 539218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') { 540218799Snwhitehorn gpart_show_error("Error", NULL, errstr); 541218799Snwhitehorn gctl_free(r); 542218799Snwhitehorn goto editpart; 543218799Snwhitehorn } 544218799Snwhitehorn gctl_free(r); 545218799Snwhitehorn 546219892Snwhitehorn newfs_command(items[0].text, newfs, 1); 547218799Snwhitehorn set_default_part_metadata(pp->lg_name, scheme, items[0].text, 548219892Snwhitehorn items[2].text, (strcmp(oldtype, items[0].text) != 0) ? 549219892Snwhitehorn newfs : NULL); 550218799Snwhitehorn 551227222Snwhitehornendedit: 552227222Snwhitehorn if (strcmp(oldtype, items[0].text) != 0 && cp != NULL) 553227222Snwhitehorn gpart_destroy(cp->lg_geom); 554227222Snwhitehorn if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text, 555227222Snwhitehorn "freebsd") == 0) 556227222Snwhitehorn gpart_partition(pp->lg_name, "BSD"); 557227222Snwhitehorn 558218799Snwhitehorn for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) 559218799Snwhitehorn if (items[i].text_free) 560218799Snwhitehorn free(items[i].text); 561218799Snwhitehorn} 562218799Snwhitehorn 563218799Snwhitehornvoid 564218799Snwhitehornset_default_part_metadata(const char *name, const char *scheme, 565219892Snwhitehorn const char *type, const char *mountpoint, const char *newfs) 566218799Snwhitehorn{ 567218799Snwhitehorn struct partition_metadata *md; 568218799Snwhitehorn 569218799Snwhitehorn /* Set part metadata */ 570218799Snwhitehorn md = get_part_metadata(name, 1); 571218799Snwhitehorn 572218799Snwhitehorn if (newfs) { 573218799Snwhitehorn if (md->newfs != NULL) { 574218799Snwhitehorn free(md->newfs); 575218799Snwhitehorn md->newfs = NULL; 576218799Snwhitehorn } 577218799Snwhitehorn 578219892Snwhitehorn if (newfs != NULL && newfs[0] != '\0') { 579219892Snwhitehorn md->newfs = malloc(strlen(newfs) + strlen(" /dev/") + 580219892Snwhitehorn strlen(name) + 1); 581219892Snwhitehorn sprintf(md->newfs, "%s /dev/%s", newfs, name); 582218799Snwhitehorn } 583218799Snwhitehorn } 584218799Snwhitehorn 585218799Snwhitehorn if (strcmp(type, "freebsd-swap") == 0) 586218799Snwhitehorn mountpoint = "none"; 587218799Snwhitehorn if (strcmp(type, "freebsd-boot") == 0) 588218799Snwhitehorn md->bootcode = 1; 589218799Snwhitehorn 590218799Snwhitehorn /* VTOC8 needs partcode in UFS partitions */ 591218799Snwhitehorn if (strcmp(scheme, "VTOC8") == 0 && strcmp(type, "freebsd-ufs") == 0) 592218799Snwhitehorn md->bootcode = 1; 593218799Snwhitehorn 594218799Snwhitehorn if (mountpoint == NULL || mountpoint[0] == '\0') { 595218799Snwhitehorn if (md->fstab != NULL) { 596218799Snwhitehorn free(md->fstab->fs_spec); 597218799Snwhitehorn free(md->fstab->fs_file); 598218799Snwhitehorn free(md->fstab->fs_vfstype); 599218799Snwhitehorn free(md->fstab->fs_mntops); 600218799Snwhitehorn free(md->fstab->fs_type); 601218799Snwhitehorn free(md->fstab); 602218799Snwhitehorn md->fstab = NULL; 603218799Snwhitehorn } 604218799Snwhitehorn } else { 605218799Snwhitehorn if (md->fstab == NULL) { 606218799Snwhitehorn md->fstab = malloc(sizeof(struct fstab)); 607218799Snwhitehorn } else { 608218799Snwhitehorn free(md->fstab->fs_spec); 609218799Snwhitehorn free(md->fstab->fs_file); 610218799Snwhitehorn free(md->fstab->fs_vfstype); 611218799Snwhitehorn free(md->fstab->fs_mntops); 612218799Snwhitehorn free(md->fstab->fs_type); 613218799Snwhitehorn } 614218799Snwhitehorn md->fstab->fs_spec = malloc(strlen(name) + 6); 615218799Snwhitehorn sprintf(md->fstab->fs_spec, "/dev/%s", name); 616218799Snwhitehorn md->fstab->fs_file = strdup(mountpoint); 617218799Snwhitehorn /* Get VFS from text after freebsd-, if possible */ 618219892Snwhitehorn if (strncmp("freebsd-", type, 8) == 0) 619218799Snwhitehorn md->fstab->fs_vfstype = strdup(&type[8]); 620219892Snwhitehorn else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0) 621219892Snwhitehorn md->fstab->fs_vfstype = strdup("msdosfs"); 622218799Snwhitehorn else 623218799Snwhitehorn md->fstab->fs_vfstype = strdup(type); /* Guess */ 624218799Snwhitehorn if (strcmp(type, "freebsd-swap") == 0) { 625218799Snwhitehorn md->fstab->fs_type = strdup(FSTAB_SW); 626218799Snwhitehorn md->fstab->fs_freq = 0; 627218799Snwhitehorn md->fstab->fs_passno = 0; 628218799Snwhitehorn } else { 629218799Snwhitehorn md->fstab->fs_type = strdup(FSTAB_RW); 630218799Snwhitehorn if (strcmp(mountpoint, "/") == 0) { 631218799Snwhitehorn md->fstab->fs_freq = 1; 632218799Snwhitehorn md->fstab->fs_passno = 1; 633218799Snwhitehorn } else { 634218799Snwhitehorn md->fstab->fs_freq = 2; 635218799Snwhitehorn md->fstab->fs_passno = 2; 636218799Snwhitehorn } 637218799Snwhitehorn } 638218799Snwhitehorn md->fstab->fs_mntops = strdup(md->fstab->fs_type); 639218799Snwhitehorn } 640218799Snwhitehorn} 641218799Snwhitehorn 642218799Snwhitehornstatic 643218799Snwhitehornint part_compare(const void *xa, const void *xb) 644218799Snwhitehorn{ 645218799Snwhitehorn struct gprovider **a = (struct gprovider **)xa; 646218799Snwhitehorn struct gprovider **b = (struct gprovider **)xb; 647218799Snwhitehorn intmax_t astart, bstart; 648218799Snwhitehorn struct gconfig *gc; 649218799Snwhitehorn 650218799Snwhitehorn astart = bstart = 0; 651218799Snwhitehorn LIST_FOREACH(gc, &(*a)->lg_config, lg_config) 652218799Snwhitehorn if (strcmp(gc->lg_name, "start") == 0) { 653218799Snwhitehorn astart = strtoimax(gc->lg_val, NULL, 0); 654218799Snwhitehorn break; 655218799Snwhitehorn } 656218799Snwhitehorn LIST_FOREACH(gc, &(*b)->lg_config, lg_config) 657218799Snwhitehorn if (strcmp(gc->lg_name, "start") == 0) { 658218799Snwhitehorn bstart = strtoimax(gc->lg_val, NULL, 0); 659218799Snwhitehorn break; 660218799Snwhitehorn } 661218799Snwhitehorn 662218799Snwhitehorn if (astart < bstart) 663218799Snwhitehorn return -1; 664218799Snwhitehorn else if (astart > bstart) 665218799Snwhitehorn return 1; 666218799Snwhitehorn else 667218799Snwhitehorn return 0; 668218799Snwhitehorn} 669218799Snwhitehorn 670218799Snwhitehornintmax_t 671218799Snwhitehorngpart_max_free(struct ggeom *geom, intmax_t *npartstart) 672218799Snwhitehorn{ 673218799Snwhitehorn struct gconfig *gc; 674218799Snwhitehorn struct gprovider *pp, **providers; 675218799Snwhitehorn intmax_t lastend; 676218799Snwhitehorn intmax_t start, end; 677218799Snwhitehorn intmax_t maxsize, maxstart; 678218799Snwhitehorn intmax_t partstart, partend; 679218799Snwhitehorn int i, nparts; 680218799Snwhitehorn 681218799Snwhitehorn /* Now get the maximum free size and free start */ 682218799Snwhitehorn start = end = 0; 683218799Snwhitehorn LIST_FOREACH(gc, &geom->lg_config, lg_config) { 684218799Snwhitehorn if (strcmp(gc->lg_name, "first") == 0) 685218799Snwhitehorn start = strtoimax(gc->lg_val, NULL, 0); 686218799Snwhitehorn if (strcmp(gc->lg_name, "last") == 0) 687218799Snwhitehorn end = strtoimax(gc->lg_val, NULL, 0); 688218799Snwhitehorn } 689218799Snwhitehorn 690218799Snwhitehorn i = nparts = 0; 691218799Snwhitehorn LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 692218799Snwhitehorn nparts++; 693218799Snwhitehorn providers = calloc(nparts, sizeof(providers[0])); 694218799Snwhitehorn LIST_FOREACH(pp, &geom->lg_provider, lg_provider) 695218799Snwhitehorn providers[i++] = pp; 696218799Snwhitehorn qsort(providers, nparts, sizeof(providers[0]), part_compare); 697218799Snwhitehorn 698218799Snwhitehorn lastend = start - 1; 699218799Snwhitehorn maxsize = 0; 700218799Snwhitehorn for (i = 0; i < nparts; i++) { 701218799Snwhitehorn pp = providers[i]; 702218799Snwhitehorn 703218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) { 704218799Snwhitehorn if (strcmp(gc->lg_name, "start") == 0) 705218799Snwhitehorn partstart = strtoimax(gc->lg_val, NULL, 0); 706218799Snwhitehorn if (strcmp(gc->lg_name, "end") == 0) 707218799Snwhitehorn partend = strtoimax(gc->lg_val, NULL, 0); 708218799Snwhitehorn } 709218799Snwhitehorn 710218799Snwhitehorn if (partstart - lastend > maxsize) { 711218799Snwhitehorn maxsize = partstart - lastend - 1; 712218799Snwhitehorn maxstart = lastend + 1; 713218799Snwhitehorn } 714218799Snwhitehorn 715218799Snwhitehorn lastend = partend; 716218799Snwhitehorn } 717218799Snwhitehorn 718218799Snwhitehorn if (end - lastend > maxsize) { 719218799Snwhitehorn maxsize = end - lastend - 1; 720218799Snwhitehorn maxstart = lastend + 1; 721218799Snwhitehorn } 722218799Snwhitehorn 723218799Snwhitehorn pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; 724218799Snwhitehorn 725218799Snwhitehorn /* Compute beginning of new partition and maximum available space */ 726218799Snwhitehorn if (pp->lg_stripesize > 0 && 727218799Snwhitehorn (maxstart*pp->lg_sectorsize % pp->lg_stripesize) != 0) { 728218799Snwhitehorn intmax_t offset = (pp->lg_stripesize - 729218799Snwhitehorn ((maxstart*pp->lg_sectorsize) % pp->lg_stripesize)) / 730218799Snwhitehorn pp->lg_sectorsize; 731218799Snwhitehorn maxstart += offset; 732218799Snwhitehorn maxsize -= offset; 733218799Snwhitehorn } 734218799Snwhitehorn 735218799Snwhitehorn if (npartstart != NULL) 736218799Snwhitehorn *npartstart = maxstart; 737218799Snwhitehorn 738218799Snwhitehorn return (maxsize); 739218799Snwhitehorn} 740218799Snwhitehorn 741218799Snwhitehornvoid 742218799Snwhitehorngpart_create(struct gprovider *pp, char *default_type, char *default_size, 743218799Snwhitehorn char *default_mountpoint, char **partname, int interactive) 744218799Snwhitehorn{ 745218799Snwhitehorn struct gctl_req *r; 746218799Snwhitehorn struct gconfig *gc; 747218799Snwhitehorn struct gconsumer *cp; 748218799Snwhitehorn struct ggeom *geom; 749218799Snwhitehorn const char *errstr, *scheme; 750226250Snwhitehorn char sizestr[32], startstr[32], output[64], *newpartname; 751219892Snwhitehorn char newfs[64], options_fstype[64]; 752218799Snwhitehorn intmax_t maxsize, size, sector, firstfree, stripe; 753218799Snwhitehorn uint64_t bytes; 754218799Snwhitehorn int nitems, choice, junk; 755218799Snwhitehorn unsigned i; 756218799Snwhitehorn 757218799Snwhitehorn DIALOG_FORMITEM items[] = { 758218799Snwhitehorn {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0, 759218799Snwhitehorn FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)", 760218799Snwhitehorn FALSE}, 761218799Snwhitehorn {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0, 762218799Snwhitehorn FALSE, "Partition size. Append K, M, G for kilobytes, " 763218799Snwhitehorn "megabytes or gigabytes.", FALSE}, 764218799Snwhitehorn {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, 765218799Snwhitehorn FALSE, "Path at which to mount partition (blank for " 766218799Snwhitehorn "swap, / for root filesystem)", FALSE}, 767218799Snwhitehorn {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, 768218799Snwhitehorn "Partition name. Not all partition schemes support this.", 769218799Snwhitehorn FALSE}, 770218799Snwhitehorn }; 771218799Snwhitehorn 772218799Snwhitehorn if (partname != NULL) 773218799Snwhitehorn *partname = NULL; 774218799Snwhitehorn 775218799Snwhitehorn /* Record sector and stripe sizes */ 776218799Snwhitehorn sector = pp->lg_sectorsize; 777218799Snwhitehorn stripe = pp->lg_stripesize; 778218799Snwhitehorn 779218799Snwhitehorn /* 780218799Snwhitehorn * Find the PART geom we are manipulating. This may be a consumer of 781218799Snwhitehorn * this provider, or its parent. Check the consumer case first. 782218799Snwhitehorn */ 783218799Snwhitehorn geom = NULL; 784218799Snwhitehorn LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 785218799Snwhitehorn if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 786218799Snwhitehorn geom = cp->lg_geom; 787218799Snwhitehorn break; 788218799Snwhitehorn } 789218799Snwhitehorn 790218799Snwhitehorn if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) 791218799Snwhitehorn geom = pp->lg_geom; 792218799Snwhitehorn 793218799Snwhitehorn /* Now get the partition scheme */ 794218799Snwhitehorn scheme = NULL; 795218799Snwhitehorn if (geom != NULL) { 796218799Snwhitehorn LIST_FOREACH(gc, &geom->lg_config, lg_config) 797218799Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) 798218799Snwhitehorn scheme = gc->lg_val; 799218799Snwhitehorn } 800218799Snwhitehorn 801218799Snwhitehorn if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { 802225066Snwhitehorn if (gpart_partition(pp->lg_name, NULL) == 0) 803218799Snwhitehorn dialog_msgbox("", 804218799Snwhitehorn "The partition table has been successfully created." 805218799Snwhitehorn " Please press Create again to create partitions.", 806218799Snwhitehorn 0, 0, TRUE); 807218799Snwhitehorn 808218799Snwhitehorn return; 809218799Snwhitehorn } 810218799Snwhitehorn 811218799Snwhitehorn /* 812218799Snwhitehorn * If we still don't have a geom, either the user has 813218799Snwhitehorn * canceled partitioning or there has been an error which has already 814218799Snwhitehorn * been displayed, so bail. 815218799Snwhitehorn */ 816218799Snwhitehorn if (geom == NULL) 817218799Snwhitehorn return; 818218799Snwhitehorn 819218799Snwhitehorn maxsize = size = gpart_max_free(geom, &firstfree); 820218799Snwhitehorn if (size <= 0) { 821218799Snwhitehorn dialog_msgbox("Error", "No free space left on device.", 0, 0, 822218799Snwhitehorn TRUE); 823218799Snwhitehorn return; 824218799Snwhitehorn } 825218799Snwhitehorn 826218799Snwhitehorn humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, 827218799Snwhitehorn HN_NOSPACE | HN_DECIMAL); 828218799Snwhitehorn items[1].text = sizestr; 829218799Snwhitehorn 830218799Snwhitehorn /* Special-case the MBR default type for nested partitions */ 831225066Snwhitehorn if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0) { 832218799Snwhitehorn items[0].text = "freebsd"; 833225066Snwhitehorn items[0].help = "Filesystem type (e.g. freebsd, fat32)"; 834225066Snwhitehorn } 835218799Snwhitehorn 836218853Snwhitehorn nitems = scheme_supports_labels(scheme) ? 4 : 3; 837218799Snwhitehorn 838218799Snwhitehorn if (default_type != NULL) 839218799Snwhitehorn items[0].text = default_type; 840218799Snwhitehorn if (default_size != NULL) 841218799Snwhitehorn items[1].text = default_size; 842218799Snwhitehorn if (default_mountpoint != NULL) 843218799Snwhitehorn items[2].text = default_mountpoint; 844218799Snwhitehorn 845219892Snwhitehorn /* Default options */ 846219892Snwhitehorn strncpy(options_fstype, items[0].text, 847219892Snwhitehorn sizeof(options_fstype)); 848219892Snwhitehorn newfs_command(options_fstype, newfs, 1); 849218799Snwhitehornaddpartform: 850218799Snwhitehorn if (interactive) { 851219892Snwhitehorn dialog_vars.extra_label = "Options"; 852219892Snwhitehorn dialog_vars.extra_button = TRUE; 853218799Snwhitehorn choice = dlg_form("Add Partition", "", 0, 0, 0, nitems, 854218799Snwhitehorn items, &junk); 855219892Snwhitehorn dialog_vars.extra_button = FALSE; 856219892Snwhitehorn switch (choice) { 857219892Snwhitehorn case 0: /* OK */ 858219892Snwhitehorn break; 859219892Snwhitehorn case 1: /* Cancel */ 860218799Snwhitehorn return; 861219892Snwhitehorn case 3: /* Options */ 862219892Snwhitehorn strncpy(options_fstype, items[0].text, 863219892Snwhitehorn sizeof(options_fstype)); 864219892Snwhitehorn newfs_command(options_fstype, newfs, 0); 865219892Snwhitehorn goto addpartform; 866219892Snwhitehorn } 867218799Snwhitehorn } 868218799Snwhitehorn 869219892Snwhitehorn /* 870219892Snwhitehorn * If the user changed the fs type after specifying options, undo 871219892Snwhitehorn * their choices in favor of the new filesystem's defaults. 872219892Snwhitehorn */ 873225613Snwhitehorn if (strcmp(options_fstype, items[0].text) != 0) { 874219892Snwhitehorn strncpy(options_fstype, items[0].text, sizeof(options_fstype)); 875219892Snwhitehorn newfs_command(options_fstype, newfs, 1); 876219892Snwhitehorn } 877219892Snwhitehorn 878218799Snwhitehorn size = maxsize; 879218799Snwhitehorn if (strlen(items[1].text) > 0) { 880218799Snwhitehorn if (expand_number(items[1].text, &bytes) != 0) { 881218799Snwhitehorn char error[512]; 882218799Snwhitehorn 883218799Snwhitehorn sprintf(error, "Invalid size: %s\n", strerror(errno)); 884218799Snwhitehorn dialog_msgbox("Error", error, 0, 0, TRUE); 885218799Snwhitehorn goto addpartform; 886218799Snwhitehorn } 887218799Snwhitehorn size = MIN((intmax_t)(bytes/sector), maxsize); 888218799Snwhitehorn } 889218799Snwhitehorn 890218799Snwhitehorn /* Check if the label has a / in it */ 891218799Snwhitehorn if (strchr(items[3].text, '/') != NULL) { 892218799Snwhitehorn dialog_msgbox("Error", "Label contains a /, which is not an " 893218799Snwhitehorn "allowed character.", 0, 0, TRUE); 894218799Snwhitehorn goto addpartform; 895218799Snwhitehorn } 896218799Snwhitehorn 897218799Snwhitehorn /* Warn if no mountpoint set */ 898218799Snwhitehorn if (strcmp(items[0].text, "freebsd-ufs") == 0 && 899218799Snwhitehorn items[2].text[0] != '/') { 900218799Snwhitehorn dialog_vars.defaultno = TRUE; 901218799Snwhitehorn choice = dialog_yesno("Warning", 902218799Snwhitehorn "This partition does not have a valid mountpoint " 903218799Snwhitehorn "(for the partition from which you intend to boot the " 904218799Snwhitehorn "operating system, the mountpoint should be /). Are you " 905218799Snwhitehorn "sure you want to continue?" 906218799Snwhitehorn , 0, 0); 907218799Snwhitehorn dialog_vars.defaultno = FALSE; 908218799Snwhitehorn if (choice == 1) /* cancel */ 909218799Snwhitehorn goto addpartform; 910218799Snwhitehorn } 911218799Snwhitehorn 912226249Snwhitehorn /* 913226249Snwhitehorn * Error if this scheme needs nested partitions, this is one, and 914226249Snwhitehorn * a mountpoint was set. 915226249Snwhitehorn */ 916226249Snwhitehorn if (strcmp(items[0].text, "freebsd") == 0 && 917226249Snwhitehorn strlen(items[2].text) > 0) { 918226249Snwhitehorn dialog_msgbox("Error", "Partitions of type \"freebsd\" are " 919226249Snwhitehorn "nested BSD-type partition schemes and cannot have " 920226249Snwhitehorn "mountpoints. After creating one, select it and press " 921226249Snwhitehorn "Create again to add the actual file systems.", 0, 0, TRUE); 922226249Snwhitehorn goto addpartform; 923226249Snwhitehorn } 924226249Snwhitehorn 925218799Snwhitehorn /* If this is the root partition, check that this scheme is bootable */ 926218799Snwhitehorn if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) { 927218799Snwhitehorn char message[512]; 928218799Snwhitehorn sprintf(message, "This partition scheme (%s) is not bootable " 929218799Snwhitehorn "on this platform. Are you sure you want to proceed?", 930218799Snwhitehorn scheme); 931218799Snwhitehorn dialog_vars.defaultno = TRUE; 932218799Snwhitehorn choice = dialog_yesno("Warning", message, 0, 0); 933218799Snwhitehorn dialog_vars.defaultno = FALSE; 934218799Snwhitehorn if (choice == 1) /* cancel */ 935218799Snwhitehorn goto addpartform; 936218799Snwhitehorn } 937218799Snwhitehorn 938218799Snwhitehorn /* 939218799Snwhitehorn * If this is the root partition, and we need a boot partition, ask 940218799Snwhitehorn * the user to add one. 941218799Snwhitehorn */ 942226249Snwhitehorn 943226249Snwhitehorn /* Check for existing freebsd-boot partition */ 944226249Snwhitehorn LIST_FOREACH(pp, &geom->lg_provider, lg_provider) { 945226249Snwhitehorn struct partition_metadata *md; 946226249Snwhitehorn md = get_part_metadata(pp->lg_name, 0); 947226249Snwhitehorn if (md == NULL || !md->bootcode) 948226249Snwhitehorn continue; 949226249Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) 950226249Snwhitehorn if (strcmp(gc->lg_name, "type") == 0) 951226249Snwhitehorn break; 952226249Snwhitehorn if (gc != NULL && strcmp(gc->lg_val, "freebsd-boot") == 0) 953226249Snwhitehorn break; 954226249Snwhitehorn } 955226249Snwhitehorn 956226249Snwhitehorn /* If there isn't one, and we need one, ask */ 957226249Snwhitehorn if (strcmp(items[2].text, "/") == 0 && bootpart_size(scheme) > 0 && 958226249Snwhitehorn pp == NULL) { 959218799Snwhitehorn if (interactive) 960218799Snwhitehorn choice = dialog_yesno("Boot Partition", 961218799Snwhitehorn "This partition scheme requires a boot partition " 962218799Snwhitehorn "for the disk to be bootable. Would you like to " 963218799Snwhitehorn "make one now?", 0, 0); 964218799Snwhitehorn else 965218799Snwhitehorn choice = 0; 966218799Snwhitehorn 967218799Snwhitehorn if (choice == 0) { /* yes */ 968218799Snwhitehorn r = gctl_get_handle(); 969218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 970218799Snwhitehorn gctl_ro_param(r, "arg0", -1, geom->lg_name); 971218799Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 972218799Snwhitehorn gctl_ro_param(r, "verb", -1, "add"); 973218799Snwhitehorn gctl_ro_param(r, "type", -1, "freebsd-boot"); 974218799Snwhitehorn snprintf(sizestr, sizeof(sizestr), "%jd", 975218799Snwhitehorn bootpart_size(scheme) / sector); 976218799Snwhitehorn gctl_ro_param(r, "size", -1, sizestr); 977218799Snwhitehorn snprintf(startstr, sizeof(startstr), "%jd", firstfree); 978218799Snwhitehorn gctl_ro_param(r, "start", -1, startstr); 979218799Snwhitehorn gctl_rw_param(r, "output", sizeof(output), output); 980218799Snwhitehorn errstr = gctl_issue(r); 981218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') 982218799Snwhitehorn gpart_show_error("Error", NULL, errstr); 983218799Snwhitehorn gctl_free(r); 984218799Snwhitehorn 985218799Snwhitehorn get_part_metadata(strtok(output, " "), 1)->bootcode = 1; 986218799Snwhitehorn 987218799Snwhitehorn /* Now adjust the part we are really adding forward */ 988218799Snwhitehorn firstfree += bootpart_size(scheme) / sector; 989218799Snwhitehorn size -= (bootpart_size(scheme) + stripe)/sector; 990218799Snwhitehorn if (stripe > 0 && (firstfree*sector % stripe) != 0) 991218799Snwhitehorn firstfree += (stripe - ((firstfree*sector) % 992218799Snwhitehorn stripe)) / sector; 993218799Snwhitehorn } 994218799Snwhitehorn } 995218799Snwhitehorn 996218799Snwhitehorn r = gctl_get_handle(); 997218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 998218799Snwhitehorn gctl_ro_param(r, "arg0", -1, geom->lg_name); 999218799Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1000218799Snwhitehorn gctl_ro_param(r, "verb", -1, "add"); 1001218799Snwhitehorn 1002218799Snwhitehorn gctl_ro_param(r, "type", -1, items[0].text); 1003218799Snwhitehorn snprintf(sizestr, sizeof(sizestr), "%jd", size); 1004218799Snwhitehorn gctl_ro_param(r, "size", -1, sizestr); 1005218799Snwhitehorn snprintf(startstr, sizeof(startstr), "%jd", firstfree); 1006218799Snwhitehorn gctl_ro_param(r, "start", -1, startstr); 1007218799Snwhitehorn if (items[3].text[0] != '\0') 1008218799Snwhitehorn gctl_ro_param(r, "label", -1, items[3].text); 1009218799Snwhitehorn gctl_rw_param(r, "output", sizeof(output), output); 1010218799Snwhitehorn errstr = gctl_issue(r); 1011218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') { 1012218799Snwhitehorn gpart_show_error("Error", NULL, errstr); 1013218799Snwhitehorn gctl_free(r); 1014218799Snwhitehorn goto addpartform; 1015218799Snwhitehorn } 1016226250Snwhitehorn newpartname = strtok(output, " "); 1017226250Snwhitehorn gctl_free(r); 1018218799Snwhitehorn 1019226250Snwhitehorn /* 1020226250Snwhitehorn * Try to destroy any geom that gpart picked up already here from 1021226250Snwhitehorn * dirty blocks. 1022226250Snwhitehorn */ 1023226250Snwhitehorn r = gctl_get_handle(); 1024226250Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 1025226250Snwhitehorn gctl_ro_param(r, "arg0", -1, newpartname); 1026226250Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1027226250Snwhitehorn junk = 1; 1028226250Snwhitehorn gctl_ro_param(r, "force", sizeof(junk), &junk); 1029226250Snwhitehorn gctl_ro_param(r, "verb", -1, "destroy"); 1030226250Snwhitehorn gctl_issue(r); /* Error usually expected and non-fatal */ 1031226250Snwhitehorn gctl_free(r); 1032226250Snwhitehorn 1033218799Snwhitehorn if (strcmp(items[0].text, "freebsd-boot") == 0) 1034226250Snwhitehorn get_part_metadata(newpartname, 1)->bootcode = 1; 1035218799Snwhitehorn else if (strcmp(items[0].text, "freebsd") == 0) 1036226250Snwhitehorn gpart_partition(newpartname, "BSD"); 1037218799Snwhitehorn else 1038226250Snwhitehorn set_default_part_metadata(newpartname, scheme, 1039219892Snwhitehorn items[0].text, items[2].text, newfs); 1040218799Snwhitehorn 1041218799Snwhitehorn for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) 1042218799Snwhitehorn if (items[i].text_free) 1043218799Snwhitehorn free(items[i].text); 1044218799Snwhitehorn 1045218799Snwhitehorn if (partname != NULL) 1046226250Snwhitehorn *partname = strdup(newpartname); 1047218799Snwhitehorn} 1048218799Snwhitehorn 1049218799Snwhitehornvoid 1050218799Snwhitehorngpart_delete(struct gprovider *pp) 1051218799Snwhitehorn{ 1052218799Snwhitehorn struct gconfig *gc; 1053218799Snwhitehorn struct ggeom *geom; 1054218799Snwhitehorn struct gconsumer *cp; 1055218799Snwhitehorn struct gctl_req *r; 1056218799Snwhitehorn const char *errstr; 1057218799Snwhitehorn intmax_t idx; 1058226212Snwhitehorn int is_partition; 1059218799Snwhitehorn 1060218799Snwhitehorn /* Is it a partition? */ 1061218799Snwhitehorn is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); 1062218799Snwhitehorn 1063218799Snwhitehorn /* Find out if this is the root of a gpart geom */ 1064218799Snwhitehorn geom = NULL; 1065218799Snwhitehorn LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1066218799Snwhitehorn if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { 1067218799Snwhitehorn geom = cp->lg_geom; 1068218799Snwhitehorn break; 1069218799Snwhitehorn } 1070218799Snwhitehorn 1071226212Snwhitehorn /* If so, destroy all children */ 1072218799Snwhitehorn if (geom != NULL) { 1073226212Snwhitehorn gpart_destroy(geom); 1074226212Snwhitehorn 1075226212Snwhitehorn /* If this is a partition, revert it, so it can be deleted */ 1076218799Snwhitehorn if (is_partition) { 1077226212Snwhitehorn r = gctl_get_handle(); 1078226212Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 1079226212Snwhitehorn gctl_ro_param(r, "arg0", -1, geom->lg_name); 1080226212Snwhitehorn gctl_ro_param(r, "verb", -1, "undo"); 1081226212Snwhitehorn gctl_issue(r); /* Ignore non-fatal errors */ 1082226212Snwhitehorn gctl_free(r); 1083218799Snwhitehorn } 1084226212Snwhitehorn } 1085218799Snwhitehorn 1086218799Snwhitehorn /* 1087218799Snwhitehorn * If this is not a partition, see if that is a problem, complain if 1088218799Snwhitehorn * necessary, and return always, since we need not do anything further, 1089218799Snwhitehorn * error or no. 1090218799Snwhitehorn */ 1091218799Snwhitehorn if (!is_partition) { 1092218799Snwhitehorn if (geom == NULL) 1093218799Snwhitehorn dialog_msgbox("Error", 1094218799Snwhitehorn "Only partitions can be deleted.", 0, 0, TRUE); 1095218799Snwhitehorn return; 1096218799Snwhitehorn } 1097218799Snwhitehorn 1098218799Snwhitehorn r = gctl_get_handle(); 1099218799Snwhitehorn gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); 1100218799Snwhitehorn gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); 1101218799Snwhitehorn gctl_ro_param(r, "flags", -1, GPART_FLAGS); 1102218799Snwhitehorn gctl_ro_param(r, "verb", -1, "delete"); 1103218799Snwhitehorn 1104218799Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) { 1105218799Snwhitehorn if (strcmp(gc->lg_name, "index") == 0) { 1106218799Snwhitehorn idx = atoi(gc->lg_val); 1107218799Snwhitehorn gctl_ro_param(r, "index", sizeof(idx), &idx); 1108218799Snwhitehorn break; 1109218799Snwhitehorn } 1110218799Snwhitehorn } 1111218799Snwhitehorn 1112218799Snwhitehorn errstr = gctl_issue(r); 1113218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') { 1114218799Snwhitehorn gpart_show_error("Error", NULL, errstr); 1115218799Snwhitehorn gctl_free(r); 1116218799Snwhitehorn return; 1117218799Snwhitehorn } 1118218799Snwhitehorn 1119218799Snwhitehorn gctl_free(r); 1120218799Snwhitehorn 1121218799Snwhitehorn delete_part_metadata(pp->lg_name); 1122218799Snwhitehorn} 1123218799Snwhitehorn 1124218799Snwhitehornvoid 1125218799Snwhitehorngpart_revert_all(struct gmesh *mesh) 1126218799Snwhitehorn{ 1127218799Snwhitehorn struct gclass *classp; 1128218799Snwhitehorn struct gconfig *gc; 1129218799Snwhitehorn struct ggeom *gp; 1130218799Snwhitehorn struct gctl_req *r; 1131218799Snwhitehorn const char *modified; 1132218799Snwhitehorn 1133218799Snwhitehorn LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1134218799Snwhitehorn if (strcmp(classp->lg_name, "PART") == 0) 1135218799Snwhitehorn break; 1136218799Snwhitehorn } 1137218799Snwhitehorn 1138218799Snwhitehorn if (strcmp(classp->lg_name, "PART") != 0) { 1139218799Snwhitehorn dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); 1140218799Snwhitehorn return; 1141218799Snwhitehorn } 1142218799Snwhitehorn 1143218799Snwhitehorn LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1144218799Snwhitehorn modified = "true"; /* XXX: If we don't know (kernel too old), 1145218799Snwhitehorn * assume there are modifications. */ 1146218799Snwhitehorn LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1147218799Snwhitehorn if (strcmp(gc->lg_name, "modified") == 0) { 1148218799Snwhitehorn modified = gc->lg_val; 1149218799Snwhitehorn break; 1150218799Snwhitehorn } 1151218799Snwhitehorn } 1152218799Snwhitehorn 1153218799Snwhitehorn if (strcmp(modified, "false") == 0) 1154218799Snwhitehorn continue; 1155218799Snwhitehorn 1156218799Snwhitehorn r = gctl_get_handle(); 1157218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 1158218799Snwhitehorn gctl_ro_param(r, "arg0", -1, gp->lg_name); 1159218799Snwhitehorn gctl_ro_param(r, "verb", -1, "undo"); 1160218799Snwhitehorn 1161226250Snwhitehorn gctl_issue(r); 1162218799Snwhitehorn gctl_free(r); 1163218799Snwhitehorn } 1164218799Snwhitehorn} 1165218799Snwhitehorn 1166218799Snwhitehornvoid 1167218799Snwhitehorngpart_commit(struct gmesh *mesh) 1168218799Snwhitehorn{ 1169218799Snwhitehorn struct partition_metadata *md; 1170218799Snwhitehorn struct gclass *classp; 1171218799Snwhitehorn struct ggeom *gp; 1172218799Snwhitehorn struct gconfig *gc; 1173218799Snwhitehorn struct gconsumer *cp; 1174218799Snwhitehorn struct gprovider *pp; 1175218799Snwhitehorn struct gctl_req *r; 1176218799Snwhitehorn const char *errstr; 1177218799Snwhitehorn const char *modified; 1178218799Snwhitehorn 1179218799Snwhitehorn LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 1180218799Snwhitehorn if (strcmp(classp->lg_name, "PART") == 0) 1181218799Snwhitehorn break; 1182218799Snwhitehorn } 1183218799Snwhitehorn 1184218799Snwhitehorn if (strcmp(classp->lg_name, "PART") != 0) { 1185218799Snwhitehorn dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); 1186218799Snwhitehorn return; 1187218799Snwhitehorn } 1188218799Snwhitehorn 1189218799Snwhitehorn LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 1190218799Snwhitehorn modified = "true"; /* XXX: If we don't know (kernel too old), 1191218799Snwhitehorn * assume there are modifications. */ 1192218799Snwhitehorn LIST_FOREACH(gc, &gp->lg_config, lg_config) { 1193218799Snwhitehorn if (strcmp(gc->lg_name, "modified") == 0) { 1194218799Snwhitehorn modified = gc->lg_val; 1195218799Snwhitehorn break; 1196218799Snwhitehorn } 1197218799Snwhitehorn } 1198218799Snwhitehorn 1199218799Snwhitehorn if (strcmp(modified, "false") == 0) 1200218799Snwhitehorn continue; 1201218799Snwhitehorn 1202218799Snwhitehorn /* Add bootcode if necessary, before the commit */ 1203218799Snwhitehorn md = get_part_metadata(gp->lg_name, 0); 1204218799Snwhitehorn if (md != NULL && md->bootcode) 1205218799Snwhitehorn gpart_bootcode(gp); 1206218799Snwhitehorn 1207218799Snwhitehorn /* Now install partcode on its partitions, if necessary */ 1208218799Snwhitehorn LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 1209218799Snwhitehorn md = get_part_metadata(pp->lg_name, 0); 1210218799Snwhitehorn if (md == NULL || !md->bootcode) 1211218799Snwhitehorn continue; 1212218799Snwhitehorn 1213218799Snwhitehorn /* Mark this partition active if that's required */ 1214218799Snwhitehorn gpart_activate(pp); 1215218799Snwhitehorn 1216218799Snwhitehorn /* Check if the partition has sub-partitions */ 1217218799Snwhitehorn LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 1218218799Snwhitehorn if (strcmp(cp->lg_geom->lg_class->lg_name, 1219218799Snwhitehorn "PART") == 0) 1220218799Snwhitehorn break; 1221218799Snwhitehorn 1222218799Snwhitehorn if (cp == NULL) /* No sub-partitions */ 1223218799Snwhitehorn gpart_partcode(pp); 1224218799Snwhitehorn } 1225218799Snwhitehorn 1226218799Snwhitehorn r = gctl_get_handle(); 1227218799Snwhitehorn gctl_ro_param(r, "class", -1, "PART"); 1228218799Snwhitehorn gctl_ro_param(r, "arg0", -1, gp->lg_name); 1229218799Snwhitehorn gctl_ro_param(r, "verb", -1, "commit"); 1230218799Snwhitehorn 1231218799Snwhitehorn errstr = gctl_issue(r); 1232218799Snwhitehorn if (errstr != NULL && errstr[0] != '\0') 1233218799Snwhitehorn gpart_show_error("Error", NULL, errstr); 1234218799Snwhitehorn gctl_free(r); 1235218799Snwhitehorn } 1236218799Snwhitehorn} 1237218799Snwhitehorn 1238