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