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