1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * This file contains functions to implement the partition menu commands. 27 */ 28 #include "global.h" 29 #include <stdlib.h> 30 #include <string.h> 31 32 #include "partition.h" 33 #include "menu_partition.h" 34 #include "menu_command.h" 35 #include "misc.h" 36 #include "param.h" 37 38 static void nspaces(int); 39 static int ndigits(uint64_t); 40 41 /* 42 * This routine implements the 'a' command. It changes the 'a' partition. 43 */ 44 int 45 p_apart(void) 46 { 47 48 change_partition(0); 49 return (0); 50 } 51 52 /* 53 * This routine implements the 'b' command. It changes the 'b' partition. 54 */ 55 int 56 p_bpart(void) 57 { 58 59 change_partition(1); 60 return (0); 61 } 62 63 /* 64 * This routine implements the 'c' command. It changes the 'c' partition. 65 */ 66 int 67 p_cpart(void) 68 { 69 70 change_partition(2); 71 return (0); 72 } 73 74 /* 75 * This routine implements the 'd' command. It changes the 'd' partition. 76 */ 77 int 78 p_dpart(void) 79 { 80 81 change_partition(3); 82 return (0); 83 } 84 85 /* 86 * This routine implements the 'e' command. It changes the 'e' partition. 87 */ 88 int 89 p_epart(void) 90 { 91 92 change_partition(4); 93 return (0); 94 } 95 96 /* 97 * This routine implements the 'f' command. It changes the 'f' partition. 98 */ 99 int 100 p_fpart(void) 101 { 102 103 change_partition(5); 104 return (0); 105 } 106 107 /* 108 * This routine implements the 'g' command. It changes the 'g' partition. 109 */ 110 int 111 p_gpart(void) 112 { 113 114 change_partition(6); 115 return (0); 116 } 117 118 /* 119 * This routine implements the 'h' command. It changes the 'h' partition. 120 */ 121 int 122 p_hpart(void) 123 { 124 125 change_partition(7); 126 return (0); 127 } 128 129 /* 130 * This routine implements the 'i' command. It is valid only for EFI 131 * labeled disks. This can be used only in expert mode. 132 */ 133 int 134 p_ipart(void) 135 { 136 change_partition(8); 137 return (0); 138 } 139 140 #if defined(i386) 141 /* 142 * This routine implements the 'j' command. It changes the 'j' partition. 143 */ 144 int 145 p_jpart(void) 146 { 147 148 change_partition(9); 149 return (0); 150 } 151 #endif /* defined(i386) */ 152 153 int 154 p_expand(void) 155 { 156 uint64_t delta; 157 uint_t nparts; 158 struct dk_gpt *efi_label = cur_parts->etoc; 159 160 if (cur_parts->etoc->efi_altern_lba == 1 || 161 (cur_parts->etoc->efi_altern_lba >= 162 cur_parts->etoc->efi_last_lba)) { 163 err_print("Warning: No expanded capacity is found.\n"); 164 return (0); 165 } 166 167 delta = efi_label->efi_last_lba - efi_label->efi_altern_lba; 168 nparts = efi_label->efi_nparts; 169 170 enter_critical(); 171 efi_label->efi_parts[nparts - 1].p_start += delta; 172 efi_label->efi_last_u_lba += delta; 173 efi_label->efi_altern_lba = cur_parts->etoc->efi_last_lba; 174 exit_critical(); 175 176 fmt_print("The expanded capacity is added to the unallocated space.\n"); 177 return (0); 178 } 179 180 /* 181 * This routine implements the 'select' command. It allows the user 182 * to make a pre-defined partition map the current map. 183 */ 184 int 185 p_select(void) 186 { 187 struct partition_info *pptr, *parts; 188 u_ioparam_t ioparam; 189 int i, index, deflt, *defltptr = NULL; 190 blkaddr_t b_cylno; 191 #if defined(i386) 192 blkaddr_t cyl_offset; 193 #endif 194 195 parts = cur_dtype->dtype_plist; 196 /* 197 * If there are no pre-defined maps for this disk type, it's 198 * an error. 199 */ 200 if (parts == NULL) { 201 err_print("No defined partition tables.\n"); 202 return (-1); 203 } 204 205 /* 206 * Loop through the pre-defined maps and list them by name. If 207 * the current map is one of them, make it the default. If any 208 * the maps are unnamed, label them as such. 209 */ 210 for (i = 0, pptr = parts; pptr != NULL; pptr = pptr->pinfo_next) { 211 if (cur_parts == pptr) { 212 deflt = i; 213 defltptr = &deflt; 214 } 215 if (pptr->pinfo_name == NULL) 216 fmt_print(" %d. unnamed\n", i++); 217 else 218 fmt_print(" %d. %s\n", i++, pptr->pinfo_name); 219 } 220 ioparam.io_bounds.lower = 0; 221 ioparam.io_bounds.upper = i - 1; 222 /* 223 * Ask which map should be made current. 224 */ 225 index = input(FIO_INT, "Specify table (enter its number)", ':', 226 &ioparam, defltptr, DATA_INPUT); 227 for (i = 0, pptr = parts; i < index; i++, pptr = pptr->pinfo_next) 228 ; 229 if (cur_label == L_TYPE_EFI) { 230 enter_critical(); 231 cur_disk->disk_parts = cur_parts = pptr; 232 exit_critical(); 233 fmt_print("\n"); 234 return (0); 235 } 236 #if defined(i386) 237 /* 238 * Adjust for the boot and alternate sectors partition - assuming that 239 * the alternate sectors partition physical location follows 240 * immediately the boot partition and partition sizes are 241 * expressed in multiple of cylinder size. 242 */ 243 cyl_offset = pptr->pinfo_map[I_PARTITION].dkl_cylno + 1; 244 if (pptr->pinfo_map[J_PARTITION].dkl_nblk != 0) { 245 cyl_offset = pptr->pinfo_map[J_PARTITION].dkl_cylno + 246 ((pptr->pinfo_map[J_PARTITION].dkl_nblk + 247 (spc() - 1)) / spc()); 248 } 249 #else /* !defined(i386) */ 250 251 b_cylno = 0; 252 253 #endif /* defined(i386) */ 254 255 /* 256 * Before we blow the current map away, do some limits checking. 257 */ 258 for (i = 0; i < NDKMAP; i++) { 259 260 #if defined(i386) 261 if (i == I_PARTITION || i == J_PARTITION || i == C_PARTITION) { 262 b_cylno = 0; 263 } else if (pptr->pinfo_map[i].dkl_nblk == 0) { 264 /* 265 * Always accept starting cyl 0 if the size is 0 also 266 */ 267 b_cylno = 0; 268 } else { 269 b_cylno = cyl_offset; 270 } 271 #endif /* defined(i386) */ 272 if (pptr->pinfo_map[i].dkl_cylno < b_cylno || 273 pptr->pinfo_map[i].dkl_cylno > (ncyl-1)) { 274 err_print("partition %c: starting cylinder %d is out " 275 "of range\n", (PARTITION_BASE + i), 276 pptr->pinfo_map[i].dkl_cylno); 277 return (0); 278 } 279 if (pptr->pinfo_map[i].dkl_nblk > ((ncyl - 280 pptr->pinfo_map[i].dkl_cylno) * spc())) { 281 err_print( 282 "partition %c: specified # of blocks, %u, " 283 "is out of range\n", 284 (PARTITION_BASE+i), 285 pptr->pinfo_map[i].dkl_nblk); 286 return (0); 287 } 288 } 289 /* 290 * Lock out interrupts so the lists don't get mangled. 291 */ 292 enter_critical(); 293 /* 294 * If the old current map is unnamed, delete it. 295 */ 296 if (cur_parts != NULL && cur_parts != pptr && 297 cur_parts->pinfo_name == NULL) 298 delete_partition(cur_parts); 299 /* 300 * Make the selected map current. 301 */ 302 cur_disk->disk_parts = cur_parts = pptr; 303 304 #if defined(_SUNOS_VTOC_16) 305 for (i = 0; i < NDKMAP; i++) { 306 cur_parts->vtoc.v_part[i].p_start = 307 (blkaddr_t)(cur_parts->pinfo_map[i].dkl_cylno * 308 (nhead * nsect)); 309 cur_parts->vtoc.v_part[i].p_size = 310 (blkaddr_t)cur_parts->pinfo_map[i].dkl_nblk; 311 } 312 #endif /* defined(_SUNOS_VTOC_16) */ 313 314 exit_critical(); 315 fmt_print("\n"); 316 return (0); 317 } 318 319 /* 320 * This routine implements the 'name' command. It allows the user 321 * to name the current partition map. If the map was already named, 322 * the name is changed. Once a map is named, the values of the partitions 323 * cannot be changed. Attempts to change them will cause another map 324 * to be created. 325 */ 326 int 327 p_name(void) 328 { 329 char *name; 330 331 /* 332 * check if there exists a partition table for the disk. 333 */ 334 if (cur_parts == NULL) { 335 err_print("Current Disk has no partition table.\n"); 336 return (-1); 337 } 338 339 340 /* 341 * Ask for the name. Note that the input routine will malloc 342 * space for the name since we are using the OSTR input type. 343 */ 344 name = (char *)(uintptr_t)input(FIO_OSTR, 345 "Enter table name (remember quotes)", 346 ':', NULL, NULL, DATA_INPUT); 347 /* 348 * Lock out interrupts. 349 */ 350 enter_critical(); 351 /* 352 * If it was already named, destroy the old name. 353 */ 354 if (cur_parts->pinfo_name != NULL) 355 destroy_data(cur_parts->pinfo_name); 356 /* 357 * Set the name. 358 */ 359 cur_parts->pinfo_name = name; 360 exit_critical(); 361 fmt_print("\n"); 362 return (0); 363 } 364 365 366 /* 367 * This routine implements the 'print' command. It lists the values 368 * for all the partitions in the current partition map. 369 */ 370 int 371 p_print(void) 372 { 373 /* 374 * check if there exists a partition table for the disk. 375 */ 376 if (cur_parts == NULL) { 377 err_print("Current Disk has no partition table.\n"); 378 return (-1); 379 } 380 381 /* 382 * Print the volume name, if it appears to be set 383 */ 384 if (chk_volname(cur_disk)) { 385 fmt_print("Volume: "); 386 print_volname(cur_disk); 387 fmt_print("\n"); 388 } 389 /* 390 * Print the name of the current map. 391 */ 392 if ((cur_parts->pinfo_name != NULL) && (cur_label == L_TYPE_SOLARIS)) { 393 fmt_print("Current partition table (%s):\n", 394 cur_parts->pinfo_name); 395 fmt_print("Total disk cylinders available: %d + %d " 396 "(reserved cylinders)\n\n", ncyl, acyl); 397 } else if (cur_label == L_TYPE_SOLARIS) { 398 fmt_print("Current partition table (unnamed):\n"); 399 fmt_print("Total disk cylinders available: %d + %d " 400 "(reserved cylinders)\n\n", ncyl, acyl); 401 } else if (cur_label == L_TYPE_EFI) { 402 unsigned reserved; 403 404 reserved = efi_reserved_sectors(cur_parts->etoc); 405 fmt_print("Current partition table (%s):\n", 406 cur_parts->pinfo_name != NULL ? 407 cur_parts->pinfo_name : "unnamed"); 408 fmt_print("Total disk sectors available: %llu + %u " 409 "(reserved sectors)\n\n", 410 cur_parts->etoc->efi_last_u_lba - reserved - 411 cur_parts->etoc->efi_first_u_lba + 1, reserved); 412 } 413 414 415 /* 416 * Print the partition map itself 417 */ 418 print_map(cur_parts); 419 return (0); 420 } 421 422 423 /* 424 * Print a partition map 425 */ 426 void 427 print_map(struct partition_info *map) 428 { 429 int i; 430 int want_header; 431 struct dk_gpt *vtoc64; 432 433 if (cur_label == L_TYPE_EFI) { 434 vtoc64 = map->etoc; 435 want_header = 1; 436 for (i = 0; i < vtoc64->efi_nparts; i++) { 437 /* 438 * we want to print partitions above 7 in expert mode only 439 * or if the partition is reserved 440 */ 441 if (i >= 7 && !expert_mode && 442 ((int)vtoc64->efi_parts[i].p_tag != 443 V_RESERVED)) { 444 continue; 445 } 446 447 print_efi_partition(vtoc64, i, want_header); 448 want_header = 0; 449 } 450 fmt_print("\n"); 451 return; 452 } 453 /* 454 * Loop through each partition, printing the header 455 * the first time. 456 */ 457 want_header = 1; 458 for (i = 0; i < NDKMAP; i++) { 459 if (i > 9) { 460 break; 461 } 462 print_partition(map, i, want_header); 463 want_header = 0; 464 } 465 466 fmt_print("\n"); 467 } 468 469 /* 470 * Print out one line of partition information, 471 * with optional header for EFI type disks. 472 */ 473 /*ARGSUSED*/ 474 void 475 print_efi_partition(struct dk_gpt *map, int partnum, int want_header) 476 { 477 int ncyl2_digits = 0; 478 float scaled; 479 char *s; 480 uint64_t secsize; 481 482 ncyl2_digits = ndigits(map->efi_last_u_lba); 483 if (want_header) { 484 fmt_print("Part "); 485 fmt_print("Tag Flag "); 486 fmt_print("First Sector"); 487 nspaces(ncyl2_digits); 488 fmt_print("Size"); 489 nspaces(ncyl2_digits); 490 fmt_print("Last Sector\n"); 491 } 492 493 fmt_print(" %d ", partnum); 494 s = find_string(ptag_choices, (int)map->efi_parts[partnum].p_tag); 495 if (s == NULL) 496 s = "-"; 497 nspaces(10 - (int)strlen(s)); 498 fmt_print("%s", s); 499 500 s = find_string(pflag_choices, (int)map->efi_parts[partnum].p_flag); 501 if (s == NULL) 502 s = "-"; 503 nspaces(6 - (int)strlen(s)); 504 fmt_print("%s", s); 505 506 nspaces(2); 507 508 secsize = map->efi_parts[partnum].p_size; 509 if (secsize == 0) { 510 fmt_print("%16llu", map->efi_parts[partnum].p_start); 511 nspaces(ncyl2_digits); 512 fmt_print(" 0 "); 513 } else { 514 fmt_print("%16llu", map->efi_parts[partnum].p_start); 515 scaled = bn2mb(secsize); 516 nspaces(ncyl2_digits - 5); 517 if (scaled >= (float)1024.0 * 1024) { 518 fmt_print("%8.2fTB", scaled/((float)1024.0 * 1024)); 519 } else if (scaled >= (float)1024.0) { 520 fmt_print("%8.2fGB", scaled/(float)1024.0); 521 } else { 522 fmt_print("%8.2fMB", scaled); 523 } 524 } 525 nspaces(ncyl2_digits); 526 if ((map->efi_parts[partnum].p_start + secsize - 1) == UINT_MAX64) { 527 fmt_print(" 0 \n"); 528 } else { 529 fmt_print(" %llu \n", 530 map->efi_parts[partnum].p_start + secsize - 1); 531 } 532 } 533 534 /* 535 * Print out one line of partition information, 536 * with optional header. 537 */ 538 /*ARGSUSED*/ 539 void 540 print_partition(struct partition_info *pinfo, int partnum, int want_header) 541 { 542 int i; 543 blkaddr_t nblks; 544 int cyl1; 545 int cyl2; 546 float scaled; 547 int maxcyl2; 548 int ncyl2_digits; 549 char *s; 550 blkaddr_t maxnblks = 0; 551 blkaddr_t len; 552 553 /* 554 * To align things nicely, we need to know the maximum 555 * width of the number of cylinders field. 556 */ 557 maxcyl2 = 0; 558 for (i = 0; i < NDKMAP; i++) { 559 nblks = (uint_t)pinfo->pinfo_map[i].dkl_nblk; 560 cyl1 = pinfo->pinfo_map[i].dkl_cylno; 561 cyl2 = cyl1 + (nblks / spc()) - 1; 562 if (nblks > 0) { 563 maxcyl2 = max(cyl2, maxcyl2); 564 maxnblks = max(nblks, maxnblks); 565 } 566 } 567 /* 568 * Get the number of digits required 569 */ 570 ncyl2_digits = ndigits(maxcyl2); 571 572 /* 573 * Print the header, if necessary 574 */ 575 if (want_header) { 576 fmt_print("Part "); 577 fmt_print("Tag Flag "); 578 fmt_print("Cylinders"); 579 nspaces(ncyl2_digits); 580 fmt_print(" Size Blocks\n"); 581 } 582 583 /* 584 * Print the partition information 585 */ 586 nblks = pinfo->pinfo_map[partnum].dkl_nblk; 587 cyl1 = pinfo->pinfo_map[partnum].dkl_cylno; 588 cyl2 = cyl1 + (nblks / spc()) - 1; 589 590 fmt_print(" %x ", partnum); 591 592 /* 593 * Print the partition tag. If invalid, print - 594 */ 595 s = find_string(ptag_choices, (int)pinfo->vtoc.v_part[partnum].p_tag); 596 if (s == NULL) 597 s = "-"; 598 nspaces(10 - (int)strlen(s)); 599 fmt_print("%s", s); 600 601 /* 602 * Print the partition flag. If invalid print - 603 */ 604 s = find_string(pflag_choices, (int)pinfo->vtoc.v_part[partnum].p_flag); 605 if (s == NULL) 606 s = "-"; 607 nspaces(6 - (int)strlen(s)); 608 fmt_print("%s", s); 609 610 nspaces(2); 611 612 if (nblks == 0) { 613 fmt_print("%6d ", cyl1); 614 nspaces(ncyl2_digits); 615 fmt_print(" 0 "); 616 } else { 617 fmt_print("%6d - ", cyl1); 618 nspaces(ncyl2_digits - ndigits(cyl2)); 619 fmt_print("%d ", cyl2); 620 scaled = bn2mb(nblks); 621 if (scaled > (float)1024.0 * 1024.0) { 622 fmt_print("%8.2fTB ", 623 scaled/((float)1024.0 * 1024.0)); 624 } else if (scaled > (float)1024.0) { 625 fmt_print("%8.2fGB ", scaled/(float)1024.0); 626 } else { 627 fmt_print("%8.2fMB ", scaled); 628 } 629 } 630 fmt_print("("); 631 pr_dblock(fmt_print, nblks); 632 fmt_print(")"); 633 634 nspaces(ndigits(maxnblks/spc()) - ndigits(nblks/spc())); 635 /* 636 * Allocates size of the printf format string. 637 * ndigits(ndigits(maxblks)) gives the byte size of 638 * the printf width field for maxnblks. 639 */ 640 len = strlen(" %") + ndigits(ndigits(maxnblks)) + strlen("d\n") + 1; 641 s = zalloc(len); 642 (void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n"); 643 fmt_print(s, nblks); 644 (void) free(s); 645 } 646 647 648 /* 649 * Return true if a disk has a volume name 650 */ 651 int 652 chk_volname(struct disk_info *disk) 653 { 654 return (disk->v_volume[0] != 0); 655 } 656 657 658 /* 659 * Print the volume name, if it appears to be set 660 */ 661 void 662 print_volname(struct disk_info *disk) 663 { 664 int i; 665 char *p; 666 667 p = disk->v_volume; 668 for (i = 0; i < LEN_DKL_VVOL; i++, p++) { 669 if (*p == 0) 670 break; 671 fmt_print("%c", *p); 672 } 673 } 674 675 676 /* 677 * Print a number of spaces 678 */ 679 static void 680 nspaces(int n) 681 { 682 while (n-- > 0) 683 fmt_print(" "); 684 } 685 686 /* 687 * Return the number of digits required to print a number 688 */ 689 static int 690 ndigits(uint64_t n) 691 { 692 int i; 693 694 i = 0; 695 while (n > 0) { 696 n /= 10; 697 i++; 698 } 699 700 return (i == 0 ? 1 : i); 701 } 702