1 /* 2 * Mach Operating System 3 * Copyright (c) 1992 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie Mellon 24 * the rights to redistribute these changes. 25 * 26 * $FreeBSD: /repoman/r/ncvs/src/sbin/i386/fdisk/fdisk.c,v 1.36.2.14 2004/01/30 14:40:47 harti Exp $ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/diskslice.h> 31 #include <sys/diskmbr.h> 32 #include <sys/sysctl.h> 33 #include <sys/stat.h> 34 #include <ctype.h> 35 #include <fcntl.h> 36 #include <err.h> 37 #include <fstab.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #include <bus/cam/scsi/scsi_daio.h> 45 46 #define LBUF 100 47 static char lbuf[LBUF]; 48 49 /* 50 * 51 * Ported to 386bsd by Julian Elischer Thu Oct 15 20:26:46 PDT 1992 52 * 53 * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University 54 * Copyright (c) 1989 Robert. V. Baron 55 * Created. 56 */ 57 58 #define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp 59 #define MAX_SEC_SIZE 2048 /* maximum section size that is supported */ 60 #define MIN_SEC_SIZE 512 /* the sector size to start sensing at */ 61 #define MAX_SECTORS_PER_TRACK 0x3f /* maximum number of sectors per track */ 62 #define MIN_SECTORS_PER_TRACK 0x1 /* minimum number of sectors per track */ 63 #define MAX_HEADS 0xff /* maximum number of head */ 64 static int secsize = 0; /* the sensed sector size */ 65 66 static int fd; /* file descriptor of the given disk */ 67 static const char *disk; 68 static const char *disks[] = 69 { 70 "/dev/ad0", "/dev/da0", "/dev/vkd0", 0 71 }; 72 73 static int cyls, sectors, heads, cylsecs; 74 static int64_t disksecs; 75 76 struct mboot 77 { 78 unsigned char padding[2]; /* force the longs to be long aligned */ 79 unsigned char *bootinst; /* boot code */ 80 off_t bootinst_size; 81 struct dos_partition parts[NDOSPART]; 82 }; 83 static struct mboot mboot; 84 85 #define ACTIVE 0x80 86 87 int dos_cyls; 88 int dos_heads; 89 int dos_sectors; 90 int dos_cylsecs; 91 92 #define DOSSECT(s,c) ((s & MAX_SECTORS_PER_TRACK) | ((c >> 2) & 0xc0)) 93 #define DOSCYL(c) (c & 0xff) 94 #define MAXCYL 1023 95 static int partition = -1; 96 97 #define MAX_ARGS 10 98 static int current_line_number; 99 100 static int geom_processed = 0; 101 static int part_processed = 0; 102 static int active_processed = 0; 103 104 typedef struct cmd { 105 char cmd; 106 int n_args; 107 struct arg { 108 char argtype; 109 long long arg_val; 110 } args[MAX_ARGS]; 111 } CMD; 112 113 static int B_flag = 0; /* replace boot code */ 114 static int C_flag = 0; /* use wrapped values for CHS */ 115 static int E_flag = 0; /* Erase through TRIM */ 116 static int I_flag = 0; /* use entire disk for DragonFly */ 117 static int a_flag = 0; /* set active partition */ 118 static char *b_flag = NULL; /* path to boot code */ 119 static int i_flag = 0; /* replace partition data */ 120 static int u_flag = 0; /* update partition data */ 121 static int p_flag = 0; /* operate on a disk image file */ 122 static int s_flag = 0; /* Print a summary and exit */ 123 static int t_flag = 0; /* test only */ 124 static int x_flag = 0; /* Expand-to-fit device */ 125 static char *f_flag = NULL; /* Read config info from file */ 126 static int v_flag = 0; /* Be verbose */ 127 128 static void print_s0(int which); 129 static void print_part(int i); 130 static void init_sector0(unsigned long start); 131 static void init_boot(void); 132 static void change_part(int i); 133 static void print_params(void); 134 static void change_active(int which); 135 static void change_code(void); 136 static void get_params_to_use(void); 137 static void dos(struct dos_partition *partp); 138 static int open_disk(void); 139 static void erase_partition(int i); 140 static ssize_t read_disk(off_t sector, void *buf); 141 static ssize_t write_disk(off_t sector, void *buf); 142 static int get_params(void); 143 static int read_s0(void); 144 static int write_s0(void); 145 static int ok(const char *str); 146 static int decimal(const char *str, int *num, int deflt); 147 static const char *get_type(int type); 148 static int read_config(char *config_file); 149 static void reset_boot(void); 150 static int sanitize_partition(struct dos_partition *); 151 static void usage(void); 152 static int expand_table(void); 153 154 int 155 main(int argc, char *argv[]) 156 { 157 int c, i; 158 159 while ((c = getopt(argc, argv, "BCEIab:f:p:istuvx1234")) != -1) 160 switch (c) { 161 case 'B': 162 B_flag = 1; 163 break; 164 case 'C': 165 C_flag = 1; 166 break; 167 case 'E': 168 E_flag = 1; 169 break; 170 case 'I': 171 I_flag = 1; 172 break; 173 case 'a': 174 a_flag = 1; 175 break; 176 case 'b': 177 b_flag = optarg; 178 break; 179 case 'f': 180 f_flag = optarg; 181 break; 182 case 'p': 183 disk = optarg; 184 p_flag = 1; 185 break; 186 case 'i': 187 i_flag = 1; 188 break; 189 case 's': 190 s_flag = 1; 191 break; 192 case 't': 193 t_flag = 1; 194 break; 195 case 'u': 196 u_flag = 1; 197 break; 198 case 'v': 199 v_flag = 1; 200 break; 201 case 'x': 202 ++x_flag; 203 break; 204 case '1': 205 case '2': 206 case '3': 207 case '4': 208 partition = c - '0'; 209 break; 210 default: 211 usage(); 212 } 213 if (f_flag || i_flag) 214 u_flag = 1; 215 if (t_flag) 216 v_flag = 1; 217 argc -= optind; 218 argv += optind; 219 220 if (argc > 0) { 221 disk = getdevpath(argv[0], 0); 222 if (!disk) 223 err(1, "cannot open disk %s", disk); 224 if (open_disk() < 0) 225 err(1, "cannot open disk %s", disk); 226 } else if (disk == NULL) { 227 int rv = 0; 228 229 for(i = 0; disks[i]; i++) 230 { 231 disk = disks[i]; 232 rv = open_disk(); 233 if (rv != -2) break; 234 } 235 if (rv < 0) 236 err(1, "cannot open any disk"); 237 } else { 238 if (open_disk() < 0) 239 err(1, "cannot open disk %s", disk); 240 } 241 242 /* (abu)use mboot.bootinst to probe for the sector size */ 243 if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL) 244 err(1, "cannot allocate buffer to determine disk sector size"); 245 if (read_disk(0, mboot.bootinst) == -1) 246 { 247 free(mboot.bootinst); 248 errx(1, "could not detect sector size"); 249 } 250 free(mboot.bootinst); 251 mboot.bootinst = NULL; 252 253 if (s_flag) 254 { 255 int j; 256 struct dos_partition *partp; 257 258 if (read_s0()) 259 err(1, "read_s0"); 260 printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads, 261 dos_sectors); 262 printf("Part %11s %11s Type Flags\n", "Start", "Size"); 263 for (j = 0; j < NDOSPART; j++) { 264 partp = ((struct dos_partition *) &mboot.parts) + j; 265 if (partp->dp_start == 0 && partp->dp_size == 0) 266 continue; 267 printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", j + 1, 268 (u_long) partp->dp_start, 269 (u_long) partp->dp_size, partp->dp_typ, 270 partp->dp_flag); 271 } 272 exit(0); 273 } 274 275 printf("******* Working on device %s *******\n",disk); 276 277 if (I_flag) 278 { 279 struct dos_partition *partp; 280 281 read_s0(); 282 reset_boot(); 283 partp = (struct dos_partition *) (&mboot.parts[0]); 284 partp->dp_typ = DOSPTYP_DFLYBSD; 285 partp->dp_flag = ACTIVE; 286 partp->dp_start = dos_sectors; 287 if (disksecs - dos_sectors > 0xFFFFFFFFU) { 288 printf("Warning: Ending logical block > 2TB, using max value\n"); 289 partp->dp_size = 0xFFFFFFFFU; 290 } else { 291 partp->dp_size = 292 rounddown(disksecs, dos_cylsecs) - dos_sectors; 293 } 294 dos(partp); 295 if (v_flag) 296 print_s0(-1); 297 298 if (E_flag) { 299 /* Trim now if we're using the entire device */ 300 erase_partition(0); 301 } 302 303 if (!t_flag) 304 if (write_s0() == -1) 305 err(1, "can't write fdisk partition table"); 306 exit(0); 307 } 308 if (f_flag) 309 { 310 if (read_s0() || i_flag) 311 { 312 reset_boot(); 313 } 314 315 if (!read_config(f_flag)) 316 { 317 exit(1); 318 } 319 if (x_flag) 320 x_flag = expand_table(); 321 if (v_flag) 322 { 323 print_s0(-1); 324 } 325 if (!t_flag) 326 { 327 if (write_s0() == -1) 328 err(1, "can't write fdisk partition table"); 329 } 330 } 331 else 332 { 333 if (u_flag) 334 { 335 get_params_to_use(); 336 } 337 else 338 { 339 print_params(); 340 } 341 342 if (read_s0()) 343 init_sector0(dos_sectors); 344 345 printf("Media sector size is %d\n", secsize); 346 printf("Warning: BIOS sector numbering starts with sector 1\n"); 347 printf("Information from DOS bootblock is:\n"); 348 if (partition == -1) 349 for (i = 1; i <= NDOSPART; i++) 350 change_part(i); 351 else 352 change_part(partition); 353 354 if (u_flag || a_flag) 355 change_active(partition); 356 357 if (B_flag) 358 change_code(); 359 360 if (x_flag) 361 x_flag = expand_table(); 362 363 if (x_flag || u_flag || a_flag || B_flag) { 364 if (!t_flag) { 365 printf("\nWe haven't changed the partition table yet. "); 366 printf("This is your last chance.\n"); 367 } 368 print_s0(-1); 369 if (!t_flag) { 370 if (ok("Should we write new partition table?")) { 371 if (E_flag && u_flag) { 372 /* 373 * Trim now because we've committed to 374 * updating the partition. 375 */ 376 if (partition == -1) 377 for (i = 0; i < NDOSPART; i++) 378 erase_partition(i); 379 else 380 erase_partition(partition); 381 } 382 if (write_s0() == -1) 383 err(1, "can't write fdisk partition table"); 384 } 385 } 386 else 387 { 388 printf("\n-t flag specified -- partition table not written.\n"); 389 } 390 } 391 } 392 393 exit(0); 394 } 395 396 static void 397 usage(void) 398 { 399 fprintf(stderr, "%s%s", 400 "usage: fdisk [-BCEIaistu] [-b bootcode] [-p diskimage] [-1234] [disk]\n", 401 " fdisk -f configfile [-itv] [disk]\n"); 402 exit(1); 403 } 404 405 static int 406 expand_table(void) 407 { 408 struct dos_partition *part; 409 struct dos_partition *best; 410 int i; 411 412 printf("\n"); 413 414 best = NULL; 415 for (i = 0; i < NDOSPART; ++i) { 416 part = ((struct dos_partition *) &mboot.parts) + i; 417 if (part->dp_start == 0 && part->dp_size == 0) 418 continue; 419 if (best == NULL || best->dp_start < part->dp_start) 420 best = part; 421 } 422 if (best) { 423 if (best->dp_typ == 0xEE || best->dp_typ == 0xEF) { 424 printf("Cannot use fdisk to resize a GPT label\n"); 425 printf("use 'gpt expand <device>' instead\n"); 426 best = NULL; 427 return 0; 428 } 429 if (disksecs - best->dp_start > 0xFFFFFFFFU) { 430 if (best->dp_size == 0xFFFFFFFFU) { 431 printf("Last slice already using max value, " 432 "no changes required\n"); 433 best = NULL; 434 } 435 } else if (best->dp_size == rounddown(disksecs, dos_cylsecs) - 436 best->dp_start) { 437 printf("Last slice already properly sized, " 438 "no changes required\n"); 439 best = NULL; 440 } 441 } 442 443 if (best) { 444 printf("Changing size of last slice %u -> ", best->dp_start); 445 if (disksecs - best->dp_start > 0xFFFFFFFFU) { 446 printf("max-value\n"); 447 best->dp_size = 0xFFFFFFFFU; 448 } else { 449 best->dp_size = rounddown(disksecs, dos_cylsecs) - 450 best->dp_start; 451 printf("%u\n", best->dp_size); 452 } 453 dos(best); 454 return 1; 455 } 456 return 0; 457 } 458 459 static void 460 print_s0(int which) 461 { 462 int i; 463 464 print_params(); 465 printf("Information from DOS bootblock is:\n"); 466 if (which == -1) 467 for (i = 1; i <= NDOSPART; i++) 468 printf("%d: ", i), print_part(i); 469 else 470 print_part(which); 471 } 472 473 static struct dos_partition mtpart = { 0 }; 474 475 static void 476 print_part(int i) 477 { 478 struct dos_partition *partp; 479 uint64_t part_mb; 480 481 partp = ((struct dos_partition *) &mboot.parts) + i - 1; 482 483 if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) { 484 printf("<UNUSED>\n"); 485 return; 486 } 487 /* 488 * Be careful not to overflow. 489 */ 490 part_mb = partp->dp_size; 491 part_mb *= secsize; 492 part_mb /= (1024 * 1024); 493 printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ)); 494 printf(" start %lu, size %lu (%jd Meg), flag %x%s\n", 495 (u_long)partp->dp_start, 496 (u_long)partp->dp_size, 497 (intmax_t)part_mb, 498 partp->dp_flag, 499 partp->dp_flag == ACTIVE ? " (active)" : ""); 500 printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n" 501 ,DPCYL(partp->dp_scyl, partp->dp_ssect) 502 ,partp->dp_shd 503 ,DPSECT(partp->dp_ssect) 504 ,DPCYL(partp->dp_ecyl, partp->dp_esect) 505 ,partp->dp_ehd 506 ,DPSECT(partp->dp_esect)); 507 } 508 509 510 static void 511 init_boot(void) 512 { 513 const char *fname; 514 int boot_fd, n; 515 struct stat sb; 516 517 fname = b_flag ? b_flag : "/boot/mbr"; 518 if ((boot_fd = open(fname, O_RDONLY)) == -1 || 519 fstat(boot_fd, &sb) == -1) 520 err(1, "%s", fname); 521 if ((mboot.bootinst_size = sb.st_size) % secsize != 0) 522 { 523 close(boot_fd); 524 errx(1, "%s: length must be a multiple of sector size", fname); 525 } 526 if (mboot.bootinst != NULL) 527 free(mboot.bootinst); 528 if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL) 529 { 530 close(boot_fd); 531 errx(1, "%s: unable to allocate read buffer", fname); 532 } 533 if ((n = read(boot_fd, mboot.bootinst, mboot.bootinst_size)) == -1 || 534 close(boot_fd)) 535 err(1, "%s", fname); 536 if (n != mboot.bootinst_size) 537 { 538 close(boot_fd); 539 errx(1, "%s: short read", fname); 540 } 541 } 542 543 544 static void 545 init_sector0(unsigned long start) 546 { 547 struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]); 548 549 init_boot(); 550 551 partp->dp_typ = DOSPTYP_DFLYBSD; 552 partp->dp_flag = ACTIVE; 553 start = roundup(start, dos_sectors); 554 if (start == 0) 555 start = dos_sectors; 556 partp->dp_start = start; 557 if (disksecs - start > 0xFFFFFFFFU) { 558 printf("Warning: Ending logical block > 2TB, using max value\n"); 559 partp->dp_size = 0xFFFFFFFFU; 560 } else { 561 partp->dp_size = rounddown(disksecs, dos_cylsecs) - start; 562 } 563 564 dos(partp); 565 } 566 567 static void 568 change_part(int i) 569 { 570 struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i - 1; 571 572 printf("The data for partition %d is:\n", i); 573 print_part(i); 574 575 if (u_flag && ok("Do you want to change it?")) { 576 int tmp; 577 578 if (i_flag) { 579 bzero((char *)partp, sizeof (struct dos_partition)); 580 if (i == 4) { 581 init_sector0(1); 582 printf("\nThe static data for the DOS partition 4 has been reinitialized to:\n"); 583 print_part(i); 584 } 585 } 586 587 do { 588 Decimal("sysid (108=DragonFly)", partp->dp_typ, tmp); 589 Decimal("start", partp->dp_start, tmp); 590 Decimal("size", partp->dp_size, tmp); 591 if (!sanitize_partition(partp)) { 592 warnx("ERROR: failed to adjust; setting sysid to 0"); 593 partp->dp_typ = 0; 594 } 595 596 if (ok("Explicitly specify beg/end address ?")) 597 { 598 int tsec,tcyl,thd; 599 tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect); 600 thd = partp->dp_shd; 601 tsec = DPSECT(partp->dp_ssect); 602 Decimal("beginning cylinder", tcyl, tmp); 603 Decimal("beginning head", thd, tmp); 604 Decimal("beginning sector", tsec, tmp); 605 if (tcyl > MAXCYL && C_flag == 0) { 606 printf("Warning: starting cylinder wraps, using all 1's\n"); 607 partp->dp_scyl = -1; 608 partp->dp_ssect = -1; 609 partp->dp_shd = -1; 610 } else { 611 partp->dp_scyl = DOSCYL(tcyl); 612 partp->dp_ssect = DOSSECT(tsec,tcyl); 613 partp->dp_shd = thd; 614 } 615 616 tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect); 617 thd = partp->dp_ehd; 618 tsec = DPSECT(partp->dp_esect); 619 Decimal("ending cylinder", tcyl, tmp); 620 Decimal("ending head", thd, tmp); 621 Decimal("ending sector", tsec, tmp); 622 if (tcyl > MAXCYL && C_flag == 0) { 623 printf("Warning: ending cylinder wraps, using all 1's\n"); 624 partp->dp_ecyl = -1; 625 partp->dp_esect = -1; 626 partp->dp_ehd = -1; 627 } else { 628 partp->dp_ecyl = DOSCYL(tcyl); 629 partp->dp_esect = DOSSECT(tsec,tcyl); 630 partp->dp_ehd = thd; 631 } 632 } else 633 dos(partp); 634 635 print_part(i); 636 } while (!ok("Are we happy with this entry?")); 637 } 638 } 639 640 static void 641 print_params(void) 642 { 643 printf("parameters extracted from device are:\n"); 644 printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n" 645 ,cyls,heads,sectors,cylsecs); 646 if ((dos_sectors > MAX_SECTORS_PER_TRACK) || (dos_cyls > MAXCYL) || (dos_heads > MAX_HEADS)) 647 printf("Figures below won't work with BIOS for partitions not in cyl 1\n"); 648 printf("parameters to be used for BIOS calculations are:\n"); 649 printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n" 650 ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs); 651 } 652 653 static void 654 change_active(int which) 655 { 656 struct dos_partition *partp = &mboot.parts[0]; 657 int active, i, new, tmp; 658 659 active = -1; 660 for (i = 0; i < NDOSPART; i++) { 661 if ((partp[i].dp_flag & ACTIVE) == 0) 662 continue; 663 printf("Partition %d is marked active\n", i + 1); 664 if (active == -1) 665 active = i + 1; 666 } 667 if (a_flag && which != -1) 668 active = which; 669 else if (active == -1) 670 active = 1; 671 672 if (!ok("Do you want to change the active partition?")) 673 return; 674 setactive: 675 do { 676 new = active; 677 Decimal("active partition", new, tmp); 678 if (new < 1 || new > NDOSPART) { 679 printf("Active partition number must be in range 1-4." 680 " Try again.\n"); 681 goto setactive; 682 } 683 active = new; 684 } while (!ok("Are you happy with this choice")); 685 for (i = 0; i < NDOSPART; i++) 686 partp[i].dp_flag = 0; 687 if (active > 0 && active <= NDOSPART) 688 partp[active-1].dp_flag = ACTIVE; 689 } 690 691 static void 692 change_code(void) 693 { 694 if (ok("Do you want to change the boot code?")) 695 init_boot(); 696 } 697 698 void 699 get_params_to_use(void) 700 { 701 int tmp; 702 print_params(); 703 if (ok("Do you want to change our idea of what BIOS thinks ?")) 704 { 705 do 706 { 707 Decimal("BIOS's idea of #cylinders", dos_cyls, tmp); 708 Decimal("BIOS's idea of #heads", dos_heads, tmp); 709 Decimal("BIOS's idea of #sectors", dos_sectors, tmp); 710 dos_cylsecs = dos_heads * dos_sectors; 711 print_params(); 712 } 713 while(!ok("Are you happy with this choice")); 714 } 715 } 716 717 718 /***********************************************\ 719 * Change real numbers into strange dos numbers * 720 \***********************************************/ 721 static void 722 dos(struct dos_partition *partp) 723 { 724 int cy, sec; 725 uint32_t end; 726 727 if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) { 728 memcpy(partp, &mtpart, sizeof(*partp)); 729 return; 730 } 731 732 /* Start c/h/s. */ 733 cy = partp->dp_start / dos_cylsecs; 734 sec = partp->dp_start % dos_sectors + 1; 735 if (cy > MAXCYL && C_flag == 0) { 736 printf("Warning: starting cylinder wraps, using all 1's\n"); 737 partp->dp_shd = -1; 738 partp->dp_scyl = -1; 739 partp->dp_ssect = -1; 740 } else { 741 partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors; 742 partp->dp_scyl = DOSCYL(cy); 743 partp->dp_ssect = DOSSECT(sec, cy); 744 } 745 746 /* End c/h/s. */ 747 end = partp->dp_start + partp->dp_size - 1; 748 cy = end / dos_cylsecs; 749 sec = end % dos_sectors + 1; 750 if (cy > MAXCYL && C_flag == 0) { 751 printf("Warning: ending cylinder wraps, using all 1's\n"); 752 partp->dp_ehd = -1; 753 partp->dp_ecyl = -1; 754 partp->dp_esect = -1; 755 } else { 756 partp->dp_ehd = end % dos_cylsecs / dos_sectors; 757 partp->dp_ecyl = DOSCYL(cy); 758 partp->dp_esect = DOSSECT(sec, cy); 759 } 760 } 761 762 static void 763 erase_partition(int i) 764 { 765 struct dos_partition *partp; 766 off_t ioarg[2]; 767 768 char *dev_name = strdup(disk); 769 770 dev_name = strtok(dev_name + strlen("/dev/da"),"s"); 771 #if 0 772 int trim_enabled = 0; 773 char sysctl_name[64]; 774 size_t olen = sizeof(trim_enabled); 775 776 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name); 777 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) { 778 printf("Device:%s does not support the TRIM command\n", disk); 779 usage(); 780 } 781 if (!trim_enabled) { 782 printf("Erase device option selected, but sysctl (%s) " 783 "is not enabled\n",sysctl_name); 784 usage(); 785 } 786 #endif 787 partp = ((struct dos_partition *) &mboot.parts) + i; 788 printf("erase sectors:%u %u\n", 789 partp->dp_start, 790 partp->dp_size); 791 792 /* Trim the Device */ 793 ioarg[0] = partp->dp_start; 794 ioarg[0] *=secsize; 795 ioarg[1] = partp->dp_size; 796 ioarg[1] *=secsize; 797 798 if (ioctl(fd, DAIOCTRIM, ioarg) < 0) { 799 printf("Device trim failed\n"); 800 usage (); 801 } 802 } 803 804 /* Getting device status */ 805 806 static int 807 open_disk(void) 808 { 809 struct stat st; 810 811 if (stat(disk, &st) == -1) { 812 if (errno == ENOENT) 813 return -2; 814 warnx("can't get file status of %s", disk); 815 return -1; 816 } 817 if (!(st.st_mode & S_IFCHR) && p_flag == 0) 818 warnx("device %s is not character special", disk); 819 if ((fd = open(disk, 820 (x_flag || a_flag || I_flag || 821 B_flag || u_flag) ? O_RDWR : O_RDONLY)) == -1) { 822 if (errno == ENXIO) 823 return -2; 824 warnx("can't open device %s", disk); 825 return -1; 826 } 827 if (get_params() == -1) { 828 warnx("can't get disk parameters on %s", disk); 829 return -1; 830 } 831 return fd; 832 } 833 834 static ssize_t 835 read_disk(off_t sector, void *buf) 836 { 837 lseek(fd,(sector * 512), 0); 838 if (secsize == 0) 839 for(secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE; secsize *= 2) 840 { 841 /* try the read */ 842 int size = read(fd, buf, secsize); 843 if (size == secsize) 844 /* it worked so return */ 845 return secsize; 846 } 847 else 848 return read(fd, buf, secsize); 849 850 /* we failed to read at any of the sizes */ 851 return -1; 852 } 853 854 static ssize_t 855 write_disk(off_t sector, void *buf) 856 { 857 lseek(fd,(sector * 512), 0); 858 /* write out in the size that the read_disk found worked */ 859 return write(fd, buf, secsize); 860 } 861 862 static int 863 get_params(void) 864 { 865 struct partinfo partinfo; /* disk parameters */ 866 struct stat st; 867 868 /* 869 * NOTE: When faking up the CHS for a file image (e.g. for USB), 870 * we must use max values. If we do not then an overflowed 871 * cylinder count will, by convention, set the CHS fields to 872 * all 1's. The heads and sectors in the CHS fields will then 873 * exceed the basic geometry which can cause BIOSes to brick. 874 */ 875 if (ioctl(fd, DIOCGPART, &partinfo) == -1) { 876 if (p_flag && fstat(fd, &st) == 0 && st.st_size) { 877 sectors = MAX_SECTORS_PER_TRACK; 878 heads = MAX_HEADS; 879 cylsecs = heads * sectors; 880 cyls = st.st_size / 512 / cylsecs; 881 } else { 882 warnx("can't get disk parameters on %s; supplying dummy ones", 883 disk); 884 heads = 1; 885 cylsecs = heads * sectors; 886 } 887 } else { 888 cyls = partinfo.d_ncylinders; 889 heads = partinfo.d_nheads; 890 sectors = partinfo.d_secpertrack; 891 cylsecs = heads * sectors; 892 secsize = partinfo.media_blksize; 893 } 894 dos_cyls = cyls; 895 dos_heads = heads; 896 dos_sectors = sectors; 897 dos_cylsecs = cylsecs; 898 disksecs = (int64_t)cyls * heads * sectors; 899 return (disksecs); 900 } 901 902 903 static int 904 read_s0(void) 905 { 906 mboot.bootinst_size = secsize; 907 if (mboot.bootinst != NULL) 908 free(mboot.bootinst); 909 if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) { 910 warnx("unable to allocate buffer to read fdisk " 911 "partition table"); 912 return -1; 913 } 914 if (read_disk(0, mboot.bootinst) == -1) { 915 warnx("can't read fdisk partition table"); 916 return -1; 917 } 918 if (*(uint16_t *)&mboot.bootinst[DOSMAGICOFF] != DOSMAGIC) { 919 warnx("invalid fdisk partition table found"); 920 /* So should we initialize things */ 921 return -1; 922 } 923 memcpy(mboot.parts, &mboot.bootinst[DOSPARTOFF], sizeof(mboot.parts)); 924 return 0; 925 } 926 927 static int 928 write_s0(void) 929 { 930 #ifdef NOT_NOW 931 int flag = 1; 932 #endif 933 int sector; 934 935 memcpy(&mboot.bootinst[DOSPARTOFF], mboot.parts, sizeof(mboot.parts)); 936 /* 937 * write enable label sector before write (if necessary), 938 * disable after writing. 939 * needed if the disklabel protected area also protects 940 * sector 0. (e.g. empty disk) 941 */ 942 #ifdef NOT_NOW 943 if (ioctl(fd, DIOCWLABEL, &flag) < 0) 944 warn("ioctl DIOCWLABEL"); 945 #endif 946 for(sector = 0; sector < mboot.bootinst_size / secsize; sector++) 947 if (write_disk(sector, 948 &mboot.bootinst[sector * secsize]) == -1) { 949 warn("can't write fdisk partition table"); 950 #ifdef NOT_NOW 951 flag = 0; 952 ioctl(fd, DIOCWLABEL, &flag); 953 #endif 954 return -1; 955 } 956 #ifdef NOT_NOW 957 flag = 0; 958 ioctl(fd, DIOCWLABEL, &flag); 959 #endif 960 return(0); 961 } 962 963 964 static int 965 ok(const char *str) 966 { 967 printf("%s [n] ", str); 968 fflush(stdout); 969 if (fgets(lbuf, LBUF, stdin) == NULL) 970 exit(1); 971 lbuf[strlen(lbuf)-1] = 0; 972 973 if (*lbuf && 974 (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") || 975 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y"))) 976 return 1; 977 else 978 return 0; 979 } 980 981 static int 982 decimal(const char *str, int *num, int deflt) 983 { 984 int acc = 0, c; 985 char *cp; 986 987 while (1) { 988 printf("Supply a decimal value for \"%s\" [%d] ", str, deflt); 989 fflush(stdout); 990 if (fgets(lbuf, LBUF, stdin) == NULL) 991 exit(1); 992 lbuf[strlen(lbuf)-1] = 0; 993 994 if (!*lbuf) 995 return 0; 996 997 cp = lbuf; 998 while ((c = *cp) && (c == ' ' || c == '\t')) cp++; 999 if (!c) 1000 return 0; 1001 while ((c = *cp++)) { 1002 if (c <= '9' && c >= '0') 1003 acc = acc * 10 + c - '0'; 1004 else 1005 break; 1006 } 1007 if (c == ' ' || c == '\t') 1008 while ((c = *cp) && (c == ' ' || c == '\t')) cp++; 1009 if (!c) { 1010 *num = acc; 1011 return 1; 1012 } else 1013 printf("%s is an invalid decimal number. Try again.\n", 1014 lbuf); 1015 } 1016 1017 } 1018 1019 static const char * 1020 get_type(int type) 1021 { 1022 int numentries = NELEM(dos_ptypes); 1023 int counter = 0; 1024 const struct dos_ptype *ptr = dos_ptypes; 1025 1026 1027 while(counter < numentries) 1028 { 1029 if (ptr->type == type) 1030 { 1031 return(ptr->name); 1032 } 1033 ptr++; 1034 counter++; 1035 } 1036 return("unknown"); 1037 } 1038 1039 1040 static void 1041 parse_config_line(char *line, CMD *command) 1042 { 1043 char *cp, *end; 1044 1045 cp = line; 1046 while (1) /* dirty trick used to insure one exit point for this 1047 function */ 1048 { 1049 memset(command, 0, sizeof(*command)); 1050 1051 while (isspace(*cp)) ++cp; 1052 if (*cp == '\0' || *cp == '#') 1053 { 1054 break; 1055 } 1056 command->cmd = *cp++; 1057 1058 /* 1059 * Parse args 1060 */ 1061 while (1) 1062 { 1063 while (isspace(*cp)) ++cp; 1064 if (*cp == '#') 1065 { 1066 break; /* found comment */ 1067 } 1068 if (isalpha(*cp)) 1069 { 1070 command->args[command->n_args].argtype = *cp++; 1071 } 1072 if (!isdigit(*cp)) 1073 { 1074 break; /* assume end of line */ 1075 } 1076 end = NULL; 1077 command->args[command->n_args].arg_val = strtoll(cp, &end, 0); 1078 if (cp == end) 1079 { 1080 break; /* couldn't parse number */ 1081 } 1082 cp = end; 1083 command->n_args++; 1084 } 1085 break; 1086 } 1087 } 1088 1089 1090 static int 1091 process_geometry(CMD *command) 1092 { 1093 int status = 1, i; 1094 1095 while (1) 1096 { 1097 geom_processed = 1; 1098 if (part_processed) 1099 { 1100 warnx( 1101 "ERROR line %d: the geometry specification line must occur before\n\ 1102 all partition specifications", 1103 current_line_number); 1104 status = 0; 1105 break; 1106 } 1107 if (command->n_args != 3) 1108 { 1109 warnx("ERROR line %d: incorrect number of geometry args", 1110 current_line_number); 1111 status = 0; 1112 break; 1113 } 1114 dos_cyls = -1; 1115 dos_heads = -1; 1116 dos_sectors = -1; 1117 for (i = 0; i < 3; ++i) 1118 { 1119 switch (command->args[i].argtype) 1120 { 1121 case 'c': 1122 dos_cyls = command->args[i].arg_val; 1123 break; 1124 case 'h': 1125 dos_heads = command->args[i].arg_val; 1126 break; 1127 case 's': 1128 dos_sectors = command->args[i].arg_val; 1129 break; 1130 default: 1131 warnx( 1132 "ERROR line %d: unknown geometry arg type: '%c' (0x%02x)", 1133 current_line_number, command->args[i].argtype, 1134 command->args[i].argtype); 1135 status = 0; 1136 break; 1137 } 1138 } 1139 if (status == 0) 1140 { 1141 break; 1142 } 1143 1144 dos_cylsecs = dos_heads * dos_sectors; 1145 1146 /* 1147 * Do sanity checks on parameter values 1148 */ 1149 if (dos_cyls < 0) 1150 { 1151 warnx("ERROR line %d: number of cylinders not specified", 1152 current_line_number); 1153 status = 0; 1154 } 1155 if (dos_cyls == 0 || dos_cyls > 1024) 1156 { 1157 warnx( 1158 "WARNING line %d: number of cylinders (%d) may be out-of-range\n\ 1159 (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\ 1160 is dedicated to DragonFly)", 1161 current_line_number, dos_cyls); 1162 } 1163 1164 if (dos_heads < 0) 1165 { 1166 warnx("ERROR line %d: number of heads not specified", 1167 current_line_number); 1168 status = 0; 1169 } 1170 else if (dos_heads < 1 || dos_heads > 256) 1171 { 1172 warnx("ERROR line %d: number of heads must be within (1-256)", 1173 current_line_number); 1174 status = 0; 1175 } 1176 1177 if (dos_sectors < 0) 1178 { 1179 warnx("ERROR line %d: number of sectors not specified", 1180 current_line_number); 1181 status = 0; 1182 } 1183 else if (dos_sectors < MIN_SECTORS_PER_TRACK || dos_sectors > MAX_SECTORS_PER_TRACK) 1184 { 1185 warnx("ERROR line %d: number of sectors must be within (1-63)", 1186 current_line_number); 1187 status = 0; 1188 } 1189 1190 break; 1191 } 1192 return (status); 1193 } 1194 1195 1196 static int 1197 process_partition(CMD *command) 1198 { 1199 int status = 0, part; 1200 uint32_t prev_head_boundary, prev_cyl_boundary; 1201 uint32_t adj_size, max_end; 1202 struct dos_partition *partp; 1203 1204 while (1) 1205 { 1206 part_processed = 1; 1207 if (command->n_args != 4) 1208 { 1209 warnx("ERROR line %d: incorrect number of partition args", 1210 current_line_number); 1211 break; 1212 } 1213 part = command->args[0].arg_val; 1214 if (part < 1 || part > NDOSPART) 1215 { 1216 warnx("ERROR line %d: invalid partition number %d", 1217 current_line_number, part); 1218 break; 1219 } 1220 partp = ((struct dos_partition *) &mboot.parts) + part - 1; 1221 bzero((char *)partp, sizeof (struct dos_partition)); 1222 partp->dp_typ = command->args[1].arg_val; 1223 partp->dp_start = command->args[2].arg_val; 1224 partp->dp_size = command->args[3].arg_val; 1225 max_end = partp->dp_start + partp->dp_size; 1226 1227 if (partp->dp_typ == 0) 1228 { 1229 /* 1230 * Get out, the partition is marked as unused. 1231 */ 1232 /* 1233 * Insure that it's unused. 1234 */ 1235 bzero((char *)partp, sizeof (struct dos_partition)); 1236 status = 1; 1237 break; 1238 } 1239 1240 /* 1241 * Adjust start upwards, if necessary, to fall on an head boundary. 1242 */ 1243 if (partp->dp_start % dos_sectors != 0) 1244 { 1245 prev_head_boundary = rounddown(partp->dp_start, dos_sectors); 1246 if (max_end < (uint32_t)dos_sectors || 1247 prev_head_boundary > max_end - dos_sectors) 1248 { 1249 /* 1250 * Can't go past end of partition 1251 */ 1252 warnx( 1253 "ERROR line %d: unable to adjust start of partition %d to fall on\n\ 1254 a head boundary", 1255 current_line_number, part); 1256 break; 1257 } 1258 warnx( 1259 "WARNING: adjusting start offset of partition %d\n\ 1260 from %u to %u, to fall on a head boundary", 1261 part, (u_int)partp->dp_start, 1262 (u_int)(prev_head_boundary + dos_sectors)); 1263 partp->dp_start = prev_head_boundary + dos_sectors; 1264 } 1265 1266 /* 1267 * Adjust size downwards, if necessary, to fall on a cylinder 1268 * boundary. 1269 */ 1270 prev_cyl_boundary = 1271 rounddown(partp->dp_start + partp->dp_size, dos_cylsecs); 1272 if (prev_cyl_boundary > partp->dp_start) 1273 adj_size = prev_cyl_boundary - partp->dp_start; 1274 else 1275 { 1276 warnx( 1277 "ERROR: could not adjust partition to start on a head boundary\n\ 1278 and end on a cylinder boundary."); 1279 return (0); 1280 } 1281 if (adj_size != partp->dp_size) 1282 { 1283 warnx( 1284 "WARNING: adjusting size of partition %d from %u to %u\n\ 1285 to end on a cylinder boundary", 1286 part, (u_int)partp->dp_size, (u_int)adj_size); 1287 partp->dp_size = adj_size; 1288 } 1289 if (partp->dp_size == 0) 1290 { 1291 warnx("ERROR line %d: size of partition %d is zero", 1292 current_line_number, part); 1293 break; 1294 } 1295 1296 dos(partp); 1297 status = 1; 1298 break; 1299 } 1300 return (status); 1301 } 1302 1303 1304 static int 1305 process_active(CMD *command) 1306 { 1307 int status = 0, part, i; 1308 struct dos_partition *partp; 1309 1310 while (1) 1311 { 1312 active_processed = 1; 1313 if (command->n_args != 1) 1314 { 1315 warnx("ERROR line %d: incorrect number of active args", 1316 current_line_number); 1317 status = 0; 1318 break; 1319 } 1320 part = command->args[0].arg_val; 1321 if (part < 1 || part > NDOSPART) 1322 { 1323 warnx("ERROR line %d: invalid partition number %d", 1324 current_line_number, part); 1325 break; 1326 } 1327 /* 1328 * Reset active partition 1329 */ 1330 partp = ((struct dos_partition *) &mboot.parts); 1331 for (i = 0; i < NDOSPART; i++) 1332 partp[i].dp_flag = 0; 1333 partp[part-1].dp_flag = ACTIVE; 1334 1335 status = 1; 1336 break; 1337 } 1338 return (status); 1339 } 1340 1341 1342 static int 1343 process_line(char *line) 1344 { 1345 CMD command; 1346 int status = 1; 1347 1348 while (1) 1349 { 1350 parse_config_line(line, &command); 1351 switch (command.cmd) 1352 { 1353 case 0: 1354 /* 1355 * Comment or blank line 1356 */ 1357 break; 1358 case 'g': 1359 /* 1360 * Set geometry 1361 */ 1362 status = process_geometry(&command); 1363 break; 1364 case 'p': 1365 status = process_partition(&command); 1366 break; 1367 case 'a': 1368 status = process_active(&command); 1369 break; 1370 default: 1371 status = 0; 1372 break; 1373 } 1374 break; 1375 } 1376 return (status); 1377 } 1378 1379 1380 static int 1381 read_config(char *config_file) 1382 { 1383 FILE *fp = NULL; 1384 int status = 1; 1385 char buf[1010]; 1386 1387 while (1) /* dirty trick used to insure one exit point for this 1388 function */ 1389 { 1390 if (strcmp(config_file, "-") != 0) 1391 { 1392 /* 1393 * We're not reading from stdin 1394 */ 1395 if ((fp = fopen(config_file, "r")) == NULL) 1396 { 1397 status = 0; 1398 break; 1399 } 1400 } 1401 else 1402 { 1403 fp = stdin; 1404 } 1405 current_line_number = 0; 1406 while (!feof(fp)) 1407 { 1408 if (fgets(buf, sizeof(buf), fp) == NULL) 1409 { 1410 break; 1411 } 1412 ++current_line_number; 1413 status = process_line(buf); 1414 if (status == 0) 1415 { 1416 break; 1417 } 1418 } 1419 break; 1420 } 1421 if (fp) 1422 { 1423 /* 1424 * It doesn't matter if we're reading from stdin, as we've reached EOF 1425 */ 1426 fclose(fp); 1427 } 1428 return (status); 1429 } 1430 1431 1432 static void 1433 reset_boot(void) 1434 { 1435 int i; 1436 struct dos_partition *partp; 1437 1438 init_boot(); 1439 for (i = 0; i < NDOSPART; ++i) 1440 { 1441 partp = ((struct dos_partition *) &mboot.parts) + i; 1442 bzero((char *)partp, sizeof (struct dos_partition)); 1443 } 1444 } 1445 1446 static int 1447 sanitize_partition(struct dos_partition *partp) 1448 { 1449 uint32_t prev_head_boundary, prev_cyl_boundary; 1450 uint32_t max_end, size, start; 1451 1452 start = partp->dp_start; 1453 size = partp->dp_size; 1454 max_end = start + size; 1455 /* Only allow a zero size if the partition is being marked unused. */ 1456 if (size == 0) { 1457 if (start == 0 && partp->dp_typ == 0) 1458 return (1); 1459 warnx("ERROR: size of partition is zero"); 1460 return (0); 1461 } 1462 /* Return if no adjustment is necessary. */ 1463 if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0) 1464 return (1); 1465 1466 if (start == 0) { 1467 warnx("WARNING: partition overlaps with partition table"); 1468 if (ok("Correct this automatically?")) 1469 start = dos_sectors; 1470 } 1471 if (start % dos_sectors != 0) 1472 warnx("WARNING: partition does not start on a head boundary"); 1473 if ((start +size) % dos_sectors != 0) 1474 warnx("WARNING: partition does not end on a cylinder boundary"); 1475 warnx("WARNING: this may confuse the BIOS or some operating systems"); 1476 if (!ok("Correct this automatically?")) 1477 return (1); 1478 1479 /* 1480 * Adjust start upwards, if necessary, to fall on an head boundary. 1481 */ 1482 if (start % dos_sectors != 0) { 1483 prev_head_boundary = rounddown(start, dos_sectors); 1484 if (max_end < (uint32_t)dos_sectors || 1485 prev_head_boundary >= max_end - dos_sectors) { 1486 /* 1487 * Can't go past end of partition 1488 */ 1489 warnx( 1490 "ERROR: unable to adjust start of partition to fall on a head boundary"); 1491 return (0); 1492 } 1493 start = prev_head_boundary + dos_sectors; 1494 } 1495 1496 /* 1497 * Adjust size downwards, if necessary, to fall on a cylinder 1498 * boundary. 1499 */ 1500 prev_cyl_boundary = rounddown(start + size, dos_cylsecs); 1501 if (prev_cyl_boundary > start) 1502 size = prev_cyl_boundary - start; 1503 else { 1504 warnx("ERROR: could not adjust partition to start on a head boundary\n\ 1505 and end on a cylinder boundary."); 1506 return (0); 1507 } 1508 1509 /* Finally, commit any changes to partp and return. */ 1510 if (start != partp->dp_start) { 1511 warnx("WARNING: adjusting start offset of partition to %u", 1512 (u_int)start); 1513 partp->dp_start = start; 1514 } 1515 if (size != partp->dp_size) { 1516 warnx("WARNING: adjusting size of partition to %u", (u_int)size); 1517 partp->dp_size = size; 1518 } 1519 1520 return (1); 1521 } 1522