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