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