]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bsdinstall/partedit/partedit.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / bsdinstall / partedit / partedit.c
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 }