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