Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bsdinstall/partedit/partedit.c
103478 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2011 Nathan Whitehorn
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
31
#include <bsddialog.h>
32
#include <err.h>
33
#include <errno.h>
34
#include <fstab.h>
35
#include <inttypes.h>
36
#include <libgeom.h>
37
#include <libutil.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <sysexits.h>
42
43
#include "diskmenu.h"
44
#include "partedit.h"
45
46
struct pmetadata_head part_metadata;
47
static int sade_mode = 0;
48
49
static int apply_changes(struct gmesh *mesh);
50
static void apply_workaround(struct gmesh *mesh);
51
static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems);
52
static void add_geom_children(struct ggeom *gp, int recurse,
53
struct partedit_item **items, int *nitems);
54
static void init_fstab_metadata(void);
55
static void get_mount_points(struct partedit_item *items, int nitems);
56
static int validate_setup(void);
57
58
static void
59
sigint_handler(int sig)
60
{
61
struct gmesh mesh;
62
63
/* Revert all changes and exit dialog-mode cleanly on SIGINT */
64
if (geom_gettree(&mesh) == 0) {
65
gpart_revert_all(&mesh);
66
geom_deletetree(&mesh);
67
}
68
69
bsddialog_end();
70
71
exit(1);
72
}
73
74
int
75
main(int argc, const char **argv)
76
{
77
struct partition_metadata *md;
78
const char *progname, *prompt;
79
struct partedit_item *items = NULL;
80
struct gmesh mesh;
81
int i, op, nitems;
82
int error;
83
struct bsddialog_conf conf;
84
85
progname = getprogname();
86
if (strcmp(progname, "sade") == 0)
87
sade_mode = 1;
88
89
TAILQ_INIT(&part_metadata);
90
91
init_fstab_metadata();
92
93
if (bsddialog_init() == BSDDIALOG_ERROR)
94
err(1, "%s", bsddialog_geterror());
95
bsddialog_initconf(&conf);
96
if (!sade_mode)
97
bsddialog_backtitle(&conf, OSNAME " Installer");
98
i = 0;
99
100
/* Revert changes on SIGINT */
101
signal(SIGINT, sigint_handler);
102
103
if (strcmp(progname, "autopart") == 0) { /* Guided */
104
prompt = "Please review the disk setup. When complete, press "
105
"the Finish button.";
106
/* Experimental ZFS autopartition support */
107
if (argc > 1 && strcmp(argv[1], "zfs") == 0) {
108
part_wizard("zfs");
109
} else {
110
part_wizard("ufs");
111
}
112
} else if (strcmp(progname, "scriptedpart") == 0) {
113
error = scripted_editor(argc, argv);
114
prompt = NULL;
115
if (error != 0) {
116
bsddialog_end();
117
return (error);
118
}
119
} else {
120
prompt = "Create partitions for " OSNAME ", F1 for help.\n"
121
"No changes will be made until you select Finish.";
122
}
123
124
/* Show the part editor either immediately, or to confirm wizard */
125
while (prompt != NULL) {
126
bsddialog_clear(0);
127
if (!sade_mode)
128
bsddialog_backtitle(&conf, OSNAME " Installer");
129
130
error = geom_gettree(&mesh);
131
if (error == 0)
132
items = read_geom_mesh(&mesh, &nitems);
133
if (error || items == NULL) {
134
conf.title = "Error";
135
bsddialog_msgbox(&conf, "No disks found. If you need "
136
"to install a kernel driver, choose Shell at the "
137
"installation menu.", 0, 0);
138
break;
139
}
140
141
get_mount_points(items, nitems);
142
143
if (i >= nitems)
144
i = nitems - 1;
145
op = diskmenu_show("Partition Editor", prompt, items, nitems,
146
&i);
147
148
switch (op) {
149
case BUTTON_CREATE:
150
gpart_create((struct gprovider *)(items[i].cookie),
151
NULL, NULL, NULL, NULL, 1);
152
break;
153
case BUTTON_DELETE:
154
gpart_delete((struct gprovider *)(items[i].cookie));
155
break;
156
case BUTTON_MODIFY:
157
gpart_edit((struct gprovider *)(items[i].cookie));
158
break;
159
case BUTTON_REVERT:
160
gpart_revert_all(&mesh);
161
while ((md = TAILQ_FIRST(&part_metadata)) != NULL) {
162
if (md->fstab != NULL) {
163
free(md->fstab->fs_spec);
164
free(md->fstab->fs_file);
165
free(md->fstab->fs_vfstype);
166
free(md->fstab->fs_mntops);
167
free(md->fstab->fs_type);
168
free(md->fstab);
169
}
170
if (md->newfs != NULL)
171
free(md->newfs);
172
free(md->name);
173
174
TAILQ_REMOVE(&part_metadata, md, metadata);
175
free(md);
176
}
177
init_fstab_metadata();
178
break;
179
case BUTTON_AUTO:
180
part_wizard("ufs");
181
break;
182
}
183
184
error = 0;
185
if (op == BUTTON_FINISH) {
186
conf.button.ok_label = "Commit";
187
conf.button.with_extra = true;
188
conf.button.extra_label = "Revert & Exit";
189
conf.button.cancel_label = "Back";
190
conf.title = "Confirmation";
191
op = bsddialog_yesno(&conf, "Your changes will now be "
192
"written to disk. If you have chosen to overwrite "
193
"existing data, it will be PERMANENTLY ERASED. Are "
194
"you sure you want to commit your changes?", 0, 0);
195
conf.button.ok_label = NULL;
196
conf.button.with_extra = false;
197
conf.button.extra_label = NULL;
198
conf.button.cancel_label = NULL;
199
200
if (op == BSDDIALOG_OK && validate_setup()) { /* Save */
201
error = apply_changes(&mesh);
202
if (!error)
203
apply_workaround(&mesh);
204
break;
205
} else if (op == BSDDIALOG_EXTRA) { /* Quit */
206
gpart_revert_all(&mesh);
207
error = -1;
208
break;
209
}
210
}
211
212
geom_deletetree(&mesh);
213
free(items);
214
}
215
216
if (prompt == NULL) {
217
error = geom_gettree(&mesh);
218
if (error == 0) {
219
if (validate_setup()) {
220
error = apply_changes(&mesh);
221
} else {
222
gpart_revert_all(&mesh);
223
error = -1;
224
}
225
geom_deletetree(&mesh);
226
}
227
}
228
229
bsddialog_end();
230
231
return (error);
232
}
233
234
struct partition_metadata *
235
get_part_metadata(const char *name, int create)
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
break;
242
243
if (md == NULL && create) {
244
md = calloc(1, sizeof(*md));
245
md->name = strdup(name);
246
TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
247
}
248
249
return (md);
250
}
251
252
void
253
delete_part_metadata(const char *name)
254
{
255
struct partition_metadata *md;
256
257
TAILQ_FOREACH(md, &part_metadata, metadata) {
258
if (md->name != NULL && strcmp(md->name, name) == 0) {
259
if (md->fstab != NULL) {
260
free(md->fstab->fs_spec);
261
free(md->fstab->fs_file);
262
free(md->fstab->fs_vfstype);
263
free(md->fstab->fs_mntops);
264
free(md->fstab->fs_type);
265
free(md->fstab);
266
}
267
if (md->newfs != NULL)
268
free(md->newfs);
269
free(md->name);
270
271
TAILQ_REMOVE(&part_metadata, md, metadata);
272
free(md);
273
break;
274
}
275
}
276
}
277
278
static int
279
validate_setup(void)
280
{
281
struct partition_metadata *md, *root = NULL;
282
int button;
283
struct bsddialog_conf conf;
284
285
TAILQ_FOREACH(md, &part_metadata, metadata) {
286
if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)
287
root = md;
288
289
/* XXX: Check for duplicate mountpoints */
290
}
291
292
bsddialog_initconf(&conf);
293
294
if (root == NULL) {
295
conf.title = "Error";
296
bsddialog_msgbox(&conf, "No root partition was found. "
297
"The root " OSNAME " partition must have a mountpoint "
298
"of '/'.", 0, 0);
299
return (false);
300
}
301
302
/*
303
* Check for root partitions that we aren't formatting, which is
304
* usually a mistake
305
*/
306
if (root->newfs == NULL && !sade_mode) {
307
conf.button.default_cancel = true;
308
conf.title = "Warning";
309
button = bsddialog_yesno(&conf, "The chosen root partition "
310
"has a preexisting filesystem. If it contains an existing "
311
OSNAME " system, please update it with freebsd-update "
312
"instead of installing a new system on it. The partition "
313
"can also be erased by pressing \"No\" and then deleting "
314
"and recreating it. Are you sure you want to proceed?",
315
0, 0);
316
if (button == BSDDIALOG_CANCEL)
317
return (false);
318
}
319
320
return (true);
321
}
322
323
static int
324
mountpoint_sorter(const void *xa, const void *xb)
325
{
326
struct partition_metadata *a = *(struct partition_metadata **)xa;
327
struct partition_metadata *b = *(struct partition_metadata **)xb;
328
329
if (a->fstab == NULL && b->fstab == NULL)
330
return 0;
331
if (a->fstab == NULL)
332
return 1;
333
if (b->fstab == NULL)
334
return -1;
335
336
return strcmp(a->fstab->fs_file, b->fstab->fs_file);
337
}
338
339
static int
340
apply_changes(struct gmesh *mesh)
341
{
342
struct partition_metadata *md;
343
char message[512];
344
int i, nitems, error, *miniperc;
345
const char **minilabel;
346
const char *fstab_path;
347
FILE *fstab;
348
char *command;
349
struct bsddialog_conf conf;
350
351
nitems = 1; /* Partition table changes */
352
TAILQ_FOREACH(md, &part_metadata, metadata) {
353
if (md->newfs != NULL)
354
nitems++;
355
}
356
minilabel = calloc(nitems, sizeof(const char *));
357
miniperc = calloc(nitems, sizeof(int));
358
minilabel[0] = "Writing partition tables";
359
miniperc[0] = BSDDIALOG_MG_INPROGRESS;
360
i = 1;
361
TAILQ_FOREACH(md, &part_metadata, metadata) {
362
if (md->newfs != NULL) {
363
char *item;
364
365
asprintf(&item, "Initializing %s", md->name);
366
minilabel[i] = item;
367
miniperc[i] = BSDDIALOG_MG_PENDING;
368
i++;
369
}
370
}
371
372
i = 0;
373
bsddialog_initconf(&conf);
374
conf.title = "Initializing";
375
bsddialog_mixedgauge(&conf,
376
"Initializing file systems. Please wait.", 0, 0, i * 100 / nitems,
377
nitems, minilabel, miniperc);
378
gpart_commit(mesh);
379
miniperc[i] = BSDDIALOG_MG_COMPLETED;
380
i++;
381
382
if (getenv("BSDINSTALL_LOG") == NULL)
383
setenv("BSDINSTALL_LOG", "/dev/null", 1);
384
385
TAILQ_FOREACH(md, &part_metadata, metadata) {
386
if (md->newfs != NULL) {
387
miniperc[i] = BSDDIALOG_MG_INPROGRESS;
388
bsddialog_mixedgauge(&conf,
389
"Initializing file systems. Please wait.", 0, 0,
390
i * 100 / nitems, nitems, minilabel, miniperc);
391
asprintf(&command, "(echo %s; %s) >>%s 2>>%s",
392
md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),
393
getenv("BSDINSTALL_LOG"));
394
error = system(command);
395
free(command);
396
miniperc[i] = (error == 0) ?
397
BSDDIALOG_MG_COMPLETED : BSDDIALOG_MG_FAILED;
398
i++;
399
}
400
}
401
bsddialog_mixedgauge(&conf, "Initializing file systems. Please wait.",
402
0, 0, i * 100 / nitems, nitems, minilabel, miniperc);
403
404
for (i = 1; i < nitems; i++)
405
free(__DECONST(char *, minilabel[i]));
406
407
free(minilabel);
408
free(miniperc);
409
410
/* Sort filesystems for fstab so that mountpoints are ordered */
411
{
412
struct partition_metadata **tobesorted;
413
struct partition_metadata *tmp;
414
int nparts = 0;
415
TAILQ_FOREACH(md, &part_metadata, metadata)
416
nparts++;
417
tobesorted = malloc(sizeof(struct partition_metadata *)*nparts);
418
nparts = 0;
419
TAILQ_FOREACH_SAFE(md, &part_metadata, metadata, tmp) {
420
tobesorted[nparts++] = md;
421
TAILQ_REMOVE(&part_metadata, md, metadata);
422
}
423
qsort(tobesorted, nparts, sizeof(tobesorted[0]),
424
mountpoint_sorter);
425
426
/* Now re-add everything */
427
while (nparts-- > 0)
428
TAILQ_INSERT_HEAD(&part_metadata,
429
tobesorted[nparts], metadata);
430
free(tobesorted);
431
}
432
433
if (getenv("PATH_FSTAB") != NULL)
434
fstab_path = getenv("PATH_FSTAB");
435
else
436
fstab_path = "/etc/fstab";
437
fstab = fopen(fstab_path, "w+");
438
if (fstab == NULL) {
439
snprintf(message, sizeof(message),
440
"Cannot open fstab file %s for writing (%s)\n",
441
getenv("PATH_FSTAB"), strerror(errno));
442
conf.title = "Error";
443
bsddialog_msgbox(&conf, message, 0, 0);
444
return (-1);
445
}
446
fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");
447
TAILQ_FOREACH(md, &part_metadata, metadata) {
448
if (md->fstab != NULL)
449
fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",
450
md->fstab->fs_spec, md->fstab->fs_file,
451
md->fstab->fs_vfstype, md->fstab->fs_mntops,
452
md->fstab->fs_freq, md->fstab->fs_passno);
453
}
454
fclose(fstab);
455
456
return (0);
457
}
458
459
static void
460
apply_workaround(struct gmesh *mesh)
461
{
462
struct gclass *classp;
463
struct ggeom *gp;
464
struct gconfig *gc;
465
const char *scheme = NULL, *modified = NULL;
466
struct bsddialog_conf conf;
467
468
LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
469
if (strcmp(classp->lg_name, "PART") == 0)
470
break;
471
}
472
473
if (strcmp(classp->lg_name, "PART") != 0) {
474
bsddialog_initconf(&conf);
475
conf.title = "Error";
476
bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
477
return;
478
}
479
480
LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
481
LIST_FOREACH(gc, &gp->lg_config, lg_config) {
482
if (strcmp(gc->lg_name, "scheme") == 0) {
483
scheme = gc->lg_val;
484
} else if (strcmp(gc->lg_name, "modified") == 0) {
485
modified = gc->lg_val;
486
}
487
}
488
489
if (scheme && strcmp(scheme, "GPT") == 0 &&
490
modified && strcmp(modified, "true") == 0) {
491
if (getenv("WORKAROUND_LENOVO"))
492
gpart_set_root(gp->lg_name, "lenovofix");
493
if (getenv("WORKAROUND_GPTACTIVE"))
494
gpart_set_root(gp->lg_name, "active");
495
}
496
}
497
}
498
499
static struct partedit_item *
500
read_geom_mesh(struct gmesh *mesh, int *nitems)
501
{
502
struct gclass *classp;
503
struct ggeom *gp;
504
struct partedit_item *items;
505
506
*nitems = 0;
507
items = NULL;
508
509
/*
510
* Build the device table. First add all disks (and CDs).
511
*/
512
513
LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
514
if (strcmp(classp->lg_name, "DISK") != 0 &&
515
strcmp(classp->lg_name, "MD") != 0)
516
continue;
517
518
/* Now recurse into all children */
519
LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
520
add_geom_children(gp, 0, &items, nitems);
521
}
522
523
return (items);
524
}
525
526
static void
527
add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,
528
int *nitems)
529
{
530
struct gconsumer *cp;
531
struct gprovider *pp;
532
struct gconfig *gc;
533
534
if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&
535
!LIST_EMPTY(&gp->lg_config)) {
536
LIST_FOREACH(gc, &gp->lg_config, lg_config) {
537
if (strcmp(gc->lg_name, "scheme") == 0)
538
(*items)[*nitems-1].type = gc->lg_val;
539
}
540
}
541
542
if (LIST_EMPTY(&gp->lg_provider))
543
return;
544
545
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
546
if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)
547
continue;
548
549
/* Skip WORM media */
550
if (strncmp(pp->lg_name, "cd", 2) == 0)
551
continue;
552
553
*items = realloc(*items,
554
(*nitems+1)*sizeof(struct partedit_item));
555
(*items)[*nitems].indentation = recurse;
556
(*items)[*nitems].name = pp->lg_name;
557
(*items)[*nitems].size = pp->lg_mediasize;
558
(*items)[*nitems].mountpoint = NULL;
559
(*items)[*nitems].type = "";
560
(*items)[*nitems].cookie = pp;
561
562
LIST_FOREACH(gc, &pp->lg_config, lg_config) {
563
if (strcmp(gc->lg_name, "type") == 0)
564
(*items)[*nitems].type = gc->lg_val;
565
}
566
567
/* Skip swap-backed MD devices */
568
if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&
569
strcmp((*items)[*nitems].type, "swap") == 0)
570
continue;
571
572
(*nitems)++;
573
574
LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
575
add_geom_children(cp->lg_geom, recurse+1, items,
576
nitems);
577
578
/* Only use first provider for acd */
579
if (strcmp(gp->lg_class->lg_name, "ACD") == 0)
580
break;
581
}
582
}
583
584
static void
585
init_fstab_metadata(void)
586
{
587
struct fstab *fstab;
588
struct partition_metadata *md;
589
590
setfsent();
591
while ((fstab = getfsent()) != NULL) {
592
md = calloc(1, sizeof(struct partition_metadata));
593
594
md->name = NULL;
595
if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)
596
md->name = strdup(&fstab->fs_spec[5]);
597
598
md->fstab = malloc(sizeof(struct fstab));
599
md->fstab->fs_spec = strdup(fstab->fs_spec);
600
md->fstab->fs_file = strdup(fstab->fs_file);
601
md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);
602
md->fstab->fs_mntops = strdup(fstab->fs_mntops);
603
md->fstab->fs_type = strdup(fstab->fs_type);
604
md->fstab->fs_freq = fstab->fs_freq;
605
md->fstab->fs_passno = fstab->fs_passno;
606
607
md->newfs = NULL;
608
609
TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
610
}
611
}
612
613
static void
614
get_mount_points(struct partedit_item *items, int nitems)
615
{
616
struct partition_metadata *md;
617
int i;
618
619
for (i = 0; i < nitems; i++) {
620
TAILQ_FOREACH(md, &part_metadata, metadata) {
621
if (md->name != NULL && md->fstab != NULL &&
622
strcmp(md->name, items[i].name) == 0) {
623
items[i].mountpoint = md->fstab->fs_file;
624
break;
625
}
626
}
627
}
628
}
629
630