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