Commit | Line | Data |
---|---|---|
7eae318c MS |
1 | /* |
2 | * Copyright (c) 2008 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_hammer.c | |
e90c1ebb | 36 | * Installer Function : Create HAMMER or HAMMER2 Subpartitions. |
7eae318c MS |
37 | */ |
38 | ||
39 | #include <stdio.h> | |
40 | #include <stdlib.h> | |
41 | #include <string.h> | |
f9602a12 | 42 | #include <ctype.h> |
e90c1ebb | 43 | #include <assert.h> |
5950cfff | 44 | #include <unistd.h> |
7eae318c MS |
45 | |
46 | #ifdef ENABLE_NLS | |
47 | #include <libintl.h> | |
48 | #define _(String) gettext (String) | |
49 | #else | |
50 | #define _(String) (String) | |
51 | #endif | |
52 | ||
53 | #include "libaura/mem.h" | |
54 | #include "libaura/buffer.h" | |
55 | #include "libaura/dict.h" | |
56 | #include "libaura/fspred.h" | |
57 | ||
58 | #include "libdfui/dfui.h" | |
59 | #include "libdfui/dump.h" | |
60 | #include "libdfui/system.h" | |
61 | ||
62 | #include "libinstaller/commands.h" | |
63 | #include "libinstaller/diskutil.h" | |
64 | #include "libinstaller/functions.h" | |
65 | #include "libinstaller/uiutil.h" | |
66 | ||
67 | #include "fn.h" | |
68 | #include "flow.h" | |
69 | #include "pathnames.h" | |
70 | ||
e90c1ebb | 71 | static int create_subpartitions(int which, struct i_fn_args *); |
e6847d54 | 72 | static long default_capacity(struct storage *, const char *); |
7eae318c | 73 | static int check_capacity(struct i_fn_args *); |
f9602a12 MD |
74 | static int check_subpartition_selections(struct dfui_response *, |
75 | struct i_fn_args *); | |
76 | static void save_subpartition_selections(struct dfui_response *, | |
77 | struct i_fn_args *); | |
78 | static void populate_create_subpartitions_form(struct dfui_form *, | |
79 | struct i_fn_args *); | |
7eae318c | 80 | static int warn_subpartition_selections(struct i_fn_args *); |
88cfb1f7 | 81 | static int warn_encrypted_boot(struct i_fn_args *); |
7eae318c | 82 | static struct dfui_form *make_create_subpartitions_form(struct i_fn_args *); |
e90c1ebb | 83 | static int show_create_subpartitions_form(int which, struct dfui_form *, |
f9602a12 MD |
84 | struct i_fn_args *); |
85 | static char *construct_lname(const char *mtpt); | |
7eae318c | 86 | |
f9602a12 MD |
87 | static const char *def_mountpt[] = {"/boot", "swap", "/", "/build", NULL}; |
88 | static long min_capacity[] = { 128, 0, DISK_MIN - 128, BUILD_MIN }; | |
7eae318c MS |
89 | static int expert = 0; |
90 | ||
91 | /* | |
92 | * Given a set of subpartitions-to-be in the selected slice, | |
93 | * create them. | |
94 | */ | |
95 | static int | |
e90c1ebb | 96 | create_subpartitions(int which, struct i_fn_args *a) |
7eae318c MS |
97 | { |
98 | struct subpartition *sp; | |
99 | struct commands *cmds; | |
100 | int result = 0; | |
7eae318c | 101 | int num_partitions; |
e90c1ebb MD |
102 | const char *whichfs; |
103 | ||
104 | switch(which) { | |
105 | case FS_HAMMER: | |
106 | whichfs = "HAMMER"; | |
107 | break; | |
108 | case FS_HAMMER2: | |
109 | whichfs = "HAMMER2"; | |
110 | break; | |
111 | default: | |
112 | whichfs = NULL; | |
113 | assert(0); | |
114 | } | |
7eae318c MS |
115 | |
116 | cmds = commands_new(); | |
117 | if (!is_file("%sinstall.disklabel.%s", | |
118 | a->tmp, | |
119 | slice_get_device_name(storage_get_selected_slice(a->s)))) { | |
120 | /* | |
121 | * Get a copy of the 'virgin' disklabel. | |
122 | * XXX It might make more sense for this to | |
123 | * happen right after format_slice() instead. | |
124 | */ | |
125 | command_add(cmds, "%s%s -r %s >%sinstall.disklabel.%s", | |
126 | a->os_root, cmd_name(a, "DISKLABEL64"), | |
127 | slice_get_device_name(storage_get_selected_slice(a->s)), | |
128 | a->tmp, | |
129 | slice_get_device_name(storage_get_selected_slice(a->s))); | |
130 | } | |
131 | ||
132 | /* | |
133 | * Weave together a new disklabel out the of the 'virgin' | |
134 | * disklabel, and the user's subpartition choices. | |
135 | */ | |
136 | ||
137 | /* | |
138 | * Take everything from the 'virgin' disklabel up until the | |
139 | * '16 partitions' line. | |
140 | */ | |
141 | num_partitions = 16; | |
f9602a12 MD |
142 | command_add(cmds, |
143 | "%s%s '$2==\"partitions:\" || " | |
144 | "cut { cut = 1 } !cut { print $0 }' " | |
145 | "<%sinstall.disklabel.%s >%sinstall.disklabel", | |
7eae318c MS |
146 | a->os_root, cmd_name(a, "AWK"), |
147 | a->tmp, | |
148 | slice_get_device_name(storage_get_selected_slice(a->s)), | |
149 | a->tmp); | |
150 | ||
151 | /* | |
152 | * 16 partitions: | |
153 | * # size offset fstype | |
154 | * c: 16383969 0 unused # 7999.985MB | |
155 | */ | |
156 | ||
157 | command_add(cmds, "%s%s '%d partitions:' >>%sinstall.disklabel", | |
158 | a->os_root, cmd_name(a, "ECHO"), num_partitions ,a->tmp); | |
159 | command_add(cmds, "%s%s '%s' >>%sinstall.disklabel", | |
160 | a->os_root, cmd_name(a, "ECHO"), | |
161 | "# size offset fstype", | |
162 | a->tmp); | |
163 | ||
164 | #ifdef DEBUG | |
165 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); | |
166 | sp != NULL; sp = subpartition_next(sp)) { | |
167 | command_add(cmds, "%s%s 'mountpoint: %s device: %s'", | |
168 | a->os_root, cmd_name(a, "ECHO"), | |
169 | subpartition_get_mountpoint(sp), | |
170 | subpartition_get_device_name(sp)); | |
171 | } | |
172 | #endif | |
173 | ||
174 | /* | |
175 | * Write a line for each subpartition the user wants. | |
176 | */ | |
177 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); | |
178 | sp != NULL; sp = subpartition_next(sp)) { | |
761cad2f | 179 | if (subpartition_is_tmpfsbacked(sp)) { |
7eae318c MS |
180 | continue; |
181 | } | |
182 | if (subpartition_is_swap(sp)) { | |
f9602a12 MD |
183 | command_add(cmds, |
184 | "%s%s ' %c:\t%s\t*\tswap' " | |
185 | ">>%sinstall.disklabel", | |
7eae318c MS |
186 | a->os_root, cmd_name(a, "ECHO"), |
187 | subpartition_get_letter(sp), | |
188 | capacity_to_string(subpartition_get_capacity(sp)), | |
189 | a->tmp); | |
e6847d54 | 190 | } else if (strcmp(subpartition_get_mountpoint(sp), "/boot") == 0) { |
f9602a12 MD |
191 | command_add(cmds, |
192 | "%s%s ' %c:\t%s\t0\t4.2BSD' " | |
193 | ">>%sinstall.disklabel", | |
e6847d54 SW |
194 | a->os_root, cmd_name(a, "ECHO"), |
195 | subpartition_get_letter(sp), | |
196 | capacity_to_string(subpartition_get_capacity(sp)), | |
197 | a->tmp); | |
7eae318c | 198 | } else { |
f9602a12 | 199 | command_add(cmds, |
e90c1ebb | 200 | "%s%s ' %c:\t%s\t*\t%s' " |
f9602a12 | 201 | ">>%sinstall.disklabel", |
7eae318c MS |
202 | a->os_root, cmd_name(a, "ECHO"), |
203 | subpartition_get_letter(sp), | |
204 | capacity_to_string(subpartition_get_capacity(sp)), | |
e90c1ebb | 205 | whichfs, a->tmp); |
7eae318c MS |
206 | } |
207 | } | |
208 | temp_file_add(a, "install.disklabel"); | |
209 | ||
210 | /* | |
211 | * Label the slice from the disklabel we just wove together. | |
212 | */ | |
213 | command_add(cmds, "%s%s -R -B -r %s %sinstall.disklabel", | |
214 | a->os_root, cmd_name(a, "DISKLABEL64"), | |
215 | slice_get_device_name(storage_get_selected_slice(a->s)), | |
216 | a->tmp); | |
217 | ||
218 | /* | |
219 | * Create a snapshot of the disklabel we just created | |
220 | * for debugging inspection in the log. | |
221 | */ | |
222 | command_add(cmds, "%s%s %s", | |
223 | a->os_root, cmd_name(a, "DISKLABEL64"), | |
224 | slice_get_device_name(storage_get_selected_slice(a->s))); | |
225 | ||
88cfb1f7 | 226 | /* |
242084fd | 227 | * If encryption was specified, read the passphrase. |
88cfb1f7 SW |
228 | */ |
229 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); | |
230 | sp != NULL; sp = subpartition_next(sp)) { | |
231 | if (subpartition_is_encrypted(sp)) { | |
242084fd | 232 | fn_get_passphrase(a, 1); |
88cfb1f7 SW |
233 | break; |
234 | } | |
235 | } | |
236 | ||
7eae318c MS |
237 | /* |
238 | * Create filesystems on the newly-created subpartitions. | |
239 | */ | |
240 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); | |
241 | sp != NULL; sp = subpartition_next(sp)) { | |
f9602a12 MD |
242 | if (subpartition_is_swap(sp) || |
243 | subpartition_is_tmpfsbacked(sp)) { | |
88cfb1f7 SW |
244 | if (subpartition_is_swap(sp) && |
245 | subpartition_is_encrypted(sp)) { | |
246 | command_add(cmds, | |
51871435 | 247 | "%s%s -d /tmp/t1 luksFormat /dev/%s", |
88cfb1f7 | 248 | a->os_root, cmd_name(a, "CRYPTSETUP"), |
88cfb1f7 SW |
249 | subpartition_get_device_name(sp)); |
250 | command_add(cmds, | |
51871435 | 251 | "%s%s -d /tmp/t1 luksOpen /dev/%s swap", |
88cfb1f7 | 252 | a->os_root, cmd_name(a, "CRYPTSETUP"), |
88cfb1f7 SW |
253 | subpartition_get_device_name(sp)); |
254 | } | |
7eae318c | 255 | continue; |
88cfb1f7 | 256 | } |
7eae318c | 257 | |
e6847d54 | 258 | if (strcmp(subpartition_get_mountpoint(sp), "/boot") == 0) { |
6f25b550 | 259 | command_add(cmds, "%s%s -i 65536 /dev/%s", |
e6847d54 | 260 | a->os_root, cmd_name(a, "NEWFS"), |
e6847d54 SW |
261 | subpartition_get_device_name(sp)); |
262 | } else { | |
f9602a12 | 263 | char *ham_name; |
88cfb1f7 SW |
264 | if (subpartition_is_encrypted(sp)) { |
265 | command_add(cmds, | |
51871435 | 266 | "%s%s -d /tmp/t1 luksFormat /dev/%s", |
88cfb1f7 | 267 | a->os_root, cmd_name(a, "CRYPTSETUP"), |
88cfb1f7 SW |
268 | subpartition_get_device_name(sp)); |
269 | command_add(cmds, | |
f9602a12 | 270 | "%s%s -d /tmp/t1 luksOpen /dev/%s %s", |
88cfb1f7 | 271 | a->os_root, cmd_name(a, "CRYPTSETUP"), |
f9602a12 | 272 | subpartition_get_device_name(sp), |
0c941bc8 | 273 | subpartition_get_mapper_name(sp, -1)); |
88cfb1f7 | 274 | } |
e90c1ebb MD |
275 | |
276 | if (which == FS_HAMMER) { | |
277 | ham_name = construct_lname( | |
278 | subpartition_get_mountpoint(sp)); | |
279 | command_add(cmds, "%s%s -f -L %s /dev/%s", | |
280 | a->os_root, cmd_name(a, "NEWFS_HAMMER"), | |
281 | ham_name, | |
282 | (subpartition_is_encrypted(sp) ? | |
283 | subpartition_get_mapper_name(sp, 0) : | |
284 | subpartition_get_device_name(sp))); | |
285 | free(ham_name); | |
286 | } else { | |
5ea14dd5 | 287 | command_add(cmds, "%s%s /dev/%s", |
e90c1ebb MD |
288 | a->os_root, cmd_name(a, "NEWFS_HAMMER2"), |
289 | (subpartition_is_encrypted(sp) ? | |
290 | subpartition_get_mapper_name(sp, 0) : | |
291 | subpartition_get_device_name(sp))); | |
292 | } | |
e6847d54 | 293 | } |
7eae318c MS |
294 | } |
295 | ||
296 | result = commands_execute(a, cmds); | |
297 | commands_free(cmds); | |
f8608e0d AL |
298 | unlink("/tmp/t1"); |
299 | ||
7eae318c MS |
300 | return(result); |
301 | } | |
302 | ||
f9602a12 MD |
303 | /* |
304 | * Return default capacity field filler. Return 0 for /build if drive | |
305 | * space minus swap is < 40GB (causes installer to use PFS's on the root | |
306 | * partition instead). | |
307 | */ | |
7eae318c | 308 | static long |
e6847d54 | 309 | default_capacity(struct storage *s, const char *mtpt) |
7eae318c | 310 | { |
f9602a12 | 311 | unsigned long boot, root, swap, build; |
7eae318c | 312 | unsigned long capacity; |
6cd7259e SW |
313 | unsigned long mem; |
314 | ||
f9602a12 | 315 | capacity = slice_get_capacity(storage_get_selected_slice(s)); /* MB */ |
6cd7259e | 316 | mem = storage_get_memsize(s); |
aaeebad4 SW |
317 | |
318 | /* | |
f9602a12 MD |
319 | * Slice capacity is at least 10G at this point. Calculate basic |
320 | * defaults. | |
aaeebad4 | 321 | */ |
6cd7259e | 322 | swap = 2 * mem; |
f9602a12 MD |
323 | if (swap > capacity / 10) /* max 1/10 capacity */ |
324 | swap = capacity / 10; | |
325 | if (swap < SWAP_MIN) /* having a little is nice */ | |
326 | swap = SWAP_MIN; | |
327 | if (swap > SWAP_MAX) /* installer cap */ | |
f76463ce | 328 | swap = SWAP_MAX; |
f9602a12 | 329 | |
344b24ad | 330 | boot = 1024; |
f9602a12 | 331 | |
34ca1b1c MD |
332 | build = (capacity - swap - boot) / 4; |
333 | ||
334 | #if 0 | |
335 | /* | |
336 | * No longer cap the size of /build, the assumption didn't hold | |
337 | * well particularly with /var/crash being placed on /build now. | |
338 | */ | |
f9602a12 MD |
339 | if (build > BUILD_MAX) |
340 | build = BUILD_MAX; | |
34ca1b1c | 341 | #endif |
f9602a12 MD |
342 | |
343 | for (;;) { | |
344 | root = (capacity - swap - boot - build); | |
345 | ||
346 | /* | |
347 | * Adjust until the defaults look sane | |
348 | * | |
349 | * root should be at least twice as large as build | |
350 | */ | |
351 | if (build && root < build * 2) { | |
352 | --build; | |
353 | continue; | |
354 | } | |
355 | ||
356 | /* | |
357 | * root should be at least 1/2 capacity | |
358 | */ | |
359 | if (build && root < capacity / 2) { | |
360 | --build; | |
361 | continue; | |
362 | } | |
363 | break; | |
364 | } | |
365 | ||
366 | /* | |
367 | * Finalize. If build is too small do not supply a /build, | |
34ca1b1c MD |
368 | * and if swap is too small do not supply swap. Cascade the |
369 | * released space to swap and root. | |
f9602a12 | 370 | */ |
34ca1b1c MD |
371 | if (build < BUILD_MIN) { |
372 | if (swap < SWAP_MIN && build >= SWAP_MIN - swap) { | |
373 | build -= SWAP_MIN - swap; | |
374 | swap = SWAP_MIN; | |
375 | } | |
376 | if (swap < 2 * mem && build >= 2 * mem - swap) { | |
377 | build -= 2 * mem - swap; | |
378 | swap = 2 * mem; | |
379 | } | |
380 | root += build; | |
f9602a12 | 381 | build = 0; |
34ca1b1c MD |
382 | } |
383 | if (swap < SWAP_MIN) { | |
384 | root += swap; | |
f9602a12 | 385 | swap = 0; |
34ca1b1c MD |
386 | } |
387 | ||
f9602a12 MD |
388 | if (build == 0) |
389 | root = -1; /* root is the last part */ | |
390 | else | |
391 | build = -1; /* last partition just use remaining space */ | |
7eae318c | 392 | |
344b24ad | 393 | if (strcmp(mtpt, "/boot") == 0) |
aaeebad4 | 394 | return(boot); |
f9602a12 MD |
395 | else if (strcmp(mtpt, "/build") == 0) |
396 | return(build); | |
aaeebad4 | 397 | else if (strcmp(mtpt, "swap") == 0) |
e6847d54 | 398 | return(swap); |
aaeebad4 | 399 | else if (strcmp(mtpt, "/") == 0) |
f9602a12 | 400 | return(root); |
aaeebad4 | 401 | |
7eae318c MS |
402 | /* shouldn't ever happen */ |
403 | return(-1); | |
404 | } | |
405 | ||
406 | static int | |
407 | check_capacity(struct i_fn_args *a) | |
408 | { | |
409 | struct subpartition *sp; | |
7eae318c | 410 | unsigned long total_capacity = 0; |
aaeebad4 SW |
411 | unsigned long remaining_capacity; |
412 | int mtpt, warn_smallpart = 0; | |
f9602a12 | 413 | int good; |
aaeebad4 | 414 | |
f9602a12 MD |
415 | remaining_capacity = slice_get_capacity( |
416 | storage_get_selected_slice(a->s)); | |
aaeebad4 SW |
417 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); |
418 | sp != NULL; sp = subpartition_next(sp)) { | |
419 | if (subpartition_get_capacity(sp) != -1) | |
420 | remaining_capacity -= subpartition_get_capacity(sp); | |
421 | } | |
7eae318c | 422 | |
7eae318c MS |
423 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); |
424 | sp != NULL; sp = subpartition_next(sp)) { | |
a41800d7 SW |
425 | long subpart_capacity = subpartition_get_capacity(sp); |
426 | const char *mountpt = subpartition_get_mountpoint(sp); | |
427 | ||
428 | if (subpart_capacity == -1) | |
7eae318c MS |
429 | total_capacity++; |
430 | else | |
a41800d7 | 431 | total_capacity += subpart_capacity; |
7eae318c | 432 | for (mtpt = 0; def_mountpt[mtpt] != NULL; mtpt++) { |
a41800d7 SW |
433 | if (strcmp(mountpt, def_mountpt[mtpt]) == 0 && |
434 | subpart_capacity < min_capacity[mtpt] && | |
435 | subpart_capacity != -1) { | |
f9602a12 MD |
436 | inform(a->c, |
437 | _("WARNING: The size (%ldM) specified for " | |
a41800d7 SW |
438 | "the %s subpartition is too small. It " |
439 | "should be at least %ldM or you will " | |
7eae318c | 440 | "risk running out of space during " |
f9602a12 | 441 | "installation or operation."), |
a41800d7 SW |
442 | subpart_capacity, mountpt, |
443 | min_capacity[mtpt]); | |
7eae318c MS |
444 | } |
445 | } | |
a41800d7 SW |
446 | if (strcmp(mountpt, "/boot") != 0 && |
447 | strcmp(mountpt, "swap") != 0) { | |
f9602a12 MD |
448 | if ((subpart_capacity == -1 && |
449 | remaining_capacity < HAMMER_WARN) || | |
450 | (subpart_capacity != -1 && | |
451 | subpart_capacity < HAMMER_WARN)) { | |
f3ba2c0c | 452 | warn_smallpart++; |
f9602a12 | 453 | } |
f3ba2c0c | 454 | } |
7eae318c MS |
455 | } |
456 | ||
457 | if (total_capacity > slice_get_capacity(storage_get_selected_slice(a->s))) { | |
458 | inform(a->c, _("The space allocated to all of your selected " | |
59b733d3 | 459 | "subpartitions (%luM) exceeds the total " |
7eae318c | 460 | "capacity of the selected primary partition " |
59b733d3 | 461 | "(%luM). Remove some subpartitions or choose " |
7eae318c | 462 | "a smaller size for them and try again."), |
f9602a12 MD |
463 | total_capacity, |
464 | slice_get_capacity(storage_get_selected_slice(a->s))); | |
7eae318c MS |
465 | return(0); |
466 | } | |
467 | ||
f9602a12 MD |
468 | if (warn_smallpart) { |
469 | good = confirm_dangerous_action(a->c, | |
470 | _("WARNING: Small HAMMER filesystems can fill up " | |
471 | "very quickly!\n" | |
472 | "You may have to run 'hammer prune-everything' and " | |
473 | "'hammer reblock'\n" | |
474 | "manually or often via a cron job, even if using a " | |
e90c1ebb MD |
475 | "nohistory mount.\n" |
476 | "For HAMMER2 you may have to run 'hammer2 bulkfree' " | |
477 | "manually or often via a cron job.\n")); | |
f9602a12 MD |
478 | } else { |
479 | good = 1; | |
480 | } | |
aaeebad4 | 481 | |
f9602a12 | 482 | return (good); |
7eae318c MS |
483 | } |
484 | ||
485 | static int | |
486 | check_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) | |
487 | { | |
488 | struct dfui_dataset *ds; | |
489 | struct dfui_dataset *star_ds = NULL; | |
490 | struct aura_dict *d; | |
491 | const char *mountpoint, *capstring; | |
492 | long capacity = 0; | |
7eae318c MS |
493 | int found_root = 0; |
494 | int valid = 1; | |
495 | ||
496 | d = aura_dict_new(1, AURA_DICT_LIST); | |
497 | ||
498 | if ((ds = dfui_response_dataset_get_first(r)) == NULL) { | |
499 | inform(a->c, _("Please set up at least one subpartition.")); | |
500 | valid = 0; | |
501 | } | |
502 | ||
503 | for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; | |
504 | ds = dfui_dataset_get_next(ds)) { | |
505 | #ifdef DEBUG | |
506 | dfui_dataset_dump(ds); | |
507 | #endif | |
508 | mountpoint = dfui_dataset_get_value(ds, "mountpoint"); | |
509 | capstring = dfui_dataset_get_value(ds, "capacity"); | |
66764e14 | 510 | |
7eae318c MS |
511 | if (aura_dict_exists(d, mountpoint, strlen(mountpoint) + 1)) { |
512 | inform(a->c, _("The same mount point cannot be specified " | |
513 | "for two different subpartitions.")); | |
514 | valid = 0; | |
515 | } | |
516 | ||
517 | if (strcmp(mountpoint, "/") == 0) | |
518 | found_root = 1; | |
519 | ||
520 | if (strcmp(capstring, "*") == 0) { | |
521 | if (star_ds != NULL) { | |
522 | inform(a->c, _("You cannot have more than one subpartition " | |
523 | "with a '*' capacity (meaning 'use the remainder " | |
524 | "of the primary partition'.)")); | |
525 | valid = 0; | |
526 | } else { | |
527 | star_ds = ds; | |
528 | } | |
529 | } | |
530 | ||
531 | if (!(!strcasecmp(mountpoint, "swap") || mountpoint[0] == '/')) { | |
532 | inform(a->c, _("Mount point must be either 'swap', or it must " | |
533 | "start with a '/'.")); | |
534 | valid = 0; | |
535 | } | |
536 | ||
537 | if (strpbrk(mountpoint, " \\\"'`") != NULL) { | |
538 | inform(a->c, _("Mount point may not contain the following " | |
539 | "characters: blank space, backslash, or " | |
540 | "single, double, or back quotes.")); | |
541 | valid = 0; | |
542 | } | |
543 | ||
544 | if (strlen(capstring) == 0) { | |
545 | inform(a->c, _("A capacity must be specified.")); | |
546 | valid = 0; | |
547 | } | |
548 | ||
549 | if (!string_to_capacity(capstring, &capacity)) { | |
1f2824e8 SW |
550 | inform(a->c, _("Capacity must be either a '*' symbol " |
551 | "to indicate 'use the rest of the primary " | |
552 | "partition', or it must be a series of decimal " | |
553 | "digits ending with an 'M' (indicating " | |
554 | "megabytes), a 'G' (indicating gigabytes) and " | |
555 | "so on (up to 'E'.)")); | |
7eae318c MS |
556 | valid = 0; |
557 | } | |
558 | ||
268b6264 MD |
559 | /* |
560 | * Maybe remove this limit entirely? | |
561 | */ | |
562 | if ((strcasecmp(mountpoint, "swap") == 0) && | |
ef48e483 SW |
563 | (capacity > SWAP_MAX)) { |
564 | inform(a->c, _("Swap capacity is limited to %dG."), | |
565 | SWAP_MAX / 1024); | |
4b220078 SW |
566 | valid = 0; |
567 | } | |
568 | ||
7eae318c MS |
569 | /* |
570 | * If we made it through that obstacle course, all is well. | |
571 | */ | |
572 | ||
573 | if (valid) | |
574 | aura_dict_store(d, mountpoint, strlen(mountpoint) + 1, "", 1); | |
575 | } | |
576 | ||
577 | if (!found_root) { | |
578 | inform(a->c, _("You must include a / (root) subpartition.")); | |
579 | valid = 0; | |
580 | } | |
581 | ||
582 | if (aura_dict_size(d) > 16) { | |
583 | inform(a->c, _("You cannot have more than 16 subpartitions " | |
584 | "on a single primary partition. Remove some " | |
585 | "and try again.")); | |
586 | valid = 0; | |
587 | } | |
588 | ||
589 | aura_dict_free(d); | |
590 | ||
591 | return(valid); | |
592 | } | |
593 | ||
594 | static void | |
595 | save_subpartition_selections(struct dfui_response *r, struct i_fn_args *a) | |
596 | { | |
597 | struct dfui_dataset *ds; | |
7eae318c MS |
598 | const char *mountpoint, *capstring; |
599 | long capacity; | |
7eae318c MS |
600 | int valid = 1; |
601 | ||
602 | subpartitions_free(storage_get_selected_slice(a->s)); | |
603 | ||
604 | for (ds = dfui_response_dataset_get_first(r); valid && ds != NULL; | |
605 | ds = dfui_dataset_get_next(ds)) { | |
606 | mountpoint = dfui_dataset_get_value(ds, "mountpoint"); | |
607 | capstring = dfui_dataset_get_value(ds, "capacity"); | |
66764e14 | 608 | |
7eae318c MS |
609 | if (string_to_capacity(capstring, &capacity)) { |
610 | subpartition_new_hammer(storage_get_selected_slice(a->s), | |
88cfb1f7 SW |
611 | mountpoint, capacity, |
612 | strcasecmp(dfui_dataset_get_value(ds, "encrypted"), "Y") == 0); | |
7eae318c MS |
613 | } |
614 | } | |
615 | } | |
616 | ||
617 | static void | |
618 | populate_create_subpartitions_form(struct dfui_form *f, struct i_fn_args *a) | |
619 | { | |
620 | struct subpartition *sp; | |
621 | struct dfui_dataset *ds; | |
e6847d54 | 622 | int i; |
7eae318c MS |
623 | long capacity; |
624 | ||
625 | if (slice_subpartition_first(storage_get_selected_slice(a->s)) != NULL) { | |
626 | /* | |
627 | * The user has already given us their subpartition | |
628 | * preferences, so use them here. | |
629 | */ | |
630 | for (sp = slice_subpartition_first(storage_get_selected_slice(a->s)); | |
631 | sp != NULL; sp = subpartition_next(sp)) { | |
632 | ds = dfui_dataset_new(); | |
633 | dfui_dataset_celldata_add(ds, "mountpoint", | |
634 | subpartition_get_mountpoint(sp)); | |
635 | dfui_dataset_celldata_add(ds, "capacity", | |
636 | capacity_to_string(subpartition_get_capacity(sp))); | |
88cfb1f7 SW |
637 | dfui_dataset_celldata_add(ds, "encrypted", |
638 | subpartition_is_encrypted(sp) ? "Y" : "N"); | |
7eae318c MS |
639 | dfui_form_dataset_add(f, ds); |
640 | } | |
641 | } else { | |
642 | /* | |
643 | * Otherwise, populate the form with datasets representing | |
644 | * reasonably-calculated defaults. The defaults are chosen | |
645 | * based on the slice's total capacity and the machine's | |
646 | * total physical memory (for swap.) | |
647 | */ | |
e6847d54 SW |
648 | for (i = 0; def_mountpt[i] != NULL; i++) { |
649 | capacity = default_capacity(a->s, def_mountpt[i]); | |
f9602a12 MD |
650 | if (capacity == 0) /* used to disable /build */ |
651 | continue; | |
7eae318c MS |
652 | ds = dfui_dataset_new(); |
653 | dfui_dataset_celldata_add(ds, "mountpoint", | |
e6847d54 | 654 | def_mountpt[i]); |
7eae318c MS |
655 | dfui_dataset_celldata_add(ds, "capacity", |
656 | capacity_to_string(capacity)); | |
88cfb1f7 | 657 | dfui_dataset_celldata_add(ds, "encrypted", "N"); |
7eae318c MS |
658 | dfui_form_dataset_add(f, ds); |
659 | } | |
660 | } | |
661 | } | |
662 | ||
663 | static int | |
664 | warn_subpartition_selections(struct i_fn_args *a) | |
665 | { | |
666 | int valid = 0; | |
7eae318c | 667 | |
e6847d54 SW |
668 | if (subpartition_find(storage_get_selected_slice(a->s), "/boot") == NULL) { |
669 | inform(a->c, _("The /boot partition must not be omitted.")); | |
f9602a12 MD |
670 | } else if (subpartition_find(storage_get_selected_slice(a->s), "/build") == NULL) { |
671 | inform(a->c, _("Without a /build, things like /usr/obj and " | |
672 | "/var/crash will just be on the root mount.")); | |
673 | valid = check_capacity(a); | |
e6847d54 SW |
674 | } else { |
675 | valid = check_capacity(a); | |
676 | } | |
7eae318c MS |
677 | |
678 | return(!valid); | |
679 | } | |
680 | ||
88cfb1f7 SW |
681 | static int |
682 | warn_encrypted_boot(struct i_fn_args *a) | |
683 | { | |
684 | int valid = 1; | |
685 | ||
686 | struct subpartition *sp; | |
687 | ||
688 | sp = subpartition_find(storage_get_selected_slice(a->s), "/boot"); | |
689 | if (sp == NULL) | |
690 | return(!valid); | |
691 | ||
692 | if (subpartition_is_encrypted(sp)) { | |
693 | switch (dfui_be_present_dialog(a->c, _("/boot cannot be encrypted"), | |
694 | _("Leave /boot unencrypted|Return to Create Subpartitions"), | |
695 | _("You have selected encryption for the /boot partition which " | |
696 | "is not supported."))) { | |
697 | case 1: | |
b8d2998c | 698 | subpartition_clr_encrypted(sp); |
88cfb1f7 SW |
699 | valid = 1; |
700 | break; | |
701 | case 2: | |
702 | valid = 0; | |
703 | break; | |
704 | default: | |
705 | abort_backend(); | |
706 | } | |
707 | } | |
708 | ||
709 | return(!valid); | |
710 | } | |
711 | ||
7eae318c MS |
712 | static struct dfui_form * |
713 | make_create_subpartitions_form(struct i_fn_args *a) | |
714 | { | |
7eae318c MS |
715 | struct dfui_form *f; |
716 | char msg_buf[1][1024]; | |
717 | ||
718 | snprintf(msg_buf[0], sizeof(msg_buf[0]), | |
719 | _("Subpartitions further divide a primary partition for " | |
720 | "use with %s. Some reasons you may want " | |
721 | "a set of subpartitions are:\n\n" | |
722 | "- you want to restrict how much data can be written " | |
723 | "to certain parts of the primary partition, to quell " | |
724 | "denial-of-service attacks; and\n" | |
725 | "- you want to speed up access to data on the disk." | |
726 | ""), OPERATING_SYSTEM_NAME); | |
727 | ||
728 | f = dfui_form_create( | |
729 | "create_subpartitions", | |
730 | _("Create Subpartitions"), | |
f9602a12 | 731 | _("Set up the subpartitions you want to have on this primary " |
e6847d54 | 732 | "partition. In most cases you should be fine with " |
f9602a12 MD |
733 | "the default settings." |
734 | " Note that /build will hold /usr/obj, /var/crash, and other" | |
735 | " elements of the topology that do not need to be backed up." | |
736 | " If no /build is specified, these dirs will be on the root." | |
737 | "\n\n" | |
7eae318c | 738 | "For Capacity, use 'M' to indicate megabytes, 'G' to " |
1f2824e8 SW |
739 | "indicate gigabytes, and so on (up to 'E'.) A single '*' " |
740 | "indicates 'use the remaining space on the primary partition'."), | |
7eae318c MS |
741 | |
742 | msg_buf[0], | |
743 | ||
744 | "p", "special", "dfinstaller_create_subpartitions", | |
745 | "p", "minimum_width","64", | |
746 | ||
747 | "f", "mountpoint", _("Mountpoint"), "", "", | |
748 | "f", "capacity", _("Capacity"), "", "", | |
749 | ||
88cfb1f7 SW |
750 | "f", "encrypted", _("Encrypted"), "", "", |
751 | "p", "control", "checkbox", | |
752 | ||
7eae318c MS |
753 | "a", "ok", _("Accept and Create"), "", "", |
754 | "a", "cancel", | |
755 | (disk_get_formatted(storage_get_selected_disk(a->s)) ? | |
756 | _("Return to Select Disk") : | |
757 | _("Return to Select Primary Partition")), "", "", | |
758 | "p", "accelerator", "ESC", | |
759 | ||
760 | NULL | |
761 | ); | |
762 | ||
763 | dfui_form_set_multiple(f, 1); | |
764 | dfui_form_set_extensible(f, 1); | |
765 | /* | |
766 | * Remove ATM until HAMMER installer support is better | |
767 | * dfui_form_set_extensible(f, 1); | |
768 | */ | |
769 | #if 0 | |
770 | if (expert) { | |
771 | fi = dfui_form_field_add(f, "softupdates", | |
772 | dfui_info_new(_("Softupdates"), "", "")); | |
773 | dfui_field_property_set(fi, "control", "checkbox"); | |
774 | ||
761cad2f SW |
775 | fi = dfui_form_field_add(f, "tmpfsbacked", |
776 | dfui_info_new(_("TMPFS"), "", "")); | |
7eae318c MS |
777 | dfui_field_property_set(fi, "control", "checkbox"); |
778 | ||
779 | fi = dfui_form_field_add(f, "fsize", | |
780 | dfui_info_new(_("Frag Sz"), "", "")); | |
781 | ||
782 | fi = dfui_form_field_add(f, "bsize", | |
783 | dfui_info_new(_("Block Sz"), "", "")); | |
784 | ||
785 | dfui_form_action_add(f, "switch", | |
786 | dfui_info_new(_("Switch to Normal Mode"), "", "")); | |
787 | } else { | |
788 | dfui_form_action_add(f, "switch", | |
789 | dfui_info_new(_("Switch to Expert Mode"), "", "")); | |
790 | } | |
791 | #endif | |
792 | return(f); | |
793 | } | |
794 | ||
795 | /* | |
796 | * Returns: | |
797 | * -1 = the form should be redisplayed | |
798 | * 0 = failure, function is over | |
799 | * 1 = success, function is over | |
800 | */ | |
801 | static int | |
e90c1ebb MD |
802 | show_create_subpartitions_form(int which, struct dfui_form *f, |
803 | struct i_fn_args *a) | |
7eae318c MS |
804 | { |
805 | struct dfui_dataset *ds; | |
806 | struct dfui_response *r; | |
807 | ||
808 | for (;;) { | |
809 | if (dfui_form_dataset_get_first(f) == NULL) | |
810 | populate_create_subpartitions_form(f, a); | |
811 | ||
812 | if (!dfui_be_present(a->c, f, &r)) | |
813 | abort_backend(); | |
814 | ||
815 | if (strcmp(dfui_response_get_action_id(r), "cancel") == 0) { | |
816 | dfui_response_free(r); | |
817 | return(0); | |
818 | } else if (strcmp(dfui_response_get_action_id(r), "switch") == 0) { | |
819 | if (check_subpartition_selections(r, a)) { | |
820 | save_subpartition_selections(r, a); | |
821 | expert = expert ? 0 : 1; | |
822 | dfui_response_free(r); | |
823 | return(-1); | |
824 | } | |
825 | } else { | |
826 | if (check_subpartition_selections(r, a)) { | |
827 | save_subpartition_selections(r, a); | |
88cfb1f7 SW |
828 | if (!warn_subpartition_selections(a) && |
829 | !warn_encrypted_boot(a)) { | |
e90c1ebb | 830 | if (!create_subpartitions(which, a)) { |
7eae318c MS |
831 | inform(a->c, _("The subpartitions you chose were " |
832 | "not correctly created, and the " | |
833 | "primary partition may " | |
834 | "now be in an inconsistent state. " | |
835 | "We recommend re-formatting it " | |
836 | "before proceeding.")); | |
837 | dfui_response_free(r); | |
838 | return(0); | |
839 | } else { | |
840 | dfui_response_free(r); | |
841 | return(1); | |
842 | } | |
843 | } | |
844 | } | |
845 | } | |
846 | ||
847 | dfui_form_datasets_free(f); | |
848 | /* dfui_form_datasets_add_from_response(f, r); */ | |
849 | for (ds = dfui_response_dataset_get_first(r); ds != NULL; | |
850 | ds = dfui_dataset_get_next(ds)) { | |
851 | dfui_form_dataset_add(f, dfui_dataset_dup(ds)); | |
852 | } | |
853 | } | |
854 | } | |
855 | ||
856 | /* | |
857 | * fn_create_subpartitions_hammer: let the user specify what subpartitions they | |
858 | * want on the disk, how large each should be, and where it should be mounted. | |
859 | */ | |
860 | void | |
e90c1ebb | 861 | fn_create_subpartitions_hammer(int which, struct i_fn_args *a) |
7eae318c MS |
862 | { |
863 | struct dfui_form *f; | |
344b24ad | 864 | unsigned long capacity; |
7eae318c MS |
865 | int done = 0; |
866 | ||
867 | a->result = 0; | |
a07abcb9 | 868 | capacity = disk_get_capacity(storage_get_selected_disk(a->s)); |
e90c1ebb | 869 | if (which == FS_HAMMER && capacity < HAMMER_MIN) { |
a07abcb9 MD |
870 | inform(a->c, _("The selected %dM disk is smaller than the " |
871 | "required %dM for the HAMMER filesystem."), | |
872 | (int)capacity, | |
873 | (int)HAMMER_MIN); | |
344b24ad SW |
874 | return; |
875 | } | |
7eae318c MS |
876 | while (!done) { |
877 | f = make_create_subpartitions_form(a); | |
e90c1ebb | 878 | switch (show_create_subpartitions_form(which, f, a)) { |
7eae318c MS |
879 | case -1: |
880 | done = 0; | |
881 | break; | |
882 | case 0: | |
883 | done = 1; | |
884 | a->result = 0; | |
885 | break; | |
886 | case 1: | |
887 | done = 1; | |
888 | a->result = 1; | |
889 | break; | |
890 | } | |
891 | dfui_form_free(f); | |
892 | } | |
893 | } | |
f9602a12 MD |
894 | |
895 | static | |
896 | char * | |
897 | construct_lname(const char *mtpt) | |
898 | { | |
899 | char *res; | |
900 | int i; | |
901 | ||
902 | if (strcmp(mtpt, "/") == 0) { | |
903 | res = strdup("ROOT"); | |
904 | } else { | |
905 | if (strrchr(mtpt, '/')) | |
906 | mtpt = strrchr(mtpt, '/') + 1; | |
907 | if (*mtpt == 0) | |
908 | mtpt = "unknown"; | |
909 | res = malloc(strlen(mtpt) + 1); | |
910 | for (i = 0; mtpt[i]; ++i) | |
911 | res[i] = toupper(mtpt[i]); | |
912 | res[i] = 0; | |
913 | } | |
914 | return res; | |
915 | } |