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