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