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 45 static int apply_changes(struct gmesh *mesh); 46 static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems); 47 static void add_geom_children(struct ggeom *gp, int recurse, 48 struct partedit_item **items, int *nitems); 49 static void init_fstab_metadata(void); 50 static void get_mount_points(struct partedit_item *items, int nitems); 51 static int validate_setup(void); 52 53 static void 54 sigint_handler(int sig) 55 { 56 struct gmesh mesh; 57 58 /* Revert all changes and exit dialog-mode cleanly on SIGINT */ 59 geom_gettree(&mesh); 60 gpart_revert_all(&mesh); 61 geom_deletetree(&mesh); 62 63 end_dialog(); 64 65 exit(1); 66 } 67 68 int 69 main(int argc, const char **argv) 70 { 71 struct partition_metadata *md; 72 const char *prompt; 73 struct partedit_item *items; 74 struct gmesh mesh; 75 int i, op, nitems, nscroll; 76 int error; 77 78 TAILQ_INIT(&part_metadata); 79 80 init_fstab_metadata(); 81 82 init_dialog(stdin, stdout); 83 if (strcmp(basename(argv[0]), "sade") != 0) 84 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); 85 dialog_vars.item_help = TRUE; 86 nscroll = i = 0; 87 88 /* Revert changes on SIGINT */ 89 signal(SIGINT, sigint_handler); 90 91 if (strcmp(basename(argv[0]), "autopart") == 0) { /* Guided */ 92 prompt = "Please review the disk setup. When complete, press " 93 "the Finish button."; 94 part_wizard(); 95 } else { 96 prompt = "Create partitions for FreeBSD. No changes will be " 97 "made until you select Finish."; 98 } 99 100 /* Show the part editor either immediately, or to confirm wizard */ 101 while (1) { 102 error = geom_gettree(&mesh); 103 items = read_geom_mesh(&mesh, &nitems); 104 get_mount_points(items, nitems); 105 dlg_clear(); 106 dlg_put_backtitle(); 107 108 if (i >= nitems) 109 i = nitems - 1; 110 op = diskeditor_show("Partition Editor", prompt, 111 items, nitems, &i, &nscroll); 112 113 switch (op) { 114 case 0: /* Create */ 115 gpart_create((struct gprovider *)(items[i].cookie), 116 NULL, NULL, NULL, NULL, 1); 117 break; 118 case 1: /* Delete */ 119 gpart_delete((struct gprovider *)(items[i].cookie)); 120 break; 121 case 2: /* Modify */ 122 gpart_edit((struct gprovider *)(items[i].cookie)); 123 break; 124 case 3: /* Revert */ 125 gpart_revert_all(&mesh); 126 while ((md = TAILQ_FIRST(&part_metadata)) != NULL) { 127 if (md->fstab != NULL) { 128 free(md->fstab->fs_spec); 129 free(md->fstab->fs_file); 130 free(md->fstab->fs_vfstype); 131 free(md->fstab->fs_mntops); 132 free(md->fstab->fs_type); 133 free(md->fstab); 134 } 135 if (md->newfs != NULL) 136 free(md->newfs); 137 free(md->name); 138 139 TAILQ_REMOVE(&part_metadata, md, metadata); 140 free(md); 141 } 142 init_fstab_metadata(); 143 break; 144 case 4: /* Auto */ 145 part_wizard(); 146 break; 147 } 148 149 error = 0; 150 if (op == 5) { /* Finished */ 151 dialog_vars.ok_label = __DECONST(char *, "Commit"); 152 dialog_vars.extra_label = 153 __DECONST(char *, "Revert & Exit"); 154 dialog_vars.extra_button = TRUE; 155 dialog_vars.cancel_label = __DECONST(char *, "Back"); 156 op = dialog_yesno("Confirmation", "Your changes will " 157 "now be written to disk. If you have chosen to " 158 "overwrite existing data, it will be PERMANENTLY " 159 "ERASED. Are you sure you want to commit your " 160 "changes?", 0, 0); 161 dialog_vars.ok_label = NULL; 162 dialog_vars.extra_button = FALSE; 163 dialog_vars.cancel_label = NULL; 164 165 if (op == 0 && validate_setup()) { /* Save */ 166 error = apply_changes(&mesh); 167 break; 168 } else if (op == 3) { /* Quit */ 169 gpart_revert_all(&mesh); 170 error = -1; 171 break; 172 } 173 } 174 175 geom_deletetree(&mesh); 176 free(items); 177 } 178 179 180 geom_deletetree(&mesh); 181 free(items); 182 end_dialog(); 183 184 return (error); 185 } 186 187 struct partition_metadata * 188 get_part_metadata(const char *name, int create) 189 { 190 struct partition_metadata *md; 191 192 TAILQ_FOREACH(md, &part_metadata, metadata) 193 if (md->name != NULL && strcmp(md->name, name) == 0) 194 break; 195 196 if (md == NULL && create) { 197 md = calloc(1, sizeof(*md)); 198 md->name = strdup(name); 199 TAILQ_INSERT_TAIL(&part_metadata, md, metadata); 200 } 201 202 return (md); 203 } 204 205 void 206 delete_part_metadata(const char *name) 207 { 208 struct partition_metadata *md; 209 210 TAILQ_FOREACH(md, &part_metadata, metadata) { 211 if (md->name != NULL && strcmp(md->name, name) == 0) { 212 if (md->fstab != NULL) { 213 free(md->fstab->fs_spec); 214 free(md->fstab->fs_file); 215 free(md->fstab->fs_vfstype); 216 free(md->fstab->fs_mntops); 217 free(md->fstab->fs_type); 218 free(md->fstab); 219 } 220 if (md->newfs != NULL) 221 free(md->newfs); 222 free(md->name); 223 224 TAILQ_REMOVE(&part_metadata, md, metadata); 225 free(md); 226 break; 227 } 228 } 229 } 230 231 static int 232 validate_setup(void) 233 { 234 struct partition_metadata *md; 235 int root_found = FALSE; 236 237 TAILQ_FOREACH(md, &part_metadata, metadata) { 238 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) 239 root_found = TRUE; 240 241 /* XXX: Check for duplicate mountpoints */ 242 } 243 244 if (!root_found) { 245 dialog_msgbox("Error", "No root partition was found. " 246 "The root FreeBSD partition must have a mountpoint of '/'.", 247 0, 0, TRUE); 248 return (FALSE); 249 } 250 251 return (TRUE); 252 } 253 254 static int 255 apply_changes(struct gmesh *mesh) 256 { 257 struct partition_metadata *md; 258 char message[512]; 259 int i, nitems, error; 260 const char **items; 261 const char *fstab_path; 262 FILE *fstab; 263 264 nitems = 1; /* Partition table changes */ 265 TAILQ_FOREACH(md, &part_metadata, metadata) { 266 if (md->newfs != NULL) 267 nitems++; 268 } 269 items = calloc(nitems * 2, sizeof(const char *)); 270 items[0] = "Writing partition tables"; 271 items[1] = "7"; /* In progress */ 272 i = 1; 273 TAILQ_FOREACH(md, &part_metadata, metadata) { 274 if (md->newfs != NULL) { 275 char *item; 276 item = malloc(255); 277 sprintf(item, "Initializing %s", md->name); 278 items[i*2] = item; 279 items[i*2 + 1] = "Pending"; 280 i++; 281 } 282 } 283 284 i = 0; 285 dialog_mixedgauge("Initializing", 286 "Initializing file systems. Please wait.", 0, 0, i*100/nitems, 287 nitems, __DECONST(char **, items)); 288 gpart_commit(mesh); 289 items[i*2 + 1] = "3"; 290 i++; 291 292 if (getenv("BSDINSTALL_LOG") == NULL) 293 setenv("BSDINSTALL_LOG", "/dev/null", 1); 294 295 TAILQ_FOREACH(md, &part_metadata, metadata) { 296 if (md->newfs != NULL) { 297 items[i*2 + 1] = "7"; /* In progress */ 298 dialog_mixedgauge("Initializing", 299 "Initializing file systems. Please wait.", 0, 0, 300 i*100/nitems, nitems, __DECONST(char **, items)); 301 sprintf(message, "(echo %s; %s) >>%s 2>>%s", 302 md->newfs, md->newfs, getenv("BSDINSTALL_LOG"), 303 getenv("BSDINSTALL_LOG")); 304 error = system(message); 305 items[i*2 + 1] = (error == 0) ? "3" : "1"; 306 i++; 307 } 308 } 309 dialog_mixedgauge("Initializing", 310 "Initializing file systems. Please wait.", 0, 0, 311 i*100/nitems, nitems, __DECONST(char **, items)); 312 313 for (i = 1; i < nitems; i++) 314 free(__DECONST(char *, items[i*2])); 315 free(items); 316 317 if (getenv("PATH_FSTAB") != NULL) 318 fstab_path = getenv("PATH_FSTAB"); 319 else 320 fstab_path = "/etc/fstab"; 321 fstab = fopen(fstab_path, "w+"); 322 if (fstab == NULL) { 323 sprintf(message, "Cannot open fstab file %s for writing (%s)\n", 324 getenv("PATH_FSTAB"), strerror(errno)); 325 dialog_msgbox("Error", message, 0, 0, TRUE); 326 return (-1); 327 } 328 fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n"); 329 TAILQ_FOREACH(md, &part_metadata, metadata) { 330 if (md->fstab != NULL) 331 fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n", 332 md->fstab->fs_spec, md->fstab->fs_file, 333 md->fstab->fs_vfstype, md->fstab->fs_mntops, 334 md->fstab->fs_freq, md->fstab->fs_passno); 335 } 336 fclose(fstab); 337 338 return (0); 339 } 340 341 static struct partedit_item * 342 read_geom_mesh(struct gmesh *mesh, int *nitems) 343 { 344 struct gclass *classp; 345 struct ggeom *gp; 346 struct partedit_item *items; 347 348 *nitems = 0; 349 items = NULL; 350 351 /* 352 * Build the device table. First add all disks (and CDs). 353 */ 354 355 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 356 if (strcmp(classp->lg_name, "DISK") != 0 && 357 strcmp(classp->lg_name, "MD") != 0) 358 continue; 359 360 /* Now recurse into all children */ 361 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) 362 add_geom_children(gp, 0, &items, nitems); 363 } 364 365 return (items); 366 } 367 368 static void 369 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items, 370 int *nitems) 371 { 372 struct gconsumer *cp; 373 struct gprovider *pp; 374 struct gconfig *gc; 375 376 if (strcmp(gp->lg_class->lg_name, "PART") == 0 && 377 !LIST_EMPTY(&gp->lg_config)) { 378 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 379 if (strcmp(gc->lg_name, "scheme") == 0) 380 (*items)[*nitems-1].type = gc->lg_val; 381 } 382 } 383 384 if (LIST_EMPTY(&gp->lg_provider)) 385 return; 386 387 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 388 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0) 389 continue; 390 391 /* Skip WORM media */ 392 if (strncmp(pp->lg_name, "cd", 2) == 0) 393 continue; 394 395 *items = realloc(*items, 396 (*nitems+1)*sizeof(struct partedit_item)); 397 (*items)[*nitems].indentation = recurse; 398 (*items)[*nitems].name = pp->lg_name; 399 (*items)[*nitems].size = pp->lg_mediasize; 400 (*items)[*nitems].mountpoint = NULL; 401 (*items)[*nitems].type = ""; 402 (*items)[*nitems].cookie = pp; 403 404 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 405 if (strcmp(gc->lg_name, "type") == 0) 406 (*items)[*nitems].type = gc->lg_val; 407 } 408 409 /* Skip swap-backed MD devices */ 410 if (strcmp(gp->lg_class->lg_name, "MD") == 0 && 411 strcmp((*items)[*nitems].type, "swap") == 0) 412 continue; 413 414 (*nitems)++; 415 416 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) 417 add_geom_children(cp->lg_geom, recurse+1, items, 418 nitems); 419 420 /* Only use first provider for acd */ 421 if (strcmp(gp->lg_class->lg_name, "ACD") == 0) 422 break; 423 } 424 } 425 426 static void 427 init_fstab_metadata(void) 428 { 429 struct fstab *fstab; 430 struct partition_metadata *md; 431 432 setfsent(); 433 while ((fstab = getfsent()) != NULL) { 434 md = calloc(1, sizeof(struct partition_metadata)); 435 436 md->name = NULL; 437 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0) 438 md->name = strdup(&fstab->fs_spec[5]); 439 440 md->fstab = malloc(sizeof(struct fstab)); 441 md->fstab->fs_spec = strdup(fstab->fs_spec); 442 md->fstab->fs_file = strdup(fstab->fs_file); 443 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype); 444 md->fstab->fs_mntops = strdup(fstab->fs_mntops); 445 md->fstab->fs_type = strdup(fstab->fs_type); 446 md->fstab->fs_freq = fstab->fs_freq; 447 md->fstab->fs_passno = fstab->fs_passno; 448 449 md->newfs = NULL; 450 451 TAILQ_INSERT_TAIL(&part_metadata, md, metadata); 452 } 453 } 454 455 static void 456 get_mount_points(struct partedit_item *items, int nitems) 457 { 458 struct partition_metadata *md; 459 int i; 460 461 for (i = 0; i < nitems; i++) { 462 TAILQ_FOREACH(md, &part_metadata, metadata) { 463 if (md->name != NULL && md->fstab != NULL && 464 strcmp(md->name, items[i].name) == 0) { 465 items[i].mountpoint = md->fstab->fs_file; 466 break; 467 } 468 } 469 } 470 } 471