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