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