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() 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 fmt_print("Current partition table (%s):\n", 415 cur_parts->pinfo_name != NULL ? 416 cur_parts->pinfo_name : "unnamed"); 417 fmt_print("Total disk sectors available: %llu + %d " 418 "(reserved sectors)\n\n", 419 cur_parts->etoc->efi_last_u_lba - EFI_MIN_RESV_SIZE - 420 cur_parts->etoc->efi_first_u_lba + 1, EFI_MIN_RESV_SIZE); 421 } 422 423 424 /* 425 * Print the partition map itself 426 */ 427 print_map(cur_parts); 428 return (0); 429 } 430 431 432 /* 433 * Print a partition map 434 */ 435 void 436 print_map(struct partition_info *map) 437 { 438 int i; 439 int want_header; 440 struct dk_gpt *vtoc64; 441 442 if (cur_label == L_TYPE_EFI) { 443 vtoc64 = map->etoc; 444 want_header = 1; 445 for (i = 0; i < vtoc64->efi_nparts; i++) { 446 /* 447 * we want to print partitions above 7 in expert mode only 448 * or if the partition is reserved 449 */ 450 if (i >= 7 && !expert_mode && 451 ((int)vtoc64->efi_parts[i].p_tag != 452 V_RESERVED)) { 453 continue; 454 } 455 456 print_efi_partition(vtoc64, i, want_header); 457 want_header = 0; 458 } 459 fmt_print("\n"); 460 return; 461 } 462 /* 463 * Loop through each partition, printing the header 464 * the first time. 465 */ 466 want_header = 1; 467 for (i = 0; i < NDKMAP; i++) { 468 if (i > 9) { 469 break; 470 } 471 print_partition(map, i, want_header); 472 want_header = 0; 473 } 474 475 fmt_print("\n"); 476 } 477 478 /* 479 * Print out one line of partition information, 480 * with optional header for EFI type disks. 481 */ 482 /*ARGSUSED*/ 483 void 484 print_efi_partition(struct dk_gpt *map, int partnum, int want_header) 485 { 486 int ncyl2_digits = 0; 487 float scaled; 488 char *s; 489 uint64_t secsize; 490 491 ncyl2_digits = ndigits(map->efi_last_u_lba); 492 if (want_header) { 493 fmt_print("Part "); 494 fmt_print("Tag Flag "); 495 fmt_print("First Sector"); 496 nspaces(ncyl2_digits); 497 fmt_print("Size"); 498 nspaces(ncyl2_digits); 499 fmt_print("Last Sector\n"); 500 } 501 502 fmt_print(" %d ", partnum); 503 s = find_string(ptag_choices, 504 (int)map->efi_parts[partnum].p_tag); 505 if (s == (char *)NULL) 506 s = "-"; 507 nspaces(10 - (int)strlen(s)); 508 fmt_print("%s", s); 509 510 s = find_string(pflag_choices, 511 (int)map->efi_parts[partnum].p_flag); 512 if (s == (char *)NULL) 513 s = "-"; 514 nspaces(6 - (int)strlen(s)); 515 fmt_print("%s", s); 516 517 nspaces(2); 518 519 secsize = map->efi_parts[partnum].p_size; 520 if (secsize == 0) { 521 fmt_print("%16llu", map->efi_parts[partnum].p_start); 522 nspaces(ncyl2_digits); 523 fmt_print(" 0 "); 524 } else { 525 fmt_print("%16llu", map->efi_parts[partnum].p_start); 526 scaled = bn2mb(secsize); 527 nspaces(ncyl2_digits - 5); 528 if (scaled >= (float)1024.0 * 1024) { 529 fmt_print("%8.2fTB", scaled/((float)1024.0 * 1024)); 530 } else if (scaled >= (float)1024.0) { 531 fmt_print("%8.2fGB", scaled/(float)1024.0); 532 } else { 533 fmt_print("%8.2fMB", scaled); 534 } 535 } 536 nspaces(ncyl2_digits); 537 if ((map->efi_parts[partnum].p_start+secsize - 1) == 538 UINT_MAX64) { 539 fmt_print(" 0 \n"); 540 } else { 541 fmt_print(" %llu \n", 542 map->efi_parts[partnum].p_start+secsize - 1); 543 } 544 } 545 546 /* 547 * Print out one line of partition information, 548 * with optional header. 549 */ 550 /*ARGSUSED*/ 551 void 552 print_partition(struct partition_info *pinfo, int partnum, int want_header) 553 { 554 int i; 555 blkaddr_t nblks; 556 int cyl1; 557 int cyl2; 558 float scaled; 559 int maxcyl2; 560 int ncyl2_digits; 561 char *s; 562 blkaddr_t maxnblks = 0; 563 blkaddr_t len; 564 565 /* 566 * To align things nicely, we need to know the maximum 567 * width of the number of cylinders field. 568 */ 569 maxcyl2 = 0; 570 for (i = 0; i < NDKMAP; i++) { 571 nblks = (uint_t)pinfo->pinfo_map[i].dkl_nblk; 572 cyl1 = pinfo->pinfo_map[i].dkl_cylno; 573 cyl2 = cyl1 + (nblks / spc()) - 1; 574 if (nblks > 0) { 575 maxcyl2 = max(cyl2, maxcyl2); 576 maxnblks = max(nblks, maxnblks); 577 } 578 } 579 /* 580 * Get the number of digits required 581 */ 582 ncyl2_digits = ndigits(maxcyl2); 583 584 /* 585 * Print the header, if necessary 586 */ 587 if (want_header) { 588 fmt_print("Part "); 589 fmt_print("Tag Flag "); 590 fmt_print("Cylinders"); 591 nspaces(ncyl2_digits); 592 fmt_print(" Size Blocks\n"); 593 } 594 595 /* 596 * Print the partition information 597 */ 598 nblks = pinfo->pinfo_map[partnum].dkl_nblk; 599 cyl1 = pinfo->pinfo_map[partnum].dkl_cylno; 600 cyl2 = cyl1 + (nblks / spc()) - 1; 601 602 fmt_print(" %x ", partnum); 603 604 /* 605 * Print the partition tag. If invalid, print - 606 */ 607 s = find_string(ptag_choices, 608 (int)pinfo->vtoc.v_part[partnum].p_tag); 609 if (s == (char *)NULL) 610 s = "-"; 611 nspaces(10 - (int)strlen(s)); 612 fmt_print("%s", s); 613 614 /* 615 * Print the partition flag. If invalid print - 616 */ 617 s = find_string(pflag_choices, 618 (int)pinfo->vtoc.v_part[partnum].p_flag); 619 if (s == (char *)NULL) 620 s = "-"; 621 nspaces(6 - (int)strlen(s)); 622 fmt_print("%s", s); 623 624 nspaces(2); 625 626 if (nblks == 0) { 627 fmt_print("%6d ", cyl1); 628 nspaces(ncyl2_digits); 629 fmt_print(" 0 "); 630 } else { 631 fmt_print("%6d - ", cyl1); 632 nspaces(ncyl2_digits - ndigits(cyl2)); 633 fmt_print("%d ", cyl2); 634 scaled = bn2mb(nblks); 635 if (scaled > (float)1024.0 * 1024.0) { 636 fmt_print("%8.2fTB ", 637 scaled/((float)1024.0 * 1024.0)); 638 } else if (scaled > (float)1024.0) { 639 fmt_print("%8.2fGB ", scaled/(float)1024.0); 640 } else { 641 fmt_print("%8.2fMB ", scaled); 642 } 643 } 644 fmt_print("("); 645 pr_dblock(fmt_print, nblks); 646 fmt_print(")"); 647 648 nspaces(ndigits(maxnblks/spc()) - ndigits(nblks/spc())); 649 /* 650 * Allocates size of the printf format string. 651 * ndigits(ndigits(maxblks)) gives the byte size of 652 * the printf width field for maxnblks. 653 */ 654 len = strlen(" %") + ndigits(ndigits(maxnblks)) + strlen("d\n") + 1; 655 s = zalloc(len); 656 (void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n"); 657 fmt_print(s, nblks); 658 (void) free(s); 659 } 660 661 662 /* 663 * Return true if a disk has a volume name 664 */ 665 int 666 chk_volname(disk) 667 struct disk_info *disk; 668 { 669 return (disk->v_volume[0] != 0); 670 } 671 672 673 /* 674 * Print the volume name, if it appears to be set 675 */ 676 void 677 print_volname(disk) 678 struct disk_info *disk; 679 { 680 int i; 681 char *p; 682 683 p = disk->v_volume; 684 for (i = 0; i < LEN_DKL_VVOL; i++, p++) { 685 if (*p == 0) 686 break; 687 fmt_print("%c", *p); 688 } 689 } 690 691 692 /* 693 * Print a number of spaces 694 */ 695 static void 696 nspaces(n) 697 int n; 698 { 699 while (n-- > 0) 700 fmt_print(" "); 701 } 702 703 /* 704 * Return the number of digits required to print a number 705 */ 706 static int 707 ndigits(n) 708 uint64_t n; 709 { 710 int i; 711 712 i = 0; 713 while (n > 0) { 714 n /= 10; 715 i++; 716 } 717 718 return (i == 0 ? 1 : i); 719 } 720