label.c revision 1.37 1 /* $NetBSD: label.c,v 1.37 2022/06/18 13:52:42 martin Exp $ */
2
3 /*
4 * Copyright 1997 Jonathan Stone
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project by
18 * Jonathan Stone.
19 * 4. The name of Jonathan Stone may not be used to endorse
20 * or promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JONATHAN STONE ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 * THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 __RCSID("$NetBSD: label.c,v 1.37 2022/06/18 13:52:42 martin Exp $");
40 #endif
41
42 #include <sys/types.h>
43 #include <stddef.h>
44 #include <assert.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <fcntl.h>
48 #include <util.h>
49 #include <unistd.h>
50 #include <sys/dkio.h>
51 #include <sys/param.h>
52 #include <sys/bootblock.h>
53 #include <sys/bitops.h>
54 #include <ufs/ffs/fs.h>
55
56 #include "defs.h"
57 #include "msg_defs.h"
58 #include "menu_defs.h"
59
60 /*
61 * local prototypes
62 */
63 static bool boringpart(const struct disk_part_info *info);
64 static bool checklabel(struct disk_partitions*, char *, char *);
65 static void show_partition_adder(menudesc *, struct partition_usage_set*);
66
67 /*
68 * Return 1 if a partition should be ignored when checking
69 * for overlapping partitions.
70 */
71 static bool
72 boringpart(const struct disk_part_info *info)
73 {
74
75 if (info->size == 0)
76 return true;
77 if (info->flags &
78 (PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK|PTI_SEC_CONTAINER|
79 PTI_RAW_PART))
80 return true;
81
82 return false;
83 }
84
85 /*
86 * We have some partitions in our "wanted" list that we may not edit,
87 * like the RAW_PART in disklabel, some that just represent external
88 * mount entries for the final fstab or similar.
89 * We have previously sorted pset->parts and pset->infos to be in sync,
90 * but the former "array" may be shorter.
91 * Here are a few quick predicates to check for them.
92 */
93 static bool
94 real_partition(const struct partition_usage_set *pset, int index)
95 {
96 if (index < 0 || (size_t)index >= pset->num)
97 return false;
98
99 return pset->infos[index].cur_part_id != NO_PART;
100 }
101
102 /*
103 * Check partitioning for overlapping partitions.
104 * Returns true if no overlapping partition found.
105 * Sets reference arguments ovly1 and ovly2 to the indices of
106 * overlapping partitions if any are found.
107 */
108 static bool
109 checklabel(struct disk_partitions *parts,
110 char *ovl1, char *ovl2)
111 {
112 part_id i, j;
113 struct disk_part_info info;
114 daddr_t istart, iend, jstart, jend;
115 unsigned int fs_type, fs_sub_type;
116
117 if (parts->num_part == 0)
118 return true;
119
120 for (i = 0; i < parts->num_part - 1; i ++ ) {
121 if (!parts->pscheme->get_part_info(parts, i, &info))
122 continue;
123
124 /* skip unused or reserved partitions */
125 if (boringpart(&info))
126 continue;
127
128 /*
129 * check succeeding partitions for overlap.
130 * O(n^2), but n is small.
131 */
132 istart = info.start;
133 iend = istart + info.size;
134 fs_type = info.fs_type;
135 fs_sub_type = info.fs_sub_type;
136
137 for (j = i+1; j < parts->num_part; j++) {
138
139 if (!parts->pscheme->get_part_info(parts, j, &info))
140 continue;
141
142 /* skip unused or reserved partitions */
143 if (boringpart(&info))
144 continue;
145
146 jstart = info.start;
147 jend = jstart + info.size;
148
149 /* overlap? */
150 if ((istart <= jstart && jstart < iend) ||
151 (jstart <= istart && istart < jend)) {
152 snprintf(ovl1, MENUSTRSIZE,
153 "%" PRIu64 " - %" PRIu64 " %s, %s",
154 istart / sizemult, iend / sizemult,
155 multname,
156 getfslabelname(fs_type, fs_sub_type));
157 snprintf(ovl2, MENUSTRSIZE,
158 "%" PRIu64 " - %" PRIu64 " %s, %s",
159 jstart / sizemult, jend / sizemult,
160 multname,
161 getfslabelname(info.fs_type,
162 info.fs_sub_type));
163 return false;
164 }
165 }
166 }
167
168 return true;
169 }
170
171 int
172 checkoverlap(struct disk_partitions *parts)
173 {
174 char desc1[MENUSTRSIZE], desc2[MENUSTRSIZE];
175 if (!checklabel(parts, desc1, desc2)) {
176 msg_display_subst(MSG_partitions_overlap, 2, desc1, desc2);
177 return 1;
178 }
179 return 0;
180 }
181
182 /*
183 * return (see post_edit_verify):
184 * 0 -> abort
185 * 1 -> re-edit
186 * 2 -> continue installation
187 */
188 static int
189 verify_parts(struct partition_usage_set *pset, bool install)
190 {
191 struct part_usage_info *wanted;
192 struct disk_partitions *parts;
193 size_t i, num_root;
194 daddr_t first_bsdstart, inst_start;
195 int rv;
196
197 first_bsdstart = inst_start = -1;
198 num_root = 0;
199 parts = pset->parts;
200 for (i = 0; i < pset->num; i++) {
201 wanted = &pset->infos[i];
202
203 if (wanted->flags & PUIFLG_JUST_MOUNTPOINT)
204 continue;
205 if (wanted->cur_part_id == NO_PART)
206 continue;
207 if (!(wanted->instflags & PUIINST_MOUNT))
208 continue;
209 if (strcmp(wanted->mount, "/") != 0)
210 continue;
211 num_root++;
212
213 if (first_bsdstart <= 0) {
214 first_bsdstart = wanted->cur_start;
215 }
216 if (inst_start < 0 &&
217 (wanted->cur_flags & PTI_INSTALL_TARGET)) {
218 inst_start = wanted->cur_start;
219 }
220 }
221
222 if ((num_root == 0 && install) ||
223 (num_root > 1 && inst_start < 0)) {
224 if (num_root == 0 && install)
225 msg_display_subst(MSG_must_be_one_root, 2,
226 msg_string(parts->pscheme->name),
227 msg_string(parts->pscheme->short_name));
228 else
229 msg_display_subst(MSG_multbsdpart, 2,
230 msg_string(parts->pscheme->name),
231 msg_string(parts->pscheme->short_name));
232 rv = ask_reedit(parts);
233 if (rv != 2)
234 return rv;
235 }
236
237 /* Check for overlaps */
238 if (checkoverlap(parts) != 0) {
239 rv = ask_reedit(parts);
240 if (rv != 2)
241 return rv;
242 }
243
244 /*
245 * post_edit_verify returns:
246 * 0 -> abort
247 * 1 -> re-edit
248 * 2 -> continue installation
249 */
250 if (parts->pscheme->post_edit_verify)
251 return parts->pscheme->post_edit_verify(parts, false);
252
253 return 2;
254 }
255
256 static int
257 edit_fs_start(menudesc *m, void *arg)
258 {
259 struct single_part_fs_edit *edit = arg;
260 daddr_t start, end;
261
262 start = getpartoff(edit->pset->parts, edit->info.start);
263 if (edit->info.size != 0) {
264 /* Try to keep end in the same place */
265 end = edit->info.start + edit->info.size;
266 if (end < start)
267 edit->info.size = edit->pset->parts->pscheme->
268 max_free_space_at(edit->pset->parts,
269 edit->info.start);
270 else
271 edit->info.size = end - start;
272 }
273 edit->info.start = start;
274 return 0;
275 }
276
277 static int
278 edit_fs_size(menudesc *m, void *arg)
279 {
280 struct single_part_fs_edit *edit = arg;
281 struct disk_part_info pinfo;
282 daddr_t size;
283
284 /* get original partition data, in case start moved already */
285 edit->pset->parts->pscheme->get_part_info(edit->pset->parts,
286 edit->id, &pinfo);
287 /* ask for new size with old start and current values */
288 size = getpartsize(edit->pset->parts, pinfo.start,
289 edit->info.start, edit->info.size);
290 if (size < 0)
291 return 0;
292 if (size > edit->pset->parts->disk_size)
293 size = edit->pset->parts->disk_size - edit->info.start;
294 edit->info.size = size;
295 return 0;
296 }
297
298 static int
299 set_ffs_opt_pow2(menudesc *m, void *arg)
300 {
301 struct single_part_fs_edit *edit = arg;
302 size_t val = 1 << (edit->offset+m->cursel);
303
304 if (edit->mode == 1) {
305 edit->info.fs_opt1 = val;
306 edit->wanted->fs_opt1 = val;
307 } else if (edit->mode == 2) {
308 edit->info.fs_opt2 = val;
309 edit->wanted->fs_opt2 = val;
310 }
311 return 0;
312 }
313
314 static int
315 edit_fs_ffs_opt(menudesc *m, void *arg, msg head,
316 size_t min_val, size_t max_val)
317 {
318 struct single_part_fs_edit *edit = arg;
319 menu_ent opts[min(MAXPHYS/4096, 8)];
320 char names[min(MAXPHYS/4096, 8)][20];
321 size_t i, val;
322 int menu;
323
324 edit->offset = ilog2(min_val);
325 memset(opts, 0, sizeof opts);
326 for (i = 0, val = min_val; val <= max_val; i++, val <<= 1) {
327 snprintf(names[i], sizeof names[i], "%zu", val);
328 opts[i].opt_name = names[i];
329 opts[i].opt_action = set_ffs_opt_pow2;
330 opts[i].opt_flags = OPT_EXIT;
331 }
332 menu = new_menu(head, opts, i, 40, 6, 0, 0, MC_NOEXITOPT,
333 NULL, NULL, NULL, NULL, NULL);
334 if (menu < 0)
335 return 1;
336 process_menu(menu, arg);
337 free_menu(menu);
338 return 0;
339 }
340
341 static int
342 edit_fs_ffs_block(menudesc *m, void *arg)
343 {
344 struct single_part_fs_edit *edit = arg;
345
346 edit->mode = 1; /* edit fs_opt1 */
347 return edit_fs_ffs_opt(m, arg, MSG_Select_file_system_block_size,
348 4096, MAXPHYS);
349 }
350
351 static int
352 edit_fs_ffs_frag(menudesc *m, void *arg)
353 {
354 struct single_part_fs_edit *edit = arg;
355 size_t bsize, sec_size;
356
357 edit->mode = 2; /* edit fs_opt2 */
358 bsize = edit->info.fs_opt1;
359 if (bsize == 0) {
360 sec_size = edit->wanted->parts->bytes_per_sector;
361 if (edit->wanted->size >= (daddr_t)(128L*(GIG/sec_size)))
362 bsize = 32*1024;
363 else if (edit->wanted->size >= (daddr_t)(1000L*(MEG/sec_size)))
364 bsize = 16*1024;
365 else if (edit->wanted->size >= (daddr_t)(20L*(MEG/sec_size)))
366 bsize = 8*1024;
367 else
368 bsize = 4+1024;
369 }
370 return edit_fs_ffs_opt(m, arg, MSG_Select_file_system_fragment_size,
371 bsize / 8, bsize);
372 }
373
374 static int
375 edit_fs_ffs_avg_size(menudesc *m, void *arg)
376 {
377 struct single_part_fs_edit *edit = arg;
378 char answer[12];
379
380 snprintf(answer, sizeof answer, "%u", edit->info.fs_opt3);
381 msg_prompt_win(MSG_ptn_isize_prompt, -1, 18, 0, 0,
382 answer, answer, sizeof answer);
383 edit->info.fs_opt3 = atol(answer);
384 edit->wanted->fs_opt3 = edit->info.fs_opt3;
385
386 return 0;
387 }
388
389 static int
390 edit_fs_preserve(menudesc *m, void *arg)
391 {
392 struct single_part_fs_edit *edit = arg;
393
394 edit->wanted->instflags ^= PUIINST_NEWFS;
395 return 0;
396 }
397
398 static int
399 edit_install(menudesc *m, void *arg)
400 {
401 struct single_part_fs_edit *edit = arg;
402
403 edit->info.flags ^= PTI_INSTALL_TARGET;
404 return 0;
405 }
406
407 static int
408 edit_fs_mount(menudesc *m, void *arg)
409 {
410 struct single_part_fs_edit *edit = arg;
411
412 edit->wanted->instflags ^= PUIINST_MOUNT;
413 return 0;
414 }
415
416 static int
417 edit_fs_mountpt(menudesc *m, void *arg)
418 {
419 struct single_part_fs_edit *edit = arg;
420 char *p, *first, *last, buf[MOUNTLEN];
421
422 strlcpy(buf, edit->wanted->mount, sizeof buf);
423 msg_prompt_win(MSG_mountpoint, -1, 18, 0, 0,
424 buf, buf, MOUNTLEN);
425
426 /*
427 * Trim all leading and trailing whitespace
428 */
429 for (first = NULL, last = NULL, p = buf; *p; p++) {
430 if (isspace((unsigned char)*p))
431 continue;
432 if (first == NULL)
433 first = p;
434 last = p;
435 }
436 if (last != NULL)
437 last[1] = 0;
438
439 if (first == NULL || *first == 0 || strcmp(first, "none") == 0) {
440 edit->wanted->mount[0] = 0;
441 edit->wanted->instflags &= ~PUIINST_MOUNT;
442 return 0;
443 }
444
445 if (*first != '/') {
446 edit->wanted->mount[0] = '/';
447 strlcpy(&edit->wanted->mount[1], first,
448 sizeof(edit->wanted->mount)-1);
449 } else {
450 strlcpy(edit->wanted->mount, first, sizeof edit->wanted->mount);
451 }
452
453 return 0;
454 }
455
456 static int
457 edit_restore(menudesc *m, void *arg)
458 {
459 struct single_part_fs_edit *edit = arg;
460
461 edit->info = edit->old_info;
462 *edit->wanted = edit->old_usage;
463 return 0;
464 }
465
466 static int
467 edit_cancel(menudesc *m, void *arg)
468 {
469 struct single_part_fs_edit *edit = arg;
470
471 edit->rv = -1;
472 return 1;
473 }
474
475 static int
476 edit_delete_ptn(menudesc *m, void *arg)
477 {
478 struct single_part_fs_edit *edit = arg;
479
480 edit->rv = -2;
481 return 1;
482 }
483
484 /*
485 * We have added/removed partitions, all cur_part_id values are
486 * out of sync. Re-fetch and reorder partitions accordingly.
487 */
488 static void
489 renumber_partitions(struct partition_usage_set *pset)
490 {
491 struct part_usage_info *ninfos;
492 struct disk_part_info info;
493 size_t i;
494 part_id pno;
495
496 ninfos = calloc(pset->parts->num_part, sizeof(*ninfos));
497 if (ninfos == NULL) {
498 err_msg_win(err_outofmem);
499 return;
500 }
501
502 for (pno = 0; pno < pset->parts->num_part; pno++) {
503 if (!pset->parts->pscheme->get_part_info(pset->parts, pno,
504 &info))
505 continue;
506 for (i = 0; i < pset->parts->num_part; i++) {
507 if (pset->infos[i].cur_start != info.start)
508 continue;
509 if (pset->infos[i].cur_flags != info.flags)
510 continue;
511 if ((info.fs_type != FS_UNUSED &&
512 info.fs_type == pset->infos[i].fs_type) ||
513 (pset->infos[i].type ==
514 info.nat_type->generic_ptype)) {
515 memcpy(&ninfos[pno], &pset->infos[i],
516 sizeof(ninfos[pno]));
517 ninfos[pno].cur_part_id = pno;
518 break;
519 }
520 }
521 }
522
523 memcpy(pset->infos, ninfos, sizeof(*pset->infos)*pset->parts->num_part);
524 free(ninfos);
525 }
526
527 /*
528 * Most often used file system types, we offer them in a first level menu.
529 */
530 static const uint edit_fs_common_types[] =
531 { FS_BSDFFS, FS_SWAP, FS_MSDOS, FS_EFI_SP, FS_BSDLFS, FS_EX2FS };
532
533 /*
534 * Functions for uncommon file system types - we offer the full list,
535 * but put FFSv2 and FFSv1 at the front and duplicate FS_MSDOS as
536 * EFI system partition.
537 */
538 static void
539 init_fs_type_ext(menudesc *menu, void *arg)
540 {
541 struct single_part_fs_edit *edit = arg;
542 uint t = edit->info.fs_type;
543 size_t i, ndx, max = menu->numopts;
544
545 if (t == FS_BSDFFS) {
546 if (edit->info.fs_sub_type == 2)
547 menu->cursel = 0;
548 else
549 menu->cursel = 1;
550 return;
551 } else if (t == FS_EX2FS && edit->info.fs_sub_type == 1) {
552 menu->cursel = FSMAXTYPES;
553 return;
554 }
555 /* skip the two FFS entries, and do not add FFS later again */
556 for (ndx = 2, i = 0; i < FSMAXTYPES && ndx < max; i++) {
557 if (i == FS_UNUSED)
558 continue;
559 if (i == FS_BSDFFS)
560 continue;
561 if (fstypenames[i] == NULL)
562 continue;
563
564 if (i == t) {
565 menu->cursel = ndx;
566 break;
567 }
568 if (i == FS_MSDOS) {
569 ndx++;
570 if (t == FS_EFI_SP) {
571 menu->cursel = ndx;
572 break;
573 }
574 }
575 ndx++;
576 }
577 }
578
579 static int
580 set_fstype_ext(menudesc *menu, void *arg)
581 {
582 struct single_part_fs_edit *edit = arg;
583 size_t i, ndx, max = menu->numopts;
584 enum part_type pt;
585
586 if (menu->cursel == 0 || menu->cursel == 1) {
587 edit->info.fs_type = FS_BSDFFS;
588 edit->info.fs_sub_type = menu->cursel == 0 ? 2 : 1;
589 goto found_type;
590 } else if (menu->cursel == FSMAXTYPES) {
591 edit->info.fs_type = FS_EX2FS;
592 edit->info.fs_sub_type = 1;
593 goto found_type;
594 }
595
596 for (ndx = 2, i = 0; i < FSMAXTYPES && ndx < max; i++) {
597 if (i == FS_UNUSED)
598 continue;
599 if (i == FS_BSDFFS)
600 continue;
601 if (fstypenames[i] == NULL)
602 continue;
603
604 if (ndx == (size_t)menu->cursel) {
605 edit->info.fs_type = i;
606 edit->info.fs_sub_type = 0;
607 goto found_type;
608 }
609 ndx++;
610 if (i == FS_MSDOS) {
611 ndx++;
612 if (ndx == (size_t)menu->cursel) {
613 edit->info.fs_type = FS_EFI_SP;
614 edit->info.fs_sub_type = 0;
615 goto found_type;
616 }
617 }
618 }
619 return 1;
620
621 found_type:
622 pt = edit->info.nat_type ? edit->info.nat_type->generic_ptype : PT_root;
623 edit->info.nat_type = edit->pset->parts->pscheme->
624 get_fs_part_type(pt, edit->info.fs_type, edit->info.fs_sub_type);
625 if (edit->info.nat_type == NULL)
626 edit->info.nat_type = edit->pset->parts->pscheme->
627 get_generic_part_type(PT_root);
628 edit->wanted->type = edit->info.nat_type->generic_ptype;
629 edit->wanted->fs_type = edit->info.fs_type;
630 edit->wanted->fs_version = edit->info.fs_sub_type;
631 return 1;
632 }
633
634 /*
635 * Offer a menu with "exotic" file system types, start with FFSv2 and FFSv1,
636 * skip later FFS entry in the generic list.
637 */
638 static int
639 edit_fs_type_ext(menudesc *menu, void *arg)
640 {
641 menu_ent *opts;
642 int m;
643 size_t i, ndx, cnt;
644
645 cnt = __arraycount(fstypenames)+1;
646 opts = calloc(cnt, sizeof(*opts));
647 if (opts == NULL)
648 return 1;
649
650 ndx = 0;
651 opts[ndx].opt_name = msg_string(MSG_fs_type_ffsv2);
652 opts[ndx].opt_action = set_fstype_ext;
653 ndx++;
654 opts[ndx].opt_name = msg_string(MSG_fs_type_ffs);
655 opts[ndx].opt_action = set_fstype_ext;
656 ndx++;
657 for (i = 0; i < FSMAXTYPES && ndx < cnt; i++) {
658 if (i == FS_UNUSED)
659 continue;
660 if (i == FS_BSDFFS)
661 continue;
662 if (fstypenames[i] == NULL)
663 continue;
664 opts[ndx].opt_name = fstypenames[i];
665 opts[ndx].opt_action = set_fstype_ext;
666 ndx++;
667 if (i == FS_MSDOS) {
668 opts[ndx] = opts[ndx-1];
669 opts[ndx].opt_name = getfslabelname(FS_EFI_SP, 0);
670 ndx++;
671 }
672 }
673 opts[ndx].opt_name = msg_string(MSG_fs_type_ext2old);
674 opts[ndx].opt_action = set_fstype_ext;
675 ndx++;
676 assert(ndx == cnt);
677 m = new_menu(MSG_Select_the_type, opts, ndx,
678 30, 6, 10, 0, MC_SUBMENU | MC_SCROLL,
679 init_fs_type_ext, NULL, NULL, NULL, MSG_unchanged);
680
681 if (m < 0)
682 return 1;
683 process_menu(m, arg);
684 free_menu(m);
685 free(opts);
686
687 return 1;
688 }
689
690 static void
691 init_fs_type(menudesc *menu, void *arg)
692 {
693 struct single_part_fs_edit *edit = arg;
694 size_t i;
695
696 /* init menu->cursel from fs type in arg */
697 if (edit->info.fs_type == FS_BSDFFS) {
698 if (edit->info.fs_sub_type == 2)
699 menu->cursel = 0;
700 else
701 menu->cursel = 1;
702 }
703 for (i = 1; i < __arraycount(edit_fs_common_types); i++) {
704 if (edit->info.fs_type == edit_fs_common_types[i]) {
705 menu->cursel = i+1;
706 break;
707 }
708 }
709 }
710
711 static int
712 set_fstype(menudesc *menu, void *arg)
713 {
714 struct single_part_fs_edit *edit = arg;
715 enum part_type pt;
716 int ndx;
717
718 pt = edit->info.nat_type ? edit->info.nat_type->generic_ptype : PT_root;
719 if (menu->cursel < 2) {
720 edit->info.fs_type = FS_BSDFFS;
721 edit->info.fs_sub_type = menu->cursel == 0 ? 2 : 1;
722 edit->info.nat_type = edit->pset->parts->pscheme->
723 get_fs_part_type(pt, FS_BSDFFS, 2);
724 if (edit->info.nat_type == NULL)
725 edit->info.nat_type = edit->pset->parts->
726 pscheme->get_generic_part_type(PT_root);
727 edit->wanted->type = edit->info.nat_type->generic_ptype;
728 edit->wanted->fs_type = edit->info.fs_type;
729 edit->wanted->fs_version = edit->info.fs_sub_type;
730 return 1;
731 }
732 ndx = menu->cursel-1;
733
734 if (ndx < 0 ||
735 (size_t)ndx >= __arraycount(edit_fs_common_types))
736 return 1;
737
738 edit->info.fs_type = edit_fs_common_types[ndx];
739 edit->info.fs_sub_type = 0;
740 edit->info.nat_type = edit->pset->parts->pscheme->
741 get_fs_part_type(pt, edit->info.fs_type, 0);
742 if (edit->info.nat_type == NULL)
743 edit->info.nat_type = edit->pset->parts->
744 pscheme->get_generic_part_type(PT_root);
745 edit->wanted->type = edit->info.nat_type->generic_ptype;
746 edit->wanted->fs_type = edit->info.fs_type;
747 edit->wanted->fs_version = edit->info.fs_sub_type;
748 return 1;
749 }
750
751 /*
752 * Offer a menu selecting the common file system types
753 */
754 static int
755 edit_fs_type(menudesc *menu, void *arg)
756 {
757 struct single_part_fs_edit *edit = arg;
758 menu_ent *opts;
759 int m, cnt;
760 size_t i;
761
762 /*
763 * Shortcut to full menu if we have an exotic value
764 */
765 if (edit->info.fs_type == FS_EX2FS && edit->info.fs_sub_type == 1) {
766 edit_fs_type_ext(menu, arg);
767 return 0;
768 }
769 for (i = 0; i < __arraycount(edit_fs_common_types); i++)
770 if (edit->info.fs_type == edit_fs_common_types[i])
771 break;
772 if (i >= __arraycount(edit_fs_common_types)) {
773 edit_fs_type_ext(menu, arg);
774 return 0;
775 }
776
777 /*
778 * Starting with a common type, show short menu first
779 */
780 cnt = __arraycount(edit_fs_common_types) + 2;
781 opts = calloc(cnt, sizeof(*opts));
782 if (opts == NULL)
783 return 0;
784
785 /* special case entry 0: two FFS entries */
786 for (i = 0; i < __arraycount(edit_fs_common_types); i++) {
787 opts[i+1].opt_name = getfslabelname(edit_fs_common_types[i], 0);
788 opts[i+1].opt_action = set_fstype;
789 }
790 /* duplicate FFS (at offset 1) into first entry */
791 opts[0] = opts[1];
792 opts[0].opt_name = msg_string(MSG_fs_type_ffsv2);
793 opts[1].opt_name = msg_string(MSG_fs_type_ffs);
794 /* add secondary sub-menu */
795 assert(i+1 < (size_t)cnt);
796 opts[i+1].opt_name = msg_string(MSG_other_fs_type);
797 opts[i+1].opt_action = edit_fs_type_ext;
798
799 m = new_menu(MSG_Select_the_type, opts, cnt,
800 30, 6, 0, 0, MC_SUBMENU | MC_SCROLL,
801 init_fs_type, NULL, NULL, NULL, MSG_unchanged);
802
803 if (m < 0)
804 return 0;
805 process_menu(m, arg);
806 free_menu(m);
807 free(opts);
808
809 return 0;
810 }
811
812
813 static void update_edit_ptn_menu(menudesc *m, void *arg);
814 static void draw_edit_ptn_line(menudesc *m, int opt, void *arg);
815 static int edit_ptn_custom_type(menudesc *m, void *arg);
816
817 static void
818 remember_deleted(struct partition_usage_set *pset,
819 struct disk_partitions *parts)
820 {
821 size_t i, num;
822 struct disk_partitions **tab;
823
824 /* do we have parts on record already? */
825 for (i = 0; i < pset->num_write_back; i++)
826 if (pset->write_back[i] == parts)
827 return;
828 /*
829 * Need to record this partition table for write back
830 */
831 num = pset->num_write_back + 1;
832 tab = realloc(pset->write_back, num*sizeof(*pset->write_back));
833 if (!tab)
834 return;
835 tab[pset->num_write_back] = parts;
836 pset->write_back = tab;
837 pset->num_write_back = num;
838 }
839
840 int
841 edit_ptn(menudesc *menu, void *arg)
842 {
843 struct partition_usage_set *pset = arg;
844 struct single_part_fs_edit edit;
845 int fspart_menu, num_opts;
846 const char *err;
847 menu_ent *mopts, *popt;
848 bool is_new_part, with_inst_opt = pset->parts->parent == NULL;
849
850 static const menu_ent edit_ptn_fields_head[] = {
851 { .opt_action=edit_fs_type },
852 { .opt_action=edit_fs_start },
853 { .opt_action=edit_fs_size },
854 { .opt_flags=OPT_IGNORE },
855 };
856
857 static const menu_ent edit_ptn_fields_head_add[] = {
858 { .opt_action=edit_install },
859 };
860
861 static const menu_ent edit_ptn_fields_head2[] = {
862 { .opt_action=edit_fs_preserve },
863 { .opt_action=edit_fs_mount },
864 { .opt_menu=MENU_mountoptions, .opt_flags=OPT_SUB },
865 { .opt_action=edit_fs_mountpt },
866 };
867
868 static const menu_ent edit_ptn_fields_ffs[] = {
869 { .opt_action=edit_fs_ffs_avg_size },
870 { .opt_action=edit_fs_ffs_block },
871 { .opt_action=edit_fs_ffs_frag },
872 };
873
874 static const menu_ent edit_ptn_fields_tail[] = {
875 { .opt_name=MSG_askunits, .opt_menu=MENU_sizechoice,
876 .opt_flags=OPT_SUB },
877 { .opt_name=MSG_restore,
878 .opt_action=edit_restore},
879 { .opt_name=MSG_Delete_partition,
880 .opt_action=edit_delete_ptn},
881 { .opt_name=MSG_cancel,
882 .opt_action=edit_cancel},
883 };
884
885 memset(&edit, 0, sizeof edit);
886 edit.pset = pset;
887 edit.index = menu->cursel;
888 edit.wanted = &pset->infos[edit.index];
889
890 if (menu->cursel < 0 || (size_t)menu->cursel > pset->parts->num_part)
891 return 0;
892 is_new_part = (size_t)menu->cursel == pset->parts->num_part;
893
894 num_opts = __arraycount(edit_ptn_fields_head) +
895 __arraycount(edit_ptn_fields_head2) +
896 __arraycount(edit_ptn_fields_tail);
897 if (edit.wanted->fs_type == FS_BSDFFS ||
898 edit.wanted->fs_type == FS_BSDLFS)
899 num_opts += __arraycount(edit_ptn_fields_ffs);
900 if (with_inst_opt)
901 num_opts += __arraycount(edit_ptn_fields_head_add);
902 if (is_new_part)
903 num_opts--;
904 else
905 num_opts += pset->parts->pscheme->custom_attribute_count;
906
907 mopts = calloc(num_opts, sizeof(*mopts));
908 if (mopts == NULL) {
909 err_msg_win(err_outofmem);
910 return 0;
911 }
912 memcpy(mopts, edit_ptn_fields_head, sizeof(edit_ptn_fields_head));
913 popt = mopts + __arraycount(edit_ptn_fields_head);
914 if (with_inst_opt) {
915 memcpy(popt, edit_ptn_fields_head_add,
916 sizeof(edit_ptn_fields_head_add));
917 popt += __arraycount(edit_ptn_fields_head_add);
918 }
919 memcpy(popt, edit_ptn_fields_head2, sizeof(edit_ptn_fields_head2));
920 popt += __arraycount(edit_ptn_fields_head2);
921 if (edit.wanted->fs_type == FS_BSDFFS ||
922 edit.wanted->fs_type == FS_BSDLFS) {
923 memcpy(popt, edit_ptn_fields_ffs, sizeof(edit_ptn_fields_ffs));
924 popt += __arraycount(edit_ptn_fields_ffs);
925 }
926 edit.first_custom_attr = popt - mopts;
927 if (!is_new_part) {
928 for (size_t i = 0;
929 i < pset->parts->pscheme->custom_attribute_count;
930 i++, popt++) {
931 popt->opt_action = edit_ptn_custom_type;
932 }
933 }
934 memcpy(popt, edit_ptn_fields_tail, sizeof(edit_ptn_fields_tail));
935 popt += __arraycount(edit_ptn_fields_tail) - 1;
936 if (is_new_part)
937 memcpy(popt-1, popt, sizeof(*popt));
938
939 if (is_new_part) {
940 struct disk_part_free_space space;
941 daddr_t align = pset->parts->pscheme->get_part_alignment(
942 pset->parts);
943
944 edit.id = NO_PART;
945 if (pset->parts->pscheme->get_free_spaces(pset->parts,
946 &space, 1, align, align, -1, -1) == 1) {
947 edit.info.start = space.start;
948 edit.info.size = space.size;
949 edit.info.fs_type = FS_BSDFFS;
950 edit.info.fs_sub_type = 2;
951 edit.info.nat_type = pset->parts->pscheme->
952 get_fs_part_type(PT_root, edit.info.fs_type,
953 edit.info.fs_sub_type);
954 edit.wanted->instflags = PUIINST_NEWFS;
955 }
956 } else {
957 edit.id = pset->infos[edit.index].cur_part_id;
958 if (!pset->parts->pscheme->get_part_info(pset->parts, edit.id,
959 &edit.info)) {
960 free(mopts);
961 return 0;
962 }
963 }
964
965 edit.old_usage = *edit.wanted;
966 edit.old_info = edit.info;
967
968 fspart_menu = new_menu(MSG_edfspart, mopts, num_opts,
969 15, 2, 0, 55, MC_NOCLEAR | MC_SCROLL,
970 update_edit_ptn_menu, draw_edit_ptn_line, NULL,
971 NULL, MSG_OK);
972
973 process_menu(fspart_menu, &edit);
974 free(mopts);
975 free_menu(fspart_menu);
976
977 if (edit.rv == 0) { /* OK, set new data */
978 edit.info.last_mounted = edit.wanted->mount;
979 if (is_new_part) {
980 edit.wanted->parts = pset->parts;
981 edit.wanted->cur_part_id = pset->parts->pscheme->
982 add_partition(pset->parts, &edit.info, &err);
983 if (edit.wanted->cur_part_id == NO_PART)
984 err_msg_win(err);
985 else {
986 pset->parts->pscheme->get_part_info(
987 pset->parts, edit.wanted->cur_part_id,
988 &edit.info);
989 edit.wanted->cur_start = edit.info.start;
990 edit.wanted->size = edit.info.size;
991 edit.wanted->type =
992 edit.info.nat_type->generic_ptype;
993 edit.wanted->fs_type = edit.info.fs_type;
994 edit.wanted->fs_version = edit.info.fs_sub_type;
995 /* things have changed, re-sort */
996 renumber_partitions(pset);
997 }
998 } else {
999 if (!pset->parts->pscheme->set_part_info(pset->parts,
1000 edit.id, &edit.info, &err))
1001 err_msg_win(err);
1002 }
1003
1004 /*
1005 * if size has changed, we may need to add or remove
1006 * the option to add partitions
1007 */
1008 show_partition_adder(menu, pset);
1009 } else if (edit.rv == -1) { /* cancel edit */
1010 if (is_new_part) {
1011 memmove(pset->infos+edit.index,
1012 pset->infos+edit.index+1,
1013 sizeof(*pset->infos)*(pset->num-edit.index));
1014 memmove(menu->opts+edit.index,
1015 menu->opts+edit.index+1,
1016 sizeof(*menu->opts)*(menu->numopts-edit.index));
1017 menu->numopts--;
1018 menu->cursel = 0;
1019 pset->num--;
1020 return -1;
1021 }
1022 pset->infos[edit.index] = edit.old_usage;
1023 } else if (!is_new_part && edit.rv == -2) { /* delete partition */
1024 if (!pset->parts->pscheme->delete_partition(pset->parts,
1025 edit.id, &err)) {
1026 err_msg_win(err);
1027 return 0;
1028 }
1029 remember_deleted(pset,
1030 pset->infos[edit.index].parts);
1031 pset->cur_free_space += pset->infos[edit.index].size;
1032 memmove(pset->infos+edit.index,
1033 pset->infos+edit.index+1,
1034 sizeof(*pset->infos)*(pset->num-edit.index));
1035 memmove(menu->opts+edit.index,
1036 menu->opts+edit.index+1,
1037 sizeof(*menu->opts)*(menu->numopts-edit.index));
1038 menu->numopts--;
1039 menu->cursel = 0;
1040 if (pset->parts->num_part == 0)
1041 menu->cursel = 1; /* skip sentinel line */
1042
1043 /* things have changed, re-sort */
1044 pset->num--;
1045 renumber_partitions(pset);
1046
1047 /* we can likely add new partitions now */
1048 show_partition_adder(menu, pset);
1049
1050 return -1;
1051 }
1052
1053 return 0;
1054 }
1055
1056 static void
1057 update_edit_ptn_menu(menudesc *m, void *arg)
1058 {
1059 struct single_part_fs_edit *edit = arg;
1060 int i;
1061 uint t = edit->info.fs_type;
1062 size_t attr_no;
1063
1064 /* Determine which of the properties can be changed */
1065 for (i = 0; i < m->numopts; i++) {
1066 if (m->opts[i].opt_action == NULL &&
1067 m->opts[i].opt_menu != MENU_mountoptions)
1068 continue;
1069
1070 /* Default to disabled... */
1071 m->opts[i].opt_flags |= OPT_IGNORE;
1072 if ((t == FS_UNUSED || t == FS_SWAP) &&
1073 (m->opts[i].opt_action == edit_fs_preserve ||
1074 m->opts[i].opt_action == edit_fs_mount ||
1075 m->opts[i].opt_action == edit_fs_mountpt ||
1076 m->opts[i].opt_menu == MENU_mountoptions))
1077 continue;
1078 if (m->opts[i].opt_action == edit_install &&
1079 edit->info.nat_type &&
1080 edit->info.nat_type->generic_ptype != PT_root)
1081 /* can only install onto PT_root partitions */
1082 continue;
1083 if (m->opts[i].opt_action == edit_fs_preserve &&
1084 t != FS_BSDFFS && t != FS_BSDLFS && t != FS_APPLEUFS &&
1085 t != FS_MSDOS && t != FS_EFI_SP && t != FS_EX2FS) {
1086 /* Can not newfs this filesystem */
1087 edit->wanted->instflags &= ~PUIINST_NEWFS;
1088 continue;
1089 }
1090 if (m->opts[i].opt_action == edit_ptn_custom_type) {
1091 attr_no = (size_t)i - edit->first_custom_attr;
1092 if (!edit->pset->parts->pscheme->
1093 custom_attribute_writable(
1094 edit->pset->parts, edit->id, attr_no))
1095 continue;
1096 }
1097 /*
1098 * Do not allow editing of size/start/type when partition
1099 * is defined in some outer partition table already
1100 */
1101 if ((edit->pset->infos[edit->index].flags & PUIFLG_IS_OUTER)
1102 && (m->opts[i].opt_action == edit_fs_type
1103 || m->opts[i].opt_action == edit_fs_start
1104 || m->opts[i].opt_action == edit_fs_size))
1105 continue;
1106 /* Ok: we want this one */
1107 m->opts[i].opt_flags &= ~OPT_IGNORE;
1108 }
1109
1110 /* Avoid starting at a (now) disabled menu item */
1111 while (m->cursel >= 0 && m->cursel < m->numopts
1112 && (m->opts[m->cursel].opt_flags & OPT_IGNORE))
1113 m->cursel++;
1114 }
1115
1116 static void
1117 draw_edit_ptn_line(menudesc *m, int opt, void *arg)
1118 {
1119 struct single_part_fs_edit *edit = arg;
1120 static int col_width;
1121 static const char *ptn_type, *ptn_start, *ptn_size, *ptn_end,
1122 *ptn_newfs, *ptn_mount, *ptn_mount_options, *ptn_mountpt,
1123 *ptn_install, *ptn_bsize, *ptn_fsize, *ptn_isize;
1124 const char *c;
1125 char val[MENUSTRSIZE];
1126 const char *attrname;
1127 size_t attr_no;
1128
1129 if (col_width == 0) {
1130 int l;
1131
1132 #define LOAD(STR) STR = msg_string(MSG_##STR); l = strlen(STR); \
1133 if (l > col_width) col_width = l
1134
1135 LOAD(ptn_type);
1136 LOAD(ptn_start);
1137 LOAD(ptn_size);
1138 LOAD(ptn_end);
1139 LOAD(ptn_install);
1140 LOAD(ptn_newfs);
1141 LOAD(ptn_mount);
1142 LOAD(ptn_mount_options);
1143 LOAD(ptn_mountpt);
1144 LOAD(ptn_bsize);
1145 LOAD(ptn_fsize);
1146 LOAD(ptn_isize);
1147 #undef LOAD
1148
1149 for (size_t i = 0;
1150 i < edit->pset->parts->pscheme->custom_attribute_count;
1151 i++) {
1152 attrname = msg_string(
1153 edit->pset->parts->pscheme->custom_attributes[i]
1154 .label);
1155 l = strlen(attrname);
1156 if (l > col_width) col_width = l;
1157 }
1158
1159 col_width += 3;
1160 }
1161
1162 if (m->opts[opt].opt_flags & OPT_IGNORE
1163 && (opt != 3 || edit->info.fs_type == FS_UNUSED)
1164 && m->opts[opt].opt_action != edit_ptn_custom_type
1165 && m->opts[opt].opt_action != edit_fs_type
1166 && m->opts[opt].opt_action != edit_fs_start
1167 && m->opts[opt].opt_action != edit_fs_size) {
1168 wprintw(m->mw, "%*s -", col_width, "");
1169 return;
1170 }
1171
1172 if (opt < 4) {
1173 switch (opt) {
1174 case 0:
1175 if (edit->info.fs_type == FS_BSDFFS)
1176 if (edit->info.fs_sub_type == 2)
1177 c = msg_string(MSG_fs_type_ffsv2);
1178 else
1179 c = msg_string(MSG_fs_type_ffs);
1180 else
1181 c = getfslabelname(edit->info.fs_type,
1182 edit->info.fs_sub_type);
1183 wprintw(m->mw, "%*s : %s", col_width, ptn_type, c);
1184 return;
1185 case 1:
1186 wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width,
1187 ptn_start, edit->info.start / sizemult, multname);
1188 return;
1189 case 2:
1190 wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width,
1191 ptn_size, edit->info.size / sizemult, multname);
1192 return;
1193 case 3:
1194 wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width,
1195 ptn_end, (edit->info.start + edit->info.size)
1196 / sizemult, multname);
1197 return;
1198 }
1199 }
1200 if (m->opts[opt].opt_action == edit_install) {
1201 wprintw(m->mw, "%*s : %s", col_width, ptn_install,
1202 msg_string((edit->info.flags & PTI_INSTALL_TARGET)
1203 ? MSG_Yes : MSG_No));
1204 return;
1205 }
1206 if (m->opts[opt].opt_action == edit_fs_preserve) {
1207 wprintw(m->mw, "%*s : %s", col_width, ptn_newfs,
1208 msg_string(edit->wanted->instflags & PUIINST_NEWFS
1209 ? MSG_Yes : MSG_No));
1210 return;
1211 }
1212 if (m->opts[opt].opt_action == edit_fs_mount) {
1213 wprintw(m->mw, "%*s : %s", col_width, ptn_mount,
1214 msg_string(edit->wanted->instflags & PUIINST_MOUNT
1215 ? MSG_Yes : MSG_No));
1216 return;
1217 }
1218 if (m->opts[opt].opt_action == edit_fs_ffs_block) {
1219 wprintw(m->mw, "%*s : %u", col_width, ptn_bsize,
1220 edit->wanted->fs_opt1);
1221 return;
1222 }
1223 if (m->opts[opt].opt_action == edit_fs_ffs_frag) {
1224 wprintw(m->mw, "%*s : %u", col_width, ptn_fsize,
1225 edit->wanted->fs_opt2);
1226 return;
1227 }
1228 if (m->opts[opt].opt_action == edit_fs_ffs_avg_size) {
1229 if (edit->wanted->fs_opt3 == 0)
1230 wprintw(m->mw, "%*s : %s", col_width, ptn_isize,
1231 msg_string(MSG_ptn_isize_dflt));
1232 else {
1233 char buf[24], *line;
1234 const char *t = buf;
1235
1236 snprintf(buf, sizeof buf, "%u", edit->wanted->fs_opt3);
1237 line = str_arg_subst(msg_string(MSG_ptn_isize_bytes),
1238 1, &t);
1239 wprintw(m->mw, "%*s : %s", col_width, ptn_isize,
1240 line);
1241 free(line);
1242 }
1243 return;
1244 }
1245 if (m->opts[opt].opt_menu == MENU_mountoptions) {
1246 wprintw(m->mw, "%*s : ", col_width, ptn_mount_options);
1247 if (edit->wanted->mountflags & PUIMNT_ASYNC)
1248 wprintw(m->mw, "async ");
1249 if (edit->wanted->mountflags & PUIMNT_NOATIME)
1250 wprintw(m->mw, "noatime ");
1251 if (edit->wanted->mountflags & PUIMNT_NODEV)
1252 wprintw(m->mw, "nodev ");
1253 if (edit->wanted->mountflags & PUIMNT_NODEVMTIME)
1254 wprintw(m->mw, "nodevmtime ");
1255 if (edit->wanted->mountflags & PUIMNT_NOEXEC)
1256 wprintw(m->mw, "noexec ");
1257 if (edit->wanted->mountflags & PUIMNT_NOSUID)
1258 wprintw(m->mw, "nosuid ");
1259 if (edit->wanted->mountflags & PUIMNT_LOG)
1260 wprintw(m->mw, "log ");
1261 if (edit->wanted->mountflags & PUIMNT_NOAUTO)
1262 wprintw(m->mw, "noauto ");
1263 return;
1264 }
1265 if (m->opts[opt].opt_action == edit_fs_mountpt) {
1266 wprintw(m->mw, "%*s : %s", col_width, ptn_mountpt,
1267 edit->wanted->mount);
1268 return;
1269 }
1270
1271 attr_no = opt - edit->first_custom_attr;
1272 edit->pset->parts->pscheme->format_custom_attribute(
1273 edit->pset->parts, edit->id, attr_no, &edit->info,
1274 val, sizeof val);
1275 attrname = msg_string(edit->pset->parts->pscheme->
1276 custom_attributes[attr_no].label);
1277 wprintw(m->mw, "%*s : %s", col_width, attrname, val);
1278 }
1279
1280 static int
1281 edit_ptn_custom_type(menudesc *m, void *arg)
1282 {
1283 struct single_part_fs_edit *edit = arg;
1284 size_t attr_no = m->cursel - edit->first_custom_attr;
1285 char line[STRSIZE];
1286
1287 switch (edit->pset->parts->pscheme->custom_attributes[attr_no].type) {
1288 case pet_bool:
1289 edit->pset->parts->pscheme->custom_attribute_toggle(
1290 edit->pset->parts, edit->id, attr_no);
1291 break;
1292 case pet_cardinal:
1293 case pet_str:
1294 edit->pset->parts->pscheme->format_custom_attribute(
1295 edit->pset->parts, edit->id, attr_no, &edit->info,
1296 line, sizeof(line));
1297 msg_prompt_win(
1298 edit->pset->parts->pscheme->custom_attributes[attr_no].
1299 label, -1, 18, 0, 0, line, line, sizeof(line));
1300 edit->pset->parts->pscheme->custom_attribute_set_str(
1301 edit->pset->parts, edit->id, attr_no, line);
1302 break;
1303 }
1304
1305 return 0;
1306 }
1307
1308
1309 /*
1310 * Some column width depend on translation, we will set these in
1311 * fmt_fspart_header and later use it when formatting single entries
1312 * in fmt_fspart_row.
1313 * The table consist of 3 "size like" columns, all fixed width, then
1314 * ptnheaders_fstype, part_header_col_flag, and finally the mount point
1315 * (which is variable width).
1316 */
1317 static int fstype_width, flags_width;
1318 static char fspart_separator[MENUSTRSIZE];
1319 static char fspart_title[2*MENUSTRSIZE];
1320
1321 /*
1322 * Format the header of the main partition editor menu.
1323 */
1324 static void
1325 fmt_fspart_header(menudesc *menu, void *arg)
1326 {
1327 struct partition_usage_set *pset = arg;
1328 char total[6], free_space[6], scol[13], ecol[13], szcol[13],
1329 sepline[MENUSTRSIZE], *p, desc[MENUSTRSIZE];
1330 const char *fstype, *flags;
1331 int i;
1332 size_t ptn;
1333 bool with_clone, with_inst_flag = pset->parts->parent == NULL;
1334
1335 with_clone = false;
1336 for (ptn = 0; ptn < pset->num && !with_clone; ptn++)
1337 if (pset->infos[ptn].flags & PUIFLG_CLONE_PARTS)
1338 with_clone = true;
1339 humanize_number(total, sizeof total,
1340 pset->parts->disk_size * pset->parts->bytes_per_sector,
1341 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1342 humanize_number(free_space, sizeof free_space,
1343 pset->cur_free_space * pset->parts->bytes_per_sector,
1344 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1345
1346 if (with_clone)
1347 strlcpy(desc, msg_string(MSG_clone_flag_desc), sizeof desc);
1348 else
1349 desc[0] = 0;
1350 if (pset->parts->pscheme->part_flag_desc)
1351 strlcat(desc, msg_string(pset->parts->pscheme->part_flag_desc),
1352 sizeof desc);
1353
1354 msg_display_subst(MSG_fspart, 7, pset->parts->disk,
1355 msg_string(pset->parts->pscheme->name),
1356 msg_string(pset->parts->pscheme->short_name),
1357 with_inst_flag ? msg_string(MSG_ptn_instflag_desc) : "",
1358 desc, total, free_space);
1359
1360 snprintf(scol, sizeof scol, "%s (%s)",
1361 msg_string(MSG_ptnheaders_start), multname);
1362 snprintf(ecol, sizeof ecol, "%s (%s)",
1363 msg_string(MSG_ptnheaders_end), multname);
1364 snprintf(szcol, sizeof szcol, "%s (%s)",
1365 msg_string(MSG_ptnheaders_size), multname);
1366
1367 fstype = msg_string(MSG_ptnheaders_fstype);
1368 flags = msg_string(MSG_part_header_col_flag);
1369 fstype_width = max(strlen(fstype), 8);
1370 flags_width = strlen(flags);
1371 for (i = 0, p = sepline; i < fstype_width; i++)
1372 *p++ = '-';
1373 for (i = 0, *p++ = ' '; i < flags_width; i++)
1374 *p++ = '-';
1375 *p = 0;
1376
1377 snprintf(fspart_separator, sizeof(fspart_separator),
1378 "------------ ------------ ------------ %s ----------------",
1379 sepline);
1380
1381 snprintf(fspart_title, sizeof(fspart_title),
1382 " %12.12s %12.12s %12.12s %*s %*s %s\n"
1383 " %s", scol, ecol, szcol, fstype_width, fstype,
1384 flags_width, flags, msg_string(MSG_ptnheaders_filesystem),
1385 fspart_separator);
1386
1387 msg_table_add("\n\n");
1388 }
1389
1390 /*
1391 * Format one partition entry in the main partition editor.
1392 */
1393 static void
1394 fmt_fspart_row(menudesc *m, int ptn, void *arg)
1395 {
1396 struct partition_usage_set *pset = arg;
1397 struct disk_part_info info;
1398 daddr_t poffset, psize, pend;
1399 const char *desc;
1400 static const char *Yes;
1401 char flag_str[MENUSTRSIZE], *fp;
1402 unsigned inst_flags;
1403 #ifndef NO_CLONES
1404 size_t clone_cnt;
1405 #endif
1406 bool with_inst_flag = pset->parts->parent == NULL;
1407
1408 if (Yes == NULL)
1409 Yes = msg_string(MSG_Yes);
1410
1411 #ifndef NO_CLONES
1412 if ((pset->infos[ptn].flags & PUIFLG_CLONE_PARTS) &&
1413 pset->infos[ptn].cur_part_id == NO_PART) {
1414 psize = pset->infos[ptn].size / sizemult;
1415 if (pset->infos[ptn].clone_ndx <
1416 pset->infos[ptn].clone_src->num_sel)
1417 clone_cnt = 1;
1418 else
1419 clone_cnt = pset->infos[ptn].clone_src->num_sel;
1420 if (pset->infos[ptn].cur_part_id == NO_PART)
1421 wprintw(m->mw, " %12" PRIu64
1422 " [%zu %s]", psize, clone_cnt,
1423 msg_string(MSG_clone_target_disp));
1424 else {
1425 poffset = pset->infos[ptn].cur_start / sizemult;
1426 pend = (pset->infos[ptn].cur_start +
1427 pset->infos[ptn].size) / sizemult - 1;
1428 wprintw(m->mw, "%12" PRIu64 " %12" PRIu64 " %12" PRIu64
1429 " [%zu %s]",
1430 poffset, pend, psize, clone_cnt,
1431 msg_string(MSG_clone_target_disp));
1432 }
1433 if (m->title == fspart_title)
1434 m->opts[ptn].opt_flags |= OPT_IGNORE;
1435 else
1436 m->opts[ptn].opt_flags &= ~OPT_IGNORE;
1437 return;
1438 }
1439 #endif
1440
1441 if (!real_partition(pset, ptn))
1442 return;
1443
1444 if (!pset->parts->pscheme->get_part_info(pset->parts,
1445 pset->infos[ptn].cur_part_id, &info))
1446 return;
1447
1448 /*
1449 * We use this function in multiple menus, but only want it
1450 * to play with enable/disable in a single one:
1451 */
1452 if (m->title == fspart_title) {
1453 /*
1454 * Enable / disable this line if it is something
1455 * like RAW_PART
1456 */
1457 if ((info.flags &
1458 (PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
1459 || (pset->infos[ptn].flags & PUIFLG_CLONE_PARTS))
1460 m->opts[ptn].opt_flags |= OPT_IGNORE;
1461 else
1462 m->opts[ptn].opt_flags &= ~OPT_IGNORE;
1463 }
1464
1465 poffset = info.start / sizemult;
1466 psize = info.size / sizemult;
1467 if (psize == 0)
1468 pend = 0;
1469 else
1470 pend = (info.start + info.size) / sizemult - 1;
1471
1472 if (info.flags & PTI_WHOLE_DISK)
1473 desc = msg_string(MSG_NetBSD_partition_cant_change);
1474 else if (info.flags & PTI_RAW_PART)
1475 desc = msg_string(MSG_Whole_disk_cant_change);
1476 else if (info.flags & PTI_BOOT)
1477 desc = msg_string(MSG_Boot_partition_cant_change);
1478 else
1479 desc = getfslabelname(info.fs_type, info.fs_sub_type);
1480
1481 fp = flag_str;
1482 inst_flags = pset->infos[ptn].instflags;
1483 if (with_inst_flag && (info.flags & PTI_INSTALL_TARGET) &&
1484 info.nat_type->generic_ptype == PT_root) {
1485 static char inst_flag;
1486
1487 if (inst_flag == 0)
1488 inst_flag = msg_string(MSG_install_flag)[0];
1489 *fp++ = inst_flag;
1490 }
1491 if (inst_flags & PUIINST_NEWFS)
1492 *fp++ = msg_string(MSG_newfs_flag)[0];
1493 if (pset->infos[ptn].flags & PUIFLG_CLONE_PARTS)
1494 *fp++ = msg_string(MSG_clone_flag)[0];
1495 *fp = 0;
1496 if (pset->parts->pscheme->get_part_attr_str != NULL)
1497 pset->parts->pscheme->get_part_attr_str(pset->parts,
1498 pset->infos[ptn].cur_part_id, fp, sizeof(flag_str)-1);
1499
1500 /* if the fstype description does not fit, check if we can overrun */
1501 if (strlen(desc) > (size_t)fstype_width &&
1502 flag_str[0] == 0 && (info.last_mounted == NULL ||
1503 info.last_mounted[0] == 0))
1504 wprintw(m->mw, "%12" PRIu64 " %12" PRIu64 " %12" PRIu64
1505 " %s",
1506 poffset, pend, psize, desc);
1507 else
1508 wprintw(m->mw, "%12" PRIu64 " %12" PRIu64 " %12" PRIu64
1509 " %*.*s %*s %s",
1510 poffset, pend, psize, fstype_width, fstype_width, desc,
1511 -flags_width, flag_str,
1512 (inst_flags & PUIINST_MOUNT) && info.last_mounted &&
1513 info.last_mounted[0] ? info.last_mounted : "");
1514 }
1515
1516 #ifndef NO_CLONES
1517 static int
1518 part_ext_clone(menudesc *m, void *arg)
1519 {
1520 struct selected_partitions selected, *clone_src;
1521 struct clone_target_menu_data data;
1522 struct partition_usage_set *pset = arg;
1523 struct part_usage_info *p;
1524 struct disk_part_info sinfo, cinfo;
1525 struct disk_partitions *csrc;
1526 struct disk_part_free_space space;
1527 menu_ent *men;
1528 daddr_t clone_size, free_size, offset, align;
1529 int num_men, i;
1530 size_t s, clone_cnt;
1531 part_id cid;
1532 struct clone_data {
1533 struct disk_part_info info;
1534 part_id new_id;
1535 size_t ndx;
1536 };
1537 struct clone_data *clones = NULL;
1538
1539 if (!select_partitions(&selected, pm->parts))
1540 return 0;
1541
1542 clone_size = selected_parts_size(&selected);
1543 num_men = pset->num+1;
1544 men = calloc(num_men, sizeof *men);
1545 if (men == NULL)
1546 return 0;
1547 for (i = 0; i < num_men; i++) {
1548 men[i].opt_action = clone_target_select;
1549 if (i == 0)
1550 free_size = pset->infos[i].cur_start;
1551 else if (i > 0 && (size_t)i < pset->num)
1552 free_size = pset->infos[i].cur_start -
1553 pset->infos[i-1].cur_start - pset->infos[i-1].size;
1554 else
1555 free_size = pset->parts->free_space;
1556 if (free_size < clone_size)
1557 men[i].opt_flags = OPT_IGNORE;
1558 }
1559 men[num_men-1].opt_name = MSG_clone_target_end;
1560
1561 memset(&data, 0, sizeof data);
1562 data.usage = *pset;
1563 data.res = -1;
1564
1565 data.usage.menu = new_menu(MSG_clone_target_hdr,
1566 men, num_men, 3, 2, 0, 65, MC_SCROLL,
1567 NULL, fmt_fspart_row, NULL, NULL, MSG_cancel);
1568 process_menu(data.usage.menu, &data);
1569 free_menu(data.usage.menu);
1570 free(men);
1571
1572 if (data.res < 0)
1573 goto err;
1574
1575 /* create temporary infos for all clones that work out */
1576 clone_cnt = 0;
1577 clones = calloc(selected.num_sel, sizeof(*clones));
1578 if (clones == NULL)
1579 goto err;
1580
1581 clone_src = malloc(sizeof(selected));
1582 if (clone_src == NULL)
1583 goto err;
1584 *clone_src = selected;
1585
1586 /* find selected offset from data.res and insert clones there */
1587 align = pset->parts->pscheme->get_part_alignment(pset->parts);
1588 offset = -1;
1589 if (data.res > 0)
1590 offset = pset->infos[data.res-1].cur_start
1591 + pset->infos[data.res-1].size;
1592 else
1593 offset = 0;
1594 for (s = 0; s < selected.num_sel; s++) {
1595 csrc = selected.selection[s].parts;
1596 cid = selected.selection[s].id;
1597 csrc->pscheme->get_part_info(csrc, cid, &sinfo);
1598 if (!pset->parts->pscheme->adapt_foreign_part_info(
1599 pset->parts, &cinfo, csrc->pscheme, &sinfo))
1600 continue;
1601 size_t cnt = pset->parts->pscheme->get_free_spaces(
1602 pset->parts, &space, 1, cinfo.size-align, align,
1603 offset, -1);
1604 if (cnt == 0)
1605 continue;
1606 cinfo.start = space.start;
1607 cid = pset->parts->pscheme->add_partition(
1608 pset->parts, &cinfo, NULL);
1609 if (cid == NO_PART)
1610 continue;
1611 pset->parts->pscheme->get_part_info(pset->parts, cid, &cinfo);
1612 clones[clone_cnt].info = cinfo;
1613 clones[clone_cnt].new_id = cid;
1614 clones[clone_cnt].ndx = s;
1615 clone_cnt++;
1616 offset = roundup(cinfo.start+cinfo.size, align);
1617 }
1618
1619 /* insert new clone records at offset data.res */
1620 men = realloc(m->opts, (m->numopts+clone_cnt)*sizeof(*m->opts));
1621 if (men == NULL)
1622 goto err;
1623 pset->menu_opts = men;
1624 m->opts = men;
1625 m->numopts += clone_cnt;
1626
1627 p = realloc(pset->infos, (pset->num+clone_cnt)*sizeof(*pset->infos));
1628 if (p == NULL)
1629 goto err;
1630 pset->infos = p;
1631
1632 men += data.res;
1633 p += data.res;
1634 memmove(men+clone_cnt, men,
1635 sizeof(*men)*(m->numopts-data.res-clone_cnt));
1636 if (pset->num > (size_t)data.res)
1637 memmove(p+clone_cnt, p, sizeof(*p)*(pset->num-data.res));
1638 memset(men, 0, sizeof(*men)*clone_cnt);
1639 memset(p, 0, sizeof(*p)*clone_cnt);
1640 for (s = 0; s < clone_cnt; s++) {
1641 p[s].cur_part_id = clones[s].new_id;
1642 p[s].cur_start = clones[s].info.start;
1643 p[s].size = clones[s].info.size;
1644 p[s].cur_flags = clones[s].info.flags;
1645 p[s].flags = PUIFLG_CLONE_PARTS;
1646 p[s].parts = pset->parts;
1647 p[s].clone_src = clone_src;
1648 p[s].clone_ndx = s;
1649 }
1650 free(clones);
1651 m->cursel = ((size_t)data.res >= pset->num) ? 0 : data.res+clone_cnt;
1652 pset->num += clone_cnt;
1653 m->h = 0;
1654 resize_menu_height(m);
1655
1656 return -1;
1657
1658 err:
1659 free(clones);
1660 free_selected_partitions(&selected);
1661 return 0;
1662 }
1663 #endif
1664
1665 static int
1666 edit_fspart_pack(menudesc *m, void *arg)
1667 {
1668 struct partition_usage_set *pset = arg;
1669 char buf[STRSIZE];
1670
1671 if (!pset->parts->pscheme->get_disk_pack_name(pset->parts,
1672 buf, sizeof buf))
1673 return 0;
1674
1675 msg_prompt_win(MSG_edit_disk_pack_hdr,
1676 -1, 18, 0, -1, buf, buf, sizeof(buf));
1677
1678 pset->parts->pscheme->set_disk_pack_name(pset->parts, buf);
1679 return 0;
1680 }
1681
1682 static int
1683 edit_fspart_add(menudesc *m, void *arg)
1684 {
1685 struct partition_usage_set *pset = arg;
1686 struct part_usage_info *ninfo;
1687 menu_ent *nmenopts;
1688 size_t cnt, off;
1689
1690 ninfo = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos));
1691 if (ninfo == NULL)
1692 return 0;
1693 pset->infos = ninfo;
1694 off = pset->parts->num_part;
1695 cnt = pset->num-pset->parts->num_part;
1696 if (cnt > 0)
1697 memmove(pset->infos+off+1,pset->infos+off,
1698 cnt*sizeof(*pset->infos));
1699 memset(pset->infos+off, 0, sizeof(*pset->infos));
1700
1701 nmenopts = realloc(m->opts, (m->numopts+1)*sizeof(*m->opts));
1702 if (nmenopts == NULL)
1703 return 0;
1704 memmove(nmenopts+off+1, nmenopts+off,
1705 (m->numopts-off)*sizeof(*nmenopts));
1706 memset(&nmenopts[off], 0, sizeof(nmenopts[off]));
1707 nmenopts[off].opt_action = edit_ptn;
1708 pset->menu_opts = m->opts = nmenopts;
1709 m->numopts++;
1710 m->cursel = off;
1711 pset->num++;
1712
1713 /* now update edit menu to fit */
1714 m->h = 0;
1715 resize_menu_height(m);
1716
1717 /* and directly invoke the partition editor for the new part */
1718 edit_ptn(m, arg);
1719
1720 show_partition_adder(m, pset);
1721
1722 return -1;
1723 }
1724
1725 static void
1726 add_partition_adder(menudesc *m, struct partition_usage_set *pset)
1727 {
1728 struct part_usage_info *ninfo;
1729 menu_ent *nmenopts;
1730 size_t off;
1731
1732 ninfo = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos));
1733 if (ninfo == NULL)
1734 return;
1735 pset->infos = ninfo;
1736 off = pset->parts->num_part+1;
1737
1738 nmenopts = realloc(m->opts, (m->numopts+1)*sizeof(*m->opts));
1739 if (nmenopts == NULL)
1740 return;
1741 memmove(nmenopts+off+1, nmenopts+off,
1742 (m->numopts-off)*sizeof(*nmenopts));
1743 memset(&nmenopts[off], 0, sizeof(nmenopts[off]));
1744
1745 nmenopts[off].opt_name = MSG_addpart;
1746 nmenopts[off].opt_flags = OPT_SUB;
1747 nmenopts[off].opt_action = edit_fspart_add;
1748
1749 m->opts = nmenopts;
1750 m->numopts++;
1751 }
1752
1753 static void
1754 remove_partition_adder(menudesc *m, struct partition_usage_set *pset)
1755 {
1756 size_t off;
1757
1758 off = pset->parts->num_part+1;
1759 memmove(m->opts+off, m->opts+off+1,
1760 (m->numopts-off-1)*sizeof(*m->opts));
1761 m->numopts--;
1762 }
1763
1764 /*
1765 * Called whenever the "add a partition" option may need to be removed
1766 * or added
1767 */
1768 static void
1769 show_partition_adder(menudesc *m, struct partition_usage_set *pset)
1770 {
1771 if (m->opts == NULL)
1772 return;
1773
1774 bool can_add_partition = pset->parts->pscheme->can_add_partition(
1775 pset->parts);
1776 bool part_adder_present =
1777 (m->opts[pset->parts->num_part].opt_flags & OPT_IGNORE) &&
1778 (m->opts[pset->parts->num_part+1].opt_action == edit_fspart_add);
1779
1780 if (can_add_partition == part_adder_present)
1781 return;
1782
1783 if (can_add_partition)
1784 add_partition_adder(m, pset);
1785 else
1786 remove_partition_adder(m, pset);
1787
1788 /* now update edit menu to fit */
1789 m->h = 0;
1790 resize_menu_height(m);
1791 }
1792
1793 static int
1794 edit_fspart_abort(menudesc *m, void *arg)
1795 {
1796 struct partition_usage_set *pset = arg;
1797
1798 pset->ok = false;
1799 return 1;
1800 }
1801
1802 /*
1803 * Check a disklabel.
1804 * If there are overlapping active partitions,
1805 * Ask the user if they want to edit the partition or give up.
1806 */
1807 int
1808 edit_and_check_label(struct pm_devs *p, struct partition_usage_set *pset,
1809 bool install)
1810 {
1811 menu_ent *op;
1812 size_t cnt, i;
1813 bool may_add = pset->parts->pscheme->can_add_partition(pset->parts);
1814 bool may_edit_pack =
1815 pset->parts->pscheme->get_disk_pack_name != NULL &&
1816 pset->parts->pscheme->set_disk_pack_name != NULL;
1817
1818 #ifdef NO_CLONES
1819 #define C_M_ITEMS 0
1820 #else
1821 #define C_M_ITEMS 1
1822 #endif
1823 pset->menu_opts = calloc(pset->parts->num_part
1824 +3+C_M_ITEMS+may_add+may_edit_pack,
1825 sizeof *pset->menu_opts);
1826 if (pset->menu_opts == NULL)
1827 return 0;
1828
1829 op = pset->menu_opts;
1830 for (i = 0; i < pset->parts->num_part; i++) {
1831 op->opt_action = edit_ptn;
1832 op++;
1833 }
1834 /* separator line between partitions and actions */
1835 op->opt_name = fspart_separator;
1836 op->opt_flags = OPT_IGNORE|OPT_NOSHORT;
1837 op++;
1838
1839 /* followed by new partition adder */
1840 if (may_add) {
1841 op->opt_name = MSG_addpart;
1842 op->opt_flags = OPT_SUB;
1843 op->opt_action = edit_fspart_add;
1844 op++;
1845 }
1846
1847 /* and unit changer */
1848 op->opt_name = MSG_askunits;
1849 op->opt_menu = MENU_sizechoice;
1850 op->opt_flags = OPT_SUB;
1851 op->opt_action = NULL;
1852 op++;
1853
1854 if (may_edit_pack) {
1855 op->opt_name = MSG_editpack;
1856 op->opt_flags = OPT_SUB;
1857 op->opt_action = edit_fspart_pack;
1858 op++;
1859 }
1860
1861 #ifndef NO_CLONES
1862 /* add a clone-from-elsewhere option */
1863 op->opt_name = MSG_clone_from_elsewhere;
1864 op->opt_action = part_ext_clone;
1865 op++;
1866 #endif
1867
1868 /* and abort option */
1869 op->opt_name = MSG_cancel;
1870 op->opt_flags = OPT_EXIT;
1871 op->opt_action = edit_fspart_abort;
1872 op++;
1873 cnt = op - pset->menu_opts;
1874 assert(cnt == pset->parts->num_part+3+C_M_ITEMS+may_add+may_edit_pack);
1875
1876 pset->menu = new_menu(fspart_title, pset->menu_opts, cnt,
1877 0, -1, 0, 74,
1878 MC_ALWAYS_SCROLL|MC_NOBOX|MC_DFLTEXIT|
1879 MC_NOCLEAR|MC_CONTINUOUS,
1880 fmt_fspart_header, fmt_fspart_row, NULL, NULL,
1881 MSG_partition_sizes_ok);
1882
1883 if (pset->menu < 0) {
1884 free(pset->menu_opts);
1885 pset->menu_opts = NULL;
1886 return 0;
1887 }
1888
1889 p->current_cylsize = p->dlcylsize;
1890
1891 for (;;) {
1892 /* first give the user the option to edit the label... */
1893 pset->ok = true;
1894 process_menu(pset->menu, pset);
1895 if (!pset->ok) {
1896 i = 0;
1897 break;
1898 }
1899
1900 /* User thinks the label is OK. */
1901 i = verify_parts(pset, install);
1902 if (i == 1)
1903 continue;
1904 break;
1905 }
1906 free(pset->menu_opts);
1907 pset->menu_opts = NULL;
1908 free_menu(pset->menu);
1909 pset->menu = -1;
1910
1911 return i != 0;
1912 }
1913
1914 /*
1915 * strip trailing / to avoid confusion in path comparisons later
1916 */
1917 void
1918 canonicalize_last_mounted(char *path)
1919 {
1920 char *p;
1921
1922 if (path == NULL)
1923 return;
1924
1925 if (strcmp(path, "/") == 0)
1926 return; /* in this case a "trailing" slash is allowed */
1927
1928 for (;;) {
1929 p = strrchr(path, '/');
1930 if (p == NULL)
1931 return;
1932 if (p[1] != 0)
1933 return;
1934 p[0] = 0;
1935 }
1936 }
1937
1938 /*
1939 * Try to get 'last mounted on' (or equiv) from fs superblock.
1940 */
1941 const char *
1942 get_last_mounted(int fd, daddr_t partstart, uint *fs_type, uint *fs_sub_type,
1943 uint flags)
1944 {
1945 static char sblk[SBLOCKSIZE]; /* is this enough? */
1946 struct fs *SB = (struct fs *)sblk;
1947 static const off_t sblocks[] = SBLOCKSEARCH;
1948 const off_t *sbp;
1949 const char *mnt = NULL;
1950 int len;
1951
1952 if (fd == -1)
1953 return "";
1954
1955 if (fs_type)
1956 *fs_type = 0;
1957 if (fs_sub_type)
1958 *fs_sub_type = 0;
1959
1960 /* Check UFS1/2 (and hence LFS) superblock */
1961 for (sbp = sblocks; mnt == NULL && *sbp != -1; sbp++) {
1962 if (pread(fd, sblk, sizeof sblk,
1963 (off_t)partstart * (off_t)512 + *sbp) != sizeof sblk)
1964 continue;
1965
1966 /*
1967 * If start of partition and allowed by flags check
1968 * for other fs types
1969 */
1970 if (*sbp == 0 && (flags & GLM_MAYBE_FAT32) &&
1971 sblk[0x42] == 0x29 && memcmp(sblk + 0x52, "FAT", 3) == 0) {
1972 /* Probably a FAT filesystem, report volume name */
1973 size_t i;
1974 for (i = 0x51; i >= 0x47; i--) {
1975 if (sblk[i] != ' ')
1976 break;
1977 sblk[i] = 0;
1978 }
1979 sblk[0x52] = 0;
1980 if (fs_type)
1981 *fs_type = FS_MSDOS;
1982 if (fs_sub_type)
1983 *fs_sub_type = sblk[0x53];
1984 return sblk + 0x47;
1985 } else if (*sbp == 0 && (flags & GLM_MAYBE_NTFS) &&
1986 memcmp(sblk+3, "NTFS ", 8) == 0) {
1987 if (fs_type)
1988 *fs_type = FS_NTFS;
1989 if (fs_sub_type)
1990 *fs_sub_type = MBR_PTYPE_NTFS;
1991 /* XXX dig for volume name attribute ? */
1992 return "";
1993 }
1994
1995 if (!(flags & GLM_LIKELY_FFS))
1996 continue;
1997
1998 /* Maybe we should validate the checksum??? */
1999 switch (SB->fs_magic) {
2000 case FS_UFS1_MAGIC:
2001 case FS_UFS1_MAGIC_SWAPPED:
2002 if (!(SB->fs_old_flags & FS_FLAGS_UPDATED)) {
2003 if (*sbp == SBLOCK_UFS1)
2004 mnt = (const char *)SB->fs_fsmnt;
2005 } else {
2006 /* Check we have the main superblock */
2007 if (SB->fs_sblockloc == *sbp)
2008 mnt = (const char *)SB->fs_fsmnt;
2009 }
2010 if (fs_type)
2011 *fs_type = FS_BSDFFS;
2012 if (fs_sub_type)
2013 *fs_sub_type = 1;
2014 continue;
2015 case FS_UFS2_MAGIC:
2016 case FS_UFS2_MAGIC_SWAPPED:
2017 /* Check we have the main superblock */
2018 if (SB->fs_sblockloc == *sbp) {
2019 mnt = (const char *)SB->fs_fsmnt;
2020 if (fs_type)
2021 *fs_type = FS_BSDFFS;
2022 if (fs_sub_type)
2023 *fs_sub_type = 2;
2024 }
2025 continue;
2026 }
2027 }
2028
2029 if (mnt == NULL)
2030 return "";
2031
2032 /* If sysinst mounted this last then strip prefix */
2033 len = strlen(targetroot_mnt);
2034 if (memcmp(mnt, targetroot_mnt, len) == 0) {
2035 if (mnt[len] == 0)
2036 return "/";
2037 if (mnt[len] == '/')
2038 return mnt + len;
2039 }
2040 return mnt;
2041 #undef SB
2042 }
2043
2044 /* Ask for a partition offset, check bounds and do the needed roundups */
2045 daddr_t
2046 getpartoff(struct disk_partitions *parts, daddr_t defpartstart)
2047 {
2048 char defstart[24], isize[24], maxpart, minspace, maxspace,
2049 *prompt, *label_msg, valid_parts[4], valid_spaces[4],
2050 space_prompt[1024], *head, *hint_part, *hint_space, *tail;
2051 size_t num_freespace, spaces, ndx;
2052 struct disk_part_free_space *freespace;
2053 daddr_t i, localsizemult, ptn_alignment, min, max;
2054 part_id partn;
2055 struct disk_part_info info;
2056 const char *errmsg = NULL;
2057
2058 min = parts->disk_start;
2059 max = min + parts->disk_size;
2060
2061 /* upper bound on the number of free spaces, plus some slope */
2062 num_freespace = parts->num_part * 2 + 5;
2063 freespace = calloc(num_freespace, sizeof(*freespace));
2064 if (freespace == NULL)
2065 return -1;
2066
2067 ptn_alignment = parts->pscheme->get_part_alignment(parts);
2068 spaces = parts->pscheme->get_free_spaces(parts, freespace,
2069 num_freespace, max(sizemult, ptn_alignment), ptn_alignment, -1,
2070 defpartstart);
2071
2072 maxpart = 'a' + parts->num_part -1;
2073 if (parts->num_part > 1) {
2074 snprintf(valid_parts, sizeof valid_parts, "a-%c", maxpart);
2075 } else if (parts->num_part == 1) {
2076 snprintf(valid_parts, sizeof valid_parts, " %c", maxpart);
2077 } else {
2078 strcpy(valid_parts, "---");
2079 }
2080 if (spaces > 1) {
2081 minspace = maxpart + 1;
2082 maxspace = minspace + spaces -1;
2083 snprintf(valid_spaces, sizeof valid_spaces, "%c-%c", minspace,
2084 maxspace);
2085 } else if (spaces == 1) {
2086 maxspace = minspace = maxpart + 1;
2087 snprintf(valid_spaces, sizeof valid_spaces, " %c", minspace);
2088 } else {
2089 minspace = 0;
2090 maxspace = 0;
2091 strcpy(valid_spaces, "---");
2092 }
2093
2094 /* Add description of start/size to user prompt */
2095 const char *mstr = msg_string(MSG_free_space_line);
2096 space_prompt[0] = 0;
2097 for (ndx = 0; ndx < spaces; ndx++) {
2098 char str_start[40], str_end[40], str_size[40], str_tag[4];
2099
2100 sprintf(str_tag, "%c: ", minspace+(int)ndx);
2101 sprintf(str_start, "%" PRIu64, freespace[ndx].start / sizemult);
2102 sprintf(str_end, "%" PRIu64,
2103 (freespace[ndx].start + freespace[ndx].size) / sizemult);
2104 sprintf(str_size, "%" PRIu64, freespace[ndx].size / sizemult);
2105 const char *args[4] = { str_start, str_end, str_size,
2106 multname };
2107 char *line = str_arg_subst(mstr, 4, args);
2108 strlcat(space_prompt, str_tag, sizeof(space_prompt));
2109 size_t len = strlcat(space_prompt, line, sizeof(space_prompt));
2110 free (line);
2111 if (len >= sizeof space_prompt)
2112 break;
2113 }
2114
2115 const char *args[] = { valid_parts, valid_spaces, multname };
2116 hint_part = NULL;
2117 hint_space = NULL;
2118 head = str_arg_subst(msg_string(MSG_label_offset_head),
2119 __arraycount(args), args);
2120 if (parts->num_part)
2121 hint_part = str_arg_subst(msg_string(
2122 MSG_label_offset_part_hint), __arraycount(args), args);
2123 if (spaces)
2124 hint_space = str_arg_subst(msg_string(
2125 MSG_label_offset_space_hint), __arraycount(args), args);
2126 tail = str_arg_subst(msg_string(MSG_label_offset_tail),
2127 __arraycount(args), args);
2128
2129 if (hint_part && hint_space)
2130 asprintf(&label_msg, "%s\n%s\n%s\n\n%s\n%s",
2131 head, hint_part, hint_space, space_prompt, tail);
2132 else if (hint_part)
2133 asprintf(&label_msg, "%s\n%s\n\n%s",
2134 head, hint_part, tail);
2135 else if (hint_space)
2136 asprintf(&label_msg, "%s\n%s\n\n%s\n%s",
2137 head, hint_space, space_prompt, tail);
2138 else
2139 asprintf(&label_msg, "%s\n\n%s",
2140 head, tail);
2141 free(head); free(hint_part); free(hint_space); free(tail);
2142
2143 localsizemult = sizemult;
2144 errmsg = NULL;
2145 for (;;) {
2146 snprintf(defstart, sizeof defstart, "%" PRIu64,
2147 defpartstart/sizemult);
2148 if (errmsg != NULL && errmsg[0] != 0)
2149 asprintf(&prompt, "%s\n\n%s", errmsg, label_msg);
2150 else
2151 prompt = label_msg;
2152 msg_prompt_win(prompt, -1, 13, 70, -1,
2153 (defpartstart > 0) ? defstart : NULL, isize, sizeof isize);
2154 if (label_msg != prompt)
2155 free(prompt);
2156 if (strcmp(defstart, isize) == 0) {
2157 /* Don't do rounding if default accepted */
2158 i = defpartstart;
2159 break;
2160 }
2161 if (isize[1] == '\0' && isize[0] >= 'a' &&
2162 isize[0] <= maxpart) {
2163 partn = isize[0] - 'a';
2164 if (parts->pscheme->get_part_info(parts, partn,
2165 &info)) {
2166 i = info.start + info.size;
2167 localsizemult = 1;
2168 } else {
2169 errmsg = msg_string(MSG_invalid_sector_number);
2170 continue;
2171 }
2172 } else if (isize[1] == '\0' && isize[0] >= minspace &&
2173 isize[0] <= maxspace) {
2174 ndx = isize[0] - minspace;
2175 i = freespace[ndx].start;
2176 localsizemult = 1;
2177 } else if (atoi(isize) == -1) {
2178 i = min;
2179 localsizemult = 1;
2180 } else {
2181 i = parse_disk_pos(isize, &localsizemult,
2182 parts->bytes_per_sector,
2183 parts->pscheme->get_cylinder_size(parts), NULL);
2184 if (i < 0) {
2185 errmsg = msg_string(MSG_invalid_sector_number);
2186 continue;
2187 }
2188 }
2189 /* round to cylinder size if localsizemult != 1 */
2190 int cylsize = parts->pscheme->get_cylinder_size(parts);
2191 i = NUMSEC(i, localsizemult, cylsize);
2192 /* Adjust to start of slice if needed */
2193 if ((i < min && (min - i) < localsizemult) ||
2194 (i > min && (i - min) < localsizemult)) {
2195 i = min;
2196 }
2197 if (max == 0 || i <= max)
2198 break;
2199 errmsg = msg_string(MSG_startoutsidedisk);
2200 }
2201 free(label_msg);
2202 free(freespace);
2203
2204 return i;
2205 }
2206
2207
2208 /* Ask for a partition size, check bounds and do the needed roundups */
2209 daddr_t
2210 getpartsize(struct disk_partitions *parts, daddr_t orig_start,
2211 daddr_t partstart, daddr_t dflt)
2212 {
2213 char dsize[24], isize[24], max_size[24], maxpartc, valid_parts[4],
2214 *label_msg, *prompt, *head, *hint, *tail;
2215 const char *errmsg = NULL;
2216 daddr_t i, partend, diskend, localsizemult, max, max_r, dflt_r;
2217 struct disk_part_info info;
2218 part_id partn;
2219
2220 diskend = parts->disk_start + parts->disk_size;
2221 max = parts->pscheme->max_free_space_at(parts, orig_start);
2222 max += orig_start - partstart;
2223 if (sizemult == 1)
2224 max--; /* with hugher scale proper rounding later will be ok */
2225
2226 /* We need to keep both the unrounded and rounded (_r) max and dflt */
2227 dflt_r = (partstart + dflt) / sizemult - partstart / sizemult;
2228 if (max == dflt)
2229 max_r = dflt_r;
2230 else
2231 max_r = max / sizemult;
2232 /* the partition may have been moved and now not fit any longer */
2233 if (dflt > max)
2234 dflt = max;
2235 if (dflt_r > max_r)
2236 dflt_r = max_r;
2237
2238 snprintf(max_size, sizeof max_size, "%" PRIu64, max_r);
2239
2240 maxpartc = 'a' + parts->num_part -1;
2241 if (parts->num_part > 1) {
2242 snprintf(valid_parts, sizeof valid_parts, "a-%c", maxpartc);
2243 } else if (parts->num_part == 1) {
2244 snprintf(valid_parts, sizeof valid_parts, " %c", maxpartc);
2245 } else {
2246 strcpy(valid_parts, "---");
2247 }
2248
2249 const char *args[] = { valid_parts, max_size, multname };
2250 hint = NULL;
2251 head = str_arg_subst(msg_string(MSG_label_size_head),
2252 __arraycount(args), args);
2253 if (parts->num_part)
2254 hint = str_arg_subst(msg_string(MSG_label_size_part_hint),
2255 __arraycount(args), args);
2256 tail = str_arg_subst(msg_string(MSG_label_size_tail),
2257 __arraycount(args), args);
2258
2259 if (hint != NULL)
2260 asprintf(&label_msg, "%s\n%s\n\n%s", head, hint, tail);
2261 else
2262 asprintf(&label_msg, "%s\n\n%s", head, tail);
2263 free(head); free(hint); free(tail);
2264
2265 localsizemult = sizemult;
2266 i = -1;
2267 for (;;) {
2268 snprintf(dsize, sizeof dsize, "%" PRIu64, dflt_r);
2269
2270 if (errmsg != NULL && errmsg[0] != 0)
2271 asprintf(&prompt, "%s\n\n%s", errmsg, label_msg);
2272 else
2273 prompt = label_msg;
2274 msg_prompt_win(prompt, -1, 12, 70, -1,
2275 (dflt != 0) ? dsize : 0, isize, sizeof isize);
2276 if (prompt != label_msg)
2277 free(prompt);
2278
2279 if (strcmp(isize, dsize) == 0) {
2280 free(label_msg);
2281 return dflt;
2282 }
2283 if (parts->num_part && isize[1] == '\0' && isize[0] >= 'a' &&
2284 isize[0] <= maxpartc) {
2285 partn = isize[0] - 'a';
2286 if (parts->pscheme->get_part_info(parts, partn,
2287 &info)) {
2288 i = info.start - partstart -1;
2289 localsizemult = 1;
2290 max_r = max;
2291 }
2292 } else if (atoi(isize) == -1) {
2293 i = max;
2294 localsizemult = 1;
2295 max_r = max;
2296 } else {
2297 i = parse_disk_pos(isize, &localsizemult,
2298 parts->bytes_per_sector,
2299 parts->pscheme->get_cylinder_size(parts), NULL);
2300 if (localsizemult != sizemult)
2301 max_r = max;
2302 }
2303 if (i < 0) {
2304 errmsg = msg_string(MSG_Invalid_numeric);
2305 continue;
2306 } else if (i > max_r) {
2307 errmsg = msg_string(MSG_Too_large);
2308 continue;
2309 }
2310 /*
2311 * partend is aligned to a cylinder if localsizemult
2312 * is not 1 sector
2313 */
2314 int cylsize = parts->pscheme->get_cylinder_size(parts);
2315 partend = NUMSEC((partstart + i*localsizemult) / localsizemult,
2316 localsizemult, cylsize);
2317 /* Align to end-of-disk or end-of-slice if close enough */
2318 if (partend > (diskend - sizemult)
2319 && partend < (diskend + sizemult))
2320 partend = diskend;
2321 if (partend > (partstart + max - sizemult)
2322 && partend < (partstart + max + sizemult))
2323 partend = partstart + max;
2324 /* sanity checks */
2325 if (partend > diskend) {
2326 partend = diskend;
2327 errmsg = msg_string(MSG_endoutsidedisk);
2328 continue;
2329 }
2330 free(label_msg);
2331 if (partend < partstart)
2332 return 0;
2333 return (partend - partstart);
2334 }
2335 /* NOTREACHED */
2336 }
2337
2338 /*
2339 * convert a string to a number of sectors, with a possible unit
2340 * 150M = 150 Megabytes
2341 * 2000c = 2000 cylinders
2342 * 150256s = 150256 sectors
2343 * Without units, use the default (sizemult).
2344 * returns the raw input value, and the unit used. Caller needs to multiply!
2345 * On invalid inputs, returns -1.
2346 */
2347 daddr_t
2348 parse_disk_pos(
2349 const char *str,
2350 daddr_t *localsizemult,
2351 daddr_t bps,
2352 daddr_t cyl_size,
2353 bool *extend_this)
2354 {
2355 daddr_t val;
2356 char *cp;
2357 bool mult_found;
2358
2359 if (str[0] == '\0') {
2360 return -1;
2361 }
2362 val = strtoull(str, &cp, 10);
2363 mult_found = false;
2364 if (extend_this)
2365 *extend_this = false;
2366 while (*cp != 0) {
2367 if (*cp == 'G' || *cp == 'g') {
2368 if (mult_found)
2369 return -1;
2370 *localsizemult = GIG / bps;
2371 goto next;
2372 }
2373 if (*cp == 'M' || *cp == 'm') {
2374 if (mult_found)
2375 return -1;
2376 *localsizemult = MEG / bps;
2377 goto next;
2378 }
2379 if (*cp == 'c' || *cp == 'C') {
2380 if (mult_found)
2381 return -1;
2382 *localsizemult = cyl_size;
2383 goto next;
2384 }
2385 if (*cp == 's' || *cp == 'S') {
2386 if (mult_found)
2387 return -1;
2388 *localsizemult = 1;
2389 goto next;
2390 }
2391 if (*cp == '+' && extend_this) {
2392 *extend_this = true;
2393 cp++;
2394 break;
2395 }
2396
2397 /* not a known unit */
2398 return -1;
2399
2400 next:
2401 mult_found = true;
2402 cp++;
2403 continue;
2404 }
2405 if (*cp != 0)
2406 return -1;
2407
2408 return val;
2409 }
2410