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