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: stable/10/usr.sbin/bsdinstall/partedit/part_wizard.c 321166 2017-07-18 18:54:47Z ngie $ 27218799Snwhitehorn */ 28218799Snwhitehorn 29218799Snwhitehorn#include <sys/param.h> 30321166Sngie#include <sys/sysctl.h> 31218799Snwhitehorn#include <errno.h> 32321166Sngie#include <inttypes.h> 33218799Snwhitehorn#include <libutil.h> 34273831Snwhitehorn#include <string.h> 35273831Snwhitehorn 36218799Snwhitehorn#include <libgeom.h> 37218799Snwhitehorn#include <dialog.h> 38218799Snwhitehorn#include <dlg_keys.h> 39218799Snwhitehorn 40218799Snwhitehorn#include "partedit.h" 41218799Snwhitehorn 42218799Snwhitehorn#define MIN_FREE_SPACE (1024*1024*1024) /* 1 GB */ 43218799Snwhitehorn#define SWAP_SIZE(available) MIN(available/20, 4*1024*1024*1024LL) 44218799Snwhitehorn 45218799Snwhitehornstatic char *boot_disk(struct gmesh *mesh); 46218799Snwhitehornstatic char *wizard_partition(struct gmesh *mesh, const char *disk); 47218799Snwhitehorn 48218799Snwhitehornint 49321166Sngiepart_wizard(const char *fsreq) 50321166Sngie{ 51218799Snwhitehorn char *disk, *schemeroot; 52273831Snwhitehorn const char *fstype; 53321166Sngie struct gmesh mesh; 54321166Sngie int error; 55218799Snwhitehorn 56273831Snwhitehorn if (fsreq != NULL) 57273831Snwhitehorn fstype = fsreq; 58273831Snwhitehorn else 59273831Snwhitehorn fstype = "ufs"; 60273831Snwhitehorn 61218799Snwhitehornstartwizard: 62218799Snwhitehorn error = geom_gettree(&mesh); 63218799Snwhitehorn 64218799Snwhitehorn dlg_put_backtitle(); 65218799Snwhitehorn error = geom_gettree(&mesh); 66218799Snwhitehorn disk = boot_disk(&mesh); 67218799Snwhitehorn if (disk == NULL) 68218799Snwhitehorn return (1); 69218799Snwhitehorn 70218799Snwhitehorn dlg_clear(); 71218799Snwhitehorn dlg_put_backtitle(); 72218799Snwhitehorn schemeroot = wizard_partition(&mesh, disk); 73218799Snwhitehorn free(disk); 74218799Snwhitehorn if (schemeroot == NULL) 75218799Snwhitehorn return (1); 76218799Snwhitehorn 77218799Snwhitehorn geom_deletetree(&mesh); 78218799Snwhitehorn dlg_clear(); 79218799Snwhitehorn dlg_put_backtitle(); 80218799Snwhitehorn error = geom_gettree(&mesh); 81218799Snwhitehorn 82273831Snwhitehorn error = wizard_makeparts(&mesh, schemeroot, fstype, 1); 83218799Snwhitehorn if (error) 84218799Snwhitehorn goto startwizard; 85218799Snwhitehorn free(schemeroot); 86273831Snwhitehorn 87218799Snwhitehorn geom_deletetree(&mesh); 88218799Snwhitehorn 89218799Snwhitehorn return (0); 90218799Snwhitehorn} 91218799Snwhitehorn 92218799Snwhitehornstatic char * 93218799Snwhitehornboot_disk(struct gmesh *mesh) 94218799Snwhitehorn{ 95218799Snwhitehorn struct gclass *classp; 96218799Snwhitehorn struct gconfig *gc; 97218799Snwhitehorn struct ggeom *gp; 98218799Snwhitehorn struct gprovider *pp; 99218799Snwhitehorn DIALOG_LISTITEM *disks = NULL; 100219058Snwhitehorn const char *type, *desc; 101218799Snwhitehorn char diskdesc[512]; 102218799Snwhitehorn char *chosen; 103218799Snwhitehorn int i, err, selected, n = 0; 104218799Snwhitehorn 105218799Snwhitehorn LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 106218799Snwhitehorn if (strcmp(classp->lg_name, "DISK") != 0 && 107225066Snwhitehorn strcmp(classp->lg_name, "RAID") != 0 && 108218799Snwhitehorn strcmp(classp->lg_name, "MD") != 0) 109218799Snwhitehorn continue; 110218799Snwhitehorn 111218799Snwhitehorn LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 112218799Snwhitehorn if (LIST_EMPTY(&gp->lg_provider)) 113218799Snwhitehorn continue; 114218799Snwhitehorn 115218799Snwhitehorn LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 116219058Snwhitehorn desc = type = NULL; 117219058Snwhitehorn LIST_FOREACH(gc, &pp->lg_config, lg_config) { 118273831Snwhitehorn if (strcmp(gc->lg_name, "type") == 0) 119218799Snwhitehorn type = gc->lg_val; 120273831Snwhitehorn if (strcmp(gc->lg_name, "descr") == 0) 121219058Snwhitehorn desc = gc->lg_val; 122219058Snwhitehorn } 123218799Snwhitehorn 124219058Snwhitehorn /* Skip swap-backed md and WORM devices */ 125218799Snwhitehorn if (strcmp(classp->lg_name, "MD") == 0 && 126218799Snwhitehorn type != NULL && strcmp(type, "swap") == 0) 127218799Snwhitehorn continue; 128219058Snwhitehorn if (strncmp(pp->lg_name, "cd", 2) == 0) 129219058Snwhitehorn continue; 130218799Snwhitehorn 131218799Snwhitehorn disks = realloc(disks, (++n)*sizeof(disks[0])); 132218799Snwhitehorn disks[n-1].name = pp->lg_name; 133218799Snwhitehorn humanize_number(diskdesc, 7, pp->lg_mediasize, 134218799Snwhitehorn "B", HN_AUTOSCALE, HN_DECIMAL); 135218799Snwhitehorn if (strncmp(pp->lg_name, "ad", 2) == 0) 136218799Snwhitehorn strcat(diskdesc, " ATA Hard Disk"); 137218799Snwhitehorn else if (strncmp(pp->lg_name, "md", 2) == 0) 138218799Snwhitehorn strcat(diskdesc, " Memory Disk"); 139219058Snwhitehorn else 140219058Snwhitehorn strcat(diskdesc, " Disk"); 141219058Snwhitehorn 142219058Snwhitehorn if (desc != NULL) 143219058Snwhitehorn snprintf(diskdesc, sizeof(diskdesc), 144219058Snwhitehorn "%s <%s>", diskdesc, desc); 145219058Snwhitehorn 146218799Snwhitehorn disks[n-1].text = strdup(diskdesc); 147218799Snwhitehorn disks[n-1].help = NULL; 148218799Snwhitehorn disks[n-1].state = 0; 149218799Snwhitehorn } 150218799Snwhitehorn } 151218799Snwhitehorn } 152218799Snwhitehorn 153218799Snwhitehorn if (n > 1) { 154218799Snwhitehorn err = dlg_menu("Partitioning", 155218799Snwhitehorn "Select the disk on which to install FreeBSD.", 0, 0, 0, 156218799Snwhitehorn n, disks, &selected, NULL); 157218799Snwhitehorn 158218799Snwhitehorn chosen = (err == 0) ? strdup(disks[selected].name) : NULL; 159218799Snwhitehorn } else if (n == 1) { 160218799Snwhitehorn chosen = strdup(disks[0].name); 161218799Snwhitehorn } else { 162218799Snwhitehorn chosen = NULL; 163218799Snwhitehorn } 164218799Snwhitehorn 165218799Snwhitehorn for (i = 0; i < n; i++) 166218799Snwhitehorn free(disks[i].text); 167218799Snwhitehorn 168218799Snwhitehorn return (chosen); 169218799Snwhitehorn} 170218799Snwhitehorn 171218799Snwhitehornstatic struct gprovider * 172218799Snwhitehornprovider_for_name(struct gmesh *mesh, const char *name) 173218799Snwhitehorn{ 174218799Snwhitehorn struct gclass *classp; 175218799Snwhitehorn struct gprovider *pp = NULL; 176218799Snwhitehorn struct ggeom *gp; 177218799Snwhitehorn 178218799Snwhitehorn LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 179218799Snwhitehorn LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 180218799Snwhitehorn if (LIST_EMPTY(&gp->lg_provider)) 181218799Snwhitehorn continue; 182218799Snwhitehorn 183218799Snwhitehorn LIST_FOREACH(pp, &gp->lg_provider, lg_provider) 184218799Snwhitehorn if (strcmp(pp->lg_name, name) == 0) 185218799Snwhitehorn break; 186218799Snwhitehorn 187218799Snwhitehorn if (pp != NULL) break; 188218799Snwhitehorn } 189218799Snwhitehorn 190218799Snwhitehorn if (pp != NULL) break; 191218799Snwhitehorn } 192218799Snwhitehorn 193218799Snwhitehorn return (pp); 194218799Snwhitehorn} 195218799Snwhitehorn 196218799Snwhitehornstatic char * 197218799Snwhitehornwizard_partition(struct gmesh *mesh, const char *disk) 198218799Snwhitehorn{ 199218799Snwhitehorn struct gclass *classp; 200218799Snwhitehorn struct ggeom *gpart = NULL; 201218799Snwhitehorn struct gconfig *gc; 202321166Sngie char *retval = NULL; 203321166Sngie const char *scheme = NULL; 204218799Snwhitehorn char message[512]; 205218799Snwhitehorn int choice; 206218799Snwhitehorn 207218799Snwhitehorn LIST_FOREACH(classp, &mesh->lg_class, lg_class) 208218799Snwhitehorn if (strcmp(classp->lg_name, "PART") == 0) 209218799Snwhitehorn break; 210218799Snwhitehorn 211218799Snwhitehorn if (classp != NULL) { 212273831Snwhitehorn LIST_FOREACH(gpart, &classp->lg_geom, lg_geom) 213218799Snwhitehorn if (strcmp(gpart->lg_name, disk) == 0) 214218799Snwhitehorn break; 215218799Snwhitehorn } 216218799Snwhitehorn 217218799Snwhitehorn if (gpart != NULL) { 218218799Snwhitehorn LIST_FOREACH(gc, &gpart->lg_config, lg_config) { 219218799Snwhitehorn if (strcmp(gc->lg_name, "scheme") == 0) { 220218799Snwhitehorn scheme = gc->lg_val; 221218799Snwhitehorn break; 222218799Snwhitehorn } 223218799Snwhitehorn } 224218799Snwhitehorn } 225218799Snwhitehorn 226218855Snwhitehorn /* Treat uncommitted scheme deletions as no scheme */ 227218855Snwhitehorn if (scheme != NULL && strcmp(scheme, "(none)") == 0) 228218855Snwhitehorn scheme = NULL; 229218855Snwhitehorn 230218799Snwhitehornquery: 231218799Snwhitehorn dialog_vars.yes_label = "Entire Disk"; 232218799Snwhitehorn dialog_vars.no_label = "Partition"; 233218799Snwhitehorn if (gpart != NULL) 234218799Snwhitehorn dialog_vars.defaultno = TRUE; 235218799Snwhitehorn 236218799Snwhitehorn snprintf(message, sizeof(message), "Would you like to use this entire " 237218799Snwhitehorn "disk (%s) for FreeBSD or partition it to share it with other " 238218799Snwhitehorn "operating systems? Using the entire disk will erase any data " 239218799Snwhitehorn "currently stored there.", disk); 240218799Snwhitehorn choice = dialog_yesno("Partition", message, 0, 0); 241218799Snwhitehorn 242218799Snwhitehorn dialog_vars.yes_label = NULL; 243218799Snwhitehorn dialog_vars.no_label = NULL; 244218799Snwhitehorn dialog_vars.defaultno = FALSE; 245218799Snwhitehorn 246218799Snwhitehorn if (choice == 1 && scheme != NULL && !is_scheme_bootable(scheme)) { 247218799Snwhitehorn char warning[512]; 248218799Snwhitehorn int subchoice; 249218799Snwhitehorn 250218799Snwhitehorn sprintf(warning, "The existing partition scheme on this " 251218799Snwhitehorn "disk (%s) is not bootable on this platform. To install " 252218799Snwhitehorn "FreeBSD, it must be repartitioned. This will destroy all " 253218799Snwhitehorn "data on the disk. Are you sure you want to proceed?", 254218799Snwhitehorn scheme); 255218799Snwhitehorn subchoice = dialog_yesno("Non-bootable Disk", warning, 0, 0); 256218799Snwhitehorn if (subchoice != 0) 257218799Snwhitehorn goto query; 258218799Snwhitehorn 259226083Snwhitehorn gpart_destroy(gpart); 260285769Sallanjude scheme = choose_part_type(default_scheme()); 261285769Sallanjude if (scheme == NULL) 262285769Sallanjude return NULL; 263285769Sallanjude gpart_partition(disk, scheme); 264218799Snwhitehorn } 265218799Snwhitehorn 266218855Snwhitehorn if (scheme == NULL || choice == 0) { 267218855Snwhitehorn if (gpart != NULL && scheme != NULL) { 268218855Snwhitehorn /* Erase partitioned disk */ 269218799Snwhitehorn choice = dialog_yesno("Confirmation", "This will erase " 270218799Snwhitehorn "the disk. Are you sure you want to proceed?", 0, 0); 271218799Snwhitehorn if (choice != 0) 272218799Snwhitehorn goto query; 273218799Snwhitehorn 274226083Snwhitehorn gpart_destroy(gpart); 275218799Snwhitehorn } 276218799Snwhitehorn 277285769Sallanjude scheme = choose_part_type(default_scheme()); 278285769Sallanjude if (scheme == NULL) 279285769Sallanjude return NULL; 280285769Sallanjude gpart_partition(disk, scheme); 281218799Snwhitehorn } 282218799Snwhitehorn 283218799Snwhitehorn if (strcmp(scheme, "PC98") == 0 || strcmp(scheme, "MBR") == 0) { 284218799Snwhitehorn struct gmesh submesh; 285218799Snwhitehorn geom_gettree(&submesh); 286218799Snwhitehorn gpart_create(provider_for_name(&submesh, disk), 287218799Snwhitehorn "freebsd", NULL, NULL, &retval, 288218799Snwhitehorn choice /* Non-interactive for "Entire Disk" */); 289218799Snwhitehorn geom_deletetree(&submesh); 290218799Snwhitehorn } else { 291218799Snwhitehorn retval = strdup(disk); 292218799Snwhitehorn } 293218799Snwhitehorn 294218799Snwhitehorn return (retval); 295218799Snwhitehorn} 296218799Snwhitehorn 297245701Snwhitehornint 298321166Sngiewizard_makeparts(struct gmesh *mesh, const char *disk, const char *fstype, 299321166Sngie int interactive) 300218799Snwhitehorn{ 301218799Snwhitehorn struct gclass *classp; 302218799Snwhitehorn struct ggeom *gp; 303218799Snwhitehorn struct gprovider *pp; 304321166Sngie char *fsnames[] = {"freebsd-ufs", "freebsd-zfs"}; 305321166Sngie char *fsname; 306321166Sngie struct gmesh submesh; 307321166Sngie char swapsizestr[10], rootsizestr[10]; 308218799Snwhitehorn intmax_t swapsize, available; 309218799Snwhitehorn int retval; 310218799Snwhitehorn 311273831Snwhitehorn if (strcmp(fstype, "zfs") == 0) { 312273831Snwhitehorn fsname = fsnames[1]; 313273831Snwhitehorn } else { 314273831Snwhitehorn /* default to UFS */ 315273831Snwhitehorn fsname = fsnames[0]; 316273831Snwhitehorn } 317273831Snwhitehorn 318218799Snwhitehorn LIST_FOREACH(classp, &mesh->lg_class, lg_class) 319218799Snwhitehorn if (strcmp(classp->lg_name, "PART") == 0) 320218799Snwhitehorn break; 321218799Snwhitehorn 322273831Snwhitehorn LIST_FOREACH(gp, &classp->lg_geom, lg_geom) 323218799Snwhitehorn if (strcmp(gp->lg_name, disk) == 0) 324218799Snwhitehorn break; 325218799Snwhitehorn 326218799Snwhitehorn pp = provider_for_name(mesh, disk); 327218799Snwhitehorn 328218799Snwhitehorn available = gpart_max_free(gp, NULL)*pp->lg_sectorsize; 329245701Snwhitehorn if (interactive && available < MIN_FREE_SPACE) { 330218799Snwhitehorn char availablestr[10], neededstr[10], message[512]; 331218799Snwhitehorn humanize_number(availablestr, 7, available, "B", HN_AUTOSCALE, 332218799Snwhitehorn HN_DECIMAL); 333218799Snwhitehorn humanize_number(neededstr, 7, MIN_FREE_SPACE, "B", HN_AUTOSCALE, 334218799Snwhitehorn HN_DECIMAL); 335218799Snwhitehorn sprintf(message, "There is not enough free space on %s to " 336218799Snwhitehorn "install FreeBSD (%s free, %s required). Would you like " 337218799Snwhitehorn "to choose another disk or to open the partition editor?", 338218799Snwhitehorn disk, availablestr, neededstr); 339218799Snwhitehorn 340218799Snwhitehorn dialog_vars.yes_label = "Another Disk"; 341218799Snwhitehorn dialog_vars.no_label = "Editor"; 342218799Snwhitehorn retval = dialog_yesno("Warning", message, 0, 0); 343218799Snwhitehorn dialog_vars.yes_label = NULL; 344218799Snwhitehorn dialog_vars.no_label = NULL; 345218799Snwhitehorn 346218799Snwhitehorn return (!retval); /* Editor -> return 0 */ 347218799Snwhitehorn } 348218799Snwhitehorn 349218799Snwhitehorn swapsize = SWAP_SIZE(available); 350218799Snwhitehorn humanize_number(swapsizestr, 7, swapsize, "B", HN_AUTOSCALE, 351218799Snwhitehorn HN_NOSPACE | HN_DECIMAL); 352218799Snwhitehorn humanize_number(rootsizestr, 7, available - swapsize - 1024*1024, 353218799Snwhitehorn "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); 354218799Snwhitehorn 355218799Snwhitehorn geom_gettree(&submesh); 356218799Snwhitehorn pp = provider_for_name(&submesh, disk); 357273831Snwhitehorn gpart_create(pp, fsname, rootsizestr, "/", NULL, 0); 358218799Snwhitehorn geom_deletetree(&submesh); 359218799Snwhitehorn 360218799Snwhitehorn geom_gettree(&submesh); 361218799Snwhitehorn pp = provider_for_name(&submesh, disk); 362218799Snwhitehorn gpart_create(pp, "freebsd-swap", swapsizestr, NULL, NULL, 0); 363218799Snwhitehorn geom_deletetree(&submesh); 364218799Snwhitehorn 365218799Snwhitehorn return (0); 366218799Snwhitehorn} 367