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