1 /* $NetBSD: label.c,v 1.12 2019/08/04 10:29:41 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.12 2019/08/04 10:29:41 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 <ufs/ffs/fs.h> 54 55 #include "defs.h" 56 #include "msg_defs.h" 57 #include "menu_defs.h" 58 59 /* 60 * local prototypes 61 */ 62 static bool boringpart(const struct disk_part_info *info); 63 static bool checklabel(struct disk_partitions*, char[MENUSTRSIZE], 64 char[MENUSTRSIZE]); 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 0 if no overlapping partition found, nonzero otherwise. 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[MENUSTRSIZE], char ovl2[MENUSTRSIZE]) 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 for (i = 0; i < parts->num_part - 1; i ++ ) { 118 if (!parts->pscheme->get_part_info(parts, i, &info)) 119 continue; 120 121 /* skip unused or reserved partitions */ 122 if (boringpart(&info)) 123 continue; 124 125 /* 126 * check succeeding partitions for overlap. 127 * O(n^2), but n is small (currently <= 16). 128 */ 129 istart = info.start; 130 iend = istart + info.size; 131 fs_type = info.fs_type; 132 fs_sub_type = info.fs_sub_type; 133 134 for (j = i+1; j < parts->num_part; j++) { 135 136 if (!parts->pscheme->get_part_info(parts, j, &info)) 137 continue; 138 139 /* skip unused or reserved partitions */ 140 if (boringpart(&info)) 141 continue; 142 143 jstart = info.start; 144 jend = jstart + info.size; 145 146 /* overlap? */ 147 if ((istart <= jstart && jstart < iend) || 148 (jstart <= istart && istart < jend)) { 149 snprintf(ovl1, sizeof(*ovl1), 150 "%" PRIu64 " - %" PRIu64 " %s, %s", 151 istart / sizemult, iend / sizemult, 152 multname, 153 getfslabelname(fs_type, fs_sub_type)); 154 snprintf(ovl2, sizeof(*ovl2), 155 "%" PRIu64 " - %" PRIu64 " %s, %s", 156 jstart / sizemult, jend / sizemult, 157 multname, 158 getfslabelname(info.fs_type, 159 info.fs_sub_type)); 160 return false; 161 } 162 } 163 } 164 165 return true; 166 } 167 168 int 169 checkoverlap(struct disk_partitions *parts) 170 { 171 char desc1[MENUSTRSIZE], desc2[MENUSTRSIZE]; 172 if (!checklabel(parts, desc1, desc2)) { 173 msg_display_subst(MSG_partitions_overlap, 2, desc1, desc2); 174 return 1; 175 } 176 return 0; 177 } 178 179 /* 180 * return (see post_edit_verify): 181 * 0 -> abort 182 * 1 -> re-edit 183 * 2 -> continue installation 184 */ 185 static int 186 verify_parts(struct partition_usage_set *pset) 187 { 188 struct part_usage_info *wanted; 189 struct disk_partitions *parts; 190 size_t i, num_root; 191 daddr_t first_bsdstart, first_bsdsize, inst_start, inst_size; 192 int rv; 193 194 first_bsdstart = first_bsdsize = 0; 195 inst_start = inst_size = 0; 196 num_root = 0; 197 parts = pset->parts; 198 for (i = 0; i < pset->num; i++) { 199 wanted = &pset->infos[i]; 200 201 if (wanted->flags & PUIFLG_JUST_MOUNTPOINT) 202 continue; 203 if (wanted->cur_part_id == NO_PART) 204 continue; 205 if (!(wanted->instflags & PUIINST_MOUNT)) 206 continue; 207 if (strcmp(wanted->mount, "/") != 0) 208 continue; 209 num_root++; 210 211 if (first_bsdstart == 0) { 212 first_bsdstart = wanted->cur_start; 213 first_bsdsize = wanted->size; 214 } 215 if (inst_start == 0 && wanted->cur_start == pm->ptstart) { 216 inst_start = wanted->cur_start; 217 inst_size = wanted->size; 218 } 219 } 220 221 if (num_root == 0 || 222 (num_root > 1 && inst_start == 0)) { 223 if (num_root == 0) 224 msg_display_subst(MSG_must_be_one_root, 2, 225 msg_string(parts->pscheme->name), 226 msg_string(parts->pscheme->short_name)); 227 else 228 msg_display_subst(MSG_multbsdpart, 2, 229 msg_string(parts->pscheme->name), 230 msg_string(parts->pscheme->short_name)); 231 rv = ask_reedit(parts); 232 if (rv != 2) 233 return rv; 234 } 235 236 if (pm->ptstart == 0) { 237 if (inst_start > 0) { 238 pm->ptstart = inst_start; 239 pm->ptsize = inst_size; 240 } else if (first_bsdstart > 0) { 241 pm->ptstart = first_bsdstart; 242 pm->ptsize = first_bsdsize; 243 } else if (parts->pscheme->guess_install_target && 244 parts->pscheme->guess_install_target( 245 parts, &inst_start, &inst_size)) { 246 pm->ptstart = inst_start; 247 pm->ptsize = inst_size; 248 } 249 } 250 251 /* Check for overlaps */ 252 if (checkoverlap(parts) != 0) { 253 rv = ask_reedit(parts); 254 if (rv != 2) 255 return rv; 256 } 257 258 /* 259 * post_edit_verify returns: 260 * 0 -> abort 261 * 1 -> re-edit 262 * 2 -> continue installation 263 */ 264 if (parts->pscheme->post_edit_verify) 265 return parts->pscheme->post_edit_verify(parts, false); 266 267 return 2; 268 } 269 270 static int 271 edit_fs_start(menudesc *m, void *arg) 272 { 273 struct single_part_fs_edit *edit = arg; 274 daddr_t start, end; 275 276 start = getpartoff(edit->pset->parts, edit->info.start); 277 if (edit->info.size != 0) { 278 /* Try to keep end in the same place */ 279 end = edit->info.start + edit->info.size; 280 if (end < start) 281 edit->info.size = edit->pset->parts->pscheme-> 282 max_free_space_at(edit->pset->parts, 283 edit->info.start); 284 else 285 edit->info.size = end - start; 286 } 287 edit->info.start = start; 288 return 0; 289 } 290 291 static int 292 edit_fs_size(menudesc *m, void *arg) 293 { 294 struct single_part_fs_edit *edit = arg; 295 daddr_t size; 296 297 size = getpartsize(edit->pset->parts, edit->info.start, 298 edit->info.size); 299 if (size < 0) 300 return 0; 301 if (size > edit->pset->parts->disk_size) 302 size = edit->pset->parts->disk_size - edit->info.start; 303 edit->info.size = size; 304 return 0; 305 } 306 307 static int 308 edit_fs_preserve(menudesc *m, void *arg) 309 { 310 struct single_part_fs_edit *edit = arg; 311 312 edit->wanted->instflags ^= PUIINST_NEWFS; 313 return 0; 314 } 315 316 static int 317 edit_install(menudesc *m, void *arg) 318 { 319 struct single_part_fs_edit *edit = arg; 320 321 if (edit->info.start == pm->ptstart) 322 pm->ptstart = 0; 323 else 324 pm->ptstart = edit->info.start; 325 return 0; 326 } 327 328 static int 329 edit_fs_mount(menudesc *m, void *arg) 330 { 331 struct single_part_fs_edit *edit = arg; 332 333 edit->wanted->instflags ^= PUIINST_MOUNT; 334 return 0; 335 } 336 337 static int 338 edit_fs_mountpt(menudesc *m, void *arg) 339 { 340 struct single_part_fs_edit *edit = arg; 341 char *p, *first, *last, buf[MOUNTLEN]; 342 343 strlcpy(buf, edit->wanted->mount, sizeof buf); 344 msg_prompt_win(MSG_mountpoint, -1, 18, 0, 0, 345 buf, buf, MOUNTLEN); 346 347 /* 348 * Trim all leading and trailing whitespace 349 */ 350 for (first = NULL, last = NULL, p = buf; *p; p++) { 351 if (isspace((unsigned char)*p)) 352 continue; 353 if (first == NULL) 354 first = p; 355 last = p; 356 } 357 if (last != NULL) 358 last[1] = 0; 359 360 if (*first == 0 || strcmp(first, "none") == 0) { 361 edit->wanted->mount[0] = 0; 362 edit->wanted->instflags &= ~PUIINST_MOUNT; 363 return 0; 364 } 365 366 if (*first != '/') { 367 edit->wanted->mount[0] = '/'; 368 strlcpy(&edit->wanted->mount[1], first, 369 sizeof(edit->wanted->mount)-1); 370 } else { 371 strlcpy(edit->wanted->mount, first, sizeof edit->wanted->mount); 372 } 373 374 return 0; 375 } 376 377 static int 378 edit_restore(menudesc *m, void *arg) 379 { 380 struct single_part_fs_edit *edit = arg; 381 382 edit->info = edit->old_info; 383 *edit->wanted = edit->old_usage; 384 return 0; 385 } 386 387 static int 388 edit_cancel(menudesc *m, void *arg) 389 { 390 struct single_part_fs_edit *edit = arg; 391 392 edit->rv = -1; 393 return 1; 394 } 395 396 static int 397 edit_delete_ptn(menudesc *m, void *arg) 398 { 399 struct single_part_fs_edit *edit = arg; 400 401 edit->rv = -2; 402 return 1; 403 } 404 405 /* 406 * We have added/removed partitions, all cur_part_id values are 407 * out of sync. Re-fetch and reorder partitions accordingly. 408 */ 409 static void 410 renumber_partitions(struct partition_usage_set *pset) 411 { 412 struct part_usage_info *ninfos; 413 struct disk_part_info info; 414 size_t i; 415 part_id pno; 416 417 ninfos = calloc(pset->parts->num_part, sizeof(*ninfos)); 418 if (ninfos == NULL) { 419 err_msg_win(err_outofmem); 420 return; 421 } 422 423 for (pno = 0; pno < pset->parts->num_part; pno++) { 424 if (!pset->parts->pscheme->get_part_info(pset->parts, pno, 425 &info)) 426 continue; 427 for (i = 0; i < pset->parts->num_part; i++) { 428 if (pset->infos[i].cur_start != info.start) 429 continue; 430 memcpy(&ninfos[pno], &pset->infos[i], 431 sizeof(ninfos[pno])); 432 ninfos[pno].cur_part_id = pno; 433 break; 434 } 435 } 436 437 memcpy(pset->infos, ninfos, sizeof(*pset->infos)*pset->parts->num_part); 438 free(ninfos); 439 } 440 441 /* 442 * Most often used file system types, we offer them in a first level menu. 443 */ 444 static const uint edit_fs_common_types[] = 445 { FS_BSDFFS, FS_SWAP, FS_MSDOS, FS_BSDLFS, FS_EX2FS }; 446 447 /* 448 * Functions for uncommon file system types - we offer the full list, 449 * but put FFSv2 and FFSv1 at the front. 450 */ 451 static void 452 init_fs_type_ext(menudesc *menu, void *arg) 453 { 454 struct single_part_fs_edit *edit = arg; 455 uint t = edit->info.fs_type; 456 size_t i, ndx, max = menu->numopts; 457 458 if (t == FS_BSDFFS) { 459 if (edit->info.fs_sub_type == 2) 460 menu->cursel = 0; 461 else 462 menu->cursel = 1; 463 return; 464 } 465 /* skip the two FFS entries, and do not add FFS later again */ 466 for (ndx = 2, i = 0; i < FSMAXTYPES && ndx < max; i++) { 467 if (i == FS_UNUSED) 468 continue; 469 if (i == FS_BSDFFS) 470 continue; 471 if (fstypenames[i] == NULL) 472 continue; 473 474 if (i == t) { 475 menu->cursel = ndx; 476 break; 477 } 478 ndx++; 479 } 480 } 481 482 static int 483 set_fstype_ext(menudesc *menu, void *arg) 484 { 485 struct single_part_fs_edit *edit = arg; 486 size_t i, ndx, max = menu->numopts; 487 488 if (menu->cursel == 0 || menu->cursel == 1) { 489 edit->info.fs_type = FS_BSDFFS; 490 edit->info.fs_sub_type = menu->cursel == 0 ? 2 : 1; 491 edit->info.nat_type = edit->pset->parts->pscheme-> 492 get_fs_part_type(edit->info.fs_type, 493 edit->info.fs_sub_type); 494 edit->wanted->type = edit->info.nat_type->generic_ptype; 495 edit->wanted->fs_type = edit->info.fs_type; 496 edit->wanted->fs_version = edit->info.fs_sub_type; 497 return 1; 498 } 499 500 for (ndx = 2, i = 0; i < FSMAXTYPES && ndx < max; i++) { 501 if (i == FS_UNUSED) 502 continue; 503 if (i == FS_BSDFFS) 504 continue; 505 if (fstypenames[i] == NULL) 506 continue; 507 508 if (ndx == (size_t)menu->cursel) { 509 edit->info.fs_type = i; 510 edit->info.fs_sub_type = 0; 511 edit->info.nat_type = edit->pset->parts->pscheme-> 512 get_fs_part_type(i, 0); 513 if (edit->info.nat_type == NULL) 514 edit->info.nat_type = edit->pset->parts-> 515 pscheme->get_generic_part_type(PT_root); 516 edit->wanted->type = edit->info.nat_type->generic_ptype; 517 edit->wanted->fs_type = edit->info.fs_type; 518 edit->wanted->fs_version = edit->info.fs_sub_type; 519 break; 520 } 521 ndx++; 522 } 523 return 1; 524 } 525 526 /* 527 * Offer a menu with "exotic" file system types, start with FFSv2 and FFSv1, 528 * skip later FFS entry in the generic list. 529 */ 530 static int 531 edit_fs_type_ext(menudesc *menu, void *arg) 532 { 533 menu_ent *opts; 534 int m; 535 size_t i, ndx, cnt; 536 537 cnt = __arraycount(fstypenames)-1; 538 opts = calloc(cnt, sizeof(*opts)); 539 if (opts == NULL) 540 return 1; 541 542 ndx = 0; 543 opts[ndx].opt_name = msg_string(MSG_fs_type_ffsv2); 544 opts[ndx].opt_action = set_fstype_ext; 545 ndx++; 546 opts[ndx].opt_name = msg_string(MSG_fs_type_ffs); 547 opts[ndx].opt_action = set_fstype_ext; 548 ndx++; 549 for (i = 0; i < FSMAXTYPES && ndx < cnt; i++) { 550 if (i == FS_UNUSED) 551 continue; 552 if (i == FS_BSDFFS) 553 continue; 554 if (fstypenames[i] == NULL) 555 continue; 556 opts[ndx].opt_name = fstypenames[i]; 557 opts[ndx].opt_action = set_fstype_ext; 558 ndx++; 559 } 560 assert(ndx == cnt); 561 m = new_menu(MSG_Select_the_type, opts, ndx, 562 30, 6, 10, 0, MC_SUBMENU | MC_SCROLL, 563 init_fs_type_ext, NULL, NULL, NULL, MSG_unchanged); 564 565 if (m < 0) 566 return 1; 567 process_menu(m, arg); 568 free_menu(m); 569 free(opts); 570 571 return 1; 572 } 573 574 static void 575 init_fs_type(menudesc *menu, void *arg) 576 { 577 struct single_part_fs_edit *edit = arg; 578 size_t i; 579 580 /* init menu->cursel from fs type in arg */ 581 if (edit->info.fs_type == FS_BSDFFS) { 582 if (edit->info.fs_sub_type == 2) 583 menu->cursel = 0; 584 else 585 menu->cursel = 1; 586 } 587 for (i = 1; i < __arraycount(edit_fs_common_types); i++) { 588 if (edit->info.fs_type == edit_fs_common_types[i]) { 589 menu->cursel = i+1; 590 break; 591 } 592 } 593 } 594 595 static int 596 set_fstype(menudesc *menu, void *arg) 597 { 598 struct single_part_fs_edit *edit = arg; 599 int ndx; 600 601 if (menu->cursel < 2) { 602 edit->info.fs_type = FS_BSDFFS; 603 edit->info.fs_sub_type = menu->cursel == 0 ? 2 : 1; 604 edit->info.nat_type = edit->pset->parts->pscheme-> 605 get_fs_part_type(FS_BSDFFS, 2); 606 if (edit->info.nat_type == NULL) 607 edit->info.nat_type = edit->pset->parts-> 608 pscheme->get_generic_part_type(PT_root); 609 edit->wanted->type = edit->info.nat_type->generic_ptype; 610 edit->wanted->fs_type = edit->info.fs_type; 611 edit->wanted->fs_version = edit->info.fs_sub_type; 612 return 1; 613 } 614 ndx = menu->cursel-1; 615 616 if (ndx < 0 || 617 (size_t)ndx >= __arraycount(edit_fs_common_types)) 618 return 1; 619 620 edit->info.fs_type = edit_fs_common_types[ndx]; 621 edit->info.fs_sub_type = 0; 622 edit->info.nat_type = edit->pset->parts->pscheme-> 623 get_fs_part_type(edit->info.fs_type, 0); 624 if (edit->info.nat_type == NULL) 625 edit->info.nat_type = edit->pset->parts-> 626 pscheme->get_generic_part_type(PT_root); 627 edit->wanted->type = edit->info.nat_type->generic_ptype; 628 edit->wanted->fs_type = edit->info.fs_type; 629 edit->wanted->fs_version = edit->info.fs_sub_type; 630 return 1; 631 } 632 633 /* 634 * Offer a menu selecting the common file system types 635 */ 636 static int 637 edit_fs_type(menudesc *menu, void *arg) 638 { 639 struct single_part_fs_edit *edit = arg; 640 menu_ent *opts; 641 int m, cnt; 642 size_t i; 643 644 /* 645 * Shortcut to full menu if we have an exotic value 646 */ 647 for (i = 0; i < __arraycount(edit_fs_common_types); i++) 648 if (edit->info.fs_type == edit_fs_common_types[i]) 649 break; 650 if (i >= __arraycount(edit_fs_common_types)) { 651 edit_fs_type_ext(menu, arg); 652 return 0; 653 } 654 655 /* 656 * Starting with a common type, show short menu first 657 */ 658 cnt = __arraycount(edit_fs_common_types) + 2; 659 opts = calloc(cnt, sizeof(*opts)); 660 if (opts == NULL) 661 return 0; 662 663 /* special case entry 0: two FFS entries */ 664 for (i = 0; i < __arraycount(edit_fs_common_types); i++) { 665 opts[i+1].opt_name = getfslabelname(edit_fs_common_types[i], 0); 666 opts[i+1].opt_action = set_fstype; 667 } 668 /* duplicate FFS (at offset 1) into first entry */ 669 opts[0] = opts[1]; 670 opts[0].opt_name = msg_string(MSG_fs_type_ffsv2); 671 opts[1].opt_name = msg_string(MSG_fs_type_ffs); 672 /* add secondary sub-menu */ 673 assert(i+1 < (size_t)cnt); 674 opts[i+1].opt_name = msg_string(MSG_other_fs_type); 675 opts[i+1].opt_action = edit_fs_type_ext; 676 677 m = new_menu(MSG_Select_the_type, opts, cnt, 678 30, 6, 0, 0, MC_SUBMENU | MC_SCROLL, 679 init_fs_type, NULL, NULL, NULL, MSG_unchanged); 680 681 if (m < 0) 682 return 0; 683 process_menu(m, arg); 684 free_menu(m); 685 free(opts); 686 687 return 0; 688 } 689 690 691 static void update_edit_ptn_menu(menudesc *m, void *arg); 692 static void draw_edit_ptn_line(menudesc *m, int opt, void *arg); 693 static int edit_ptn_custom_type(menudesc *m, void *arg); 694 695 int 696 edit_ptn(menudesc *menu, void *arg) 697 { 698 struct partition_usage_set *pset = arg; 699 struct single_part_fs_edit edit; 700 int fspart_menu, num_opts; 701 const char *err; 702 menu_ent *mopts, *popt; 703 bool is_new_part, with_inst_opt = pset->parts->parent == NULL; 704 705 static const menu_ent edit_ptn_fields_head[] = { 706 { .opt_action=edit_fs_type }, 707 { .opt_action=edit_fs_start }, 708 { .opt_action=edit_fs_size }, 709 { .opt_flags=OPT_IGNORE }, 710 }; 711 712 static const menu_ent edit_ptn_fields_head_add[] = { 713 { .opt_action=edit_install }, 714 }; 715 716 static const menu_ent edit_ptn_fields_head2[] = { 717 { .opt_action=edit_fs_preserve }, 718 { .opt_action=edit_fs_mount }, 719 { .opt_menu=MENU_mountoptions, .opt_flags=OPT_SUB }, 720 { .opt_action=edit_fs_mountpt }, 721 }; 722 static const menu_ent edit_ptn_fields_tail[] = { 723 { .opt_name=MSG_askunits, .opt_menu=MENU_sizechoice, 724 .opt_flags=OPT_SUB }, 725 { .opt_name=MSG_restore, 726 .opt_action=edit_restore}, 727 { .opt_name=MSG_Delete_partition, 728 .opt_action=edit_delete_ptn}, 729 { .opt_name=MSG_cancel, 730 .opt_action=edit_cancel}, 731 }; 732 733 memset(&edit, 0, sizeof edit); 734 edit.pset = pset; 735 edit.index = menu->cursel; 736 edit.wanted = &pset->infos[edit.index]; 737 738 if (menu->cursel < 0 || (size_t)menu->cursel > pset->parts->num_part) 739 return 0; 740 is_new_part = (size_t)menu->cursel == pset->parts->num_part; 741 742 num_opts = __arraycount(edit_ptn_fields_head) + 743 __arraycount(edit_ptn_fields_head2) + 744 __arraycount(edit_ptn_fields_tail); 745 if (with_inst_opt) 746 num_opts += __arraycount(edit_ptn_fields_head_add); 747 if (is_new_part) 748 num_opts--; 749 else 750 num_opts += pset->parts->pscheme->custom_attribute_count; 751 752 mopts = calloc(num_opts, sizeof(*mopts)); 753 if (mopts == NULL) { 754 err_msg_win(err_outofmem); 755 return 0; 756 } 757 memcpy(mopts, edit_ptn_fields_head, sizeof(edit_ptn_fields_head)); 758 popt = mopts + __arraycount(edit_ptn_fields_head); 759 if (with_inst_opt) { 760 memcpy(popt, edit_ptn_fields_head_add, 761 sizeof(edit_ptn_fields_head_add)); 762 popt += __arraycount(edit_ptn_fields_head_add); 763 } 764 memcpy(popt, edit_ptn_fields_head2, sizeof(edit_ptn_fields_head2)); 765 popt += __arraycount(edit_ptn_fields_head2); 766 edit.first_custom_attr = popt - mopts; 767 if (!is_new_part) { 768 for (size_t i = 0; 769 i < pset->parts->pscheme->custom_attribute_count; 770 i++, popt++) { 771 popt->opt_action = edit_ptn_custom_type; 772 } 773 } 774 memcpy(popt, edit_ptn_fields_tail, sizeof(edit_ptn_fields_tail)); 775 popt += __arraycount(edit_ptn_fields_tail) - 1; 776 if (is_new_part) 777 memcpy(popt-1, popt, sizeof(*popt)); 778 779 if (is_new_part) { 780 struct disk_part_free_space space; 781 daddr_t align = pset->parts->pscheme->get_part_alignment( 782 pset->parts); 783 784 edit.id = NO_PART; 785 if (pset->parts->pscheme->get_free_spaces(pset->parts, 786 &space, 1, align, align, -1, -1) == 1) { 787 edit.info.start = space.start; 788 edit.info.size = space.size; 789 edit.info.fs_type = FS_BSDFFS; 790 edit.info.fs_sub_type = 2; 791 edit.info.nat_type = pset->parts->pscheme-> 792 get_fs_part_type(edit.info.fs_type, 793 edit.info.fs_sub_type); 794 edit.wanted->instflags = PUIINST_NEWFS; 795 } 796 } else { 797 edit.id = pset->infos[edit.index].cur_part_id; 798 if (!pset->parts->pscheme->get_part_info(pset->parts, edit.id, 799 &edit.info)) { 800 free(mopts); 801 return 0; 802 } 803 } 804 805 edit.old_usage = *edit.wanted; 806 edit.old_info = edit.info; 807 808 fspart_menu = new_menu(MSG_edfspart, mopts, num_opts, 809 15, 2, 0, 55, MC_NOCLEAR | MC_SCROLL, 810 update_edit_ptn_menu, draw_edit_ptn_line, NULL, 811 NULL, MSG_OK); 812 813 process_menu(fspart_menu, &edit); 814 free(mopts); 815 free_menu(fspart_menu); 816 817 if (edit.rv == 0) { /* OK, set new data */ 818 edit.info.last_mounted = edit.wanted->mount; 819 if (is_new_part) { 820 edit.wanted->cur_part_id = pset->parts->pscheme-> 821 add_partition(pset->parts, &edit.info, &err); 822 if (edit.wanted->cur_part_id == NO_PART) 823 err_msg_win(err); 824 else { 825 pset->parts->pscheme->get_part_info( 826 pset->parts, edit.wanted->cur_part_id, 827 &edit.info); 828 edit.wanted->cur_start = edit.info.start; 829 edit.wanted->size = edit.info.size; 830 edit.wanted->type = 831 edit.info.nat_type->generic_ptype; 832 edit.wanted->fs_type = edit.info.fs_type; 833 edit.wanted->fs_version = edit.info.fs_sub_type; 834 /* things have changed, re-sort */ 835 renumber_partitions(pset); 836 } 837 } else { 838 if (!pset->parts->pscheme->set_part_info(pset->parts, 839 edit.id, &edit.info, &err)) 840 err_msg_win(err); 841 } 842 843 /* 844 * if size has changed, we may need to add or remove 845 * the option to add partitions 846 */ 847 show_partition_adder(menu, pset); 848 } else if (edit.rv == -1) { /* cancel edit */ 849 if (is_new_part) { 850 memmove(pset->infos+edit.index, 851 pset->infos+edit.index+1, 852 sizeof(*pset->infos)*(pset->num-edit.index)); 853 memmove(menu->opts+edit.index, 854 menu->opts+edit.index+1, 855 sizeof(*menu->opts)*(menu->numopts-edit.index)); 856 menu->numopts--; 857 menu->cursel = 0; 858 pset->num--; 859 return -1; 860 } 861 pset->infos[edit.index] = edit.old_usage; 862 } else if (!is_new_part && edit.rv == -2) { /* delete partition */ 863 if (!pset->parts->pscheme->delete_partition(pset->parts, 864 edit.id, &err)) { 865 err_msg_win(err); 866 return 0; 867 } 868 memmove(pset->infos+edit.index, 869 pset->infos+edit.index+1, 870 sizeof(*pset->infos)*(pset->num-edit.index)); 871 memmove(menu->opts+edit.index, 872 menu->opts+edit.index+1, 873 sizeof(*menu->opts)*(menu->numopts-edit.index)); 874 menu->numopts--; 875 menu->cursel = 0; 876 877 /* things have changed, re-sort */ 878 pset->num--; 879 renumber_partitions(pset); 880 881 /* we can likely add new partitions now */ 882 show_partition_adder(menu, pset); 883 884 return -1; 885 } 886 887 return 0; 888 } 889 890 static void 891 update_edit_ptn_menu(menudesc *m, void *arg) 892 { 893 struct single_part_fs_edit *edit = arg; 894 int i; 895 uint t = edit->info.fs_type; 896 size_t attr_no; 897 898 /* Determine which of the properties can be changed */ 899 for (i = 0; i < m->numopts; i++) { 900 if (m->opts[i].opt_action == NULL && 901 m->opts[i].opt_menu != MENU_mountoptions) 902 continue; 903 904 /* Default to disabled... */ 905 m->opts[i].opt_flags |= OPT_IGNORE; 906 if ((t == FS_UNUSED || t == FS_SWAP) && 907 (m->opts[i].opt_action == edit_fs_preserve || 908 m->opts[i].opt_action == edit_fs_mount || 909 m->opts[i].opt_action == edit_fs_mountpt || 910 m->opts[i].opt_menu == MENU_mountoptions)) 911 continue; 912 if (m->opts[i].opt_action == edit_install && 913 edit->info.nat_type && 914 edit->info.nat_type->generic_ptype != PT_root) 915 /* can only install onto PT_root partitions */ 916 continue; 917 if (m->opts[i].opt_action == edit_fs_preserve && 918 t != FS_BSDFFS && t != FS_BSDLFS && t != FS_APPLEUFS && 919 t != FS_MSDOS && t != FS_EX2FS) { 920 /* Can not newfs this filesystem */ 921 edit->wanted->instflags &= ~PUIINST_NEWFS; 922 continue; 923 } 924 if (m->opts[i].opt_action == edit_ptn_custom_type) { 925 attr_no = (size_t)i - edit->first_custom_attr; 926 if (!edit->pset->parts->pscheme-> 927 custom_attribute_writable( 928 edit->pset->parts, edit->id, attr_no)) 929 continue; 930 } 931 /* Ok: we want this one */ 932 m->opts[i].opt_flags &= ~OPT_IGNORE; 933 } 934 } 935 936 static void 937 draw_edit_ptn_line(menudesc *m, int opt, void *arg) 938 { 939 struct single_part_fs_edit *edit = arg; 940 static int col_width; 941 static const char *ptn_type, *ptn_start, *ptn_size, *ptn_end, 942 *ptn_newfs, *ptn_mount, *ptn_mount_options, *ptn_mountpt, 943 *ptn_install; 944 const char *c; 945 char val[MENUSTRSIZE]; 946 const char *attrname; 947 size_t attr_no; 948 949 if (col_width == 0) { 950 int l; 951 952 #define LOAD(STR) STR = msg_string(MSG_##STR); l = strlen(STR); \ 953 if (l > col_width) col_width = l 954 955 LOAD(ptn_type); 956 LOAD(ptn_start); 957 LOAD(ptn_size); 958 LOAD(ptn_end); 959 LOAD(ptn_install); 960 LOAD(ptn_newfs); 961 LOAD(ptn_mount); 962 LOAD(ptn_mount_options); 963 LOAD(ptn_mountpt); 964 #undef LOAD 965 966 for (size_t i = 0; 967 i < edit->pset->parts->pscheme->custom_attribute_count; 968 i++) { 969 attrname = msg_string( 970 edit->pset->parts->pscheme->custom_attributes[i] 971 .label); 972 l = strlen(attrname); 973 if (l > col_width) col_width = l; 974 } 975 976 col_width += 3; 977 } 978 979 if (m->opts[opt].opt_flags & OPT_IGNORE 980 && (opt != 3 || edit->info.fs_type == FS_UNUSED) 981 && m->opts[opt].opt_action != edit_ptn_custom_type) { 982 wprintw(m->mw, "%*s -", col_width, ""); 983 return; 984 } 985 986 if (opt < 4) { 987 switch (opt) { 988 case 0: 989 if (edit->info.fs_type == FS_BSDFFS) 990 if (edit->info.fs_sub_type == 2) 991 c = msg_string(MSG_fs_type_ffsv2); 992 else 993 c = msg_string(MSG_fs_type_ffs); 994 else 995 c = getfslabelname(edit->info.fs_type, 996 edit->info.fs_sub_type); 997 wprintw(m->mw, "%*s : %s", col_width, ptn_type, c); 998 return; 999 case 1: 1000 wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width, 1001 ptn_start, edit->info.start / sizemult, multname); 1002 return; 1003 case 2: 1004 wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width, 1005 ptn_size, edit->info.size / sizemult, multname); 1006 return; 1007 case 3: 1008 wprintw(m->mw, "%*s : %" PRIu64 " %s", col_width, 1009 ptn_end, (edit->info.start + edit->info.size) 1010 / sizemult, multname); 1011 return; 1012 } 1013 } 1014 if (m->opts[opt].opt_action == edit_install) { 1015 wprintw(m->mw, "%*s : %s", col_width, ptn_install, 1016 msg_string(edit->info.start == pm->ptstart 1017 ? MSG_Yes : MSG_No)); 1018 return; 1019 } 1020 if (m->opts[opt].opt_action == edit_fs_preserve) { 1021 wprintw(m->mw, "%*s : %s", col_width, ptn_newfs, 1022 msg_string(edit->wanted->instflags & PUIINST_NEWFS 1023 ? MSG_Yes : MSG_No)); 1024 return; 1025 } 1026 if (m->opts[opt].opt_action == edit_fs_mount) { 1027 wprintw(m->mw, "%*s : %s", col_width, ptn_mount, 1028 msg_string(edit->wanted->instflags & PUIINST_MOUNT 1029 ? MSG_Yes : MSG_No)); 1030 return; 1031 } 1032 if (m->opts[opt].opt_menu == MENU_mountoptions) { 1033 wprintw(m->mw, "%*s : ", col_width, ptn_mount_options); 1034 if (edit->wanted->mountflags & PUIMNT_ASYNC) 1035 wprintw(m->mw, "async "); 1036 if (edit->wanted->mountflags & PUIMNT_NOATIME) 1037 wprintw(m->mw, "noatime "); 1038 if (edit->wanted->mountflags & PUIMNT_NODEV) 1039 wprintw(m->mw, "nodev "); 1040 if (edit->wanted->mountflags & PUIMNT_NODEVMTIME) 1041 wprintw(m->mw, "nodevmtime "); 1042 if (edit->wanted->mountflags & PUIMNT_NOEXEC) 1043 wprintw(m->mw, "noexec "); 1044 if (edit->wanted->mountflags & PUIMNT_NOSUID) 1045 wprintw(m->mw, "nosuid "); 1046 if (edit->wanted->mountflags & PUIMNT_LOG) 1047 wprintw(m->mw, "log "); 1048 if (edit->wanted->mountflags & PUIMNT_NOAUTO) 1049 wprintw(m->mw, "noauto "); 1050 return; 1051 } 1052 if (m->opts[opt].opt_action == edit_fs_mountpt) { 1053 wprintw(m->mw, "%*s : %s", col_width, ptn_mountpt, 1054 edit->wanted->mount); 1055 return; 1056 } 1057 1058 attr_no = opt - edit->first_custom_attr; 1059 edit->pset->parts->pscheme->format_custom_attribute( 1060 edit->pset->parts, edit->id, attr_no, &edit->info, 1061 val, sizeof val); 1062 attrname = msg_string(edit->pset->parts->pscheme-> 1063 custom_attributes[attr_no].label); 1064 wprintw(m->mw, "%*s : %s", col_width, attrname, val); 1065 } 1066 1067 static int 1068 edit_ptn_custom_type(menudesc *m, void *arg) 1069 { 1070 struct single_part_fs_edit *edit = arg; 1071 size_t attr_no = m->cursel - edit->first_custom_attr; 1072 char line[STRSIZE]; 1073 1074 switch (edit->pset->parts->pscheme->custom_attributes[attr_no].type) { 1075 case pet_bool: 1076 edit->pset->parts->pscheme->custom_attribute_toggle( 1077 edit->pset->parts, edit->id, attr_no); 1078 break; 1079 case pet_cardinal: 1080 case pet_str: 1081 edit->pset->parts->pscheme->format_custom_attribute( 1082 edit->pset->parts, edit->id, attr_no, &edit->info, 1083 line, sizeof(line)); 1084 msg_prompt_win( 1085 edit->pset->parts->pscheme->custom_attributes[attr_no]. 1086 label, -1, 18, 0, 0, line, line, sizeof(line)); 1087 edit->pset->parts->pscheme->custom_attribute_set_str( 1088 edit->pset->parts, edit->id, attr_no, line); 1089 break; 1090 } 1091 1092 return 0; 1093 } 1094 1095 1096 /* 1097 * Some column width depend on translation, we will set these in 1098 * fmt_fspart_header and later use it when formatting single entries 1099 * in fmt_fspart_row. 1100 * The table consist of 3 "size like" columns, all fixed width, then 1101 * ptnheaders_fstype, part_header_col_flag, and finally the mount point 1102 * (which is variable width). 1103 */ 1104 static int fstype_width, flags_width; 1105 static char fspart_separator[MENUSTRSIZE]; 1106 static char fspart_title[2*MENUSTRSIZE]; 1107 1108 /* 1109 * Format the header of the main partition editor menu. 1110 */ 1111 static void 1112 fmt_fspart_header(menudesc *menu, void *arg) 1113 { 1114 struct partition_usage_set *pset = arg; 1115 char total[6], free_space[6], scol[13], ecol[13], szcol[13], 1116 sepline[MENUSTRSIZE], *p; 1117 const char *fstype, *flags; 1118 int i; 1119 bool with_inst_flag = pset->parts->parent == NULL; 1120 1121 humanize_number(total, sizeof total, 1122 pset->parts->disk_size * 512, 1123 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1124 humanize_number(free_space, sizeof free_space, 1125 pset->cur_free_space * 512, 1126 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1127 1128 msg_display_subst(MSG_fspart, 7, pset->parts->disk, 1129 msg_string(pset->parts->pscheme->name), 1130 msg_string(pset->parts->pscheme->short_name), 1131 with_inst_flag ? msg_string(MSG_ptn_instflag_desc) : "", 1132 pset->parts->pscheme->part_flag_desc ? 1133 msg_string(pset->parts->pscheme->part_flag_desc) 1134 : "", 1135 total, free_space); 1136 1137 snprintf(scol, sizeof scol, "%s (%s)", 1138 msg_string(MSG_ptnheaders_start), multname); 1139 snprintf(ecol, sizeof ecol, "%s (%s)", 1140 msg_string(MSG_ptnheaders_end), multname); 1141 snprintf(szcol, sizeof szcol, "%s (%s)", 1142 msg_string(MSG_ptnheaders_size), multname); 1143 1144 fstype = msg_string(MSG_ptnheaders_fstype); 1145 flags = msg_string(MSG_part_header_col_flag); 1146 fstype_width = max(strlen(fstype), 8); 1147 flags_width = strlen(flags); 1148 for (i = 0, p = sepline; i < fstype_width; i++) 1149 *p++ = '-'; 1150 for (i = 0, *p++ = ' '; i < flags_width; i++) 1151 *p++ = '-'; 1152 *p = 0; 1153 1154 snprintf(fspart_separator, sizeof(fspart_separator), 1155 "------------ ------------ ------------ %s ----------------", 1156 sepline); 1157 1158 snprintf(fspart_title, sizeof(fspart_title), 1159 " %12.12s %12.12s %12.12s %*s %*s %s\n" 1160 " %s", scol, ecol, szcol, fstype_width, fstype, 1161 flags_width, flags, msg_string(MSG_ptnheaders_filesystem), 1162 fspart_separator); 1163 1164 msg_table_add("\n\n"); 1165 } 1166 1167 /* 1168 * Format one partition entry in the main partition editor. 1169 */ 1170 static void 1171 fmt_fspart_row(menudesc *m, int ptn, void *arg) 1172 { 1173 struct partition_usage_set *pset = arg; 1174 struct disk_part_info info; 1175 daddr_t poffset, psize, pend; 1176 const char *desc; 1177 static const char *Yes; 1178 char flag_str[MENUSTRSIZE], *fp; 1179 unsigned inst_flags; 1180 bool with_inst_flag = pset->parts->parent == NULL; 1181 1182 if (Yes == NULL) 1183 Yes = msg_string(MSG_Yes); 1184 1185 if (!real_partition(pset, ptn)) 1186 return; 1187 1188 if (!pset->parts->pscheme->get_part_info(pset->parts, 1189 pset->infos[ptn].cur_part_id, &info)) 1190 return; 1191 1192 /* enable / disable this line if it is something like RAW_PART */ 1193 if (info.flags & (PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 1194 m->opts[ptn].opt_flags |= OPT_IGNORE; 1195 else 1196 m->opts[ptn].opt_flags &= ~OPT_IGNORE; 1197 1198 poffset = info.start / sizemult; 1199 psize = info.size / sizemult; 1200 if (psize == 0) 1201 pend = 0; 1202 else 1203 pend = (info.start + info.size) / sizemult - 1; 1204 1205 if (info.flags & PTI_WHOLE_DISK) 1206 desc = msg_string(MSG_NetBSD_partition_cant_change); 1207 else if (info.flags & PTI_RAW_PART) 1208 desc = msg_string(MSG_Whole_disk_cant_change); 1209 else if (info.flags & PTI_BOOT) 1210 desc = msg_string(MSG_Boot_partition_cant_change); 1211 else 1212 desc = getfslabelname(info.fs_type, info.fs_sub_type); 1213 1214 fp = flag_str; 1215 inst_flags = pset->infos[ptn].instflags; 1216 if (with_inst_flag && info.start == pm->ptstart && 1217 info.nat_type->generic_ptype == PT_root) { 1218 static char inst_flag; 1219 1220 if (inst_flag == 0) 1221 inst_flag = msg_string(MSG_install_flag)[0]; 1222 *fp++ = inst_flag; 1223 } 1224 if (inst_flags & PUIINST_NEWFS) 1225 *fp++ = msg_string(MSG_newfs_flag)[0]; 1226 *fp = 0; 1227 if (pset->parts->pscheme->get_part_attr_str != NULL) 1228 pset->parts->pscheme->get_part_attr_str(pset->parts, 1229 pset->infos[ptn].cur_part_id, fp, sizeof(flag_str)-1); 1230 1231 /* if the fstype description does not fit, check if we can overrun */ 1232 if (strlen(desc) > (size_t)fstype_width && 1233 flag_str[0] == 0 && (info.last_mounted == NULL || 1234 info.last_mounted[0] == 0)) 1235 wprintw(m->mw, "%12" PRIu64 " %12" PRIu64 " %12" PRIu64 1236 " %s", 1237 poffset, pend, psize, desc); 1238 else 1239 wprintw(m->mw, "%12" PRIu64 " %12" PRIu64 " %12" PRIu64 1240 " %*.*s %*s %s", 1241 poffset, pend, psize, fstype_width, fstype_width, desc, 1242 -flags_width, flag_str, 1243 (inst_flags & PUIINST_MOUNT) && info.last_mounted && 1244 info.last_mounted[0] ? info.last_mounted : ""); 1245 } 1246 1247 static int 1248 edit_fspart_pack(menudesc *m, void *arg) 1249 { 1250 struct partition_usage_set *pset = arg; 1251 char buf[STRSIZE]; 1252 1253 if (!pset->parts->pscheme->get_disk_pack_name(pset->parts, 1254 buf, sizeof buf)) 1255 return 0; 1256 1257 msg_prompt_win(MSG_edit_disk_pack_hdr, 1258 -1, 18, 0, -1, buf, buf, sizeof(buf)); 1259 1260 pset->parts->pscheme->set_disk_pack_name(pset->parts, buf); 1261 return 0; 1262 } 1263 1264 static int 1265 edit_fspart_add(menudesc *m, void *arg) 1266 { 1267 struct partition_usage_set *pset = arg; 1268 struct part_usage_info *ninfo; 1269 menu_ent *nmenopts; 1270 size_t cnt, off; 1271 1272 ninfo = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos)); 1273 if (ninfo == NULL) 1274 return 0; 1275 pset->infos = ninfo; 1276 off = pset->parts->num_part; 1277 cnt = pset->num-pset->parts->num_part; 1278 if (cnt > 0) 1279 memmove(pset->infos+off+1,pset->infos+off, 1280 cnt*sizeof(*pset->infos)); 1281 memset(pset->infos+off, 0, sizeof(*pset->infos)); 1282 1283 nmenopts = realloc(m->opts, (m->numopts+1)*sizeof(*m->opts)); 1284 if (nmenopts == NULL) 1285 return 0; 1286 memmove(nmenopts+off+1, nmenopts+off, 1287 (m->numopts-off)*sizeof(*nmenopts)); 1288 memset(&nmenopts[off], 0, sizeof(nmenopts[off])); 1289 nmenopts[off].opt_action = edit_ptn; 1290 pset->menu_opts = m->opts = nmenopts; 1291 m->numopts++; 1292 m->cursel = off; 1293 pset->num++; 1294 1295 /* now update edit menu to fit */ 1296 m->h = 0; 1297 resize_menu_height(m); 1298 1299 /* and directly invoke the partition editor for the new part */ 1300 edit_ptn(m, arg); 1301 1302 show_partition_adder(m, pset); 1303 1304 return -1; 1305 } 1306 1307 static void 1308 add_partition_adder(menudesc *m, struct partition_usage_set *pset) 1309 { 1310 struct part_usage_info *ninfo; 1311 menu_ent *nmenopts; 1312 size_t off; 1313 1314 ninfo = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos)); 1315 if (ninfo == NULL) 1316 return; 1317 pset->infos = ninfo; 1318 off = pset->parts->num_part+1; 1319 1320 nmenopts = realloc(m->opts, (m->numopts+1)*sizeof(*m->opts)); 1321 if (nmenopts == NULL) 1322 return; 1323 memmove(nmenopts+off+1, nmenopts+off, 1324 (m->numopts-off)*sizeof(*nmenopts)); 1325 memset(&nmenopts[off], 0, sizeof(nmenopts[off])); 1326 1327 nmenopts[off].opt_name = MSG_addpart; 1328 nmenopts[off].opt_flags = OPT_SUB; 1329 nmenopts[off].opt_action = edit_fspart_add; 1330 1331 m->opts = nmenopts; 1332 m->numopts++; 1333 pset->num++; 1334 } 1335 1336 static void 1337 remove_partition_adder(menudesc *m, struct partition_usage_set *pset) 1338 { 1339 size_t off; 1340 1341 off = pset->parts->num_part+1; 1342 memmove(m->opts+off, m->opts+off+1, 1343 (m->numopts-off-1)*sizeof(*m->opts)); 1344 m->numopts--; 1345 pset->num--; 1346 } 1347 1348 /* 1349 * Called whenever the "add a partition" option may need to be removed 1350 * or added 1351 */ 1352 static void 1353 show_partition_adder(menudesc *m, struct partition_usage_set *pset) 1354 { 1355 bool can_add_partition = pset->parts->pscheme->can_add_partition( 1356 pset->parts); 1357 bool part_adder_present = 1358 (m->opts[pset->parts->num_part].opt_flags & OPT_IGNORE) && 1359 (m->opts[pset->parts->num_part+1].opt_action == edit_fspart_add); 1360 1361 if (can_add_partition == part_adder_present) 1362 return; 1363 1364 if (can_add_partition) 1365 add_partition_adder(m, pset); 1366 else 1367 remove_partition_adder(m, pset); 1368 1369 /* now update edit menu to fit */ 1370 m->h = 0; 1371 resize_menu_height(m); 1372 } 1373 1374 static int 1375 edit_fspart_abort(menudesc *m, void *arg) 1376 { 1377 struct partition_usage_set *pset = arg; 1378 1379 pset->ok = false; 1380 return 1; 1381 } 1382 1383 /* 1384 * Check a disklabel. 1385 * If there are overlapping active partitions, 1386 * Ask the user if they want to edit the partition or give up. 1387 */ 1388 int 1389 edit_and_check_label(struct pm_devs *p, struct partition_usage_set *pset) 1390 { 1391 menu_ent *op; 1392 size_t cnt, i; 1393 bool may_add = pset->parts->pscheme->can_add_partition(pset->parts); 1394 bool may_edit_pack = 1395 pset->parts->pscheme->get_disk_pack_name != NULL && 1396 pset->parts->pscheme->set_disk_pack_name != NULL; 1397 1398 pset->menu_opts = calloc(pset->parts->num_part 1399 +3+may_add+may_edit_pack, 1400 sizeof *pset->menu_opts); 1401 if (pset->menu_opts == NULL) 1402 return 0; 1403 1404 op = pset->menu_opts; 1405 for (i = 0; i < pset->parts->num_part; i++) { 1406 op->opt_action = edit_ptn; 1407 op++; 1408 } 1409 /* separator line between partitions and actions */ 1410 op->opt_name = fspart_separator; 1411 op->opt_flags = OPT_IGNORE|OPT_NOSHORT; 1412 op++; 1413 1414 /* followed by new partition adder */ 1415 if (may_add) { 1416 op->opt_name = MSG_addpart; 1417 op->opt_flags = OPT_SUB; 1418 op->opt_action = edit_fspart_add; 1419 op++; 1420 } 1421 1422 /* and unit changer */ 1423 op->opt_name = MSG_askunits; 1424 op->opt_menu = MENU_sizechoice; 1425 op->opt_flags = OPT_SUB; 1426 op->opt_action = NULL; 1427 op++; 1428 1429 if (may_edit_pack) { 1430 op->opt_name = MSG_editpack; 1431 op->opt_flags = OPT_SUB; 1432 op->opt_action = edit_fspart_pack; 1433 op++; 1434 } 1435 1436 /* and abort option */ 1437 op->opt_name = MSG_cancel; 1438 op->opt_flags = OPT_EXIT; 1439 op->opt_action = edit_fspart_abort; 1440 op++; 1441 cnt = op - pset->menu_opts; 1442 assert(cnt == pset->parts->num_part+3+may_add+may_edit_pack); 1443 1444 pset->menu = new_menu(fspart_title, pset->menu_opts, cnt, 1445 0, -1, 0, 74, 1446 MC_ALWAYS_SCROLL|MC_NOBOX|MC_DFLTEXIT| 1447 MC_NOCLEAR|MC_CONTINUOUS, 1448 fmt_fspart_header, fmt_fspart_row, NULL, NULL, 1449 MSG_partition_sizes_ok); 1450 1451 if (pset->menu < 0) { 1452 free(pset->menu_opts); 1453 pset->menu_opts = NULL; 1454 return 0; 1455 } 1456 1457 p->current_cylsize = p->dlcylsize; 1458 1459 for (;;) { 1460 /* first give the user the option to edit the label... */ 1461 pset->ok = true; 1462 process_menu(pset->menu, pset); 1463 if (!pset->ok) { 1464 i = 0; 1465 break; 1466 } 1467 1468 /* User thinks the label is OK. */ 1469 i = verify_parts(pset); 1470 if (i == 1) 1471 continue; 1472 break; 1473 } 1474 free(pset->menu_opts); 1475 pset->menu_opts = NULL; 1476 free_menu(pset->menu); 1477 pset->menu = -1; 1478 1479 return i != 0; 1480 } 1481 1482 /* 1483 * strip trailing / to avoid confusion in path comparisions later 1484 */ 1485 void 1486 canonicalize_last_mounted(char *path) 1487 { 1488 char *p; 1489 1490 if (path == NULL) 1491 return; 1492 1493 if (strcmp(path, "/") == 0) 1494 return; /* in this case a "trailing" slash is allowed */ 1495 1496 for (;;) { 1497 p = strrchr(path, '/'); 1498 if (p == NULL) 1499 return; 1500 if (p[1] != 0) 1501 return; 1502 p[0] = 0; 1503 } 1504 } 1505 1506 /* 1507 * Try to get 'last mounted on' (or equiv) from fs superblock. 1508 */ 1509 const char * 1510 get_last_mounted(int fd, daddr_t partstart, uint *fs_type, uint *fs_sub_type, 1511 uint flags) 1512 { 1513 static char sblk[SBLOCKSIZE]; /* is this enough? */ 1514 struct fs *SB = (struct fs *)sblk; 1515 static const off_t sblocks[] = SBLOCKSEARCH; 1516 const off_t *sbp; 1517 const char *mnt = NULL; 1518 int len; 1519 1520 if (fd == -1) 1521 return ""; 1522 1523 if (fs_type) 1524 *fs_type = 0; 1525 if (fs_sub_type) 1526 *fs_sub_type = 0; 1527 1528 /* Check UFS1/2 (and hence LFS) superblock */ 1529 for (sbp = sblocks; mnt == NULL && *sbp != -1; sbp++) { 1530 if (pread(fd, sblk, sizeof sblk, 1531 (off_t)partstart * (off_t)512 + *sbp) != sizeof sblk) 1532 continue; 1533 1534 /* 1535 * If start of partition and allowed by flags check 1536 * for other fs types 1537 */ 1538 if (*sbp == 0 && (flags & GLM_MAYBE_FAT32) && 1539 sblk[0x42] == 0x29 && memcmp(sblk + 0x52, "FAT", 3) == 0) { 1540 /* Probably a FAT filesystem, report volume name */ 1541 size_t i; 1542 for (i = 0x51; i >= 0x47; i--) { 1543 if (sblk[i] != ' ') 1544 break; 1545 sblk[i] = 0; 1546 } 1547 sblk[0x52] = 0; 1548 if (fs_type) 1549 *fs_type = FS_MSDOS; 1550 if (fs_sub_type) 1551 *fs_sub_type = sblk[0x53]; 1552 return sblk + 0x47; 1553 } else if (*sbp == 0 && (flags & GLM_MAYBE_NTFS) && 1554 memcmp(sblk+3, "NTFS ", 8) == 0) { 1555 if (fs_type) 1556 *fs_type = FS_NTFS; 1557 if (fs_sub_type) 1558 *fs_sub_type = MBR_PTYPE_NTFS; 1559 /* XXX dig for volume name attribute ? */ 1560 return ""; 1561 } 1562 1563 if (!(flags & GLM_LIKELY_FFS)) 1564 continue; 1565 1566 /* Maybe we should validate the checksum??? */ 1567 switch (SB->fs_magic) { 1568 case FS_UFS1_MAGIC: 1569 case FS_UFS1_MAGIC_SWAPPED: 1570 if (!(SB->fs_old_flags & FS_FLAGS_UPDATED)) { 1571 if (*sbp == SBLOCK_UFS1) 1572 mnt = (const char *)SB->fs_fsmnt; 1573 } else { 1574 /* Check we have the main superblock */ 1575 if (SB->fs_sblockloc == *sbp) 1576 mnt = (const char *)SB->fs_fsmnt; 1577 } 1578 if (fs_type) 1579 *fs_type = FS_BSDFFS; 1580 if (fs_sub_type) 1581 *fs_sub_type = 1; 1582 continue; 1583 case FS_UFS2_MAGIC: 1584 case FS_UFS2_MAGIC_SWAPPED: 1585 /* Check we have the main superblock */ 1586 if (SB->fs_sblockloc == *sbp) { 1587 mnt = (const char *)SB->fs_fsmnt; 1588 if (fs_type) 1589 *fs_type = FS_BSDFFS; 1590 if (fs_sub_type) 1591 *fs_sub_type = 2; 1592 } 1593 continue; 1594 } 1595 } 1596 1597 if (mnt == NULL) 1598 return ""; 1599 1600 /* If sysinst mounted this last then strip prefix */ 1601 len = strlen(targetroot_mnt); 1602 if (memcmp(mnt, targetroot_mnt, len) == 0) { 1603 if (mnt[len] == 0) 1604 return "/"; 1605 if (mnt[len] == '/') 1606 return mnt + len; 1607 } 1608 return mnt; 1609 #undef SB 1610 } 1611 1612 /* Ask for a partition offset, check bounds and do the needed roundups */ 1613 daddr_t 1614 getpartoff(struct disk_partitions *parts, daddr_t defpartstart) 1615 { 1616 char defstart[24], isize[24], maxpart, minspace, maxspace, 1617 *prompt, *label_msg, valid_parts[4], valid_spaces[4], 1618 space_prompt[1024], *head, *hint_part, *hint_space, *tail; 1619 size_t num_freespace, spaces, ndx; 1620 struct disk_part_free_space *freespace; 1621 daddr_t i, localsizemult, ptn_alignment, min, max; 1622 part_id partn; 1623 struct disk_part_info info; 1624 const char *errmsg = NULL; 1625 1626 min = parts->disk_start; 1627 max = min + parts->disk_size; 1628 1629 /* upper bound on the number of free spaces, plus some slope */ 1630 num_freespace = parts->num_part * 2 + 5; 1631 freespace = calloc(num_freespace, sizeof(*freespace)); 1632 if (freespace == NULL) 1633 return -1; 1634 1635 ptn_alignment = parts->pscheme->get_part_alignment(parts); 1636 spaces = parts->pscheme->get_free_spaces(parts, freespace, 1637 num_freespace, max(sizemult, ptn_alignment), ptn_alignment, -1, 1638 defpartstart); 1639 1640 maxpart = 'a' + parts->num_part -1; 1641 if (parts->num_part > 1) { 1642 snprintf(valid_parts, sizeof valid_parts, "a-%c", maxpart); 1643 } else if (parts->num_part == 1) { 1644 snprintf(valid_parts, sizeof valid_parts, " %c", maxpart); 1645 } else { 1646 strcpy(valid_parts, "---"); 1647 } 1648 if (spaces > 1) { 1649 minspace = maxpart + 1; 1650 maxspace = minspace + spaces -1; 1651 snprintf(valid_spaces, sizeof valid_spaces, "%c-%c", minspace, 1652 maxspace); 1653 } else if (spaces == 1) { 1654 maxspace = minspace = maxpart + 1; 1655 snprintf(valid_spaces, sizeof valid_spaces, " %c", minspace); 1656 } else { 1657 minspace = 0; 1658 maxspace = 0; 1659 strcpy(valid_spaces, "---"); 1660 } 1661 1662 /* Add description of start/size to user prompt */ 1663 const char *mstr = msg_string(MSG_free_space_line); 1664 space_prompt[0] = 0; 1665 for (ndx = 0; ndx < spaces; ndx++) { 1666 char str_start[40], str_end[40], str_size[40], str_tag[4]; 1667 1668 sprintf(str_tag, "%c: ", minspace+(int)ndx); 1669 sprintf(str_start, "%" PRIu64, freespace[ndx].start / sizemult); 1670 sprintf(str_end, "%" PRIu64, 1671 (freespace[ndx].start + freespace[ndx].size) / sizemult); 1672 sprintf(str_size, "%" PRIu64, freespace[ndx].size / sizemult); 1673 const char *args[4] = { str_start, str_end, str_size, 1674 multname }; 1675 char *line = str_arg_subst(mstr, 4, args); 1676 strlcat(space_prompt, str_tag, sizeof(space_prompt)); 1677 size_t len = strlcat(space_prompt, line, sizeof(space_prompt)); 1678 free (line); 1679 if (len >= sizeof space_prompt) 1680 break; 1681 } 1682 1683 const char *args[] = { valid_parts, valid_spaces, multname }; 1684 hint_part = NULL; 1685 hint_space = NULL; 1686 head = str_arg_subst(msg_string(MSG_label_offset_head), 1687 __arraycount(args), args); 1688 if (parts->num_part) 1689 hint_part = str_arg_subst(msg_string( 1690 MSG_label_offset_part_hint), __arraycount(args), args); 1691 if (spaces) 1692 hint_space = str_arg_subst(msg_string( 1693 MSG_label_offset_space_hint), __arraycount(args), args); 1694 tail = str_arg_subst(msg_string(MSG_label_offset_tail), 1695 __arraycount(args), args); 1696 1697 if (hint_part && hint_space) 1698 asprintf(&label_msg, "%s\n%s\n%s\n\n%s\n%s", 1699 head, hint_part, hint_space, space_prompt, tail); 1700 else if (hint_part) 1701 asprintf(&label_msg, "%s\n%s\n\n%s", 1702 head, hint_part, tail); 1703 else if (hint_space) 1704 asprintf(&label_msg, "%s\n%s\n\n%s\n%s", 1705 head, hint_space, space_prompt, tail); 1706 else 1707 asprintf(&label_msg, "%s\n\n%s", 1708 head, tail); 1709 free(head); free(hint_part); free(hint_space); free(tail); 1710 1711 localsizemult = sizemult; 1712 errmsg = NULL; 1713 for (;;) { 1714 snprintf(defstart, sizeof defstart, "%" PRIu64, 1715 defpartstart/sizemult); 1716 if (errmsg != NULL && errmsg[0] != 0) 1717 asprintf(&prompt, "%s\n\n%s", errmsg, label_msg); 1718 else 1719 prompt = label_msg; 1720 msg_prompt_win(prompt, -1, 13, 70, -1, 1721 (defpartstart > 0) ? defstart : NULL, isize, sizeof isize); 1722 if (label_msg != prompt) 1723 free(prompt); 1724 if (strcmp(defstart, isize) == 0) { 1725 /* Don't do rounding if default accepted */ 1726 i = defpartstart; 1727 break; 1728 } 1729 if (isize[1] == '\0' && isize[0] >= 'a' && 1730 isize[0] <= maxpart) { 1731 partn = isize[0] - 'a'; 1732 if (parts->pscheme->get_part_info(parts, partn, 1733 &info)) { 1734 i = info.start + info.size; 1735 localsizemult = 1; 1736 } else { 1737 errmsg = msg_string(MSG_invalid_sector_number); 1738 continue; 1739 } 1740 } else if (isize[1] == '\0' && isize[0] >= minspace && 1741 isize[0] <= maxspace) { 1742 ndx = isize[0] - minspace; 1743 i = freespace[ndx].start; 1744 localsizemult = 1; 1745 } else if (atoi(isize) == -1) { 1746 i = min; 1747 localsizemult = 1; 1748 } else { 1749 i = parse_disk_pos(isize, &localsizemult, pm->dlcylsize, NULL); 1750 if (i < 0) { 1751 errmsg = msg_string(MSG_invalid_sector_number); 1752 continue; 1753 } 1754 } 1755 /* round to cylinder size if localsizemult != 1 */ 1756 i = NUMSEC(i, localsizemult, pm->dlcylsize); 1757 /* Adjust to start of slice if needed */ 1758 if ((i < min && (min - i) < localsizemult) || 1759 (i > min && (i - min) < localsizemult)) { 1760 i = min; 1761 } 1762 if (max == 0 || i <= max) 1763 break; 1764 errmsg = msg_string(MSG_startoutsidedisk); 1765 } 1766 free(label_msg); 1767 free(freespace); 1768 1769 return i; 1770 } 1771 1772 1773 /* Ask for a partition size, check bounds and do the needed roundups */ 1774 daddr_t 1775 getpartsize(struct disk_partitions *parts, daddr_t partstart, daddr_t dflt) 1776 { 1777 char dsize[24], isize[24], max_size[24], maxpartc, valid_parts[4], 1778 *label_msg, *prompt, *head, *hint, *tail; 1779 const char *errmsg = NULL; 1780 daddr_t i, partend, localsizemult, max, max_r, dflt_r; 1781 struct disk_part_info info; 1782 part_id partn; 1783 1784 max = parts->pscheme->max_free_space_at(parts, partstart); 1785 1786 /* We need to keep both the unrounded and rounded (_r) max and dflt */ 1787 dflt_r = (partstart + dflt) / sizemult - partstart / sizemult; 1788 if (max == dflt) 1789 max_r = dflt_r; 1790 else 1791 max_r = max / sizemult; 1792 /* the partition may have been moved and now not fit any longer */ 1793 if (dflt > max) 1794 dflt = max; 1795 if (dflt_r > max_r) 1796 dflt_r = max_r; 1797 1798 snprintf(max_size, sizeof max_size, "%" PRIu64, max_r); 1799 1800 maxpartc = 'a' + parts->num_part -1; 1801 if (parts->num_part > 1) { 1802 snprintf(valid_parts, sizeof valid_parts, "a-%c", maxpartc); 1803 } else if (parts->num_part == 1) { 1804 snprintf(valid_parts, sizeof valid_parts, " %c", maxpartc); 1805 } else { 1806 strcpy(valid_parts, "---"); 1807 } 1808 1809 const char *args[] = { valid_parts, max_size, multname }; 1810 hint = NULL; 1811 head = str_arg_subst(msg_string(MSG_label_size_head), 1812 __arraycount(args), args); 1813 if (parts->num_part) 1814 hint = str_arg_subst(msg_string(MSG_label_size_part_hint), 1815 __arraycount(args), args); 1816 tail = str_arg_subst(msg_string(MSG_label_size_tail), 1817 __arraycount(args), args); 1818 1819 if (hint != NULL) 1820 asprintf(&label_msg, "%s\n%s\n\n%s", head, hint, tail); 1821 else 1822 asprintf(&label_msg, "%s\n\n%s", head, tail); 1823 free(head); free(hint); free(tail); 1824 1825 localsizemult = sizemult; 1826 i = -1; 1827 for (;;) { 1828 snprintf(dsize, sizeof dsize, "%" PRIu64, dflt_r); 1829 1830 if (errmsg != NULL && errmsg[0] != 0) 1831 asprintf(&prompt, "%s\n\n%s", errmsg, label_msg); 1832 else 1833 prompt = label_msg; 1834 msg_prompt_win(prompt, -1, 12, 70, -1, 1835 (dflt != 0) ? dsize : 0, isize, sizeof isize); 1836 if (prompt != label_msg) 1837 free(prompt); 1838 1839 if (strcmp(isize, dsize) == 0) { 1840 free(label_msg); 1841 return dflt; 1842 } 1843 if (parts->num_part && isize[1] == '\0' && isize[0] >= 'a' && 1844 isize[0] <= maxpartc) { 1845 partn = isize[0] - 'a'; 1846 if (parts->pscheme->get_part_info(parts, partn, 1847 &info)) { 1848 i = info.start - partstart -1; 1849 localsizemult = 1; 1850 max_r = max; 1851 } 1852 } else if (atoi(isize) == -1) { 1853 i = max; 1854 localsizemult = 1; 1855 max_r = max; 1856 } else { 1857 i = parse_disk_pos(isize, &localsizemult, 1858 pm->dlcylsize, NULL); 1859 if (localsizemult != sizemult) 1860 max_r = max; 1861 } 1862 if (i < 0) { 1863 errmsg = msg_string(MSG_Invalid_numeric); 1864 continue; 1865 } else if (i > max_r) { 1866 errmsg = msg_string(MSG_Too_large); 1867 continue; 1868 } 1869 /* 1870 * partend is aligned to a cylinder if localsizemult 1871 * is not 1 sector 1872 */ 1873 partend = NUMSEC((partstart + i*localsizemult) / localsizemult, 1874 localsizemult, pm->dlcylsize); 1875 /* Align to end-of-disk or end-of-slice if close enough */ 1876 if (partend > (pm->dlsize - sizemult) 1877 && partend < (pm->dlsize + sizemult)) 1878 partend = pm->dlsize; 1879 if (partend > (partstart + max - sizemult) 1880 && partend < (partstart + max + sizemult)) 1881 partend = partstart + max; 1882 /* sanity checks */ 1883 if (partend > (partstart + pm->dlsize)) { 1884 partend = pm->dlsize; 1885 errmsg = msg_string(MSG_endoutsidedisk); 1886 continue; 1887 } 1888 free(label_msg); 1889 if (partend < partstart) 1890 return 0; 1891 return (partend - partstart); 1892 } 1893 /* NOTREACHED */ 1894 } 1895 1896 /* 1897 * convert a string to a number of sectors, with a possible unit 1898 * 150M = 150 Megabytes 1899 * 2000c = 2000 cylinders 1900 * 150256s = 150256 sectors 1901 * Without units, use the default (sizemult). 1902 * returns the raw input value, and the unit used. Caller needs to multiply! 1903 * On invalid inputs, returns -1. 1904 */ 1905 daddr_t 1906 parse_disk_pos( 1907 const char *str, 1908 daddr_t *localsizemult, 1909 daddr_t cyl_size, 1910 bool *extend_this) 1911 { 1912 daddr_t val; 1913 char *cp; 1914 bool mult_found; 1915 1916 if (str[0] == '\0') { 1917 return -1; 1918 } 1919 val = strtoull(str, &cp, 10); 1920 mult_found = false; 1921 if (extend_this) 1922 *extend_this = false; 1923 while (*cp != 0) { 1924 if (*cp == 'G' || *cp == 'g') { 1925 if (mult_found) 1926 return -1; 1927 *localsizemult = GIG / pm->sectorsize; 1928 goto next; 1929 } 1930 if (*cp == 'M' || *cp == 'm') { 1931 if (mult_found) 1932 return -1; 1933 *localsizemult = MEG / pm->sectorsize; 1934 goto next; 1935 } 1936 if (*cp == 'c' || *cp == 'C') { 1937 if (mult_found) 1938 return -1; 1939 *localsizemult = pm->dlcylsize; 1940 goto next; 1941 } 1942 if (*cp == 's' || *cp == 'S') { 1943 if (mult_found) 1944 return -1; 1945 *localsizemult = 1; 1946 goto next; 1947 } 1948 if (*cp == '+' && extend_this) { 1949 *extend_this = true; 1950 cp++; 1951 break; 1952 } 1953 1954 /* not a known unit */ 1955 return -1; 1956 1957 next: 1958 mult_found = true; 1959 cp++; 1960 continue; 1961 } 1962 if (*cp != 0) 1963 return -1; 1964 1965 return val; 1966 } 1967