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