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