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