Merge from vendor branch TNFTP:
[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 "aura/mem.h"
52 #include "aura/buffer.h"
53 #include "aura/dict.h"
54 #include "aura/fspred.h"
55
56 #include "dfui/dfui.h"
57 #include "dfui/dump.h"
58 #include "dfui/system.h"
59
60 #include "installer/commands.h"
61 #include "installer/diskutil.h"
62 #include "installer/functions.h"
63 #include "installer/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                 total_capacity += subpartition_get_capacity(sp);
345                 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) {
346                         if (strcmp(subpartition_get_mountpoint(sp), def_mountpt[mtpt]) == 0 &&
347                             min_capacity[mtpt] > 0 &&
348                             subpartition_get_capacity(sp) < min_capacity[mtpt]) {
349                                 inform(a->c, _("WARNING: the %s subpartition should "
350                                     "be at least %dM in size or you will "
351                                     "risk running out of space during "
352                                     "the installation."),
353                                     subpartition_get_mountpoint(sp), min_capacity[mtpt]);
354                         }
355                 }
356         }
357
358         if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) {
359                 inform(a->c, _("The space allocated to all of your selected "
360                     "subpartitions (%dM) exceeds the total "
361                     "capacity of the selected primary partition "
362                     "(%dM). Remove some subpartitions or choose "
363                     "a smaller size for them and try again."),
364                     total_capacity, slice_get_capacity(storage_get_selected_slice(a->s)));
365                 return(0);
366         }
367
368         return(1);
369 }
370
371 static int
372 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a)
373 {
374         struct dfui_dataset *ds;
375         struct dfui_dataset *star_ds = NULL;
376         struct aura_dict *d;
377         const char *mountpoint, *capstring;
378         long capacity = 0;
379         long bsize, fsize;
380         int found_root = 0;
381         int softupdates, mfsbacked;
382         int valid = 1;
383
384         d = aura_dict_new(1, AURA_DICT_LIST);
385
386         if ((ds = dfui_response_dataset_get_first(r)) == NULL) {
387                 inform(a->c, _("Please set up at least one subpartition."));
388                 valid = 0;
389         }
390
391         for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL;
392             ds = dfui_dataset_get_next(ds)) {
393 #ifdef DEBUG
394                 dfui_dataset_dump(ds);
395 #endif
396                 mountpoint = dfui_dataset_get_value(ds, "mountpoint");
397                 capstring = dfui_dataset_get_value(ds, "capacity");
398
399                 if (expert) {
400                         softupdates =
401                             (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0);
402                         fsize = atol(dfui_dataset_get_value(ds, "fsize"));
403                         bsize = atol(dfui_dataset_get_value(ds, "bsize"));
404                         mfsbacked = (strcmp(dfui_dataset_get_value(ds, "mfsbacked"), "Y") == 0);
405                 } else {
406                         softupdates = (strcmp(mountpoint, "/") == 0 ? 0 : 1);
407                         mfsbacked = (strcmp(mountpoint, "/tmp") == 0 ? 0 : 1);
408                         fsize = -1;
409                         bsize = -1;
410                 }
411
412                 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) {
413                         inform(a->c, _("The same mount point cannot be specified "
414                             "for two different subpartitions."));
415                         valid = 0;
416                 }
417
418                 if (strcmp(mountpoint, "/") == 0)
419                         found_root = 1;
420
421                 if (strcmp(capstring, "*") == 0) {
422                         if (star_ds != NULL) {
423                                 inform(a->c, _("You cannot have more than one subpartition "
424                                     "with a '*' capacity (meaning 'use the remainder "
425                                     "of the primary partition'.)"));
426                                 valid = 0;
427                         } else {
428                                 star_ds = ds;
429                         }
430                 }
431                 
432                 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) {
433                         inform(a->c, _("Mount point must be either 'swap', or it must "
434                             "start with a '/'."));
435                         valid = 0;
436                 }
437
438                 if (strpbrk(mountpoint, " \\\"'`") != NULL) {
439                         inform(a->c, _("Mount point may not contain the following "
440                             "characters: blank space, backslash, or "
441                             "single, double, or back quotes."));
442                         valid = 0;
443                 }
444                 
445                 if (strlen(capstring) == 0) {
446                         inform(a->c, _("A capacity must be specified."));
447                         valid = 0;
448                 }
449
450                 if (!string_to_capacity(capstring, &capacity)) {
451                         inform(a->c, _("Capacity must be either a '*' symbol to indicate "
452                             "'use the rest of the primary partition', or it "
453                             "must be a series of decimal digits ending with a "
454                             "'M' (indicating megabytes) or a 'G' (indicating "
455                             "gigabytes.)"));
456                         valid = 0;
457                 }
458
459                 /*
460                  * If we made it through that obstacle course, all is well.
461                  */
462
463                 if (valid)
464                         aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1);
465         }
466
467         if (!found_root) {
468                 inform(a->c, _("You must include a / (root) subpartition."));
469                 valid = 0;
470         }
471
472         if (aura_dict_size(d) > 16) {
473                 inform(a->c, _("You cannot have more than 16 subpartitions "
474                     "on a single primary partition.  Remove some "
475                     "and try again."));
476                 valid = 0;
477         }
478
479         aura_dict_free(d);
480
481         return(valid);
482 }
483
484 static void
485 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a)
486 {
487         struct dfui_dataset *ds;
488         char mfsbacked;
489         const char *mountpoint, *capstring;
490         long capacity;
491         long bsize, fsize;
492         int softupdates;
493         int valid = 1;
494
495         subpartitions_free(storage_get_selected_slice(a->s));
496
497         for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL;
498             ds = dfui_dataset_get_next(ds)) {
499                 mountpoint = dfui_dataset_get_value(ds, "mountpoint");
500                 capstring = dfui_dataset_get_value(ds, "capacity");
501
502                 if (expert) {
503                         softupdates =
504                             (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0);
505                         fsize = atol(dfui_dataset_get_value(ds, "fsize"));
506                         bsize = atol(dfui_dataset_get_value(ds, "bsize"));
507                         mfsbacked = (strcmp(dfui_dataset_get_value(ds, "msfbacked"), "Y") == 0);
508                 } else {
509                         softupdates = (strcmp(mountpoint, "/") == 0 ? 0 : 1);
510                         mfsbacked = 0;
511                         fsize = -1;
512                         bsize = -1;
513                 }
514
515                 if (string_to_capacity(capstring, &capacity)) {
516                         subpartition_new(storage_get_selected_slice(a->s), mountpoint, capacity,
517                             softupdates, fsize, bsize, mfsbacked);
518                 }
519         }
520 }
521
522 static void
523 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a)
524 {
525         struct subpartition *sp;
526         struct dfui_dataset *ds;
527         char temp[32];
528         int mtpt;
529         long capacity;
530
531         if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) {
532                 /*
533                  * The user has already given us their subpartition
534                  * preferences, so use them here.
535                  */
536                 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
537                      sp != NULL; sp = subpartition_next(sp)) {
538                         ds = dfui_dataset_new();
539                         dfui_dataset_celldata_add(ds, "mountpoint",
540                             subpartition_get_mountpoint(sp));
541                         dfui_dataset_celldata_add(ds, "capacity",
542                             capacity_to_string(subpartition_get_capacity(sp)));
543                         if (expert) {
544                                 dfui_dataset_celldata_add(ds, "softupdates",
545                                     subpartition_is_softupdated(sp) ? "Y" : "N");
546                                 dfui_dataset_celldata_add(ds, "mfsbacked",
547                                     subpartition_is_mfsbacked(sp) ? "Y" : "N");
548                                 snprintf(temp, 32, "%ld", subpartition_get_fsize(sp));
549                                 dfui_dataset_celldata_add(ds, "fsize",
550                                     temp);
551                                 snprintf(temp, 32, "%ld", subpartition_get_bsize(sp));
552                                 dfui_dataset_celldata_add(ds, "bsize",
553                                     temp);
554                         }
555                         dfui_form_dataset_add(f, ds);
556                 }
557         } else {
558                 /*
559                  * Otherwise, populate the form with datasets representing
560                  * reasonably-calculated defaults.  The defaults are chosen
561                  * based on the slice's total capacity and the machine's
562                  * total physical memory (for swap.)
563                  */
564                 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) {
565                         capacity = default_capacity(a->s, mtpt);
566                         ds = dfui_dataset_new();
567                         dfui_dataset_celldata_add(ds, "mountpoint",
568                             def_mountpt[mtpt]);
569                         dfui_dataset_celldata_add(ds, "capacity",
570                             capacity_to_string(capacity));
571                         if (expert) {
572                                 dfui_dataset_celldata_add(ds, "softupdates",
573                                     strcmp(def_mountpt[mtpt], "/") != 0 ? "Y" : "N");
574                                 dfui_dataset_celldata_add(ds, "mfsbacked",
575                                     "N");
576                                 dfui_dataset_celldata_add(ds, "fsize",
577                                     capacity < 1024 ? "1024" : "2048");
578                                 dfui_dataset_celldata_add(ds, "bsize",
579                                     capacity < 1024 ? "8192" : "16384");
580                         }
581                         dfui_form_dataset_add(f, ds);
582                 }
583         }
584 }
585
586 static int
587 warn_subpartition_selections(struct i_fn_args *a)
588 {
589         int valid = 0;
590         struct aura_buffer *omit, *consequences;
591
592         omit = aura_buffer_new(2048);
593         consequences = aura_buffer_new(2048);
594
595         valid = check_capacity(a);
596         if (subpartition_find(storage_get_selected_slice(a->s), "/var") == NULL) {
597                 aura_buffer_cat(omit, "/var ");
598                 aura_buffer_cat(consequences, _("/var will be a plain dir in /\n"));
599         }
600         if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL) {
601                 aura_buffer_cat(omit, "/usr ");
602                 aura_buffer_cat(consequences, _("/usr will be a plain dir in /\n"));
603         }
604         if (subpartition_find(storage_get_selected_slice(a->s), "/tmp") == NULL) {
605                 aura_buffer_cat(omit, "/tmp ");
606                 aura_buffer_cat(consequences, _("/tmp will be symlinked to /var/tmp\n"));
607         }
608         if (subpartition_find(storage_get_selected_slice(a->s), "/home") == NULL) {
609                 aura_buffer_cat(omit, "/home ");
610                 aura_buffer_cat(consequences, _("/home will be symlinked to /usr/home\n"));
611         }
612
613         if (valid && aura_buffer_len(omit) > 0) {
614                 switch (dfui_be_present_dialog(a->c, _("Really omit?"),
615                     _("Omit Subpartition(s)|Return to Create Subpartitions"),
616                     _("You have elected to not have the following "
617                     "subpartition(s):\n\n%s\n\n"
618                     "The ramifications of these subpartition(s) being "
619                     "missing will be:\n\n%s\n"
620                     "Is this really what you want to do?"),
621                     aura_buffer_buf(omit), aura_buffer_buf(consequences))) {
622                 case 1:
623                         valid = 1;
624                         break;
625                 case 2:
626                         valid = 0;
627                         break;
628                 default:
629                         abort_backend();
630                 }
631         }
632
633         aura_buffer_free(omit);
634         aura_buffer_free(consequences);
635
636         return(!valid);
637 }
638
639 static struct dfui_form *
640 make_create_subpartitions_form(struct i_fn_args *a)
641 {
642         struct dfui_field *fi;
643         struct dfui_form *f;
644         char msg_buf[1][1024];
645
646         snprintf(msg_buf[0], sizeof(msg_buf[0]),
647             _("Subpartitions further divide a primary partition for "
648             "use with %s.  Some reasons you may want "
649             "a set of subpartitions are:\n\n"
650             "- you want to restrict how much data can be written "
651             "to certain parts of the primary partition, to quell "
652             "denial-of-service attacks; and\n"
653             "- you want to speed up access to data on the disk."
654             ""), OPERATING_SYSTEM_NAME);
655
656         f = dfui_form_create(
657             "create_subpartitions",
658             _("Create Subpartitions"),
659             _("Set up the subpartitions (also known as just `partitions' "
660             "in BSD tradition) you want to have on this primary "
661             "partition.\n\n"
662             "For Capacity, use 'M' to indicate megabytes, 'G' to "
663             "indicate gigabytes, or a single '*' to indicate "
664             "'use the remaining space on the primary partition'."),
665
666             msg_buf[0],
667
668             "p", "special", "dfinstaller_create_subpartitions",
669             "p", "minimum_width","64",
670
671             "f", "mountpoint", _("Mountpoint"), "", "",
672             "f", "capacity", _("Capacity"), "", "",
673
674             "a", "ok", _("Accept and Create"), "", "",
675             "a", "cancel", 
676             (disk_get_formatted(storage_get_selected_disk(a->s)) ?
677             _("Return to Select Disk") :
678             _("Return to Select Primary Partition")), "", "",
679             "p", "accelerator", "ESC",
680
681             NULL
682         );
683
684         dfui_form_set_multiple(f, 1);
685         dfui_form_set_extensible(f, 1);
686
687         if (expert) {
688                 fi = dfui_form_field_add(f, "softupdates",
689                     dfui_info_new(_("Softupdates"), "", ""));
690                 dfui_field_property_set(fi, "control", "checkbox");
691
692                 fi = dfui_form_field_add(f, "mfsbacked",
693                     dfui_info_new(_("MFS"), "", ""));
694                 dfui_field_property_set(fi, "control", "checkbox");
695
696                 fi = dfui_form_field_add(f, "fsize",
697                     dfui_info_new(_("Frag Sz"), "", ""));
698
699                 fi = dfui_form_field_add(f, "bsize",
700                     dfui_info_new(_("Block Sz"), "", ""));
701
702                 dfui_form_action_add(f, "switch",
703                     dfui_info_new(_("Switch to Normal Mode"), "", ""));
704         } else {
705                 dfui_form_action_add(f, "switch",
706                     dfui_info_new(_("Switch to Expert Mode"), "", ""));
707         }
708
709         return(f);
710 }
711
712 /*
713  * Returns:
714  *      -1 = the form should be redisplayed
715  *       0 = failure, function is over
716  *       1 = success, function is over
717  */
718 static int
719 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a)
720 {
721         struct dfui_dataset *ds;
722         struct dfui_response *r;
723
724         for (;;) {
725                 if (dfui_form_dataset_get_first(f) == NULL)
726                         populate_create_subpartitions_form(f, a);
727
728                 if (!dfui_be_present(a->c, f, &r))
729                         abort_backend();
730         
731                 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) {
732                         dfui_response_free(r);
733                         return(0);
734                 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) {
735                         if (check_subpartition_selections(r, a)) {
736                                 save_subpartition_selections(r, a);
737                                 expert = expert ? 0 : 1;
738                                 dfui_response_free(r);
739                                 return(-1);
740                         }
741                 } else {
742                         if (check_subpartition_selections(r, a)) {
743                                 save_subpartition_selections(r, a);
744                                 if (!warn_subpartition_selections(a)) {
745                                         if (!create_subpartitions(a)) {
746                                                 inform(a->c, _("The subpartitions you chose were "
747                                                         "not correctly created, and the "
748                                                         "primary partition may "
749                                                         "now be in an inconsistent state. "
750                                                         "We recommend re-formatting it "
751                                                         "before proceeding."));
752                                                 dfui_response_free(r);
753                                                 return(0);
754                                         } else {
755                                                 dfui_response_free(r);
756                                                 return(1);
757                                         }
758                                 }
759                         }
760                 }
761                 
762                 dfui_form_datasets_free(f);
763                 /* dfui_form_datasets_add_from_response(f, r); */
764                 for (ds = dfui_response_dataset_get_first(r); ds != NULL;
765                     ds = dfui_dataset_get_next(ds)) {
766                         dfui_form_dataset_add(f, dfui_dataset_dup(ds));
767                 }
768         }
769 }
770
771 /*
772  * fn_create_subpartitions: let the user specify what subpartitions they
773  * want on the disk, how large each should be, and where it should be mounted.
774  */
775 void
776 fn_create_subpartitions(struct i_fn_args *a)
777 {
778         struct dfui_form *f;
779         int done = 0;
780
781         a->result = 0;
782         while (!done) {
783                 f = make_create_subpartitions_form(a);
784                 switch (show_create_subpartitions_form(f, a)) {
785                 case -1:
786                         done = 0;
787                         break;
788                 case 0:
789                         done = 1;
790                         a->result = 0;
791                         break;
792                 case 1:
793                         done = 1;
794                         a->result = 1;
795                         break;
796                 }
797                 dfui_form_free(f);
798         }
799 }