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