Merge branch 'master' of /home/aggelos/devel/dfly/dfly.git/
[dragonfly.git] / usr.sbin / installer / dfuibe_installer / fn_subpart_ufs.c
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 }