1 /*- 2 * Copyright (c) 2011 Nathan Whitehorn 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <libgen.h> 31 #include <libutil.h> 32 #include <inttypes.h> 33 #include <errno.h> 34 35 #include <fstab.h> 36 #include <libgeom.h> 37 #include <dialog.h> 38 #include <dlg_keys.h> 39 40 #include "diskeditor.h" 41 #include "partedit.h" 42 43 struct pmetadata_head part_metadata; 44 static int sade_mode = 0; 45 46 static int apply_changes(struct gmesh *mesh); 47 static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems); 48 static void add_geom_children(struct ggeom *gp, int recurse, 49 struct partedit_item **items, int *nitems); 50 static void init_fstab_metadata(void); 51 static void get_mount_points(struct partedit_item *items, int nitems); 52 static int validate_setup(void); 53 54 static void 55 sigint_handler(int sig) 56 { 57 struct gmesh mesh; 58 59 /* Revert all changes and exit dialog-mode cleanly on SIGINT */ 60 geom_gettree(&mesh); 61 gpart_revert_all(&mesh); 62 geom_deletetree(&mesh); 63 64 end_dialog(); 65 66 exit(1); 67 } 68 69 int 70 main(int argc, const char **argv) 71 { 72 struct partition_metadata *md; 73 const char *prompt; 74 struct partedit_item *items = NULL; 75 struct gmesh mesh; 76 int i, op, nitems, nscroll; 77 int error; 78 79 if (strcmp(basename(argv[0]), "sade") == 0) 80 sade_mode = 1; 81 82 TAILQ_INIT(&part_metadata); 83 84 init_fstab_metadata(); 85 86 init_dialog(stdin, stdout); 87 if (!sade_mode) 88 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); 89 dialog_vars.item_help = TRUE; 90 nscroll = i = 0; 91 92 /* Revert changes on SIGINT */ 93 signal(SIGINT, sigint_handler); 94 95 if (strcmp(basename(argv[0]), "autopart") == 0) { /* Guided */ 96 prompt = "Please review the disk setup. When complete, press " 97 "the Finish button."; 98 part_wizard(); 99 } else if (strcmp(basename(argv[0]), "scriptedpart") == 0) { 100 error = scripted_editor(argc, argv); 101 prompt = NULL; 102 if (error != 0) { 103 end_dialog(); 104 return (error); 105 } 106 } else { 107 prompt = "Create partitions for FreeBSD. No changes will be " 108 "made until you select Finish."; 109 } 110 111 /* Show the part editor either immediately, or to confirm wizard */ 112 while (prompt != NULL) { 113 dlg_clear(); 114 dlg_put_backtitle(); 115 116 error = geom_gettree(&mesh); 117 if (error == 0) 118 items = read_geom_mesh(&mesh, &nitems); 119 if (error || items == NULL) { 120 dialog_msgbox("Error", "No disks found. If you need to " 121 "install a kernel driver, choose Shell at the " 122 "installation menu.", 0, 0, TRUE); 123 break; 124 } 125 126 get_mount_points(items, nitems); 127 128 if (i >= nitems) 129 i = nitems - 1; 130 op = diskeditor_show("Partition Editor", prompt, 131 items, nitems, &i, &nscroll); 132 133 switch (op) { 134 case 0: /* Create */ 135 gpart_create((struct gprovider *)(items[i].cookie), 136 NULL, NULL, NULL, NULL, 1); 137 break; 138 case 1: /* Delete */ 139 gpart_delete((struct gprovider *)(items[i].cookie)); 140 break; 141 case 2: /* Modify */ 142 gpart_edit((struct gprovider *)(items[i].cookie)); 143 break; 144 case 3: /* Revert */ 145 gpart_revert_all(&mesh); 146 while ((md = TAILQ_FIRST(&part_metadata)) != NULL) { 147 if (md->fstab != NULL) { 148 free(md->fstab->fs_spec); 149 free(md->fstab->fs_file); 150 free(md->fstab->fs_vfstype); 151 free(md->fstab->fs_mntops); 152 free(md->fstab->fs_type); 153 free(md->fstab); 154 } 155 if (md->newfs != NULL) 156 free(md->newfs); 157 free(md->name); 158 159 TAILQ_REMOVE(&part_metadata, md, metadata); 160 free(md); 161 } 162 init_fstab_metadata(); 163 break; 164 case 4: /* Auto */ 165 part_wizard(); 166 break; 167 } 168 169 error = 0; 170 if (op == 5) { /* Finished */ 171 dialog_vars.ok_label = __DECONST(char *, "Commit"); 172 dialog_vars.extra_label = 173 __DECONST(char *, "Revert & Exit"); 174 dialog_vars.extra_button = TRUE; 175 dialog_vars.cancel_label = __DECONST(char *, "Back"); 176 op = dialog_yesno("Confirmation", "Your changes will " 177 "now be written to disk. If you have chosen to " 178 "overwrite existing data, it will be PERMANENTLY " 179 "ERASED. Are you sure you want to commit your " 180 "changes?", 0, 0); 181 dialog_vars.ok_label = NULL; 182 dialog_vars.extra_button = FALSE; 183 dialog_vars.cancel_label = NULL; 184 185 if (op == 0 && validate_setup()) { /* Save */ 186 error = apply_changes(&mesh); 187 break; 188 } else if (op == 3) { /* Quit */ 189 gpart_revert_all(&mesh); 190 error = -1; 191 break; 192 } 193 } 194 195 geom_deletetree(&mesh); 196 free(items); 197 } 198 199 if (prompt == NULL) { 200 error = geom_gettree(&mesh); 201 if (validate_setup()) { 202 error = apply_changes(&mesh); 203 } else { 204 gpart_revert_all(&mesh); 205 error = -1; 206 } 207 } 208 209 geom_deletetree(&mesh); 210 free(items); 211 end_dialog(); 212 213 return (error); 214 } 215 216 struct partition_metadata * 217 get_part_metadata(const char *name, int create) 218 { 219 struct partition_metadata *md; 220 221 TAILQ_FOREACH(md, &part_metadata, metadata) 222 if (md->name != NULL && strcmp(md->name, name) == 0) 223 break; 224 225 if (md == NULL && create) { 226 md = calloc(1, sizeof(*md)); 227 md->name = strdup(name); 228 TAILQ_INSERT_TAIL(&part_metadata, md, metadata); 229 } 230 231 return (md); 232 } 233 234 void 235 delete_part_metadata(const char *name) 236 { 237 struct partition_metadata *md; 238 239 TAILQ_FOREACH(md, &part_metadata, metadata) { 240 if (md->name != NULL && strcmp(md->name, name) == 0) { 241 if (md->fstab != NULL) { 242 free(md->fstab->fs_spec); 243 free(md->fstab->fs_file); 244 free(md->fstab->fs_vfstype); 245 free(md->fstab->fs_mntops); 246 free(md->fstab->fs_type); 247 free(md->fstab); 248 } 249 if (md->newfs != NULL) 250 free(md->newfs); 251 free(md->name); 252 253 TAILQ_REMOVE(&part_metadata, md, metadata); 254 free(md); 255 break; 256 } 257 } 258 } 259 260 static int 261 validate_setup(void) 262 { 263 struct partition_metadata *md, *root = NULL; 264 int cancel; 265 266 TAILQ_FOREACH(md, &part_metadata, metadata) { 267 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) 268 root = md; 269 270 /* XXX: Check for duplicate mountpoints */ 271 } 272 273 if (root == NULL) { 274 dialog_msgbox("Error", "No root partition was found. " 275 "The root FreeBSD partition must have a mountpoint of '/'.", 276 0, 0, TRUE); 277 return (FALSE); 278 } 279 280 /* 281 * Check for root partitions that we aren't formatting, which is 282 * usually a mistake 283 */ 284 if (root->newfs == NULL && !sade_mode) { 285 dialog_vars.defaultno = TRUE; 286 cancel = dialog_yesno("Warning", "The chosen root partition " 287 "has a preexisting filesystem. If it contains an existing " 288 "FreeBSD system, please update it with freebsd-update " 289 "instead of installing a new system on it. The partition " 290 "can also be erased by pressing \"No\" and then deleting " 291 "and recreating it. Are you sure you want to proceed?", 292 0, 0); 293 dialog_vars.defaultno = FALSE; 294 if (cancel) 295 return (FALSE); 296 } 297 298 return (TRUE); 299 } 300 301 static int 302 apply_changes(struct gmesh *mesh) 303 { 304 struct partition_metadata *md; 305 char message[512]; 306 int i, nitems, error; 307 const char **items; 308 const char *fstab_path; 309 FILE *fstab; 310 311 nitems = 1; /* Partition table changes */ 312 TAILQ_FOREACH(md, &part_metadata, metadata) { 313 if (md->newfs != NULL) 314 nitems++; 315 } 316 items = calloc(nitems * 2, sizeof(const char *)); 317 items[0] = "Writing partition tables"; 318 items[1] = "7"; /* In progress */ 319 i = 1; 320 TAILQ_FOREACH(md, &part_metadata, metadata) { 321 if (md->newfs != NULL) { 322 char *item; 323 item = malloc(255); 324 sprintf(item, "Initializing %s", md->name); 325 items[i*2] = item; 326 items[i*2 + 1] = "Pending"; 327 i++; 328 } 329 } 330 331 i = 0; 332 dialog_mixedgauge("Initializing", 333 "Initializing file systems. Please wait.", 0, 0, i*100/nitems, 334 nitems, __DECONST(char **, items)); 335 gpart_commit(mesh); 336 items[i*2 + 1] = "3"; 337 i++; 338 339 if (getenv("BSDINSTALL_LOG") == NULL) 340 setenv("BSDINSTALL_LOG", "/dev/null", 1); 341 342 TAILQ_FOREACH(md, &part_metadata, metadata) { 343 if (md->newfs != NULL) { 344 items[i*2 + 1] = "7"; /* In progress */ 345 dialog_mixedgauge("Initializing", 346 "Initializing file systems. Please wait.", 0, 0, 347 i*100/nitems, nitems, __DECONST(char **, items)); 348 sprintf(message, "(echo %s; %s) >>%s 2>>%s", 349 md->newfs, md->newfs, getenv("BSDINSTALL_LOG"), 350 getenv("BSDINSTALL_LOG")); 351 error = system(message); 352 items[i*2 + 1] = (error == 0) ? "3" : "1"; 353 i++; 354 } 355 } 356 dialog_mixedgauge("Initializing", 357 "Initializing file systems. Please wait.", 0, 0, 358 i*100/nitems, nitems, __DECONST(char **, items)); 359 360 for (i = 1; i < nitems; i++) 361 free(__DECONST(char *, items[i*2])); 362 free(items); 363 364 if (getenv("PATH_FSTAB") != NULL) 365 fstab_path = getenv("PATH_FSTAB"); 366 else 367 fstab_path = "/etc/fstab"; 368 fstab = fopen(fstab_path, "w+"); 369 if (fstab == NULL) { 370 sprintf(message, "Cannot open fstab file %s for writing (%s)\n", 371 getenv("PATH_FSTAB"), strerror(errno)); 372 dialog_msgbox("Error", message, 0, 0, TRUE); 373 return (-1); 374 } 375 fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n"); 376 TAILQ_FOREACH(md, &part_metadata, metadata) { 377 if (md->fstab != NULL) 378 fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n", 379 md->fstab->fs_spec, md->fstab->fs_file, 380 md->fstab->fs_vfstype, md->fstab->fs_mntops, 381 md->fstab->fs_freq, md->fstab->fs_passno); 382 } 383 fclose(fstab); 384 385 return (0); 386 } 387 388 static struct partedit_item * 389 read_geom_mesh(struct gmesh *mesh, int *nitems) 390 { 391 struct gclass *classp; 392 struct ggeom *gp; 393 struct partedit_item *items; 394 395 *nitems = 0; 396 items = NULL; 397 398 /* 399 * Build the device table. First add all disks (and CDs). 400 */ 401 402 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 403 if (strcmp(classp->lg_name, "DISK") != 0 && 404 strcmp(classp->lg_name, "MD") != 0) 405 continue; 406 407 /* Now recurse into all children */ 408 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) 409 add_geom_children(gp, 0, &items, nitems); 410 } 411 412 return (items); 413 } 414 415 static void 416 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items, 417 int *nitems) 418 { 419 struct gconsumer *cp; 420 struct gprovider *pp; 421 struct gconfig *gc; 422 423 if (strcmp(gp->lg_class->lg_name, "PART") == 0 && 424 !LIST_EMPTY(&gp->lg_config)) { 425 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 426 if (strcmp(gc->lg_name, "scheme") == 0) 427 (*items)[*nitems-1].type = gc->lg_val; 428 } 429 } 430 431 if (LIST_EMPTY(&gp->lg_provider)) 432 return; 433 434 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 435 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0) 436 continue; 437 438 /* Skip WORM media */ 439 if (strncmp(pp->lg_name, "cd", 2) == 0) 440 continue; 441 442 *items = realloc(*items, 443 (*nitems+1)*sizeof(struct partedit_item)); 444 (*items)[*nitems].indentation = recurse; 445 (*items)[*nitems].name = pp->lg_name; 446 (*items)[*nitems].size = pp->lg_mediasize; 447 (*items)[*nitems].mountpoint = NULL; 448 (*items)[*nitems].type = ""; 449 (*items)[*nitems].cookie = pp; 450 451 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 452 if (strcmp(gc->lg_name, "type") == 0) 453 (*items)[*nitems].type = gc->lg_val; 454 } 455 456 /* Skip swap-backed MD devices */ 457 if (strcmp(gp->lg_class->lg_name, "MD") == 0 && 458 strcmp((*items)[*nitems].type, "swap") == 0) 459 continue; 460 461 (*nitems)++; 462 463 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 464 add_geom_children(cp->lg_geom, recurse+1, items, 465 nitems); 466 467 /* Only use first provider for acd */ 468 if (strcmp(gp->lg_class->lg_name, "ACD") == 0) 469 break; 470 } 471 } 472 473 static void 474 init_fstab_metadata(void) 475 { 476 struct fstab *fstab; 477 struct partition_metadata *md; 478 479 setfsent(); 480 while ((fstab = getfsent()) != NULL) { 481 md = calloc(1, sizeof(struct partition_metadata)); 482 483 md->name = NULL; 484 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0) 485 md->name = strdup(&fstab->fs_spec[5]); 486 487 md->fstab = malloc(sizeof(struct fstab)); 488 md->fstab->fs_spec = strdup(fstab->fs_spec); 489 md->fstab->fs_file = strdup(fstab->fs_file); 490 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype); 491 md->fstab->fs_mntops = strdup(fstab->fs_mntops); 492 md->fstab->fs_type = strdup(fstab->fs_type); 493 md->fstab->fs_freq = fstab->fs_freq; 494 md->fstab->fs_passno = fstab->fs_passno; 495 496 md->newfs = NULL; 497 498 TAILQ_INSERT_TAIL(&part_metadata, md, metadata); 499 } 500 } 501 502 static void 503 get_mount_points(struct partedit_item *items, int nitems) 504 { 505 struct partition_metadata *md; 506 int i; 507 508 for (i = 0; i < nitems; i++) { 509 TAILQ_FOREACH(md, &part_metadata, metadata) { 510 if (md->name != NULL && md->fstab != NULL && 511 strcmp(md->name, items[i].name) == 0) { 512 items[i].mountpoint = md->fstab->fs_file; 513 break; 514 } 515 } 516 } 517 } 518