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