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 2006 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 that implement the fdisk menu commands. 30 */ 31 #include "global.h" 32 #include <errno.h> 33 #include <sys/time.h> 34 #include <sys/resource.h> 35 #include <sys/wait.h> 36 #include <signal.h> 37 #include <string.h> 38 #include <fcntl.h> 39 #include <stdlib.h> 40 #include <sys/dktp/fdisk.h> 41 #include <sys/stat.h> 42 #include <sys/dklabel.h> 43 44 #include "main.h" 45 #include "analyze.h" 46 #include "menu.h" 47 #include "menu_command.h" 48 #include "menu_defect.h" 49 #include "menu_partition.h" 50 #include "menu_fdisk.h" 51 #include "param.h" 52 #include "misc.h" 53 #include "label.h" 54 #include "startup.h" 55 #include "partition.h" 56 #include "prompts.h" 57 #include "checkdev.h" 58 #include "io.h" 59 #include "ctlr_scsi.h" 60 #include "auto_sense.h" 61 62 extern struct menu_item menu_fdisk[]; 63 64 /* 65 * Byte swapping macros for accessing struct ipart 66 * to resolve little endian on Sparc. 67 */ 68 #if defined(sparc) 69 #define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF)) 70 #define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \ 71 (les((unsigned)((val)&0xffff0000)>>16))) 72 73 #elif defined(i386) 74 75 #define les(val) (val) 76 #define lel(val) (val) 77 78 #else /* defined(sparc) */ 79 80 #error No Platform defined 81 82 #endif /* defined(sparc) */ 83 84 85 /* Function prototypes */ 86 #ifdef __STDC__ 87 88 #if defined(sparc) 89 90 static int getbyte(uchar_t **); 91 static int getlong(uchar_t **); 92 93 #endif /* defined(sparc) */ 94 95 static int get_solaris_part(int fd, struct ipart *ipart); 96 97 #else /* __STDC__ */ 98 99 #if defined(sparc) 100 101 static int getbyte(); 102 static int getlong(); 103 104 #endif /* defined(sparc) */ 105 106 static int get_solaris_part(); 107 108 #endif /* __STDC__ */ 109 110 /* 111 * Handling the alignment problem of struct ipart. 112 */ 113 static void 114 fill_ipart(char *bootptr, struct ipart *partp) 115 { 116 #if defined(sparc) 117 /* 118 * Sparc platform: 119 * 120 * Packing short/word for struct ipart to resolve 121 * little endian on Sparc since it is not 122 * properly aligned on Sparc. 123 */ 124 partp->bootid = getbyte((uchar_t **)&bootptr); 125 partp->beghead = getbyte((uchar_t **)&bootptr); 126 partp->begsect = getbyte((uchar_t **)&bootptr); 127 partp->begcyl = getbyte((uchar_t **)&bootptr); 128 partp->systid = getbyte((uchar_t **)&bootptr); 129 partp->endhead = getbyte((uchar_t **)&bootptr); 130 partp->endsect = getbyte((uchar_t **)&bootptr); 131 partp->endcyl = getbyte((uchar_t **)&bootptr); 132 partp->relsect = getlong((uchar_t **)&bootptr); 133 partp->numsect = getlong((uchar_t **)&bootptr); 134 #elif defined(i386) 135 /* 136 * i386 platform: 137 * 138 * The fdisk table does not begin on a 4-byte boundary within 139 * the master boot record; so, we need to recopy its contents 140 * to another data structure to avoid an alignment exception. 141 */ 142 (void) bcopy(bootptr, partp, sizeof (struct ipart)); 143 #else 144 #error No Platform defined 145 #endif /* defined(sparc) */ 146 } 147 148 /* 149 * Get a correct byte/short/word routines for Sparc platform. 150 */ 151 #if defined(sparc) 152 static int 153 getbyte(uchar_t **bp) 154 { 155 int b; 156 157 b = **bp; 158 *bp = *bp + 1; 159 return (b); 160 } 161 162 #ifdef DEADCODE 163 static int 164 getshort(uchar_t **bp) 165 { 166 int b; 167 168 b = ((**bp) << 8) | *(*bp + 1); 169 *bp += 2; 170 return (b); 171 } 172 #endif /* DEADCODE */ 173 174 static int 175 getlong(uchar_t **bp) 176 { 177 int b, bh, bl; 178 179 bh = ((**bp) << 8) | *(*bp + 1); 180 *bp += 2; 181 bl = ((**bp) << 8) | *(*bp + 1); 182 *bp += 2; 183 184 b = (bh << 16) | bl; 185 return (b); 186 } 187 #endif /* defined(sparc) */ 188 189 /* 190 * Convert cn[tn]dn to cn[tn]dns2 path 191 */ 192 static void 193 get_sname(char *name) 194 { 195 char buf[MAXPATHLEN]; 196 char *devp = "/dev/dsk"; 197 char *rdevp = "/dev/rdsk"; 198 char np[MAXNAMELEN]; 199 char *npt; 200 201 /* 202 * If it is a full path /dev/[r]dsk/cn[tn]dn, use this path 203 */ 204 (void) strcpy(np, cur_disk->disk_name); 205 if (strncmp(rdevp, cur_disk->disk_name, strlen(rdevp)) == 0 || 206 strncmp(devp, cur_disk->disk_name, strlen(devp)) == 0) { 207 /* 208 * Skip if the path is already included with sN 209 */ 210 if (strchr(np, 's') == strrchr(np, 's')) { 211 npt = strrchr(np, 'p'); 212 /* If pN is found, do not include it */ 213 if (isdigit(*++npt)) { 214 *--npt = '\0'; 215 } 216 (void) snprintf(buf, sizeof (buf), "%ss2", np); 217 } else { 218 (void) snprintf(buf, sizeof (buf), "%s", np); 219 } 220 } else { 221 (void) snprintf(buf, sizeof (buf), "/dev/rdsk/%ss2", np); 222 } 223 (void) strcpy(name, buf); 224 } 225 226 /* 227 * Convert cn[tn]dnsn to cn[tn]dnp0 path 228 */ 229 static void 230 get_pname(char *name) 231 { 232 char buf[MAXPATHLEN]; 233 char *devp = "/dev/dsk"; 234 char *rdevp = "/dev/rdsk"; 235 char np[MAXNAMELEN]; 236 char *npt; 237 238 /* 239 * If it is a full path /dev/[r]dsk/cn[tn]dnsn, use this path 240 */ 241 if (cur_disk == NULL) { 242 (void) strcpy(np, x86_devname); 243 } else { 244 (void) strcpy(np, cur_disk->disk_name); 245 } 246 247 if (strncmp(rdevp, np, strlen(rdevp)) == 0 || 248 strncmp(devp, np, strlen(devp)) == 0) { 249 /* 250 * Skip if the path is already included with pN 251 */ 252 if (strchr(np, 'p') == NULL) { 253 npt = strrchr(np, 's'); 254 /* If sN is found, do not include it */ 255 if (isdigit(*++npt)) { 256 *--npt = '\0'; 257 } 258 (void) snprintf(buf, sizeof (buf), "%sp0", np); 259 } else { 260 (void) snprintf(buf, sizeof (buf), "%s", np); 261 } 262 } else { 263 (void) snprintf(buf, sizeof (buf), "/dev/rdsk/%sp0", np); 264 } 265 (void) strcpy(name, buf); 266 } 267 268 /* 269 * Open file descriptor for current disk (cur_file) 270 * with "p0" path or cur_disk->disk_path 271 */ 272 void 273 open_cur_file(int mode) 274 { 275 char *dkpath; 276 char pbuf[MAXPATHLEN]; 277 278 switch (mode) { 279 case FD_USE_P0_PATH: 280 (void) get_pname(&pbuf[0]); 281 dkpath = pbuf; 282 break; 283 case FD_USE_CUR_DISK_PATH: 284 if (cur_disk->fdisk_part.systid == SUNIXOS || 285 cur_disk->fdisk_part.systid == SUNIXOS2) { 286 (void) get_sname(&pbuf[0]); 287 dkpath = pbuf; 288 } else { 289 dkpath = cur_disk->disk_path; 290 } 291 break; 292 default: 293 err_print("Error: Invalid mode option for opening cur_file\n"); 294 fullabort(); 295 } 296 297 /* Close previous cur_file */ 298 (void) close(cur_file); 299 /* Open cur_file with the required path dkpath */ 300 if ((cur_file = open_disk(dkpath, O_RDWR | O_NDELAY)) < 0) { 301 err_print( 302 "Error: can't open selected disk '%s'.\n", dkpath); 303 fullabort(); 304 } 305 } 306 307 308 /* 309 * This routine implements the 'fdisk' command. It simply runs 310 * the fdisk command on the current disk. 311 * Use of this is restricted to interactive mode only. 312 */ 313 int 314 c_fdisk() 315 { 316 317 char buf[MAXPATHLEN]; 318 char pbuf[MAXPATHLEN]; 319 struct stat statbuf; 320 321 /* 322 * We must be in interactive mode to use the fdisk command 323 */ 324 if (option_f != (char *)NULL || isatty(0) != 1 || isatty(1) != 1) { 325 err_print("Fdisk command is for interactive use only!\n"); 326 return (-1); 327 } 328 329 /* 330 * There must be a current disk type and a current disk 331 */ 332 if (cur_dtype == NULL) { 333 err_print("Current Disk Type is not set.\n"); 334 return (-1); 335 } 336 337 /* 338 * If disk is larger than 1TB then an EFI label is required 339 * and there is no point in running fdisk 340 */ 341 if (cur_dtype->capacity > INFINITY) { 342 err_print("This disk must use an EFI label.\n"); 343 return (-1); 344 } 345 346 /* 347 * Before running the fdisk command, get file status of 348 * /dev/rdsk/cn[tn]dnp0 path to see if this disk 349 * supports fixed disk partition table. 350 */ 351 (void) get_pname(&pbuf[0]); 352 if (stat(pbuf, (struct stat *)&statbuf) == -1 || 353 !S_ISCHR(statbuf.st_mode)) { 354 err_print( 355 "Disk does not support fixed disk partition table\n"); 356 return (0); 357 } 358 359 /* 360 * Run the fdisk program. 361 */ 362 (void) snprintf(buf, sizeof (buf), "fdisk %s\n", pbuf); 363 (void) system(buf); 364 365 /* 366 * Open cur_file with "p0" path for accessing the fdisk table 367 */ 368 (void) open_cur_file(FD_USE_P0_PATH); 369 370 /* 371 * Get solaris partition information in the fdisk partition table 372 */ 373 if (get_solaris_part(cur_file, &cur_disk->fdisk_part) == -1) { 374 err_print("No fdisk solaris partition found\n"); 375 cur_disk->fdisk_part.numsect = 0; /* No Solaris */ 376 } 377 378 /* 379 * Restore cur_file with cur_disk->disk_path 380 */ 381 (void) open_cur_file(FD_USE_CUR_DISK_PATH); 382 383 return (0); 384 } 385 386 /* 387 * Read MBR on the disk 388 * if the Solaris partition has changed, 389 * reread the vtoc 390 */ 391 #ifdef DEADCODE 392 static void 393 update_cur_parts() 394 { 395 396 int i; 397 register struct partition_info *parts; 398 399 for (i = 0; i < NDKMAP; i++) { 400 #if defined(_SUNOS_VTOC_16) 401 if (cur_parts->vtoc.v_part[i].p_tag && 402 cur_parts->vtoc.v_part[i].p_tag != V_ALTSCTR) { 403 cur_parts->vtoc.v_part[i].p_start = 0; 404 cur_parts->vtoc.v_part[i].p_size = 0; 405 406 #endif 407 cur_parts->pinfo_map[i].dkl_nblk = 0; 408 cur_parts->pinfo_map[i].dkl_cylno = 0; 409 cur_parts->vtoc.v_part[i].p_tag = 410 default_vtoc_map[i].p_tag; 411 cur_parts->vtoc.v_part[i].p_flag = 412 default_vtoc_map[i].p_flag; 413 #if defined(_SUNOS_VTOC_16) 414 } 415 #endif 416 } 417 cur_parts->pinfo_map[C_PARTITION].dkl_nblk = ncyl * spc(); 418 419 #if defined(_SUNOS_VTOC_16) 420 /* 421 * Adjust for the boot partitions 422 */ 423 cur_parts->pinfo_map[I_PARTITION].dkl_nblk = spc(); 424 cur_parts->pinfo_map[I_PARTITION].dkl_cylno = 0; 425 cur_parts->vtoc.v_part[C_PARTITION].p_start = 426 cur_parts->pinfo_map[C_PARTITION].dkl_cylno * nhead * nsect; 427 cur_parts->vtoc.v_part[C_PARTITION].p_size = 428 cur_parts->pinfo_map[C_PARTITION].dkl_nblk; 429 430 cur_parts->vtoc.v_part[I_PARTITION].p_start = 431 cur_parts->pinfo_map[I_PARTITION].dkl_cylno; 432 cur_parts->vtoc.v_part[I_PARTITION].p_size = 433 cur_parts->pinfo_map[I_PARTITION].dkl_nblk; 434 435 #endif /* defined(_SUNOS_VTOC_16) */ 436 parts = cur_dtype->dtype_plist; 437 cur_dtype->dtype_ncyl = ncyl; 438 cur_dtype->dtype_plist = cur_parts; 439 parts->pinfo_name = cur_parts->pinfo_name; 440 cur_disk->disk_parts = cur_parts; 441 cur_ctype->ctype_dlist = cur_dtype; 442 443 } 444 #endif /* DEADCODE */ 445 446 static int 447 get_solaris_part(int fd, struct ipart *ipart) 448 { 449 int i; 450 struct ipart ip; 451 int status; 452 char *bootptr; 453 struct dk_label update_label; 454 455 (void) lseek(fd, 0, 0); 456 status = read(fd, (caddr_t)&boot_sec, NBPSCTR); 457 458 if (status != NBPSCTR) { 459 err_print("Bad read of fdisk partition. Status = %x\n", status); 460 err_print("Cannot read fdisk partition information.\n"); 461 return (-1); 462 } 463 464 for (i = 0; i < FD_NUMPART; i++) { 465 int ipc; 466 467 ipc = i * sizeof (struct ipart); 468 469 /* Handling the alignment problem of struct ipart */ 470 bootptr = &boot_sec.parts[ipc]; 471 (void) fill_ipart(bootptr, &ip); 472 473 /* 474 * we are interested in Solaris and EFI partition types 475 */ 476 if (ip.systid == SUNIXOS || 477 ip.systid == SUNIXOS2 || 478 ip.systid == EFI_PMBR) { 479 /* 480 * if the disk has an EFI label, nhead and nsect may 481 * be zero. This test protects us from FPE's, and 482 * format still seems to work fine 483 */ 484 if (nhead != 0 && nsect != 0) { 485 pcyl = lel(ip.numsect) / (nhead * nsect); 486 xstart = lel(ip.relsect) / (nhead * nsect); 487 ncyl = pcyl - acyl; 488 } 489 #ifdef DEBUG 490 else { 491 err_print("Critical geometry values are zero:\n" 492 "\tnhead = %d; nsect = %d\n", nhead, 493 nsect); 494 } 495 #endif /* DEBUG */ 496 497 solaris_offset = lel(ip.relsect); 498 break; 499 } 500 } 501 502 if (i == FD_NUMPART) { 503 err_print("Solaris fdisk partition not found\n"); 504 return (-1); 505 } 506 507 /* 508 * compare the previous and current Solaris partition 509 * but don't use bootid in determination of Solaris partition changes 510 */ 511 ipart->bootid = ip.bootid; 512 status = bcmp(&ip, ipart, sizeof (struct ipart)); 513 514 bcopy(&ip, ipart, sizeof (struct ipart)); 515 516 /* if the disk partitioning has changed - get the VTOC */ 517 if (status) { 518 status = ioctl(fd, DKIOCGVTOC, &cur_parts->vtoc); 519 if (status == -1) { 520 i = errno; 521 err_print("Bad ioctl DKIOCGVTOC.\n"); 522 err_print("errno=%d %s\n", i, strerror(i)); 523 err_print("Cannot read vtoc information.\n"); 524 return (-1); 525 } 526 527 status = read_label(fd, &update_label); 528 if (status == -1) { 529 err_print("Cannot read label information.\n"); 530 return (-1); 531 } 532 533 #if defined(_SUNOS_VTOC_16) 534 /* 535 * this is to update the slice table on x86 536 * we don't care about VTOC8 here 537 */ 538 for (i = 0; i < NDKMAP; i ++) { 539 cur_parts->pinfo_map[i].dkl_cylno = 540 update_label.dkl_vtoc.v_part[i].p_start / 541 ((int)(update_label.dkl_nhead * 542 update_label.dkl_nsect)); 543 cur_parts->pinfo_map[i].dkl_nblk = 544 update_label.dkl_vtoc.v_part[i].p_size; 545 } 546 #endif /* defined(_SUNOS_VTOC_16) */ 547 548 cur_dtype->dtype_ncyl = update_label.dkl_ncyl; 549 cur_dtype->dtype_pcyl = update_label.dkl_pcyl; 550 cur_dtype->dtype_acyl = update_label.dkl_acyl; 551 cur_dtype->dtype_nhead = update_label.dkl_nhead; 552 cur_dtype->dtype_nsect = update_label.dkl_nsect; 553 ncyl = cur_dtype->dtype_ncyl; 554 acyl = cur_dtype->dtype_acyl; 555 pcyl = cur_dtype->dtype_pcyl; 556 nsect = cur_dtype->dtype_nsect; 557 nhead = cur_dtype->dtype_nhead; 558 } 559 return (0); 560 } 561 562 563 int 564 copy_solaris_part(struct ipart *ipart) 565 { 566 567 int status, i, fd; 568 struct mboot mboot; 569 struct ipart ip; 570 char buf[MAXPATHLEN]; 571 char *bootptr; 572 struct stat statbuf; 573 574 (void) get_pname(&buf[0]); 575 if (stat(buf, &statbuf) == -1 || 576 !S_ISCHR(statbuf.st_mode) || 577 ((cur_label == L_TYPE_EFI) && 578 (cur_disk->disk_flags & DSK_LABEL_DIRTY))) { 579 /* 580 * Make sure to reset solaris_offset to zero if it is 581 * previously set by a selected disk that 582 * supports the fdisk table. 583 */ 584 solaris_offset = 0; 585 /* 586 * Return if this disk does not support fdisk table or 587 * if it uses an EFI label but has not yet been labelled. 588 * If the EFI label has not been written then the open 589 * on the partition will fail. 590 */ 591 return (0); 592 } 593 594 if ((fd = open(buf, O_RDONLY)) < 0) { 595 err_print("Error: can't open disk '%s'.\n", buf); 596 return (-1); 597 } 598 599 status = read(fd, (caddr_t)&mboot, sizeof (struct mboot)); 600 601 if (status != sizeof (struct mboot)) { 602 err_print("Bad read of fdisk partition.\n"); 603 (void) close(fd); 604 return (-1); 605 } 606 607 for (i = 0; i < FD_NUMPART; i++) { 608 int ipc; 609 610 ipc = i * sizeof (struct ipart); 611 612 /* Handling the alignment problem of struct ipart */ 613 bootptr = &mboot.parts[ipc]; 614 (void) fill_ipart(bootptr, &ip); 615 616 if (ip.systid == SUNIXOS || 617 ip.systid == SUNIXOS2 || 618 ip.systid == EFI_PMBR) { 619 solaris_offset = lel(ip.relsect); 620 bcopy(&ip, ipart, sizeof (struct ipart)); 621 622 /* 623 * if the disk has an EFI label, we typically won't 624 * have values for nhead and nsect. format seems to 625 * work without them, and we need to protect ourselves 626 * from FPE's 627 */ 628 if (nhead != 0 && nsect != 0) { 629 pcyl = lel(ip.numsect) / (nhead * nsect); 630 ncyl = pcyl - acyl; 631 } 632 #ifdef DEBUG 633 else { 634 err_print("Critical geometry values are zero:\n" 635 "\tnhead = %d; nsect = %d\n", nhead, 636 nsect); 637 } 638 #endif /* DEBUG */ 639 640 break; 641 } 642 } 643 644 (void) close(fd); 645 return (0); 646 647 } 648 649 #if defined(_FIRMWARE_NEEDS_FDISK) 650 int 651 auto_solaris_part(struct dk_label *label) 652 { 653 654 int status, i, fd; 655 struct mboot mboot; 656 struct ipart ip; 657 char *bootptr; 658 char pbuf[MAXPATHLEN]; 659 660 661 (void) get_pname(&pbuf[0]); 662 if ((fd = open_disk(pbuf, O_RDONLY)) < 0) { 663 err_print("Error: can't open selected disk '%s'.\n", pbuf); 664 return (-1); 665 } 666 667 status = read(fd, (caddr_t)&mboot, sizeof (struct mboot)); 668 669 if (status != sizeof (struct mboot)) { 670 err_print("Bad read of fdisk partition.\n"); 671 return (-1); 672 } 673 674 for (i = 0; i < FD_NUMPART; i++) { 675 int ipc; 676 677 ipc = i * sizeof (struct ipart); 678 679 /* Handling the alignment problem of struct ipart */ 680 bootptr = &mboot.parts[ipc]; 681 (void) fill_ipart(bootptr, &ip); 682 683 /* 684 * if the disk has an EFI label, the nhead and nsect fields 685 * the label may be zero. This protects us from FPE's, and 686 * format still seems to work happily 687 */ 688 if (ip.systid == SUNIXOS || 689 ip.systid == SUNIXOS2 || 690 ip.systid == EFI_PMBR) { 691 if ((label->dkl_nhead != 0) && 692 (label->dkl_nsect != 0)) { 693 label->dkl_pcyl = lel(ip.numsect) / 694 (label->dkl_nhead * label->dkl_nsect); 695 label->dkl_ncyl = label->dkl_pcyl - 696 label->dkl_acyl; 697 } 698 #ifdef DEBUG 699 else { 700 err_print("Critical label fields aren't " 701 "non-zero:\n" 702 "\tlabel->dkl_nhead = %d; " 703 "label->dkl_nsect = " 704 "%d\n", label->dkl_nhead, 705 label->dkl_nsect); 706 } 707 #endif /* DEBUG */ 708 709 solaris_offset = lel(ip.relsect); 710 break; 711 } 712 } 713 714 (void) close(fd); 715 716 return (0); 717 } 718 #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ 719 720 721 int 722 good_fdisk() 723 { 724 char buf[MAXPATHLEN]; 725 struct stat statbuf; 726 727 (void) get_pname(&buf[0]); 728 if (stat(buf, &statbuf) == -1 || 729 !S_ISCHR(statbuf.st_mode) || 730 cur_label == L_TYPE_EFI) { 731 /* 732 * Return if this disk does not support fdisk table or 733 * if the disk is labeled with EFI. 734 */ 735 return (1); 736 } 737 738 if (lel(cur_disk->fdisk_part.numsect) > 0) 739 return (1); 740 else 741 return (0); 742 } 743