]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - usr.sbin/bsdinstall/partedit/partedit.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.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
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;
244         int root_found = FALSE;
245
246         TAILQ_FOREACH(md, &part_metadata, metadata) {
247                 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)
248                         root_found = TRUE;
249
250                 /* XXX: Check for duplicate mountpoints */
251         }
252
253         if (!root_found) {
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         return (TRUE);
261 }
262
263 static int
264 apply_changes(struct gmesh *mesh)
265 {
266         struct partition_metadata *md;
267         char message[512];
268         int i, nitems, error;
269         const char **items;
270         const char *fstab_path;
271         FILE *fstab;
272
273         nitems = 1; /* Partition table changes */
274         TAILQ_FOREACH(md, &part_metadata, metadata) {
275                 if (md->newfs != NULL)
276                         nitems++;
277         }
278         items = calloc(nitems * 2, sizeof(const char *));
279         items[0] = "Writing partition tables";
280         items[1] = "7"; /* In progress */
281         i = 1;
282         TAILQ_FOREACH(md, &part_metadata, metadata) {
283                 if (md->newfs != NULL) {
284                         char *item;
285                         item = malloc(255);
286                         sprintf(item, "Initializing %s", md->name);
287                         items[i*2] = item;
288                         items[i*2 + 1] = "Pending";
289                         i++;
290                 }
291         }
292
293         i = 0;
294         dialog_mixedgauge("Initializing",
295             "Initializing file systems. Please wait.", 0, 0, i*100/nitems,
296             nitems, __DECONST(char **, items));
297         gpart_commit(mesh);
298         items[i*2 + 1] = "3";
299         i++;
300
301         if (getenv("BSDINSTALL_LOG") == NULL) 
302                 setenv("BSDINSTALL_LOG", "/dev/null", 1);
303
304         TAILQ_FOREACH(md, &part_metadata, metadata) {
305                 if (md->newfs != NULL) {
306                         items[i*2 + 1] = "7"; /* In progress */
307                         dialog_mixedgauge("Initializing",
308                             "Initializing file systems. Please wait.", 0, 0,
309                             i*100/nitems, nitems, __DECONST(char **, items));
310                         sprintf(message, "(echo %s; %s) >>%s 2>>%s",
311                             md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),
312                             getenv("BSDINSTALL_LOG"));
313                         error = system(message);
314                         items[i*2 + 1] = (error == 0) ? "3" : "1";
315                         i++;
316                 }
317         }
318         dialog_mixedgauge("Initializing",
319             "Initializing file systems. Please wait.", 0, 0,
320             i*100/nitems, nitems, __DECONST(char **, items));
321
322         for (i = 1; i < nitems; i++)
323                 free(__DECONST(char *, items[i*2]));
324         free(items);
325
326         if (getenv("PATH_FSTAB") != NULL)
327                 fstab_path = getenv("PATH_FSTAB");
328         else
329                 fstab_path = "/etc/fstab";
330         fstab = fopen(fstab_path, "w+");
331         if (fstab == NULL) {
332                 sprintf(message, "Cannot open fstab file %s for writing (%s)\n",
333                     getenv("PATH_FSTAB"), strerror(errno));
334                 dialog_msgbox("Error", message, 0, 0, TRUE);
335                 return (-1);
336         }
337         fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");
338         TAILQ_FOREACH(md, &part_metadata, metadata) {
339                 if (md->fstab != NULL)
340                         fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",
341                             md->fstab->fs_spec, md->fstab->fs_file,
342                             md->fstab->fs_vfstype, md->fstab->fs_mntops,
343                             md->fstab->fs_freq, md->fstab->fs_passno);
344         }
345         fclose(fstab);
346
347         return (0);
348 }
349
350 static struct partedit_item *
351 read_geom_mesh(struct gmesh *mesh, int *nitems)
352 {
353         struct gclass *classp;
354         struct ggeom *gp;
355         struct partedit_item *items;
356
357         *nitems = 0;
358         items = NULL;
359
360         /*
361          * Build the device table. First add all disks (and CDs).
362          */
363         
364         LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
365                 if (strcmp(classp->lg_name, "DISK") != 0 &&
366                     strcmp(classp->lg_name, "MD") != 0)
367                         continue;
368
369                 /* Now recurse into all children */
370                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) 
371                         add_geom_children(gp, 0, &items, nitems);
372         }
373
374         return (items);
375 }
376
377 static void
378 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,
379     int *nitems)
380 {
381         struct gconsumer *cp;
382         struct gprovider *pp;
383         struct gconfig *gc;
384
385         if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&
386             !LIST_EMPTY(&gp->lg_config)) {
387                 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
388                         if (strcmp(gc->lg_name, "scheme") == 0)
389                                 (*items)[*nitems-1].type = gc->lg_val;
390                 }
391         }
392
393         if (LIST_EMPTY(&gp->lg_provider)) 
394                 return;
395
396         LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
397                 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)
398                         continue;
399
400                 /* Skip WORM media */
401                 if (strncmp(pp->lg_name, "cd", 2) == 0)
402                         continue;
403
404                 *items = realloc(*items,
405                     (*nitems+1)*sizeof(struct partedit_item));
406                 (*items)[*nitems].indentation = recurse;
407                 (*items)[*nitems].name = pp->lg_name;
408                 (*items)[*nitems].size = pp->lg_mediasize;
409                 (*items)[*nitems].mountpoint = NULL;
410                 (*items)[*nitems].type = "";
411                 (*items)[*nitems].cookie = pp;
412
413                 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
414                         if (strcmp(gc->lg_name, "type") == 0)
415                                 (*items)[*nitems].type = gc->lg_val;
416                 }
417
418                 /* Skip swap-backed MD devices */
419                 if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&
420                     strcmp((*items)[*nitems].type, "swap") == 0)
421                         continue;
422
423                 (*nitems)++;
424
425                 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
426                         add_geom_children(cp->lg_geom, recurse+1, items,
427                             nitems);
428
429                 /* Only use first provider for acd */
430                 if (strcmp(gp->lg_class->lg_name, "ACD") == 0)
431                         break;
432         }
433 }
434
435 static void
436 init_fstab_metadata(void)
437 {
438         struct fstab *fstab;
439         struct partition_metadata *md;
440
441         setfsent();
442         while ((fstab = getfsent()) != NULL) {
443                 md = calloc(1, sizeof(struct partition_metadata));
444
445                 md->name = NULL;
446                 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)
447                         md->name = strdup(&fstab->fs_spec[5]);
448
449                 md->fstab = malloc(sizeof(struct fstab));
450                 md->fstab->fs_spec = strdup(fstab->fs_spec);
451                 md->fstab->fs_file = strdup(fstab->fs_file);
452                 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);
453                 md->fstab->fs_mntops = strdup(fstab->fs_mntops);
454                 md->fstab->fs_type = strdup(fstab->fs_type);
455                 md->fstab->fs_freq = fstab->fs_freq;
456                 md->fstab->fs_passno = fstab->fs_passno;
457
458                 md->newfs = NULL;
459                 
460                 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
461         }
462 }
463
464 static void
465 get_mount_points(struct partedit_item *items, int nitems)
466 {
467         struct partition_metadata *md;
468         int i;
469         
470         for (i = 0; i < nitems; i++) {
471                 TAILQ_FOREACH(md, &part_metadata, metadata) {
472                         if (md->name != NULL && md->fstab != NULL &&
473                             strcmp(md->name, items[i].name) == 0) {
474                                 items[i].mountpoint = md->fstab->fs_file;
475                                 break;
476                         }
477                 }
478         }
479 }