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