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 2009 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 > ((ncyl - 293 pptr->pinfo_map[i].dkl_cylno) * spc())) { 294 err_print( 295 "partition %c: specified # of blocks, %u, " 296 "is out of range\n", 297 (PARTITION_BASE+i), 298 pptr->pinfo_map[i].dkl_nblk); 299 return (0); 300 } 301 } 302 /* 303 * Lock out interrupts so the lists don't get mangled. 304 */ 305 enter_critical(); 306 /* 307 * If the old current map is unnamed, delete it. 308 */ 309 if (cur_parts != NULL && cur_parts != pptr && 310 cur_parts->pinfo_name == NULL) 311 delete_partition(cur_parts); 312 /* 313 * Make the selected map current. 314 */ 315 cur_disk->disk_parts = cur_parts = pptr; 316 317 #if defined(_SUNOS_VTOC_16) 318 for (i = 0; i < NDKMAP; i++) { 319 cur_parts->vtoc.v_part[i].p_start = 320 (blkaddr_t)(cur_parts->pinfo_map[i].dkl_cylno * 321 (nhead * nsect)); 322 cur_parts->vtoc.v_part[i].p_size = 323 (blkaddr_t)cur_parts->pinfo_map[i].dkl_nblk; 324 } 325 #endif /* defined(_SUNOS_VTOC_16) */ 326 327 exit_critical(); 328 fmt_print("\n"); 329 return (0); 330 } 331 332 /* 333 * This routine implements the 'name' command. It allows the user 334 * to name the current partition map. If the map was already named, 335 * the name is changed. Once a map is named, the values of the partitions 336 * cannot be changed. Attempts to change them will cause another map 337 * to be created. 338 */ 339 int 340 p_name() 341 { 342 char *name; 343 344 /* 345 * check if there exists a partition table for the disk. 346 */ 347 if (cur_parts == NULL) { 348 err_print("Current Disk has no partition table.\n"); 349 return (-1); 350 } 351 352 353 /* 354 * Ask for the name. Note that the input routine will malloc 355 * space for the name since we are using the OSTR input type. 356 */ 357 name = (char *)(uintptr_t)input(FIO_OSTR, 358 "Enter table name (remember quotes)", 359 ':', (u_ioparam_t *)NULL, (int *)NULL, DATA_INPUT); 360 /* 361 * Lock out interrupts. 362 */ 363 enter_critical(); 364 /* 365 * If it was already named, destroy the old name. 366 */ 367 if (cur_parts->pinfo_name != NULL) 368 destroy_data(cur_parts->pinfo_name); 369 /* 370 * Set the name. 371 */ 372 cur_parts->pinfo_name = name; 373 exit_critical(); 374 fmt_print("\n"); 375 return (0); 376 } 377 378 379 /* 380 * This routine implements the 'print' command. It lists the values 381 * for all the partitions in the current partition map. 382 */ 383 int 384 p_print() 385 { 386 /* 387 * check if there exists a partition table for the disk. 388 */ 389 if (cur_parts == NULL) { 390 err_print("Current Disk has no partition table.\n"); 391 return (-1); 392 } 393 394 /* 395 * Print the volume name, if it appears to be set 396 */ 397 if (chk_volname(cur_disk)) { 398 fmt_print("Volume: "); 399 print_volname(cur_disk); 400 fmt_print("\n"); 401 } 402 /* 403 * Print the name of the current map. 404 */ 405 if ((cur_parts->pinfo_name != NULL) && (cur_label == L_TYPE_SOLARIS)) { 406 fmt_print("Current partition table (%s):\n", 407 cur_parts->pinfo_name); 408 fmt_print( 409 "Total disk cylinders available: %d + %d (reserved cylinders)\n\n", ncyl, acyl); 410 } else if ((cur_label == L_TYPE_EFI) && (cur_parts->pinfo_name != 411 NULL)) { 412 fmt_print("Current partition table (%s):\n", 413 cur_parts->pinfo_name); 414 fmt_print( 415 "Total disk sectors available: %llu + %d (reserved sectors)\n\n", 416 cur_parts->etoc->efi_last_u_lba - EFI_MIN_RESV_SIZE, 417 EFI_MIN_RESV_SIZE); 418 } else if (cur_label == L_TYPE_SOLARIS) { 419 fmt_print("Current partition table (unnamed):\n"); 420 fmt_print( 421 "Total disk cylinders available: %d + %d (reserved cylinders)\n\n", ncyl, acyl); 422 } else if (cur_label == L_TYPE_EFI) { 423 fmt_print("Current partition table (unnamed):\n"); 424 fmt_print( 425 "Total disk sectors available: %llu + %d (reserved sectors)\n\n", 426 cur_parts->etoc->efi_last_u_lba - EFI_MIN_RESV_SIZE, 427 EFI_MIN_RESV_SIZE); 428 } 429 430 431 /* 432 * Print the partition map itself 433 */ 434 print_map(cur_parts); 435 return (0); 436 } 437 438 439 /* 440 * Print a partition map 441 */ 442 void 443 print_map(struct partition_info *map) 444 { 445 int i; 446 int want_header; 447 struct dk_gpt *vtoc64; 448 449 if (cur_label == L_TYPE_EFI) { 450 vtoc64 = map->etoc; 451 want_header = 1; 452 for (i = 0; i < vtoc64->efi_nparts; i++) { 453 /* 454 * we want to print partitions above 7 in expert mode only 455 * or if the partition is reserved 456 */ 457 if (i >= 7 && !expert_mode && 458 ((int)vtoc64->efi_parts[i].p_tag != 459 V_RESERVED)) { 460 continue; 461 } 462 463 print_efi_partition(vtoc64, i, want_header); 464 want_header = 0; 465 } 466 fmt_print("\n"); 467 return; 468 } 469 /* 470 * Loop through each partition, printing the header 471 * the first time. 472 */ 473 want_header = 1; 474 for (i = 0; i < NDKMAP; i++) { 475 if (i > 9) { 476 break; 477 } 478 print_partition(map, i, want_header); 479 want_header = 0; 480 } 481 482 fmt_print("\n"); 483 } 484 485 /* 486 * Print out one line of partition information, 487 * with optional header for EFI type disks. 488 */ 489 /*ARGSUSED*/ 490 void 491 print_efi_partition(struct dk_gpt *map, int partnum, int want_header) 492 { 493 int ncyl2_digits = 0; 494 float scaled; 495 char *s; 496 uint64_t secsize; 497 498 ncyl2_digits = ndigits(map->efi_last_u_lba); 499 if (want_header) { 500 fmt_print("Part "); 501 fmt_print("Tag Flag "); 502 fmt_print("First Sector"); 503 nspaces(ncyl2_digits); 504 fmt_print("Size"); 505 nspaces(ncyl2_digits); 506 fmt_print("Last Sector\n"); 507 } 508 509 fmt_print(" %d ", partnum); 510 s = find_string(ptag_choices, 511 (int)map->efi_parts[partnum].p_tag); 512 if (s == (char *)NULL) 513 s = "-"; 514 nspaces(10 - (int)strlen(s)); 515 fmt_print("%s", s); 516 517 s = find_string(pflag_choices, 518 (int)map->efi_parts[partnum].p_flag); 519 if (s == (char *)NULL) 520 s = "-"; 521 nspaces(6 - (int)strlen(s)); 522 fmt_print("%s", s); 523 524 nspaces(2); 525 526 secsize = map->efi_parts[partnum].p_size; 527 if (secsize == 0) { 528 fmt_print("%16llu", map->efi_parts[partnum].p_start); 529 nspaces(ncyl2_digits); 530 fmt_print(" 0 "); 531 } else { 532 fmt_print("%16llu", map->efi_parts[partnum].p_start); 533 scaled = bn2mb(secsize); 534 nspaces(ncyl2_digits - 5); 535 if (scaled >= (float)1024.0 * 1024) { 536 fmt_print("%8.2fTB", scaled/((float)1024.0 * 1024)); 537 } else if (scaled >= (float)1024.0) { 538 fmt_print("%8.2fGB", scaled/(float)1024.0); 539 } else { 540 fmt_print("%8.2fMB", scaled); 541 } 542 } 543 nspaces(ncyl2_digits); 544 if ((map->efi_parts[partnum].p_start+secsize - 1) == 545 UINT_MAX64) { 546 fmt_print(" 0 \n"); 547 } else { 548 fmt_print(" %llu \n", 549 map->efi_parts[partnum].p_start+secsize - 1); 550 } 551 } 552 553 /* 554 * Print out one line of partition information, 555 * with optional header. 556 */ 557 /*ARGSUSED*/ 558 void 559 print_partition(struct partition_info *pinfo, int partnum, int want_header) 560 { 561 int i; 562 blkaddr_t nblks; 563 int cyl1; 564 int cyl2; 565 float scaled; 566 int maxcyl2; 567 int ncyl2_digits; 568 char *s; 569 blkaddr_t maxnblks = 0; 570 blkaddr_t len; 571 572 /* 573 * To align things nicely, we need to know the maximum 574 * width of the number of cylinders field. 575 */ 576 maxcyl2 = 0; 577 for (i = 0; i < NDKMAP; i++) { 578 nblks = (uint_t)pinfo->pinfo_map[i].dkl_nblk; 579 cyl1 = pinfo->pinfo_map[i].dkl_cylno; 580 cyl2 = cyl1 + (nblks / spc()) - 1; 581 if (nblks > 0) { 582 maxcyl2 = max(cyl2, maxcyl2); 583 maxnblks = max(nblks, maxnblks); 584 } 585 } 586 /* 587 * Get the number of digits required 588 */ 589 ncyl2_digits = ndigits(maxcyl2); 590 591 /* 592 * Print the header, if necessary 593 */ 594 if (want_header) { 595 fmt_print("Part "); 596 fmt_print("Tag Flag "); 597 fmt_print("Cylinders"); 598 nspaces(ncyl2_digits); 599 fmt_print(" Size Blocks\n"); 600 } 601 602 /* 603 * Print the partition information 604 */ 605 nblks = pinfo->pinfo_map[partnum].dkl_nblk; 606 cyl1 = pinfo->pinfo_map[partnum].dkl_cylno; 607 cyl2 = cyl1 + (nblks / spc()) - 1; 608 609 fmt_print(" %x ", partnum); 610 611 /* 612 * Print the partition tag. If invalid, print - 613 */ 614 s = find_string(ptag_choices, 615 (int)pinfo->vtoc.v_part[partnum].p_tag); 616 if (s == (char *)NULL) 617 s = "-"; 618 nspaces(10 - (int)strlen(s)); 619 fmt_print("%s", s); 620 621 /* 622 * Print the partition flag. If invalid print - 623 */ 624 s = find_string(pflag_choices, 625 (int)pinfo->vtoc.v_part[partnum].p_flag); 626 if (s == (char *)NULL) 627 s = "-"; 628 nspaces(6 - (int)strlen(s)); 629 fmt_print("%s", s); 630 631 nspaces(2); 632 633 if (nblks == 0) { 634 fmt_print("%6d ", cyl1); 635 nspaces(ncyl2_digits); 636 fmt_print(" 0 "); 637 } else { 638 fmt_print("%6d - ", cyl1); 639 nspaces(ncyl2_digits - ndigits(cyl2)); 640 fmt_print("%d ", cyl2); 641 scaled = bn2mb(nblks); 642 if (scaled > (float)1024.0 * 1024.0) { 643 fmt_print("%8.2fTB ", 644 scaled/((float)1024.0 * 1024.0)); 645 } else if (scaled > (float)1024.0) { 646 fmt_print("%8.2fGB ", scaled/(float)1024.0); 647 } else { 648 fmt_print("%8.2fMB ", scaled); 649 } 650 } 651 fmt_print("("); 652 pr_dblock(fmt_print, nblks); 653 fmt_print(")"); 654 655 nspaces(ndigits(maxnblks/spc()) - ndigits(nblks/spc())); 656 /* 657 * Allocates size of the printf format string. 658 * ndigits(ndigits(maxblks)) gives the byte size of 659 * the printf width field for maxnblks. 660 */ 661 len = strlen(" %") + ndigits(ndigits(maxnblks)) + strlen("d\n") + 1; 662 s = zalloc(len); 663 (void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n"); 664 fmt_print(s, nblks); 665 (void) free(s); 666 } 667 668 669 /* 670 * Return true if a disk has a volume name 671 */ 672 int 673 chk_volname(disk) 674 struct disk_info *disk; 675 { 676 return (disk->v_volume[0] != 0); 677 } 678 679 680 /* 681 * Print the volume name, if it appears to be set 682 */ 683 void 684 print_volname(disk) 685 struct disk_info *disk; 686 { 687 int i; 688 char *p; 689 690 p = disk->v_volume; 691 for (i = 0; i < LEN_DKL_VVOL; i++, p++) { 692 if (*p == 0) 693 break; 694 fmt_print("%c", *p); 695 } 696 } 697 698 699 /* 700 * Print a number of spaces 701 */ 702 static void 703 nspaces(n) 704 int n; 705 { 706 while (n-- > 0) 707 fmt_print(" "); 708 } 709 710 /* 711 * Return the number of digits required to print a number 712 */ 713 static int 714 ndigits(n) 715 uint64_t n; 716 { 717 int i; 718 719 i = 0; 720 while (n > 0) { 721 n /= 10; 722 i++; 723 } 724 725 return (i == 0 ? 1 : i); 726 } 727