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