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