installer: Clean up check_capacity() (UFS) a bit and improve the message.
[dragonfly.git] / usr.sbin / installer / dfuibe_installer / fn_subpart_ufs.c
1 /*
2  * Copyright (c)2004 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  *   Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in
13  *   the documentation and/or other materials provided with the
14  *   distribution.
15  *
16  *   Neither the name of the DragonFly Project nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /*
35  * fn_subpart.c
36  * Installer Function : Create Subpartitions.
37  * $Id: fn_subpart.c,v 1.50 2005/04/07 20:22:40 cpressey Exp $
38  */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #ifdef ENABLE_NLS
45 #include <libintl.h>
46 #define _(String) gettext (String)
47 #else
48 #define _(String) (String)
49 #endif
50
51 #include "libaura/mem.h"
52 #include "libaura/buffer.h"
53 #include "libaura/dict.h"
54 #include "libaura/fspred.h"
55
56 #include "libdfui/dfui.h"
57 #include "libdfui/dump.h"
58 #include "libdfui/system.h"
59
60 #include "libinstaller/commands.h"
61 #include "libinstaller/diskutil.h"
62 #include "libinstaller/functions.h"
63 #include "libinstaller/uiutil.h"
64
65 #include "fn.h"
66 #include "flow.h"
67 #include "pathnames.h"
68
69 static int      create_subpartitions(struct i_fn_args *);
70 static long     default_capacity(struct storage *, int);
71 static int      check_capacity(struct i_fn_args *);
72 static int      check_subpartition_selections(struct dfui_response *, struct i_fn_args *);
73 static void     save_subpartition_selections(struct dfui_response *, struct i_fn_args *);
74 static void     populate_create_subpartitions_form(struct dfui_form *, struct i_fn_args *);
75 static int      warn_subpartition_selections(struct i_fn_args *);
76 static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *);
77 static int      show_create_subpartitions_form(struct dfui_form *, struct i_fn_args *);
78
79 static const char *def_mountpt[]  = {"/", "swap", "/var", "/tmp", "/usr", "/home", NULL};
80 static int expert = 0;
81
82 /*
83  * Given a set of subpartitions-to-be in the selected slice,
84  * create them.
85  */
86 static int
87 create_subpartitions(struct i_fn_args *a)
88 {
89         struct subpartition *sp;
90         struct commands *cmds;
91         int result = 0;
92         int num_partitions;
93
94         cmds = commands_new();
95         if (!is_file("%sinstall.disklabel.%s",
96             a->tmp,
97             slice_get_device_name(storage_get_selected_slice(a->s)))) {
98                 /*
99                  * Get a copy of the 'virgin' disklabel.
100                  * XXX It might make more sense for this to
101                  * happen right after format_slice() instead.
102                  */
103                 command_add(cmds, "%s%s -r %s >%sinstall.disklabel.%s",
104                     a->os_root, cmd_name(a, "DISKLABEL64"),
105                     slice_get_device_name(storage_get_selected_slice(a->s)),
106                     a->tmp,
107                     slice_get_device_name(storage_get_selected_slice(a->s)));
108         }
109
110         /*
111          * Weave together a new disklabel out the of the 'virgin'
112          * disklabel, and the user's subpartition choices.
113          */
114
115         /*
116          * Take everything from the 'virgin' disklabel up until the
117          * '16 partitions' line.
118          */
119         num_partitions = 16;
120         command_add(cmds, "%s%s '$2==\"partitions:\" || cut { cut = 1 } !cut { print $0 }' <%sinstall.disklabel.%s >%sinstall.disklabel",
121             a->os_root, cmd_name(a, "AWK"),
122             a->tmp,
123             slice_get_device_name(storage_get_selected_slice(a->s)),
124             a->tmp);
125
126         /*
127          * 16 partitions:
128          * #          size     offset    fstype
129          *   c:   16383969          0    unused #    7999.985MB
130          */
131
132         command_add(cmds, "%s%s '%d partitions:' >>%sinstall.disklabel",
133             a->os_root, cmd_name(a, "ECHO"), num_partitions ,a->tmp);
134         command_add(cmds, "%s%s '%s' >>%sinstall.disklabel",
135             a->os_root, cmd_name(a, "ECHO"),
136             "#          size     offset    fstype",
137             a->tmp);
138
139 #ifdef DEBUG
140         for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
141              sp != NULL; sp = subpartition_next(sp)) {
142                 command_add(cmds, "%s%s 'mountpoint: %s device: %s'",
143                      a->os_root, cmd_name(a, "ECHO"),
144                      subpartition_get_mountpoint(sp),
145                      subpartition_get_device_name(sp));
146         }
147 #endif
148
149         /*
150          * Write a line for each subpartition the user wants.
151          */
152         for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
153              sp != NULL; sp = subpartition_next(sp)) {
154                 if (subpartition_is_tmpfsbacked(sp)) {
155                         continue;
156                 }
157                 if (subpartition_is_swap(sp)) {
158                         command_add(cmds, "%s%s '  %c:\t%s\t*\tswap' >>%sinstall.disklabel",
159                             a->os_root, cmd_name(a, "ECHO"),
160                             subpartition_get_letter(sp),
161                             capacity_to_string(subpartition_get_capacity(sp)),
162                             a->tmp);
163                 } else {
164                         command_add(cmds, "%s%s '  %c:\t%s\t%s\t4.2BSD' >>%sinstall.disklabel",
165                             a->os_root, cmd_name(a, "ECHO"),
166                             subpartition_get_letter(sp),
167                             capacity_to_string(subpartition_get_capacity(sp)),
168                             subpartition_get_letter(sp) == 'a' ? "0" : "*",
169                             a->tmp);
170                 }
171         }
172         temp_file_add(a, "install.disklabel");
173
174         /*
175          * Label the slice from the disklabel we just wove together.
176          */
177         command_add(cmds, "%s%s -R -B -r %s %sinstall.disklabel",
178             a->os_root, cmd_name(a, "DISKLABEL64"),
179             slice_get_device_name(storage_get_selected_slice(a->s)),
180             a->tmp);
181
182         /*
183          * Create a snapshot of the disklabel we just created
184          * for debugging inspection in the log.
185          */
186         command_add(cmds, "%s%s %s",
187             a->os_root, cmd_name(a, "DISKLABEL64"),
188             slice_get_device_name(storage_get_selected_slice(a->s)));
189
190         /*
191          * Create filesystems on the newly-created subpartitions.
192          */
193         for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
194              sp != NULL; sp = subpartition_next(sp)) {
195                 if (subpartition_is_swap(sp) || subpartition_is_tmpfsbacked(sp))
196                         continue;
197
198                 command_add(cmds, "%s%s%s -b %ld -f %ld %sdev/%s",
199                     a->os_root, cmd_name(a, "NEWFS"),
200                     subpartition_is_softupdated(sp) ? " -U" : "",
201                     subpartition_get_bsize(sp),
202                     subpartition_get_fsize(sp),
203                     a->os_root,
204                     subpartition_get_device_name(sp));
205         }
206
207         result = commands_execute(a, cmds);
208         commands_free(cmds);
209         return(result);
210 }
211
212 static long
213 default_capacity(struct storage *s, int mtpt)
214 {
215         unsigned long swap;
216         unsigned long capacity;
217         unsigned long mem;
218
219         if (mtpt == MTPT_HOME)
220                 return(-1);
221
222         capacity = slice_get_capacity(storage_get_selected_slice(s));
223         mem = storage_get_memsize(s);
224         swap = 2 * mem;
225         if (mem > (capacity / 2) || capacity < 4096)
226                 swap = mem;
227         if (mem > capacity)
228                 swap = capacity / 2;
229         if (swap > 8192)
230                 swap = 8192;
231
232         if (capacity < DISK_MIN) {
233                 /*
234                  * For the purposes of this installer:
235                  * can't be done.  Sorry.
236                  */
237                 return(-1);
238         } else if (capacity < 4096) {
239                 switch (mtpt) {
240                 case MTPT_ROOT: return(320);
241                 case MTPT_SWAP: return(swap);
242                 case MTPT_VAR:  return(128);
243                 case MTPT_TMP:  return(128);
244                 case MTPT_USR:  return(1472);
245                 }
246         } else if (capacity < 10240) {
247                 switch (mtpt) {
248                 case MTPT_ROOT: return(640);
249                 case MTPT_SWAP: return(swap);
250                 case MTPT_VAR:  return(256);
251                 case MTPT_TMP:  return(256);
252                 case MTPT_USR:  return(2688);
253                 }
254         } else {
255                 switch (mtpt) {
256                 case MTPT_ROOT: return(768);
257                 case MTPT_SWAP: return(swap);
258                 case MTPT_VAR:  return(256);
259                 case MTPT_TMP:  return(256);
260                 case MTPT_USR:  return(7680);
261                 }
262         }
263         /* shouldn't ever happen */
264         return(-1);
265 }
266
267 static int
268 check_capacity(struct i_fn_args *a)
269 {
270         struct subpartition *sp;
271         long min_capacity[] = {320, 0, 16, 0, 1472, 0, 0};
272         unsigned long total_capacity = 0;
273         int mtpt;
274
275         if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL)
276                 min_capacity[MTPT_ROOT] += min_capacity[MTPT_USR];
277
278         for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
279              sp != NULL; sp = subpartition_next(sp)) {
280                 long subpart_capacity = subpartition_get_capacity(sp);
281                 const char *mountpt = subpartition_get_mountpoint(sp);
282
283                 if (subpart_capacity == -1)
284                         total_capacity++;
285                 else
286                         total_capacity += subpart_capacity;
287                 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) {
288                         if (strcmp(mountpt, def_mountpt[mtpt]) == 0 &&
289                             min_capacity[mtpt] > 0 &&
290                             subpart_capacity < min_capacity[mtpt]) {
291                                 inform(a->c, _("WARNING: The size (%ldM) specified for "
292                                     "the %s subpartition is too small. It "
293                                     "should be at least %ldM or you will "
294                                     "risk running out of space during "
295                                     "the installation."),
296                                     subpart_capacity, mountpt,
297                                     min_capacity[mtpt]);
298                         }
299                 }
300         }
301
302         if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) {
303                 inform(a->c, _("The space allocated to all of your selected "
304                     "subpartitions (%dM) exceeds the total "
305                     "capacity of the selected primary partition "
306                     "(%dM). Remove some subpartitions or choose "
307                     "a smaller size for them and try again."),
308                     total_capacity, slice_get_capacity(storage_get_selected_slice(a->s)));
309                 return(0);
310         }
311
312         return(1);
313 }
314
315 static int
316 check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a)
317 {
318         struct dfui_dataset *ds;
319         struct dfui_dataset *star_ds = NULL;
320         struct aura_dict *d;
321         const char *mountpoint, *capstring;
322         long capacity = 0;
323         long bsize, fsize;
324         int found_root = 0;
325         int softupdates, tmpfsbacked;
326         int valid = 1;
327
328         d = aura_dict_new(1, AURA_DICT_LIST);
329
330         if ((ds = dfui_response_dataset_get_first(r)) == NULL) {
331                 inform(a->c, _("Please set up at least one subpartition."));
332                 valid = 0;
333         }
334
335         for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL;
336             ds = dfui_dataset_get_next(ds)) {
337 #ifdef DEBUG
338                 dfui_dataset_dump(ds);
339 #endif
340                 mountpoint = dfui_dataset_get_value(ds, "mountpoint");
341                 capstring = dfui_dataset_get_value(ds, "capacity");
342
343                 if (expert) {
344                         softupdates =
345                             (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0);
346                         fsize = atol(dfui_dataset_get_value(ds, "fsize"));
347                         bsize = atol(dfui_dataset_get_value(ds, "bsize"));
348                         tmpfsbacked = (strcmp(dfui_dataset_get_value(ds, "tmpfsbacked"), "Y") == 0);
349                 } else {
350                         softupdates = (strcmp(mountpoint, "/") == 0 ? 0 : 1);
351                         tmpfsbacked = (strcmp(mountpoint, "/tmp") == 0 ? 0 : 1);
352                         fsize = -1;
353                         bsize = -1;
354                 }
355
356                 if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) {
357                         inform(a->c, _("The same mount point cannot be specified "
358                             "for two different subpartitions."));
359                         valid = 0;
360                 }
361
362                 if (strcmp(mountpoint, "/") == 0)
363                         found_root = 1;
364
365                 if (strcmp(capstring, "*") == 0) {
366                         if (star_ds != NULL) {
367                                 inform(a->c, _("You cannot have more than one subpartition "
368                                     "with a '*' capacity (meaning 'use the remainder "
369                                     "of the primary partition'.)"));
370                                 valid = 0;
371                         } else {
372                                 star_ds = ds;
373                         }
374                 }
375
376                 if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) {
377                         inform(a->c, _("Mount point must be either 'swap', or it must "
378                             "start with a '/'."));
379                         valid = 0;
380                 }
381
382                 if (strpbrk(mountpoint, " \\\"'`") != NULL) {
383                         inform(a->c, _("Mount point may not contain the following "
384                             "characters: blank space, backslash, or "
385                             "single, double, or back quotes."));
386                         valid = 0;
387                 }
388
389                 if (strlen(capstring) == 0) {
390                         inform(a->c, _("A capacity must be specified."));
391                         valid = 0;
392                 }
393
394                 if (!string_to_capacity(capstring, &capacity)) {
395                         inform(a->c, _("Capacity must be either a '*' symbol to indicate "
396                             "'use the rest of the primary partition', or it "
397                             "must be a series of decimal digits ending with a "
398                             "'M' (indicating megabytes) or a 'G' (indicating "
399                             "gigabytes.)"));
400                         valid = 0;
401                 }
402
403                 if ((strcasecmp(mountpoint, "swap") == 0) && (capacity > 8192)) {
404                         inform(a->c, _("Swap capacity is limited to 8G."));
405                         valid = 0;
406                 }
407
408                 /*
409                  * If we made it through that obstacle course, all is well.
410                  */
411
412                 if (valid)
413                         aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1);
414         }
415
416         if (!found_root) {
417                 inform(a->c, _("You must include a / (root) subpartition."));
418                 valid = 0;
419         }
420
421         if (aura_dict_size(d) > 16) {
422                 inform(a->c, _("You cannot have more than 16 subpartitions "
423                     "on a single primary partition.  Remove some "
424                     "and try again."));
425                 valid = 0;
426         }
427
428         aura_dict_free(d);
429
430         return(valid);
431 }
432
433 static void
434 save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a)
435 {
436         struct dfui_dataset *ds;
437         char tmpfsbacked;
438         const char *mountpoint, *capstring;
439         long capacity;
440         long bsize, fsize;
441         int softupdates;
442         int valid = 1;
443
444         subpartitions_free(storage_get_selected_slice(a->s));
445
446         for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL;
447             ds = dfui_dataset_get_next(ds)) {
448                 mountpoint = dfui_dataset_get_value(ds, "mountpoint");
449                 capstring = dfui_dataset_get_value(ds, "capacity");
450
451                 if (expert) {
452                         softupdates =
453                             (strcmp(dfui_dataset_get_value(ds, "softupdates"), "Y") == 0);
454                         fsize = atol(dfui_dataset_get_value(ds, "fsize"));
455                         bsize = atol(dfui_dataset_get_value(ds, "bsize"));
456                         tmpfsbacked = (strcmp(dfui_dataset_get_value(ds, "tmpfsbacked"), "Y") == 0);
457                 } else {
458                         softupdates = (strcmp(mountpoint, "/") == 0 ? 0 : 1);
459                         tmpfsbacked = 0;
460                         fsize = -1;
461                         bsize = -1;
462                 }
463
464                 if (string_to_capacity(capstring, &capacity)) {
465                         subpartition_new(storage_get_selected_slice(a->s), mountpoint, capacity,
466                             softupdates, fsize, bsize, tmpfsbacked);
467                 }
468         }
469 }
470
471 static void
472 populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a)
473 {
474         struct subpartition *sp;
475         struct dfui_dataset *ds;
476         char temp[32];
477         int mtpt;
478         long capacity;
479
480         if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) {
481                 /*
482                  * The user has already given us their subpartition
483                  * preferences, so use them here.
484                  */
485                 for (sp = slice_subpartition_first(storage_get_selected_slice(a->s));
486                      sp != NULL; sp = subpartition_next(sp)) {
487                         ds = dfui_dataset_new();
488                         dfui_dataset_celldata_add(ds, "mountpoint",
489                             subpartition_get_mountpoint(sp));
490                         dfui_dataset_celldata_add(ds, "capacity",
491                             capacity_to_string(subpartition_get_capacity(sp)));
492                         if (expert) {
493                                 dfui_dataset_celldata_add(ds, "softupdates",
494                                     subpartition_is_softupdated(sp) ? "Y" : "N");
495                                 dfui_dataset_celldata_add(ds, "tmpfsbacked",
496                                     subpartition_is_tmpfsbacked(sp) ? "Y" : "N");
497                                 snprintf(temp, 32, "%ld", subpartition_get_fsize(sp));
498                                 dfui_dataset_celldata_add(ds, "fsize",
499                                     temp);
500                                 snprintf(temp, 32, "%ld", subpartition_get_bsize(sp));
501                                 dfui_dataset_celldata_add(ds, "bsize",
502                                     temp);
503                         }
504                         dfui_form_dataset_add(f, ds);
505                 }
506         } else {
507                 /*
508                  * Otherwise, populate the form with datasets representing
509                  * reasonably-calculated defaults.  The defaults are chosen
510                  * based on the slice's total capacity and the machine's
511                  * total physical memory (for swap.)
512                  */
513                 for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) {
514                         capacity = default_capacity(a->s, mtpt);
515                         ds = dfui_dataset_new();
516                         dfui_dataset_celldata_add(ds, "mountpoint",
517                             def_mountpt[mtpt]);
518                         dfui_dataset_celldata_add(ds, "capacity",
519                             capacity_to_string(capacity));
520                         if (expert) {
521                                 dfui_dataset_celldata_add(ds, "softupdates",
522                                     strcmp(def_mountpt[mtpt], "/") != 0 ? "Y" : "N");
523                                 dfui_dataset_celldata_add(ds, "tmpfsbacked",
524                                     "N");
525                                 dfui_dataset_celldata_add(ds, "fsize",
526                                     capacity < 1024 ? "1024" : "2048");
527                                 dfui_dataset_celldata_add(ds, "bsize",
528                                     capacity < 1024 ? "8192" : "16384");
529                         }
530                         dfui_form_dataset_add(f, ds);
531                 }
532         }
533 }
534
535 static int
536 warn_subpartition_selections(struct i_fn_args *a)
537 {
538         int valid = 0;
539         struct aura_buffer *omit, *consequences;
540
541         omit = aura_buffer_new(2048);
542         consequences = aura_buffer_new(2048);
543
544         valid = check_capacity(a);
545         if (subpartition_find(storage_get_selected_slice(a->s), "/var") == NULL) {
546                 aura_buffer_cat(omit, "/var ");
547                 aura_buffer_cat(consequences, _("/var will be a plain dir in /\n"));
548         }
549         if (subpartition_find(storage_get_selected_slice(a->s), "/usr") == NULL) {
550                 aura_buffer_cat(omit, "/usr ");
551                 aura_buffer_cat(consequences, _("/usr will be a plain dir in /\n"));
552         }
553         if (subpartition_find(storage_get_selected_slice(a->s), "/tmp") == NULL) {
554                 aura_buffer_cat(omit, "/tmp ");
555                 aura_buffer_cat(consequences, _("/tmp will be symlinked to /var/tmp\n"));
556         }
557         if (subpartition_find(storage_get_selected_slice(a->s), "/home") == NULL) {
558                 aura_buffer_cat(omit, "/home ");
559                 aura_buffer_cat(consequences, _("/home will be symlinked to /usr/home\n"));
560         }
561
562         if (valid && aura_buffer_len(omit) > 0) {
563                 switch (dfui_be_present_dialog(a->c, _("Really omit?"),
564                     _("Omit Subpartition(s)|Return to Create Subpartitions"),
565                     _("You have elected to not have the following "
566                     "subpartition(s):\n\n%s\n\n"
567                     "The ramifications of these subpartition(s) being "
568                     "missing will be:\n\n%s\n"
569                     "Is this really what you want to do?"),
570                     aura_buffer_buf(omit), aura_buffer_buf(consequences))) {
571                 case 1:
572                         valid = 1;
573                         break;
574                 case 2:
575                         valid = 0;
576                         break;
577                 default:
578                         abort_backend();
579                 }
580         }
581
582         aura_buffer_free(omit);
583         aura_buffer_free(consequences);
584
585         return(!valid);
586 }
587
588 static struct dfui_form *
589 make_create_subpartitions_form(struct i_fn_args *a)
590 {
591         struct dfui_field *fi;
592         struct dfui_form *f;
593         char msg_buf[1][1024];
594
595         snprintf(msg_buf[0], sizeof(msg_buf[0]),
596             _("Subpartitions further divide a primary partition for "
597             "use with %s.  Some reasons you may want "
598             "a set of subpartitions are:\n\n"
599             "- you want to restrict how much data can be written "
600             "to certain parts of the primary partition, to quell "
601             "denial-of-service attacks; and\n"
602             "- you want to speed up access to data on the disk."
603             ""), OPERATING_SYSTEM_NAME);
604
605         f = dfui_form_create(
606             "create_subpartitions",
607             _("Create Subpartitions"),
608             _("Set up the subpartitions (also known as just `partitions' "
609             "in BSD tradition) you want to have on this primary "
610             "partition.\n\n"
611             "For Capacity, use 'M' to indicate megabytes, 'G' to "
612             "indicate gigabytes, or a single '*' to indicate "
613             "'use the remaining space on the primary partition'."),
614
615             msg_buf[0],
616
617             "p", "special", "dfinstaller_create_subpartitions",
618             "p", "minimum_width","64",
619
620             "f", "mountpoint", _("Mountpoint"), "", "",
621             "f", "capacity", _("Capacity"), "", "",
622
623             "a", "ok", _("Accept and Create"), "", "",
624             "a", "cancel",
625             (disk_get_formatted(storage_get_selected_disk(a->s)) ?
626             _("Return to Select Disk") :
627             _("Return to Select Primary Partition")), "", "",
628             "p", "accelerator", "ESC",
629
630             NULL
631         );
632
633         dfui_form_set_multiple(f, 1);
634         dfui_form_set_extensible(f, 1);
635
636         if (expert) {
637                 fi = dfui_form_field_add(f, "softupdates",
638                     dfui_info_new(_("Softupdates"), "", ""));
639                 dfui_field_property_set(fi, "control", "checkbox");
640
641                 fi = dfui_form_field_add(f, "tmpfsbacked",
642                     dfui_info_new(_("TMPFS"), "", ""));
643                 dfui_field_property_set(fi, "control", "checkbox");
644
645                 fi = dfui_form_field_add(f, "fsize",
646                     dfui_info_new(_("Frag Sz"), "", ""));
647
648                 fi = dfui_form_field_add(f, "bsize",
649                     dfui_info_new(_("Block Sz"), "", ""));
650
651                 dfui_form_action_add(f, "switch",
652                     dfui_info_new(_("Switch to Normal Mode"), "", ""));
653         } else {
654                 dfui_form_action_add(f, "switch",
655                     dfui_info_new(_("Switch to Expert Mode"), "", ""));
656         }
657
658         return(f);
659 }
660
661 /*
662  * Returns:
663  *      -1 = the form should be redisplayed
664  *       0 = failure, function is over
665  *       1 = success, function is over
666  */
667 static int
668 show_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a)
669 {
670         struct dfui_dataset *ds;
671         struct dfui_response *r;
672
673         for (;;) {
674                 if (dfui_form_dataset_get_first(f) == NULL)
675                         populate_create_subpartitions_form(f, a);
676
677                 if (!dfui_be_present(a->c, f, &r))
678                         abort_backend();
679
680                 if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) {
681                         dfui_response_free(r);
682                         return(0);
683                 } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) {
684                         if (check_subpartition_selections(r, a)) {
685                                 save_subpartition_selections(r, a);
686                                 expert = expert ? 0 : 1;
687                                 dfui_response_free(r);
688                                 return(-1);
689                         }
690                 } else {
691                         if (check_subpartition_selections(r, a)) {
692                                 save_subpartition_selections(r, a);
693                                 if (!warn_subpartition_selections(a)) {
694                                         if (!create_subpartitions(a)) {
695                                                 inform(a->c, _("The subpartitions you chose were "
696                                                         "not correctly created, and the "
697                                                         "primary partition may "
698                                                         "now be in an inconsistent state. "
699                                                         "We recommend re-formatting it "
700                                                         "before proceeding."));
701                                                 dfui_response_free(r);
702                                                 return(0);
703                                         } else {
704                                                 dfui_response_free(r);
705                                                 return(1);
706                                         }
707                                 }
708                         }
709                 }
710
711                 dfui_form_datasets_free(f);
712                 /* dfui_form_datasets_add_from_response(f, r); */
713                 for (ds = dfui_response_dataset_get_first(r); ds != NULL;
714                     ds = dfui_dataset_get_next(ds)) {
715                         dfui_form_dataset_add(f, dfui_dataset_dup(ds));
716                 }
717         }
718 }
719
720 void
721 fn_create_subpartitions_ufs(struct i_fn_args *a)
722 {
723         struct dfui_form *f;
724         int done = 0;
725
726         a->result = 0;
727         while (!done) {
728                 f = make_create_subpartitions_form(a);
729                 switch (show_create_subpartitions_form(f, a)) {
730                 case -1:
731                         done = 0;
732                         break;
733                 case 0:
734                         done = 1;
735                         a->result = 0;
736                         break;
737                 case 1:
738                         done = 1;
739                         a->result = 1;
740                         break;
741                 }
742                 dfui_form_free(f);
743         }
744 }