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 if (read_s0()) 282 err(1, "read_s0"); 283 reset_boot(); 284 partp = (struct dos_partition *) (&mboot.parts[0]); 285 partp->dp_typ = DOSPTYP_DFLYBSD; 286 partp->dp_flag = ACTIVE; 287 partp->dp_start = dos_sectors; 288 if (disksecs - dos_sectors > 0xFFFFFFFFU) { 289 printf("Warning: Ending logical block > 2TB, using max value\n"); 290 partp->dp_size = 0xFFFFFFFFU; 291 } else { 292 partp->dp_size = 293 rounddown(disksecs, dos_cylsecs) - dos_sectors; 294 } 295 dos(partp); 296 if (v_flag) 297 print_s0(-1); 298 299 if (E_flag) { 300 /* Trim now if we're using the entire device */ 301 erase_partition(0); 302 } 303 304 if (!t_flag) 305 if (write_s0() == -1) 306 err(1, "can't write fdisk partition table"); 307 exit(0); 308 } 309 if (f_flag) 310 { 311 if (read_s0() || i_flag) 312 { 313 reset_boot(); 314 } 315 316 if (!read_config(f_flag)) 317 { 318 exit(1); 319 } 320 if (x_flag) 321 x_flag = expand_table(); 322 if (v_flag) 323 { 324 print_s0(-1); 325 } 326 if (!t_flag) 327 { 328 if (write_s0() == -1) 329 err(1, "can't write fdisk partition table"); 330 } 331 } 332 else 333 { 334 if (u_flag) 335 { 336 get_params_to_use(); 337 } 338 else 339 { 340 print_params(); 341 } 342 343 if (read_s0()) 344 init_sector0(dos_sectors); 345 346 printf("Media sector size is %d\n", secsize); 347 printf("Warning: BIOS sector numbering starts with sector 1\n"); 348 printf("Information from DOS bootblock is:\n"); 349 if (partition == -1) 350 for (i = 1; i <= NDOSPART; i++) 351 change_part(i); 352 else 353 change_part(partition); 354 355 if (u_flag || a_flag) 356 change_active(partition); 357 358 if (B_flag) 359 change_code(); 360 361 if (x_flag) 362 x_flag = expand_table(); 363 364 if (x_flag || u_flag || a_flag || B_flag) { 365 if (!t_flag) { 366 printf("\nWe haven't changed the partition table yet. "); 367 printf("This is your last chance.\n"); 368 } 369 print_s0(-1); 370 if (!t_flag) { 371 if (ok("Should we write new partition table?")) { 372 if (E_flag && u_flag) { 373 /* 374 * Trim now because we've committed to 375 * updating the partition. 376 */ 377 if (partition == -1) 378 for (i = 0; i < NDOSPART; i++) 379 erase_partition(i); 380 else 381 erase_partition(partition); 382 } 383 if (write_s0() == -1) 384 err(1, "can't write fdisk partition table"); 385 } 386 } 387 else 388 { 389 printf("\n-t flag specified -- partition table not written.\n"); 390 } 391 } 392 } 393 394 exit(0); 395 } 396 397 static void 398 usage(void) 399 { 400 fprintf(stderr, "%s%s", 401 "usage: fdisk [-BCEIaistu] [-b bootcode] [-p diskimage] [-1234] [disk]\n", 402 " fdisk -f configfile [-itv] [disk]\n"); 403 exit(1); 404 } 405 406 static int 407 expand_table(void) 408 { 409 struct dos_partition *part; 410 struct dos_partition *best; 411 int i; 412 413 printf("\n"); 414 415 best = NULL; 416 for (i = 0; i < NDOSPART; ++i) { 417 part = ((struct dos_partition *) &mboot.parts) + i; 418 if (part->dp_start == 0 && part->dp_size == 0) 419 continue; 420 if (best == NULL || best->dp_start < part->dp_start) 421 best = part; 422 } 423 if (best) { 424 if (best->dp_typ == 0xEE || best->dp_typ == 0xEF) { 425 printf("Cannot use fdisk to resize a GPT label\n"); 426 printf("use 'gpt expand <device>' instead\n"); 427 best = NULL; 428 return 0; 429 } 430 if (disksecs - best->dp_start > 0xFFFFFFFFU) { 431 if (best->dp_size == 0xFFFFFFFFU) { 432 printf("Last slice already using max value, " 433 "no changes required\n"); 434 best = NULL; 435 } 436 } else if (best->dp_size == rounddown(disksecs, dos_cylsecs) - 437 best->dp_start) { 438 printf("Last slice already properly sized, " 439 "no changes required\n"); 440 best = NULL; 441 } 442 } 443 444 if (best) { 445 printf("Changing size of last slice %u -> ", best->dp_start); 446 if (disksecs - best->dp_start > 0xFFFFFFFFU) { 447 printf("max-value\n"); 448 best->dp_size = 0xFFFFFFFFU; 449 } else { 450 best->dp_size = rounddown(disksecs, dos_cylsecs) - 451 best->dp_start; 452 printf("%u\n", best->dp_size); 453 } 454 dos(best); 455 return 1; 456 } 457 return 0; 458 } 459 460 static void 461 print_s0(int which) 462 { 463 int i; 464 465 print_params(); 466 printf("Information from DOS bootblock is:\n"); 467 if (which == -1) 468 for (i = 1; i <= NDOSPART; i++) 469 printf("%d: ", i), print_part(i); 470 else 471 print_part(which); 472 } 473 474 static struct dos_partition mtpart = { 0 }; 475 476 static void 477 print_part(int i) 478 { 479 struct dos_partition *partp; 480 uint64_t part_mb; 481 482 partp = ((struct dos_partition *) &mboot.parts) + i - 1; 483 484 if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) { 485 printf("<UNUSED>\n"); 486 return; 487 } 488 /* 489 * Be careful not to overflow. 490 */ 491 part_mb = partp->dp_size; 492 part_mb *= secsize; 493 part_mb /= (1024 * 1024); 494 printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ)); 495 printf(" start %lu, size %lu (%jd Meg), flag %x%s\n", 496 (u_long)partp->dp_start, 497 (u_long)partp->dp_size, 498 (intmax_t)part_mb, 499 partp->dp_flag, 500 partp->dp_flag == ACTIVE ? " (active)" : ""); 501 printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n" 502 ,DPCYL(partp->dp_scyl, partp->dp_ssect) 503 ,partp->dp_shd 504 ,DPSECT(partp->dp_ssect) 505 ,DPCYL(partp->dp_ecyl, partp->dp_esect) 506 ,partp->dp_ehd 507 ,DPSECT(partp->dp_esect)); 508 } 509 510 511 static void 512 init_boot(void) 513 { 514 const char *fname; 515 int boot_fd, n; 516 struct stat sb; 517 518 fname = b_flag ? b_flag : "/boot/mbr"; 519 if ((boot_fd = open(fname, O_RDONLY)) == -1 || 520 fstat(boot_fd, &sb) == -1) 521 err(1, "%s", fname); 522 if ((mboot.bootinst_size = sb.st_size) % secsize != 0) 523 { 524 close(boot_fd); 525 errx(1, "%s: length must be a multiple of sector size", fname); 526 } 527 if (mboot.bootinst != NULL) 528 free(mboot.bootinst); 529 if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL) 530 { 531 close(boot_fd); 532 errx(1, "%s: unable to allocate read buffer", fname); 533 } 534 if ((n = read(boot_fd, mboot.bootinst, mboot.bootinst_size)) == -1 || 535 close(boot_fd)) 536 err(1, "%s", fname); 537 if (n != mboot.bootinst_size) 538 { 539 close(boot_fd); 540 errx(1, "%s: short read", fname); 541 } 542 } 543 544 545 static void 546 init_sector0(unsigned long start) 547 { 548 struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]); 549 550 init_boot(); 551 552 partp->dp_typ = DOSPTYP_DFLYBSD; 553 partp->dp_flag = ACTIVE; 554 start = roundup(start, dos_sectors); 555 if (start == 0) 556 start = dos_sectors; 557 partp->dp_start = start; 558 if (disksecs - start > 0xFFFFFFFFU) { 559 printf("Warning: Ending logical block > 2TB, using max value\n"); 560 partp->dp_size = 0xFFFFFFFFU; 561 } else { 562 partp->dp_size = rounddown(disksecs, dos_cylsecs) - start; 563 } 564 565 dos(partp); 566 } 567 568 static void 569 change_part(int i) 570 { 571 struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i - 1; 572 573 printf("The data for partition %d is:\n", i); 574 print_part(i); 575 576 if (u_flag && ok("Do you want to change it?")) { 577 int tmp; 578 579 if (i_flag) { 580 bzero((char *)partp, sizeof (struct dos_partition)); 581 if (i == 4) { 582 init_sector0(1); 583 printf("\nThe static data for the DOS partition 4 has been reinitialized to:\n"); 584 print_part(i); 585 } 586 } 587 588 do { 589 Decimal("sysid (108=DragonFly)", partp->dp_typ, tmp); 590 Decimal("start", partp->dp_start, tmp); 591 Decimal("size", partp->dp_size, tmp); 592 if (!sanitize_partition(partp)) { 593 warnx("ERROR: failed to adjust; setting sysid to 0"); 594 partp->dp_typ = 0; 595 } 596 597 if (ok("Explicitly specify beg/end address ?")) 598 { 599 int tsec,tcyl,thd; 600 tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect); 601 thd = partp->dp_shd; 602 tsec = DPSECT(partp->dp_ssect); 603 Decimal("beginning cylinder", tcyl, tmp); 604 Decimal("beginning head", thd, tmp); 605 Decimal("beginning sector", tsec, tmp); 606 if (tcyl > MAXCYL && C_flag == 0) { 607 printf("Warning: starting cylinder wraps, using all 1's\n"); 608 partp->dp_scyl = -1; 609 partp->dp_ssect = -1; 610 partp->dp_shd = -1; 611 } else { 612 partp->dp_scyl = DOSCYL(tcyl); 613 partp->dp_ssect = DOSSECT(tsec,tcyl); 614 partp->dp_shd = thd; 615 } 616 617 tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect); 618 thd = partp->dp_ehd; 619 tsec = DPSECT(partp->dp_esect); 620 Decimal("ending cylinder", tcyl, tmp); 621 Decimal("ending head", thd, tmp); 622 Decimal("ending sector", tsec, tmp); 623 if (tcyl > MAXCYL && C_flag == 0) { 624 printf("Warning: ending cylinder wraps, using all 1's\n"); 625 partp->dp_ecyl = -1; 626 partp->dp_esect = -1; 627 partp->dp_ehd = -1; 628 } else { 629 partp->dp_ecyl = DOSCYL(tcyl); 630 partp->dp_esect = DOSSECT(tsec,tcyl); 631 partp->dp_ehd = thd; 632 } 633 } else 634 dos(partp); 635 636 print_part(i); 637 } while (!ok("Are we happy with this entry?")); 638 } 639 } 640 641 static void 642 print_params(void) 643 { 644 printf("parameters extracted from device are:\n"); 645 printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n" 646 ,cyls,heads,sectors,cylsecs); 647 if ((dos_sectors > MAX_SECTORS_PER_TRACK) || (dos_cyls > MAXCYL) || (dos_heads > MAX_HEADS)) 648 printf("Figures below won't work with BIOS for partitions not in cyl 1\n"); 649 printf("parameters to be used for BIOS calculations are:\n"); 650 printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n" 651 ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs); 652 } 653 654 static void 655 change_active(int which) 656 { 657 struct dos_partition *partp = &mboot.parts[0]; 658 int active, i, new, tmp; 659 660 active = -1; 661 for (i = 0; i < NDOSPART; i++) { 662 if ((partp[i].dp_flag & ACTIVE) == 0) 663 continue; 664 printf("Partition %d is marked active\n", i + 1); 665 if (active == -1) 666 active = i + 1; 667 } 668 if (a_flag && which != -1) 669 active = which; 670 else if (active == -1) 671 active = 1; 672 673 if (!ok("Do you want to change the active partition?")) 674 return; 675 setactive: 676 do { 677 new = active; 678 Decimal("active partition", new, tmp); 679 if (new < 1 || new > NDOSPART) { 680 printf("Active partition number must be in range 1-4." 681 " Try again.\n"); 682 goto setactive; 683 } 684 active = new; 685 } while (!ok("Are you happy with this choice")); 686 for (i = 0; i < NDOSPART; i++) 687 partp[i].dp_flag = 0; 688 if (active > 0 && active <= NDOSPART) 689 partp[active-1].dp_flag = ACTIVE; 690 } 691 692 static void 693 change_code(void) 694 { 695 if (ok("Do you want to change the boot code?")) 696 init_boot(); 697 } 698 699 void 700 get_params_to_use(void) 701 { 702 int tmp; 703 print_params(); 704 if (ok("Do you want to change our idea of what BIOS thinks ?")) 705 { 706 do 707 { 708 Decimal("BIOS's idea of #cylinders", dos_cyls, tmp); 709 Decimal("BIOS's idea of #heads", dos_heads, tmp); 710 Decimal("BIOS's idea of #sectors", dos_sectors, tmp); 711 dos_cylsecs = dos_heads * dos_sectors; 712 print_params(); 713 } 714 while(!ok("Are you happy with this choice")); 715 } 716 } 717 718 719 /***********************************************\ 720 * Change real numbers into strange dos numbers * 721 \***********************************************/ 722 static void 723 dos(struct dos_partition *partp) 724 { 725 int cy, sec; 726 uint32_t end; 727 728 if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) { 729 memcpy(partp, &mtpart, sizeof(*partp)); 730 return; 731 } 732 733 /* Start c/h/s. */ 734 cy = partp->dp_start / dos_cylsecs; 735 sec = partp->dp_start % dos_sectors + 1; 736 if (cy > MAXCYL && C_flag == 0) { 737 printf("Warning: starting cylinder wraps, using all 1's\n"); 738 partp->dp_shd = -1; 739 partp->dp_scyl = -1; 740 partp->dp_ssect = -1; 741 } else { 742 partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors; 743 partp->dp_scyl = DOSCYL(cy); 744 partp->dp_ssect = DOSSECT(sec, cy); 745 } 746 747 /* End c/h/s. */ 748 end = partp->dp_start + partp->dp_size - 1; 749 cy = end / dos_cylsecs; 750 sec = end % dos_sectors + 1; 751 if (cy > MAXCYL && C_flag == 0) { 752 printf("Warning: ending cylinder wraps, using all 1's\n"); 753 partp->dp_ehd = -1; 754 partp->dp_ecyl = -1; 755 partp->dp_esect = -1; 756 } else { 757 partp->dp_ehd = end % dos_cylsecs / dos_sectors; 758 partp->dp_ecyl = DOSCYL(cy); 759 partp->dp_esect = DOSSECT(sec, cy); 760 } 761 } 762 763 static void 764 erase_partition(int i) 765 { 766 struct dos_partition *partp; 767 off_t ioarg[2]; 768 769 char *dev_name = strdup(disk); 770 771 dev_name = strtok(dev_name + strlen("/dev/da"),"s"); 772 #if 0 773 int trim_enabled = 0; 774 char sysctl_name[64]; 775 size_t olen = sizeof(trim_enabled); 776 777 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name); 778 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) { 779 printf("Device:%s does not support the TRIM command\n", disk); 780 usage(); 781 } 782 if (!trim_enabled) { 783 printf("Erase device option selected, but sysctl (%s) " 784 "is not enabled\n",sysctl_name); 785 usage(); 786 } 787 #endif 788 partp = ((struct dos_partition *) &mboot.parts) + i; 789 printf("erase sectors:%u %u\n", 790 partp->dp_start, 791 partp->dp_size); 792 793 /* Trim the Device */ 794 ioarg[0] = partp->dp_start; 795 ioarg[0] *=secsize; 796 ioarg[1] = partp->dp_size; 797 ioarg[1] *=secsize; 798 799 if (ioctl(fd, DAIOCTRIM, ioarg) < 0) { 800 printf("Device trim failed\n"); 801 usage (); 802 } 803 } 804 805 /* Getting device status */ 806 807 static int 808 open_disk(void) 809 { 810 struct stat st; 811 812 if (stat(disk, &st) == -1) { 813 if (errno == ENOENT) 814 return -2; 815 warnx("can't get file status of %s", disk); 816 return -1; 817 } 818 if (!(st.st_mode & S_IFCHR) && p_flag == 0) 819 warnx("device %s is not character special", disk); 820 if ((fd = open(disk, 821 (x_flag || a_flag || I_flag || 822 B_flag || u_flag) ? O_RDWR : O_RDONLY)) == -1) { 823 if (errno == ENXIO) 824 return -2; 825 warnx("can't open device %s", disk); 826 return -1; 827 } 828 if (get_params() == -1) { 829 warnx("can't get disk parameters on %s", disk); 830 return -1; 831 } 832 return fd; 833 } 834 835 static ssize_t 836 read_disk(off_t sector, void *buf) 837 { 838 lseek(fd,(sector * 512), 0); 839 if (secsize == 0) 840 for(secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE; secsize *= 2) 841 { 842 /* try the read */ 843 int size = read(fd, buf, secsize); 844 if (size == secsize) 845 /* it worked so return */ 846 return secsize; 847 } 848 else 849 return read(fd, buf, secsize); 850 851 /* we failed to read at any of the sizes */ 852 return -1; 853 } 854 855 static ssize_t 856 write_disk(off_t sector, void *buf) 857 { 858 lseek(fd,(sector * 512), 0); 859 /* write out in the size that the read_disk found worked */ 860 return write(fd, buf, secsize); 861 } 862 863 static int 864 get_params(void) 865 { 866 struct partinfo partinfo; /* disk parameters */ 867 struct stat st; 868 869 /* 870 * NOTE: When faking up the CHS for a file image (e.g. for USB), 871 * we must use max values. If we do not then an overflowed 872 * cylinder count will, by convention, set the CHS fields to 873 * all 1's. The heads and sectors in the CHS fields will then 874 * exceed the basic geometry which can cause BIOSes to brick. 875 */ 876 if (ioctl(fd, DIOCGPART, &partinfo) == -1) { 877 if (p_flag && fstat(fd, &st) == 0 && st.st_size) { 878 sectors = MAX_SECTORS_PER_TRACK; 879 heads = MAX_HEADS; 880 cylsecs = heads * sectors; 881 cyls = st.st_size / 512 / cylsecs; 882 } else { 883 warnx("can't get disk parameters on %s; supplying dummy ones", 884 disk); 885 heads = 1; 886 cylsecs = heads * sectors; 887 } 888 } else { 889 cyls = partinfo.d_ncylinders; 890 heads = partinfo.d_nheads; 891 sectors = partinfo.d_secpertrack; 892 cylsecs = heads * sectors; 893 secsize = partinfo.media_blksize; 894 } 895 dos_cyls = cyls; 896 dos_heads = heads; 897 dos_sectors = sectors; 898 dos_cylsecs = cylsecs; 899 disksecs = (int64_t)cyls * heads * sectors; 900 return (disksecs); 901 } 902 903 904 static int 905 read_s0(void) 906 { 907 mboot.bootinst_size = secsize; 908 if (mboot.bootinst != NULL) 909 free(mboot.bootinst); 910 if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) { 911 warnx("unable to allocate buffer to read fdisk " 912 "partition table"); 913 return -1; 914 } 915 if (read_disk(0, mboot.bootinst) == -1) { 916 warnx("can't read fdisk partition table"); 917 return -1; 918 } 919 if (*(uint16_t *)&mboot.bootinst[DOSMAGICOFF] != DOSMAGIC) { 920 warnx("invalid fdisk partition table found"); 921 /* So should we initialize things */ 922 return -1; 923 } 924 memcpy(mboot.parts, &mboot.bootinst[DOSPARTOFF], sizeof(mboot.parts)); 925 return 0; 926 } 927 928 static int 929 write_s0(void) 930 { 931 #ifdef NOT_NOW 932 int flag = 1; 933 #endif 934 int sector; 935 936 memcpy(&mboot.bootinst[DOSPARTOFF], mboot.parts, sizeof(mboot.parts)); 937 /* 938 * write enable label sector before write (if necessary), 939 * disable after writing. 940 * needed if the disklabel protected area also protects 941 * sector 0. (e.g. empty disk) 942 */ 943 #ifdef NOT_NOW 944 if (ioctl(fd, DIOCWLABEL, &flag) < 0) 945 warn("ioctl DIOCWLABEL"); 946 #endif 947 for(sector = 0; sector < mboot.bootinst_size / secsize; sector++) 948 if (write_disk(sector, 949 &mboot.bootinst[sector * secsize]) == -1) { 950 warn("can't write fdisk partition table"); 951 #ifdef NOT_NOW 952 flag = 0; 953 ioctl(fd, DIOCWLABEL, &flag); 954 #endif 955 return -1; 956 } 957 #ifdef NOT_NOW 958 flag = 0; 959 ioctl(fd, DIOCWLABEL, &flag); 960 #endif 961 return(0); 962 } 963 964 965 static int 966 ok(const char *str) 967 { 968 printf("%s [n] ", str); 969 fflush(stdout); 970 if (fgets(lbuf, LBUF, stdin) == NULL) 971 exit(1); 972 lbuf[strlen(lbuf)-1] = 0; 973 974 if (*lbuf && 975 (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") || 976 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y"))) 977 return 1; 978 else 979 return 0; 980 } 981 982 static int 983 decimal(const char *str, int *num, int deflt) 984 { 985 int acc = 0, c; 986 char *cp; 987 988 while (1) { 989 printf("Supply a decimal value for \"%s\" [%d] ", str, deflt); 990 fflush(stdout); 991 if (fgets(lbuf, LBUF, stdin) == NULL) 992 exit(1); 993 lbuf[strlen(lbuf)-1] = 0; 994 995 if (!*lbuf) 996 return 0; 997 998 cp = lbuf; 999 while ((c = *cp) && (c == ' ' || c == '\t')) cp++; 1000 if (!c) 1001 return 0; 1002 while ((c = *cp++)) { 1003 if (c <= '9' && c >= '0') 1004 acc = acc * 10 + c - '0'; 1005 else 1006 break; 1007 } 1008 if (c == ' ' || c == '\t') 1009 while ((c = *cp) && (c == ' ' || c == '\t')) cp++; 1010 if (!c) { 1011 *num = acc; 1012 return 1; 1013 } else 1014 printf("%s is an invalid decimal number. Try again.\n", 1015 lbuf); 1016 } 1017 1018 } 1019 1020 static const char * 1021 get_type(int type) 1022 { 1023 int numentries = NELEM(dos_ptypes); 1024 int counter = 0; 1025 const struct dos_ptype *ptr = dos_ptypes; 1026 1027 1028 while(counter < numentries) 1029 { 1030 if (ptr->type == type) 1031 { 1032 return(ptr->name); 1033 } 1034 ptr++; 1035 counter++; 1036 } 1037 return("unknown"); 1038 } 1039 1040 1041 static void 1042 parse_config_line(char *line, CMD *command) 1043 { 1044 char *cp, *end; 1045 1046 cp = line; 1047 while (1) /* dirty trick used to insure one exit point for this 1048 function */ 1049 { 1050 memset(command, 0, sizeof(*command)); 1051 1052 while (isspace(*cp)) ++cp; 1053 if (*cp == '\0' || *cp == '#') 1054 { 1055 break; 1056 } 1057 command->cmd = *cp++; 1058 1059 /* 1060 * Parse args 1061 */ 1062 while (1) 1063 { 1064 while (isspace(*cp)) ++cp; 1065 if (*cp == '#') 1066 { 1067 break; /* found comment */ 1068 } 1069 if (isalpha(*cp)) 1070 { 1071 command->args[command->n_args].argtype = *cp++; 1072 } 1073 if (!isdigit(*cp)) 1074 { 1075 break; /* assume end of line */ 1076 } 1077 end = NULL; 1078 command->args[command->n_args].arg_val = strtoll(cp, &end, 0); 1079 if (cp == end) 1080 { 1081 break; /* couldn't parse number */ 1082 } 1083 cp = end; 1084 command->n_args++; 1085 } 1086 break; 1087 } 1088 } 1089 1090 1091 static int 1092 process_geometry(CMD *command) 1093 { 1094 int status = 1, i; 1095 1096 while (1) 1097 { 1098 geom_processed = 1; 1099 if (part_processed) 1100 { 1101 warnx( 1102 "ERROR line %d: the geometry specification line must occur before\n\ 1103 all partition specifications", 1104 current_line_number); 1105 status = 0; 1106 break; 1107 } 1108 if (command->n_args != 3) 1109 { 1110 warnx("ERROR line %d: incorrect number of geometry args", 1111 current_line_number); 1112 status = 0; 1113 break; 1114 } 1115 dos_cyls = -1; 1116 dos_heads = -1; 1117 dos_sectors = -1; 1118 for (i = 0; i < 3; ++i) 1119 { 1120 switch (command->args[i].argtype) 1121 { 1122 case 'c': 1123 dos_cyls = command->args[i].arg_val; 1124 break; 1125 case 'h': 1126 dos_heads = command->args[i].arg_val; 1127 break; 1128 case 's': 1129 dos_sectors = command->args[i].arg_val; 1130 break; 1131 default: 1132 warnx( 1133 "ERROR line %d: unknown geometry arg type: '%c' (0x%02x)", 1134 current_line_number, command->args[i].argtype, 1135 command->args[i].argtype); 1136 status = 0; 1137 break; 1138 } 1139 } 1140 if (status == 0) 1141 { 1142 break; 1143 } 1144 1145 dos_cylsecs = dos_heads * dos_sectors; 1146 1147 /* 1148 * Do sanity checks on parameter values 1149 */ 1150 if (dos_cyls < 0) 1151 { 1152 warnx("ERROR line %d: number of cylinders not specified", 1153 current_line_number); 1154 status = 0; 1155 } 1156 if (dos_cyls == 0 || dos_cyls > 1024) 1157 { 1158 warnx( 1159 "WARNING line %d: number of cylinders (%d) may be out-of-range\n\ 1160 (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\ 1161 is dedicated to DragonFly)", 1162 current_line_number, dos_cyls); 1163 } 1164 1165 if (dos_heads < 0) 1166 { 1167 warnx("ERROR line %d: number of heads not specified", 1168 current_line_number); 1169 status = 0; 1170 } 1171 else if (dos_heads < 1 || dos_heads > 256) 1172 { 1173 warnx("ERROR line %d: number of heads must be within (1-256)", 1174 current_line_number); 1175 status = 0; 1176 } 1177 1178 if (dos_sectors < 0) 1179 { 1180 warnx("ERROR line %d: number of sectors not specified", 1181 current_line_number); 1182 status = 0; 1183 } 1184 else if (dos_sectors < MIN_SECTORS_PER_TRACK || dos_sectors > MAX_SECTORS_PER_TRACK) 1185 { 1186 warnx("ERROR line %d: number of sectors must be within (1-63)", 1187 current_line_number); 1188 status = 0; 1189 } 1190 1191 break; 1192 } 1193 return (status); 1194 } 1195 1196 1197 static int 1198 process_partition(CMD *command) 1199 { 1200 int status = 0, part; 1201 uint32_t prev_head_boundary, prev_cyl_boundary; 1202 uint32_t adj_size, max_end; 1203 struct dos_partition *partp; 1204 1205 while (1) 1206 { 1207 part_processed = 1; 1208 if (command->n_args != 4) 1209 { 1210 warnx("ERROR line %d: incorrect number of partition args", 1211 current_line_number); 1212 break; 1213 } 1214 part = command->args[0].arg_val; 1215 if (part < 1 || part > NDOSPART) 1216 { 1217 warnx("ERROR line %d: invalid partition number %d", 1218 current_line_number, part); 1219 break; 1220 } 1221 partp = ((struct dos_partition *) &mboot.parts) + part - 1; 1222 bzero((char *)partp, sizeof (struct dos_partition)); 1223 partp->dp_typ = command->args[1].arg_val; 1224 partp->dp_start = command->args[2].arg_val; 1225 partp->dp_size = command->args[3].arg_val; 1226 max_end = partp->dp_start + partp->dp_size; 1227 1228 if (partp->dp_typ == 0) 1229 { 1230 /* 1231 * Get out, the partition is marked as unused. 1232 */ 1233 /* 1234 * Insure that it's unused. 1235 */ 1236 bzero((char *)partp, sizeof (struct dos_partition)); 1237 status = 1; 1238 break; 1239 } 1240 1241 /* 1242 * Adjust start upwards, if necessary, to fall on an head boundary. 1243 */ 1244 if (partp->dp_start % dos_sectors != 0) 1245 { 1246 prev_head_boundary = rounddown(partp->dp_start, dos_sectors); 1247 if (max_end < (uint32_t)dos_sectors || 1248 prev_head_boundary > max_end - dos_sectors) 1249 { 1250 /* 1251 * Can't go past end of partition 1252 */ 1253 warnx( 1254 "ERROR line %d: unable to adjust start of partition %d to fall on\n\ 1255 a head boundary", 1256 current_line_number, part); 1257 break; 1258 } 1259 warnx( 1260 "WARNING: adjusting start offset of partition %d\n\ 1261 from %u to %u, to fall on a head boundary", 1262 part, (u_int)partp->dp_start, 1263 (u_int)(prev_head_boundary + dos_sectors)); 1264 partp->dp_start = prev_head_boundary + dos_sectors; 1265 } 1266 1267 /* 1268 * Adjust size downwards, if necessary, to fall on a cylinder 1269 * boundary. 1270 */ 1271 prev_cyl_boundary = 1272 rounddown(partp->dp_start + partp->dp_size, dos_cylsecs); 1273 if (prev_cyl_boundary > partp->dp_start) 1274 adj_size = prev_cyl_boundary - partp->dp_start; 1275 else 1276 { 1277 warnx( 1278 "ERROR: could not adjust partition to start on a head boundary\n\ 1279 and end on a cylinder boundary."); 1280 return (0); 1281 } 1282 if (adj_size != partp->dp_size) 1283 { 1284 warnx( 1285 "WARNING: adjusting size of partition %d from %u to %u\n\ 1286 to end on a cylinder boundary", 1287 part, (u_int)partp->dp_size, (u_int)adj_size); 1288 partp->dp_size = adj_size; 1289 } 1290 if (partp->dp_size == 0) 1291 { 1292 warnx("ERROR line %d: size of partition %d is zero", 1293 current_line_number, part); 1294 break; 1295 } 1296 1297 dos(partp); 1298 status = 1; 1299 break; 1300 } 1301 return (status); 1302 } 1303 1304 1305 static int 1306 process_active(CMD *command) 1307 { 1308 int status = 0, part, i; 1309 struct dos_partition *partp; 1310 1311 while (1) 1312 { 1313 active_processed = 1; 1314 if (command->n_args != 1) 1315 { 1316 warnx("ERROR line %d: incorrect number of active args", 1317 current_line_number); 1318 status = 0; 1319 break; 1320 } 1321 part = command->args[0].arg_val; 1322 if (part < 1 || part > NDOSPART) 1323 { 1324 warnx("ERROR line %d: invalid partition number %d", 1325 current_line_number, part); 1326 break; 1327 } 1328 /* 1329 * Reset active partition 1330 */ 1331 partp = ((struct dos_partition *) &mboot.parts); 1332 for (i = 0; i < NDOSPART; i++) 1333 partp[i].dp_flag = 0; 1334 partp[part-1].dp_flag = ACTIVE; 1335 1336 status = 1; 1337 break; 1338 } 1339 return (status); 1340 } 1341 1342 1343 static int 1344 process_line(char *line) 1345 { 1346 CMD command; 1347 int status = 1; 1348 1349 while (1) 1350 { 1351 parse_config_line(line, &command); 1352 switch (command.cmd) 1353 { 1354 case 0: 1355 /* 1356 * Comment or blank line 1357 */ 1358 break; 1359 case 'g': 1360 /* 1361 * Set geometry 1362 */ 1363 status = process_geometry(&command); 1364 break; 1365 case 'p': 1366 status = process_partition(&command); 1367 break; 1368 case 'a': 1369 status = process_active(&command); 1370 break; 1371 default: 1372 status = 0; 1373 break; 1374 } 1375 break; 1376 } 1377 return (status); 1378 } 1379 1380 1381 static int 1382 read_config(char *config_file) 1383 { 1384 FILE *fp = NULL; 1385 int status = 1; 1386 char buf[1010]; 1387 1388 while (1) /* dirty trick used to insure one exit point for this 1389 function */ 1390 { 1391 if (strcmp(config_file, "-") != 0) 1392 { 1393 /* 1394 * We're not reading from stdin 1395 */ 1396 if ((fp = fopen(config_file, "r")) == NULL) 1397 { 1398 status = 0; 1399 break; 1400 } 1401 } 1402 else 1403 { 1404 fp = stdin; 1405 } 1406 current_line_number = 0; 1407 while (!feof(fp)) 1408 { 1409 if (fgets(buf, sizeof(buf), fp) == NULL) 1410 { 1411 break; 1412 } 1413 ++current_line_number; 1414 status = process_line(buf); 1415 if (status == 0) 1416 { 1417 break; 1418 } 1419 } 1420 break; 1421 } 1422 if (fp) 1423 { 1424 /* 1425 * It doesn't matter if we're reading from stdin, as we've reached EOF 1426 */ 1427 fclose(fp); 1428 } 1429 return (status); 1430 } 1431 1432 1433 static void 1434 reset_boot(void) 1435 { 1436 int i; 1437 struct dos_partition *partp; 1438 1439 init_boot(); 1440 for (i = 0; i < NDOSPART; ++i) 1441 { 1442 partp = ((struct dos_partition *) &mboot.parts) + i; 1443 bzero((char *)partp, sizeof (struct dos_partition)); 1444 } 1445 } 1446 1447 static int 1448 sanitize_partition(struct dos_partition *partp) 1449 { 1450 uint32_t prev_head_boundary, prev_cyl_boundary; 1451 uint32_t max_end, size, start; 1452 1453 start = partp->dp_start; 1454 size = partp->dp_size; 1455 max_end = start + size; 1456 /* Only allow a zero size if the partition is being marked unused. */ 1457 if (size == 0) { 1458 if (start == 0 && partp->dp_typ == 0) 1459 return (1); 1460 warnx("ERROR: size of partition is zero"); 1461 return (0); 1462 } 1463 /* Return if no adjustment is necessary. */ 1464 if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0) 1465 return (1); 1466 1467 if (start == 0) { 1468 warnx("WARNING: partition overlaps with partition table"); 1469 if (ok("Correct this automatically?")) 1470 start = dos_sectors; 1471 } 1472 if (start % dos_sectors != 0) 1473 warnx("WARNING: partition does not start on a head boundary"); 1474 if ((start +size) % dos_sectors != 0) 1475 warnx("WARNING: partition does not end on a cylinder boundary"); 1476 warnx("WARNING: this may confuse the BIOS or some operating systems"); 1477 if (!ok("Correct this automatically?")) 1478 return (1); 1479 1480 /* 1481 * Adjust start upwards, if necessary, to fall on an head boundary. 1482 */ 1483 if (start % dos_sectors != 0) { 1484 prev_head_boundary = rounddown(start, dos_sectors); 1485 if (max_end < (uint32_t)dos_sectors || 1486 prev_head_boundary >= max_end - dos_sectors) { 1487 /* 1488 * Can't go past end of partition 1489 */ 1490 warnx( 1491 "ERROR: unable to adjust start of partition to fall on a head boundary"); 1492 return (0); 1493 } 1494 start = prev_head_boundary + dos_sectors; 1495 } 1496 1497 /* 1498 * Adjust size downwards, if necessary, to fall on a cylinder 1499 * boundary. 1500 */ 1501 prev_cyl_boundary = rounddown(start + size, dos_cylsecs); 1502 if (prev_cyl_boundary > start) 1503 size = prev_cyl_boundary - start; 1504 else { 1505 warnx("ERROR: could not adjust partition to start on a head boundary\n\ 1506 and end on a cylinder boundary."); 1507 return (0); 1508 } 1509 1510 /* Finally, commit any changes to partp and return. */ 1511 if (start != partp->dp_start) { 1512 warnx("WARNING: adjusting start offset of partition to %u", 1513 (u_int)start); 1514 partp->dp_start = start; 1515 } 1516 if (size != partp->dp_size) { 1517 warnx("WARNING: adjusting size of partition to %u", (u_int)size); 1518 partp->dp_size = size; 1519 } 1520 1521 return (1); 1522 } 1523