1 /*
2 * Copyright (c)2004,2015 The DragonFly Project. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * Neither the name of the DragonFly Project nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * fn_subpart.c
36 * Installer Function : Create Subpartitions.
37 * $Id: fn_subpart.c,v 1.50 2005/04/07 20:22:40 cpressey Exp $
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #ifdef ENABLE_NLS
46 #include <libintl.h>
47 #define _(String) gettext (String)
48 #else
49 #define _(String) (String)
50 #endif
51
52 #include "libaura/mem.h"
53 #include "libaura/buffer.h"
54 #include "libaura/dict.h"
55 #include "libaura/fspred.h"
56
57 #include "libdfui/dfui.h"
58 #include "libdfui/dump.h"
59 #include "libdfui/system.h"
60
61 #include "libinstaller/commands.h"
62 #include "libinstaller/diskutil.h"
63 #include "libinstaller/functions.h"
64 #include "libinstaller/uiutil.h"
65
66 #include "fn.h"
67 #include "flow.h"
68 #include "pathnames.h"
69
70 #define MTPT_BOOT 0
71 #define MTPT_SWAP 1
72 #define MTPT_ROOT 2
73 #define MTPT_BUILD 3
74
75 static int create_subpartitions(struct i_fn_args *);
76 static long default_capacity(struct storage *, const char *);
77 static int check_capacity(struct i_fn_args *);
78 static int check_subpartition_selections(struct dfui_response *, struct i_fn_args *);
79 static void save_subpartition_selections(struct dfui_response *, struct i_fn_args *);
80 static void populate_create_subpartitions_form(struct dfui_form *, struct i_fn_args *);
81 static int warn_subpartition_selections(struct i_fn_args *);
82 static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *);
83 static int show_create_subpartitions_form(struct dfui_form *, struct i_fn_args *);
84
85 static const char *def_mountpt[] = {"/boot", "swap", "/", "/build", NULL};
86 static long min_capacity[] = { 128, 0, DISK_MIN - 128, BUILD_MIN };
87 static int expert = 0;
88
89 /*
90 * Given a set of subpartitions-to-be in the selected slice,
91 * create them.
92 */
93 static int
create_subpartitions(struct i_fn_args * a)94 create_subpartitions(struct i_fn_args *a)
95 {
96 struct subpartition *sp;
97 struct commands *cmds;
98 int result = 0;
99 int num_partitions;
100
101 cmds = commands_new();
102 if (!is_file("%sinstall.disklabel.%s",
103 a->tmp,
104 slice_get_device_name(storage_get_selected_slice(a->s)))) {
105 /*
106 * Get a copy of the 'virgin' disklabel.
107 * XXX It might make more sense for this to
108 * happen right after format_slice() instead.
109 */
110 command_add(cmds, "%s%s -r %s >%sinstall.disklabel.%s",
111 a->os_root, cmd_name(a, "DISKLABEL64"),
112 slice_get_device_name(storage_get_selected_slice(a->s)),
113 a->tmp,
114 slice_get_device_name(storage_get_selected_slice(a->s)));
115 }
116
117 /*
118 * Weave together a new disklabel out the of the 'virgin'
119 * disklabel, and the user's subpartition choices.
120 */
121
122 /*
123 * Take everything from the 'virgin' disklabel up until the
124 * '16 partitions' line.
125 */
126 num_partitions = 16;
127 command_add(cmds, "%s%s '$2==\"partitions:\" || cut { cut = 1 } !cut { print $0 }' <%sinstall.disklabel.%s >%sinstall.disklabel",
128 a->os_root, cmd_name(a, "AWK"),
129 a->tmp,
130 slice_get_device_name(storage_get_selected_slice(a->s)),
131 a->tmp);
132
133 /*
134 * 16 partitions:
135 * # size offset fstype
136 * c: 16383969 0 unused # 7999.985MB
137 */
138
139 command_add(cmds, "%s%s '%d partitions:' >>%sinstall.disklabel",
140 a->os_root, cmd_name(a, "ECHO"), num_partitions ,a->tmp);
141 command_add(cmds, "%s%s '%s' >>%sinstall.disklabel",
142 a->os_root, cmd_name(a, "ECHO"),
143 "# size offset fstype",
144 a->tmp);
145
146 #ifdef DEBUG
147 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
148 sp != NULL; sp = subpartition_next(sp)) {
149 command_add(cmds, "%s%s 'mountpoint: %s device: %s'",
150 a->os_root, cmd_name(a, "ECHO"),
151 subpartition_get_mountpoint(sp),
152 subpartition_get_device_name(sp));
153 }
154 #endif
155
156 /*
157 * Write a line for each subpartition the user wants.
158 */
159 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
160 sp != NULL; sp = subpartition_next(sp)) {
161 if (subpartition_is_tmpfsbacked(sp)) {
162 continue;
163 }
164 if (subpartition_is_swap(sp)) {
165 command_add(cmds, "%s%s ' %c:\t%s\t*\tswap' >>%sinstall.disklabel",
166 a->os_root, cmd_name(a, "ECHO"),
167 subpartition_get_letter(sp),
168 capacity_to_string(subpartition_get_capacity(sp)),
169 a->tmp);
170 } else {
171 command_add(cmds, "%s%s ' %c:\t%s\t%s\t4.2BSD' >>%sinstall.disklabel",
172 a->os_root, cmd_name(a, "ECHO"),
173 subpartition_get_letter(sp),
174 capacity_to_string(subpartition_get_capacity(sp)),
175 subpartition_get_letter(sp) == 'a' ? "0" : "*",
176 a->tmp);
177 }
178 }
179 temp_file_add(a, "install.disklabel");
180
181 /*
182 * Label the slice from the disklabel we just wove together.
183 */
184 command_add(cmds, "%s%s -R -B -r %s %sinstall.disklabel",
185 a->os_root, cmd_name(a, "DISKLABEL64"),
186 slice_get_device_name(storage_get_selected_slice(a->s)),
187 a->tmp);
188
189 /*
190 * Create a snapshot of the disklabel we just created
191 * for debugging inspection in the log.
192 */
193 command_add(cmds, "%s%s %s",
194 a->os_root, cmd_name(a, "DISKLABEL64"),
195 slice_get_device_name(storage_get_selected_slice(a->s)));
196
197 /*
198 * If encryption was specified, read the passphrase.
199 */
200 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
201 sp != NULL; sp = subpartition_next(sp)) {
202 if (subpartition_is_encrypted(sp)) {
203 fn_get_passphrase(a, 1);
204 break;
205 }
206 }
207
208 /*
209 * Create filesystems on the newly-created subpartitions.
210 */
211 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
212 sp != NULL; sp = subpartition_next(sp)) {
213 if (subpartition_is_swap(sp) || subpartition_is_tmpfsbacked(sp)) {
214 if (subpartition_is_swap(sp) &&
215 subpartition_is_encrypted(sp)) {
216 command_add(cmds,
217 "%s%s -d /tmp/t1 luksFormat /dev/%s",
218 a->os_root, cmd_name(a, "CRYPTSETUP"),
219 subpartition_get_device_name(sp));
220 command_add(cmds,
221 "%s%s -d /tmp/t1 luksOpen /dev/%s swap",
222 a->os_root, cmd_name(a, "CRYPTSETUP"),
223 subpartition_get_device_name(sp));
224 }
225 continue;
226 }
227
228 if (subpartition_is_encrypted(sp)) {
229 command_add(cmds,
230 "%s%s -d /tmp/t1 luksFormat /dev/%s",
231 a->os_root, cmd_name(a, "CRYPTSETUP"),
232 subpartition_get_device_name(sp));
233 command_add(cmds,
234 "%s%s -d /tmp/t1 luksOpen /dev/%s %s",
235 a->os_root, cmd_name(a, "CRYPTSETUP"),
236 subpartition_get_device_name(sp),
237 subpartition_get_mapper_name(sp, -1));
238 command_add(cmds, "%s%s%s -b %ld -f %ld /dev/mapper/%s",
239 a->os_root, cmd_name(a, "NEWFS"),
240 subpartition_is_softupdated(sp) ? " -U" : "",
241 subpartition_get_bsize(sp),
242 subpartition_get_fsize(sp),
243 subpartition_get_mapper_name(sp, -1));
244 } else {
245 command_add(cmds, "%s%s%s -b %ld -f %ld /dev/%s",
246 a->os_root, cmd_name(a, "NEWFS"),
247 subpartition_is_softupdated(sp) ? " -U" : "",
248 subpartition_get_bsize(sp),
249 subpartition_get_fsize(sp),
250 subpartition_get_device_name(sp));
251 }
252 }
253
254 result = commands_execute(a, cmds);
255 commands_free(cmds);
256 unlink("/tmp/t1");
257
258 return(result);
259 }
260
261 /*
262 * Return default capacity field filler. Return 0 for /build if drive
263 * space minus swap is < 40GB (causes installer to use PFS's on the root
264 * partition instead).
265 */
266 static long
default_capacity(struct storage * s,const char * mtpt)267 default_capacity(struct storage *s, const char *mtpt)
268 {
269 unsigned long boot, root, swap, build;
270 unsigned long capacity;
271 unsigned long mem;
272
273 capacity = slice_get_capacity(storage_get_selected_slice(s)); /* MB */
274 mem = storage_get_memsize(s);
275
276 /*
277 * Slice capacity is at least 10G at this point. Calculate basic
278 * defaults.
279 */
280 swap = 2 * mem;
281 if (swap > capacity / 10) /* max 1/10 capacity */
282 swap = capacity / 10;
283 if (swap < SWAP_MIN) /* having a little is nice */
284 swap = SWAP_MIN;
285 if (swap > SWAP_MAX) /* installer cap */
286 swap = SWAP_MAX;
287
288 boot = 1024;
289
290 build = (capacity - swap - boot) / 4;
291 #if 0
292 /*
293 * No longer cap the size of /build, the assumption didn't hold
294 * well particularly with /var/crash being placed on /build now.
295 */
296 if (build > BUILD_MAX)
297 build = BUILD_MAX;
298 #endif
299
300 for (;;) {
301 root = (capacity - swap - boot - build);
302
303 /*
304 * Adjust until the defaults look sane
305 *
306 * root should be at least twice as large as build
307 */
308 if (build && root < build * 2) {
309 --build;
310 continue;
311 }
312
313 /*
314 * root should be at least 1/2 capacity
315 */
316 if (build && root < capacity / 2) {
317 --build;
318 continue;
319 }
320 break;
321 }
322
323 /*
324 * Finalize. If build is too small do not supply a /build,
325 * and if swap is too small do not supply swap. Cascade the
326 * released space to swap and root.
327 */
328 if (build < BUILD_MIN) {
329 if (swap < SWAP_MIN && build >= SWAP_MIN - swap) {
330 build -= SWAP_MIN - swap;
331 swap = SWAP_MIN;
332 }
333 if (swap < 2 * mem && build >= 2 * mem - swap) {
334 build -= 2 * mem - swap;
335 swap = 2 * mem;
336 }
337 root += build;
338 build = 0;
339 }
340 if (swap < SWAP_MIN) {
341 root += swap;
342 swap = 0;
343 }
344
345 if (build == 0)
346 root = -1; /* no /build, root is the last part */
347 else
348 build = -1; /* last partition just use remaining space */
349
350 if (strcmp(mtpt, "/boot") == 0)
351 return(boot);
352 else if (strcmp(mtpt, "/build") == 0)
353 return(build);
354 else if (strcmp(mtpt, "swap") == 0)
355 return(swap);
356 else if (strcmp(mtpt, "/") == 0)
357 return(root);
358
359 /* shouldn't ever happen */
360 return(-1);
361 }
362
363 static int
check_capacity(struct i_fn_args * a)364 check_capacity(struct i_fn_args *a)
365 {
366 struct subpartition *sp;
367 unsigned long total_capacity = 0;
368 int mtpt;
369
370 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
371 sp != NULL; sp = subpartition_next(sp)) {
372 long subpart_capacity = subpartition_get_capacity(sp);
373 const char *mountpt = subpartition_get_mountpoint(sp);
374
375 if (subpart_capacity == -1)
376 total_capacity++;
377 else
378 total_capacity += subpart_capacity;
379 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) {
380 if (strcmp(mountpt, def_mountpt[mtpt]) == 0 &&
381 subpart_capacity < min_capacity[mtpt] &&
382 subpart_capacity != -1) {
383 inform(a->c, _("WARNING: The size (%ldM) specified for "
384 "the %s subpartition is too small. It "
385 "should be at least %ldM or you will "
386 "risk running out of space during "
387 "the installation."),
388 subpart_capacity, mountpt,
389 min_capacity[mtpt]);
390 }
391 }
392 }
393
394 if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) {
395 inform(a->c, _("The space allocated to all of your selected "
396 "subpartitions (%luM) exceeds the total "
397 "capacity of the selected primary partition "
398 "(%luM). Remove some subpartitions or choose "
399 "a smaller size for them and try again."),
400 total_capacity, slice_get_capacity(storage_get_selected_slice(a->s)));
401 return(0);
402 }
403
404 return(1);
405 }
406
407 static int
check_subpartition_selections(struct dfui_response * r,struct i_fn_args * a)408 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a)
409 {
410 struct dfui_dataset *ds;
411 struct dfui_dataset *star_ds = NULL;
412 struct aura_dict *d;
413 const char *mountpoint, *capstring;
414 long capacity = 0;
415 int found_root = 0;
416 int valid = 1;
417
418 d = aura_dict_new(1, AURA_DICT_LIST);
419
420 if ((ds = dfui_response_dataset_get_first(r)) == NULL) {
421 inform(a->c, _("Please set up at least one subpartition."));
422 valid = 0;
423 }
424
425 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL;
426 ds = dfui_dataset_get_next(ds)) {
427 #ifdef DEBUG
428 dfui_dataset_dump(ds);
429 #endif
430 mountpoint = dfui_dataset_get_value(ds, "mountpoint");
431 capstring = dfui_dataset_get_value(ds, "capacity");
432
433 if (expert) {
434 int tmpfsbacked;
435
436 tmpfsbacked = (strcmp(dfui_dataset_get_value(ds, "tmpfsbacked"), "Y") == 0);
437 if (tmpfsbacked && strcmp(mountpoint, "/") == 0) {
438 inform(a->c, _("/ cannot be TMPFS backed."));
439 valid = 0;
440 }
441 }
442
443 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) {
444 inform(a->c, _("The same mount point cannot be specified "
445 "for two different subpartitions."));
446 valid = 0;
447 }
448
449 if (strcmp(mountpoint, "/") == 0)
450 found_root = 1;
451
452 if (strcmp(capstring, "*") == 0) {
453 if (star_ds != NULL) {
454 inform(a->c, _("You cannot have more than one subpartition "
455 "with a '*' capacity (meaning 'use the remainder "
456 "of the primary partition'.)"));
457 valid = 0;
458 } else {
459 star_ds = ds;
460 }
461 }
462
463 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) {
464 inform(a->c, _("Mount point must be either 'swap', or it must "
465 "start with a '/'."));
466 valid = 0;
467 }
468
469 if (strpbrk(mountpoint, " \\\"'`") != NULL) {
470 inform(a->c, _("Mount point may not contain the following "
471 "characters: blank space, backslash, or "
472 "single, double, or back quotes."));
473 valid = 0;
474 }
475
476 if (strlen(capstring) == 0) {
477 inform(a->c, _("A capacity must be specified."));
478 valid = 0;
479 }
480
481 if (!string_to_capacity(capstring, &capacity)) {
482 inform(a->c, _("Capacity must be either a '*' symbol "
483 "to indicate 'use the rest of the primary "
484 "partition', or it must be a series of decimal "
485 "digits ending with an 'M' (indicating "
486 "megabytes), a 'G' (indicating gigabytes) and "
487 "so on (up to 'E'.)"));
488 valid = 0;
489 }
490
491 /*
492 * Maybe remove this limit entirely?
493 */
494 if ((strcasecmp(mountpoint, "swap") == 0) &&
495 (capacity > SWAP_MAX)) {
496 inform(a->c, _("Swap capacity is limited to %dG."),
497 SWAP_MAX / 1024);
498 valid = 0;
499 }
500
501 /*
502 * If we made it through that obstacle course, all is well.
503 */
504
505 if (valid)
506 aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1);
507 }
508
509 if (!found_root) {
510 inform(a->c, _("You must include a / (root) subpartition."));
511 valid = 0;
512 }
513
514 if (aura_dict_size(d) > 16) {
515 inform(a->c, _("You cannot have more than 16 subpartitions "
516 "on a single primary partition. Remove some "
517 "and try again."));
518 valid = 0;
519 }
520
521 aura_dict_free(d);
522
523 return(valid);
524 }
525
526 static void
save_subpartition_selections(struct dfui_response * r,struct i_fn_args * a)527 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a)
528 {
529 struct dfui_dataset *ds;
530 char tmpfsbacked;
531 const char *mountpoint, *capstring;
532 long capacity;
533 long bsize, fsize;
534 int softupdates;
535 int valid = 1;
536
537 subpartitions_free(storage_get_selected_slice(a->s));
538
539 for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL;
540 ds = dfui_dataset_get_next(ds)) {
541 mountpoint = dfui_dataset_get_value(ds, "mountpoint");
542 capstring = dfui_dataset_get_value(ds, "capacity");
543
544 if (expert) {
545 softupdates =
546 (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0);
547 fsize = atol(dfui_dataset_get_value(ds, "fsize"));
548 bsize = atol(dfui_dataset_get_value(ds, "bsize"));
549 tmpfsbacked = (strcmp(dfui_dataset_get_value(ds, "tmpfsbacked"), "Y") == 0);
550 } else {
551 softupdates = (strcmp(mountpoint, "/boot") == 0 ? 0 : 1);
552 tmpfsbacked = 0;
553 fsize = -1;
554 bsize = -1;
555 }
556
557 if (string_to_capacity(capstring, &capacity)) {
558 subpartition_new_ufs(storage_get_selected_slice(a->s),
559 mountpoint, capacity,
560 strcasecmp(dfui_dataset_get_value(ds, "encrypted"), "Y") == 0,
561 softupdates, fsize, bsize, tmpfsbacked);
562 }
563 }
564 }
565
566 static void
populate_create_subpartitions_form(struct dfui_form * f,struct i_fn_args * a)567 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a)
568 {
569 struct subpartition *sp;
570 struct dfui_dataset *ds;
571 char temp[32];
572 int mtpt;
573 long capacity;
574
575 if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) {
576 /*
577 * The user has already given us their subpartition
578 * preferences, so use them here.
579 */
580 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
581 sp != NULL; sp = subpartition_next(sp)) {
582 ds = dfui_dataset_new();
583 dfui_dataset_celldata_add(ds, "mountpoint",
584 subpartition_get_mountpoint(sp));
585 dfui_dataset_celldata_add(ds, "capacity",
586 capacity_to_string(subpartition_get_capacity(sp)));
587 dfui_dataset_celldata_add(ds, "encrypted",
588 subpartition_is_encrypted(sp) ? "Y" : "N");
589 if (expert) {
590 dfui_dataset_celldata_add(ds, "softupdates",
591 subpartition_is_softupdated(sp) ? "Y" : "N");
592 dfui_dataset_celldata_add(ds, "tmpfsbacked",
593 subpartition_is_tmpfsbacked(sp) ? "Y" : "N");
594 snprintf(temp, 32, "%ld", subpartition_get_fsize(sp));
595 dfui_dataset_celldata_add(ds, "fsize",
596 temp);
597 snprintf(temp, 32, "%ld", subpartition_get_bsize(sp));
598 dfui_dataset_celldata_add(ds, "bsize",
599 temp);
600 }
601 dfui_form_dataset_add(f, ds);
602 }
603 } else {
604 /*
605 * Otherwise, populate the form with datasets representing
606 * reasonably-calculated defaults. The defaults are chosen
607 * based on the slice's total capacity and the machine's
608 * total physical memory (for swap.)
609 */
610 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) {
611 capacity = default_capacity(a->s, def_mountpt[mtpt]);
612 if (capacity == 0)
613 continue;
614 ds = dfui_dataset_new();
615 dfui_dataset_celldata_add(ds, "mountpoint",
616 def_mountpt[mtpt]);
617 dfui_dataset_celldata_add(ds, "capacity",
618 capacity_to_string(capacity));
619 dfui_dataset_celldata_add(ds, "encrypted", "N");
620 if (expert) {
621 dfui_dataset_celldata_add(ds, "softupdates",
622 strcmp(def_mountpt[mtpt], "/boot") != 0 ? "Y" : "N");
623 dfui_dataset_celldata_add(ds, "tmpfsbacked",
624 "N");
625 dfui_dataset_celldata_add(ds, "fsize",
626 capacity < 1024 ? "1024" : "2048");
627 dfui_dataset_celldata_add(ds, "bsize",
628 capacity < 1024 ? "8192" : "16384");
629 }
630 dfui_form_dataset_add(f, ds);
631 }
632 }
633 }
634
635 static int
warn_subpartition_selections(struct i_fn_args * a)636 warn_subpartition_selections(struct i_fn_args *a)
637 {
638 int valid = 0;
639
640 /* Skip this check for disks <= 8GB */
641 if (slice_get_capacity(storage_get_selected_slice(a->s)) <= 8192)
642 return 0;
643
644 valid = check_capacity(a);
645
646 return(!valid);
647 }
648
649 static struct dfui_form *
make_create_subpartitions_form(struct i_fn_args * a)650 make_create_subpartitions_form(struct i_fn_args *a)
651 {
652 struct dfui_field *fi;
653 struct dfui_form *f;
654 char msg_buf[1][1024];
655
656 snprintf(msg_buf[0], sizeof(msg_buf[0]),
657 _("Subpartitions further divide a primary partition for "
658 "use with %s. Some reasons you may want "
659 "a set of subpartitions are:\n\n"
660 "- you want to restrict how much data can be written "
661 "to certain parts of the primary partition, to quell "
662 "denial-of-service attacks; and\n"
663 "- you want to speed up access to data on the disk."
664 ""), OPERATING_SYSTEM_NAME);
665
666 f = dfui_form_create(
667 "create_subpartitions",
668 _("Create Subpartitions"),
669 _("Set up the subpartitions (also known as just `partitions' "
670 "in BSD tradition) you want to have on this primary "
671 "partition.\n\n"
672 "For Capacity, use 'M' to indicate megabytes, 'G' to "
673 "indicate gigabytes, and so on (up to 'E'.) A single '*' "
674 "indicates 'use the remaining space on the primary partition'."),
675
676 msg_buf[0],
677
678 "p", "special", "dfinstaller_create_subpartitions",
679 "p", "minimum_width","64",
680
681 "f", "mountpoint", _("Mountpoint"), "", "",
682 "f", "capacity", _("Capacity"), "", "",
683
684 "f", "encrypted", _("Encrypted"), "", "",
685 "p", "control", "checkbox",
686
687 "a", "ok", _("Accept and Create"), "", "",
688 "a", "cancel",
689 (disk_get_formatted(storage_get_selected_disk(a->s)) ?
690 _("Return to Select Disk") :
691 _("Return to Select Primary Partition")), "", "",
692 "p", "accelerator", "ESC",
693
694 NULL
695 );
696
697 dfui_form_set_multiple(f, 1);
698 dfui_form_set_extensible(f, 1);
699
700 if (expert) {
701 fi = dfui_form_field_add(f, "softupdates",
702 dfui_info_new(_("Softupdates"), "", ""));
703 dfui_field_property_set(fi, "control", "checkbox");
704
705 fi = dfui_form_field_add(f, "tmpfsbacked",
706 dfui_info_new(_("TMPFS"), "", ""));
707 dfui_field_property_set(fi, "control", "checkbox");
708
709 fi = dfui_form_field_add(f, "fsize",
710 dfui_info_new(_("Frag Sz"), "", ""));
711
712 fi = dfui_form_field_add(f, "bsize",
713 dfui_info_new(_("Block Sz"), "", ""));
714
715 dfui_form_action_add(f, "switch",
716 dfui_info_new(_("Switch to Normal Mode"), "", ""));
717 } else {
718 dfui_form_action_add(f, "switch",
719 dfui_info_new(_("Switch to Expert Mode"), "", ""));
720 }
721
722 return(f);
723 }
724
725 /*
726 * Returns:
727 * -1 = the form should be redisplayed
728 * 0 = failure, function is over
729 * 1 = success, function is over
730 */
731 static int
show_create_subpartitions_form(struct dfui_form * f,struct i_fn_args * a)732 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a)
733 {
734 struct dfui_dataset *ds;
735 struct dfui_response *r;
736
737 for (;;) {
738 if (dfui_form_dataset_get_first(f) == NULL)
739 populate_create_subpartitions_form(f, a);
740
741 if (!dfui_be_present(a->c, f, &r))
742 abort_backend();
743
744 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) {
745 dfui_response_free(r);
746 return(0);
747 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) {
748 if (check_subpartition_selections(r, a)) {
749 save_subpartition_selections(r, a);
750 expert = expert ? 0 : 1;
751 dfui_response_free(r);
752 return(-1);
753 }
754 } else {
755 if (check_subpartition_selections(r, a)) {
756 save_subpartition_selections(r, a);
757 if (!warn_subpartition_selections(a)) {
758 if (!create_subpartitions(a)) {
759 inform(a->c, _("The subpartitions you chose were "
760 "not correctly created, and the "
761 "primary partition may "
762 "now be in an inconsistent state. "
763 "We recommend re-formatting it "
764 "before proceeding."));
765 dfui_response_free(r);
766 return(0);
767 } else {
768 dfui_response_free(r);
769 return(1);
770 }
771 }
772 }
773 }
774
775 dfui_form_datasets_free(f);
776 /* dfui_form_datasets_add_from_response(f, r); */
777 for (ds = dfui_response_dataset_get_first(r); ds != NULL;
778 ds = dfui_dataset_get_next(ds)) {
779 dfui_form_dataset_add(f, dfui_dataset_dup(ds));
780 }
781 }
782 }
783
784 void
fn_create_subpartitions_ufs(struct i_fn_args * a)785 fn_create_subpartitions_ufs(struct i_fn_args *a)
786 {
787 struct dfui_form *f;
788 unsigned long capacity;
789 int done = 0;
790
791 a->result = 0;
792 capacity = slice_get_capacity(storage_get_selected_slice(a->s));
793 if (capacity < DISK_MIN) {
794 inform(a->c, _("The selected disk is smaller than the "
795 "required %dM for the UFS filesystem."), DISK_MIN);
796 return;
797 }
798 #if 0
799 if (capacity <= 8192)
800 def_mountpt[2] = NULL; /* XXX adjust each time in a session */
801 #endif
802 while (!done) {
803 f = make_create_subpartitions_form(a);
804 switch (show_create_subpartitions_form(f, a)) {
805 case -1:
806 done = 0;
807 break;
808 case 0:
809 done = 1;
810 a->result = 0;
811 break;
812 case 1:
813 done = 1;
814 a->result = 1;
815 break;
816 }
817 dfui_form_free(f);
818 }
819 }
820