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