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
sigint_handler(int sig)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
main(int argc,const char ** argv)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 /* Experimental ZFS autopartition support */
99 if (argc > 1 && strcmp(argv[1], "zfs") == 0) {
100 part_wizard("zfs");
101 } else {
102 part_wizard("ufs");
103 }
104 } else if (strcmp(basename(argv[0]), "scriptedpart") == 0) {
105 error = scripted_editor(argc, argv);
106 prompt = NULL;
107 if (error != 0) {
108 end_dialog();
109 return (error);
110 }
111 } else {
112 prompt = "Create partitions for FreeBSD. No changes will be "
113 "made until you select Finish.";
114 }
115
116 /* Show the part editor either immediately, or to confirm wizard */
117 while (prompt != NULL) {
118 dlg_clear();
119 dlg_put_backtitle();
120
121 error = geom_gettree(&mesh);
122 if (error == 0)
123 items = read_geom_mesh(&mesh, &nitems);
124 if (error || items == NULL) {
125 dialog_msgbox("Error", "No disks found. If you need to "
126 "install a kernel driver, choose Shell at the "
127 "installation menu.", 0, 0, TRUE);
128 break;
129 }
130
131 get_mount_points(items, nitems);
132
133 if (i >= nitems)
134 i = nitems - 1;
135 op = diskeditor_show("Partition Editor", prompt,
136 items, nitems, &i, &nscroll);
137
138 switch (op) {
139 case 0: /* Create */
140 gpart_create((struct gprovider *)(items[i].cookie),
141 NULL, NULL, NULL, NULL, 1);
142 break;
143 case 1: /* Delete */
144 gpart_delete((struct gprovider *)(items[i].cookie));
145 break;
146 case 2: /* Modify */
147 gpart_edit((struct gprovider *)(items[i].cookie));
148 break;
149 case 3: /* Revert */
150 gpart_revert_all(&mesh);
151 while ((md = TAILQ_FIRST(&part_metadata)) != NULL) {
152 if (md->fstab != NULL) {
153 free(md->fstab->fs_spec);
154 free(md->fstab->fs_file);
155 free(md->fstab->fs_vfstype);
156 free(md->fstab->fs_mntops);
157 free(md->fstab->fs_type);
158 free(md->fstab);
159 }
160 if (md->newfs != NULL)
161 free(md->newfs);
162 free(md->name);
163
164 TAILQ_REMOVE(&part_metadata, md, metadata);
165 free(md);
166 }
167 init_fstab_metadata();
168 break;
169 case 4: /* Auto */
170 part_wizard("ufs");
171 break;
172 }
173
174 error = 0;
175 if (op == 5) { /* Finished */
176 dialog_vars.ok_label = __DECONST(char *, "Commit");
177 dialog_vars.extra_label =
178 __DECONST(char *, "Revert & Exit");
179 dialog_vars.extra_button = TRUE;
180 dialog_vars.cancel_label = __DECONST(char *, "Back");
181 op = dialog_yesno("Confirmation", "Your changes will "
182 "now be written to disk. If you have chosen to "
183 "overwrite existing data, it will be PERMANENTLY "
184 "ERASED. Are you sure you want to commit your "
185 "changes?", 0, 0);
186 dialog_vars.ok_label = NULL;
187 dialog_vars.extra_button = FALSE;
188 dialog_vars.cancel_label = NULL;
189
190 if (op == 0 && validate_setup()) { /* Save */
191 error = apply_changes(&mesh);
192 break;
193 } else if (op == 3) { /* Quit */
194 gpart_revert_all(&mesh);
195 error = -1;
196 break;
197 }
198 }
199
200 geom_deletetree(&mesh);
201 free(items);
202 }
203
204 if (prompt == NULL) {
205 error = geom_gettree(&mesh);
206 if (validate_setup()) {
207 error = apply_changes(&mesh);
208 } else {
209 gpart_revert_all(&mesh);
210 error = -1;
211 }
212 }
213
214 geom_deletetree(&mesh);
215 free(items);
216 end_dialog();
217
218 return (error);
219 }
220
221 struct partition_metadata *
get_part_metadata(const char * name,int create)222 get_part_metadata(const char *name, int create)
223 {
224 struct partition_metadata *md;
225
226 TAILQ_FOREACH(md, &part_metadata, metadata)
227 if (md->name != NULL && strcmp(md->name, name) == 0)
228 break;
229
230 if (md == NULL && create) {
231 md = calloc(1, sizeof(*md));
232 md->name = strdup(name);
233 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
234 }
235
236 return (md);
237 }
238
239 void
delete_part_metadata(const char * name)240 delete_part_metadata(const char *name)
241 {
242 struct partition_metadata *md;
243
244 TAILQ_FOREACH(md, &part_metadata, metadata) {
245 if (md->name != NULL && strcmp(md->name, name) == 0) {
246 if (md->fstab != NULL) {
247 free(md->fstab->fs_spec);
248 free(md->fstab->fs_file);
249 free(md->fstab->fs_vfstype);
250 free(md->fstab->fs_mntops);
251 free(md->fstab->fs_type);
252 free(md->fstab);
253 }
254 if (md->newfs != NULL)
255 free(md->newfs);
256 free(md->name);
257
258 TAILQ_REMOVE(&part_metadata, md, metadata);
259 free(md);
260 break;
261 }
262 }
263 }
264
265 static int
validate_setup(void)266 validate_setup(void)
267 {
268 struct partition_metadata *md, *root = NULL;
269 int cancel;
270
271 TAILQ_FOREACH(md, &part_metadata, metadata) {
272 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)
273 root = md;
274
275 /* XXX: Check for duplicate mountpoints */
276 }
277
278 if (root == NULL) {
279 dialog_msgbox("Error", "No root partition was found. "
280 "The root FreeBSD partition must have a mountpoint of '/'.",
281 0, 0, TRUE);
282 return (FALSE);
283 }
284
285 /*
286 * Check for root partitions that we aren't formatting, which is
287 * usually a mistake
288 */
289 if (root->newfs == NULL && !sade_mode) {
290 dialog_vars.defaultno = TRUE;
291 cancel = dialog_yesno("Warning", "The chosen root partition "
292 "has a preexisting filesystem. If it contains an existing "
293 "FreeBSD system, please update it with freebsd-update "
294 "instead of installing a new system on it. The partition "
295 "can also be erased by pressing \"No\" and then deleting "
296 "and recreating it. Are you sure you want to proceed?",
297 0, 0);
298 dialog_vars.defaultno = FALSE;
299 if (cancel)
300 return (FALSE);
301 }
302
303 return (TRUE);
304 }
305
306 static int
apply_changes(struct gmesh * mesh)307 apply_changes(struct gmesh *mesh)
308 {
309 struct partition_metadata *md;
310 char message[512];
311 int i, nitems, error;
312 const char **items;
313 const char *fstab_path;
314 FILE *fstab;
315
316 nitems = 1; /* Partition table changes */
317 TAILQ_FOREACH(md, &part_metadata, metadata) {
318 if (md->newfs != NULL)
319 nitems++;
320 }
321 items = calloc(nitems * 2, sizeof(const char *));
322 items[0] = "Writing partition tables";
323 items[1] = "7"; /* In progress */
324 i = 1;
325 TAILQ_FOREACH(md, &part_metadata, metadata) {
326 if (md->newfs != NULL) {
327 char *item;
328 item = malloc(255);
329 sprintf(item, "Initializing %s", md->name);
330 items[i*2] = item;
331 items[i*2 + 1] = "Pending";
332 i++;
333 }
334 }
335
336 i = 0;
337 dialog_mixedgauge("Initializing",
338 "Initializing file systems. Please wait.", 0, 0, i*100/nitems,
339 nitems, __DECONST(char **, items));
340 gpart_commit(mesh);
341 items[i*2 + 1] = "3";
342 i++;
343
344 if (getenv("BSDINSTALL_LOG") == NULL)
345 setenv("BSDINSTALL_LOG", "/dev/null", 1);
346
347 TAILQ_FOREACH(md, &part_metadata, metadata) {
348 if (md->newfs != NULL) {
349 items[i*2 + 1] = "7"; /* In progress */
350 dialog_mixedgauge("Initializing",
351 "Initializing file systems. Please wait.", 0, 0,
352 i*100/nitems, nitems, __DECONST(char **, items));
353 sprintf(message, "(echo %s; %s) >>%s 2>>%s",
354 md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),
355 getenv("BSDINSTALL_LOG"));
356 error = system(message);
357 items[i*2 + 1] = (error == 0) ? "3" : "1";
358 i++;
359 }
360 }
361 dialog_mixedgauge("Initializing",
362 "Initializing file systems. Please wait.", 0, 0,
363 i*100/nitems, nitems, __DECONST(char **, items));
364
365 for (i = 1; i < nitems; i++)
366 free(__DECONST(char *, items[i*2]));
367 free(items);
368
369 if (getenv("PATH_FSTAB") != NULL)
370 fstab_path = getenv("PATH_FSTAB");
371 else
372 fstab_path = "/etc/fstab";
373 fstab = fopen(fstab_path, "w+");
374 if (fstab == NULL) {
375 sprintf(message, "Cannot open fstab file %s for writing (%s)\n",
376 getenv("PATH_FSTAB"), strerror(errno));
377 dialog_msgbox("Error", message, 0, 0, TRUE);
378 return (-1);
379 }
380 fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");
381 TAILQ_FOREACH(md, &part_metadata, metadata) {
382 if (md->fstab != NULL)
383 fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",
384 md->fstab->fs_spec, md->fstab->fs_file,
385 md->fstab->fs_vfstype, md->fstab->fs_mntops,
386 md->fstab->fs_freq, md->fstab->fs_passno);
387 }
388 fclose(fstab);
389
390 return (0);
391 }
392
393 static struct partedit_item *
read_geom_mesh(struct gmesh * mesh,int * nitems)394 read_geom_mesh(struct gmesh *mesh, int *nitems)
395 {
396 struct gclass *classp;
397 struct ggeom *gp;
398 struct partedit_item *items;
399
400 *nitems = 0;
401 items = NULL;
402
403 /*
404 * Build the device table. First add all disks (and CDs).
405 */
406
407 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
408 if (strcmp(classp->lg_name, "DISK") != 0 &&
409 strcmp(classp->lg_name, "MD") != 0)
410 continue;
411
412 /* Now recurse into all children */
413 LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
414 add_geom_children(gp, 0, &items, nitems);
415 }
416
417 return (items);
418 }
419
420 static void
add_geom_children(struct ggeom * gp,int recurse,struct partedit_item ** items,int * nitems)421 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,
422 int *nitems)
423 {
424 struct gconsumer *cp;
425 struct gprovider *pp;
426 struct gconfig *gc;
427
428 if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&
429 !LIST_EMPTY(&gp->lg_config)) {
430 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
431 if (strcmp(gc->lg_name, "scheme") == 0)
432 (*items)[*nitems-1].type = gc->lg_val;
433 }
434 }
435
436 if (LIST_EMPTY(&gp->lg_provider))
437 return;
438
439 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
440 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)
441 continue;
442
443 /* Skip WORM media */
444 if (strncmp(pp->lg_name, "cd", 2) == 0)
445 continue;
446
447 *items = realloc(*items,
448 (*nitems+1)*sizeof(struct partedit_item));
449 (*items)[*nitems].indentation = recurse;
450 (*items)[*nitems].name = pp->lg_name;
451 (*items)[*nitems].size = pp->lg_mediasize;
452 (*items)[*nitems].mountpoint = NULL;
453 (*items)[*nitems].type = "";
454 (*items)[*nitems].cookie = pp;
455
456 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
457 if (strcmp(gc->lg_name, "type") == 0)
458 (*items)[*nitems].type = gc->lg_val;
459 }
460
461 /* Skip swap-backed MD devices */
462 if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&
463 strcmp((*items)[*nitems].type, "swap") == 0)
464 continue;
465
466 (*nitems)++;
467
468 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
469 add_geom_children(cp->lg_geom, recurse+1, items,
470 nitems);
471
472 /* Only use first provider for acd */
473 if (strcmp(gp->lg_class->lg_name, "ACD") == 0)
474 break;
475 }
476 }
477
478 static void
init_fstab_metadata(void)479 init_fstab_metadata(void)
480 {
481 struct fstab *fstab;
482 struct partition_metadata *md;
483
484 setfsent();
485 while ((fstab = getfsent()) != NULL) {
486 md = calloc(1, sizeof(struct partition_metadata));
487
488 md->name = NULL;
489 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)
490 md->name = strdup(&fstab->fs_spec[5]);
491
492 md->fstab = malloc(sizeof(struct fstab));
493 md->fstab->fs_spec = strdup(fstab->fs_spec);
494 md->fstab->fs_file = strdup(fstab->fs_file);
495 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);
496 md->fstab->fs_mntops = strdup(fstab->fs_mntops);
497 md->fstab->fs_type = strdup(fstab->fs_type);
498 md->fstab->fs_freq = fstab->fs_freq;
499 md->fstab->fs_passno = fstab->fs_passno;
500
501 md->newfs = NULL;
502
503 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
504 }
505 }
506
507 static void
get_mount_points(struct partedit_item * items,int nitems)508 get_mount_points(struct partedit_item *items, int nitems)
509 {
510 struct partition_metadata *md;
511 int i;
512
513 for (i = 0; i < nitems; i++) {
514 TAILQ_FOREACH(md, &part_metadata, metadata) {
515 if (md->name != NULL && md->fstab != NULL &&
516 strcmp(md->name, items[i].name) == 0) {
517 items[i].mountpoint = md->fstab->fs_file;
518 break;
519 }
520 }
521 }
522 }
523