]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bsdinstall/partedit/partedit.c
MFC r271539,273003,273005:
[FreeBSD/stable/10.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                 /* 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 *
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
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
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
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 *
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
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
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
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 }