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