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