1/* Copyright 1997-2003,2005-2007,2009 Alain Knaff. 2 * This file is part of mtools. 3 * 4 * Mtools is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * Mtools is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * mformat.c 18 */ 19#define DONT_NEED_WAIT 20 21#include "sysincludes.h" 22#include "msdos.h" 23#include "mtools.h" 24#include "mainloop.h" 25#include "fsP.h" 26#include "file.h" 27#include "plain_io.h" 28#include "nameclash.h" 29#include "buffer.h" 30#include "scsi.h" 31#include "partition.h" 32 33#ifdef OS_linux 34#include "linux/hdreg.h" 35 36#define _LINUX_STRING_H_ 37#define kdev_t int 38#include "linux/fs.h" 39#undef _LINUX_STRING_H_ 40 41#endif 42 43#define tolinear(x) \ 44(sector(x)-1+(head(x)+cyl(x)*used_dev->heads)*used_dev->sectors) 45 46 47static __inline__ void print_hsc(hsc *h) 48{ 49 printf(" h=%d s=%d c=%d\n", 50 head(*h), sector(*h), cyl(*h)); 51} 52 53static void set_offset(hsc *h, int offset, int heads, int sectors) 54{ 55 int head, sector, cyl; 56 57 if(! heads || !sectors) 58 head = sector = cyl = 0; /* linear mode */ 59 else { 60 sector = offset % sectors; 61 offset = offset / sectors; 62 63 head = offset % heads; 64 cyl = offset / heads; 65 if(cyl > 1023) cyl = 1023; 66 } 67 68 h->head = head; 69 h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2); 70 h->cyl = cyl & 0xff; 71} 72 73void setBeginEnd(struct partition *partTable, int begin, int end, 74 int heads, int sectors, int activate, int type) 75{ 76 set_offset(&partTable->start, begin, heads, sectors); 77 set_offset(&partTable->end, end-1, heads, sectors); 78 set_dword(partTable->start_sect, begin); 79 set_dword(partTable->nr_sects, end-begin); 80 if(activate) 81 partTable->boot_ind = 0x80; 82 else 83 partTable->boot_ind = 0; 84 if(!type) { 85 if(end-begin < 4096) 86 type = 1; /* DOS 12-bit FAT */ 87 else if(end-begin<32*2048) 88 type = 4; /* DOS 16-bit FAT, <32M */ 89 else 90 type = 6; /* DOS 16-bit FAT >= 32M */ 91 } 92 partTable->sys_ind = type; 93} 94 95int consistencyCheck(struct partition *partTable, int doprint, int verbose, 96 int *has_activated, unsigned int *last_end, 97 unsigned int *j, 98 struct device *used_dev, int target_partition) 99{ 100 unsigned int i; 101 unsigned int inconsistency; 102 103 *j = 0; 104 *last_end = 1; 105 106 /* quick consistency check */ 107 inconsistency = 0; 108 *has_activated = 0; 109 for(i=1; i<5; i++){ 110 if(!partTable[i].sys_ind) 111 continue; 112 if(partTable[i].boot_ind) 113 (*has_activated)++; 114 if((used_dev && 115 (used_dev->heads != head(partTable[i].end)+1 || 116 used_dev->sectors != sector(partTable[i].end))) || 117 sector(partTable[i].start) != 1){ 118 fprintf(stderr, 119 "Partition %d is not aligned\n", 120 i); 121 inconsistency=1; 122 } 123 124 if(*j && 125 *last_end > BEGIN(partTable[i])) { 126 fprintf(stderr, 127 "Partitions %d and %d badly ordered or overlapping\n", 128 *j,i); 129 inconsistency=1; 130 } 131 132 *last_end = END(partTable[i]); 133 *j = i; 134 135 if(used_dev && 136 cyl(partTable[i].start) != 1023 && 137 tolinear(partTable[i].start) != BEGIN(partTable[i])) { 138 fprintf(stderr, 139 "Start position mismatch for partition %d\n", 140 i); 141 inconsistency=1; 142 } 143 if(used_dev && 144 cyl(partTable[i].end) != 1023 && 145 tolinear(partTable[i].end)+1 != END(partTable[i])) { 146 fprintf(stderr, 147 "End position mismatch for partition %d\n", 148 i); 149 inconsistency=1; 150 } 151 152 if(doprint && verbose) { 153 if(i==target_partition) 154 putchar('*'); 155 else 156 putchar(' '); 157 printf("Partition %d\n",i); 158 159 printf(" active=%x\n", partTable[i].boot_ind); 160 printf(" start:"); 161 print_hsc(&partTable[i].start); 162 printf(" type=0x%x\n", partTable[i].sys_ind); 163 printf(" end:"); 164 print_hsc(&partTable[i].end); 165 printf(" start=%d\n", BEGIN(partTable[i])); 166 printf(" nr=%d\n", _DWORD(partTable[i].nr_sects)); 167 printf("\n"); 168 } 169 } 170 return inconsistency; 171} 172 173/* setsize function. Determines scsicam mapping if this cannot be inferred from 174 * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */ 175 176/* 177 * Function : static int setsize(unsigned long capacity,unsigned int *cyls, 178 * unsigned int *hds, unsigned int *secs); 179 * 180 * Purpose : to determine a near-optimal int 0x13 mapping for a 181 * SCSI disk in terms of lost space of size capacity, storing 182 * the results in *cyls, *hds, and *secs. 183 * 184 * Returns : -1 on failure, 0 on success. 185 * 186 * Extracted from 187 * 188 * WORKING X3T9.2 189 * DRAFT 792D 190 * 191 * 192 * Revision 6 193 * 10-MAR-94 194 * Information technology - 195 * SCSI-2 Common access method 196 * transport and SCSI interface module 197 * 198 * ANNEX A : 199 * 200 * setsize() converts a read capacity value to int 13h 201 * head-cylinder-sector requirements. It minimizes the value for 202 * number of heads and maximizes the number of cylinders. This 203 * will support rather large disks before the number of heads 204 * will not fit in 4 bits (or 6 bits). This algorithm also 205 * minimizes the number of sectors that will be unused at the end 206 * of the disk while allowing for very large disks to be 207 * accommodated. This algorithm does not use physical geometry. 208 */ 209 210static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds, 211 unsigned int *secs) { 212 unsigned int rv = 0; 213 unsigned long heads, sectors, cylinders, temp; 214 215 cylinders = 1024L; /* Set number of cylinders to max */ 216 sectors = 62L; /* Maximize sectors per track */ 217 218 temp = cylinders * sectors; /* Compute divisor for heads */ 219 heads = capacity / temp; /* Compute value for number of heads */ 220 if (capacity % temp) { /* If no remainder, done! */ 221 heads++; /* Else, increment number of heads */ 222 temp = cylinders * heads; /* Compute divisor for sectors */ 223 sectors = capacity / temp; /* Compute value for sectors per 224 track */ 225 if (capacity % temp) { /* If no remainder, done! */ 226 sectors++; /* Else, increment number of sectors */ 227 temp = heads * sectors; /* Compute divisor for cylinders */ 228 cylinders = capacity / temp;/* Compute number of cylinders */ 229 } 230 } 231 if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */ 232 233 *cyls = (unsigned int) cylinders; /* Stuff return values */ 234 *secs = (unsigned int) sectors; 235 *hds = (unsigned int) heads; 236 return(rv); 237} 238 239static void setsize0(unsigned long capacity,unsigned int *cyls, 240 unsigned int *hds, unsigned int *secs) 241{ 242 int r; 243 244 /* 1. First try "Megabyte" sizes */ 245 if(capacity < 1024 * 2048 && !(capacity % 1024)) { 246 *cyls = capacity >> 11; 247 *hds = 64; 248 *secs = 32; 249 return; 250 } 251 252 /* then try scsicam's size */ 253 r = setsize(capacity,cyls,hds,secs); 254 if(r || *hds > 255 || *secs > 63) { 255 /* scsicam failed. Do megabytes anyways */ 256 *cyls = capacity >> 11; 257 *hds = 64; 258 *secs = 32; 259 return; 260 } 261} 262 263 264static void usage(int ret) NORETURN; 265static void usage(int ret) 266{ 267 fprintf(stderr, 268 "Mtools version %s, dated %s\n", mversion, mdate); 269 fprintf(stderr, 270 "Usage: %s [-pradcv] [-I] [-B bootsect-template] [-s sectors] " 271 "[-t cylinders] " 272 "[-h heads] [-T type] [-b begin] [-l length] " 273 "drive\n", progname); 274 exit(ret); 275} 276 277void mpartition(int argc, char **argv, int dummy) 278{ 279 Stream_t *Stream; 280 unsigned int dummy2; 281 282 unsigned int i,j; 283 284 int sec_per_cyl; 285 int doprint = 0; 286 int verbose = 0; 287 int create = 0; 288 int force = 0; 289 int length = 0; 290 int do_remove = 0; 291 int initialize = 0; 292 unsigned int tot_sectors=0; 293 int type = 0; 294 int begin_set = 0; 295 int size_set = 0; 296 int end_set = 0; 297 unsigned int last_end = 0; 298 int activate = 0; 299 int has_activated = 0; 300 int inconsistency=0; 301 int begin=0; 302 int end=0; 303 int sizetest=0; 304 int dirty = 0; 305 int open2flags = NO_OFFSET; 306 307 int c; 308 struct device used_dev; 309 int argtracks, argheads, argsectors; 310 311 char drive, name[EXPAND_BUF]; 312 unsigned char buf[512]; 313 struct partition *partTable=(struct partition *)(buf+ 0x1ae); 314 struct device *dev; 315 char errmsg[200]; 316 char *bootSector=0; 317 318 argtracks = 0; 319 argheads = 0; 320 argsectors = 0; 321 322 /* get command line options */ 323 if(helpFlag(argc, argv)) 324 usage(0); 325 while ((c = getopt(argc, argv, "i:adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) { 326 switch (c) { 327 case 'i': 328 set_cmd_line_image(optarg, 0); 329 break; 330 case 'B': 331 bootSector = optarg; 332 break; 333 case 'a': 334 /* no privs, as it could be abused to 335 * make other partitions unbootable, or 336 * to boot a rogue kernel from this one */ 337 open2flags |= NO_PRIV; 338 activate = 1; 339 dirty = 1; 340 break; 341 case 'd': 342 activate = -1; 343 dirty = 1; 344 break; 345 case 'p': 346 doprint = 1; 347 break; 348 case 'r': 349 do_remove = 1; 350 dirty = 1; 351 break; 352 case 'I': 353 /* could be abused to nuke all other 354 * partitions */ 355 open2flags |= NO_PRIV; 356 initialize = 1; 357 dirty = 1; 358 break; 359 case 'c': 360 create = 1; 361 dirty = 1; 362 break; 363 364 case 'T': 365 /* could be abused to "manually" create 366 * extended partitions */ 367 open2flags |= NO_PRIV; 368 type = strtoul(optarg,0,0); 369 break; 370 371 case 't': 372 argtracks = atoi(optarg); 373 break; 374 case 'h': 375 argheads = atoi(optarg); 376 break; 377 case 's': 378 argsectors = atoi(optarg); 379 break; 380 381 case 'f': 382 /* could be abused by creating overlapping 383 * partitions and other such Snafu */ 384 open2flags |= NO_PRIV; 385 force = 1; 386 break; 387 388 case 'v': 389 verbose++; 390 break; 391 case 'S': 392 /* testing only */ 393 /* could be abused to create partitions 394 * extending beyond the actual size of the 395 * device */ 396 open2flags |= NO_PRIV; 397 tot_sectors = strtoul(optarg,0,0); 398 sizetest = 1; 399 break; 400 case 'b': 401 begin_set = 1; 402 begin = atoi(optarg); 403 break; 404 case 'l': 405 size_set = 1; 406 length = atoi(optarg); 407 break; 408 409 default: 410 usage(1); 411 } 412 } 413 414 if (argc - optind != 1 || 415 !argv[optind][0] || argv[optind][1] != ':') 416 usage(1); 417 418 drive = toupper(argv[optind][0]); 419 420 /* check out a drive whose letter and parameters match */ 421 sprintf(errmsg, "Drive '%c:' not supported", drive); 422 Stream = 0; 423 for(dev=devices;dev->drive;dev++) { 424 int mode ; 425 426 FREE(&(Stream)); 427 /* drive letter */ 428 if (dev->drive != drive) 429 continue; 430 if (dev->partition < 1 || dev->partition > 4) { 431 sprintf(errmsg, 432 "Drive '%c:' is not a partition", 433 drive); 434 continue; 435 } 436 used_dev = *dev; 437 438 SET_INT(used_dev.tracks, argtracks); 439 SET_INT(used_dev.heads, argheads); 440 SET_INT(used_dev.sectors, argsectors); 441 442 expand(dev->name, name); 443 444 mode = dirty ? O_RDWR : O_RDONLY; 445 if(initialize) 446 mode |= O_CREAT; 447 448#ifdef USING_NEW_VOLD 449 strcpy(name, getVoldName(dev, name)); 450#endif 451 Stream = SimpleFileOpen(&used_dev, dev, name, mode, 452 errmsg, open2flags, 1, 0); 453 454 if (!Stream) { 455#ifdef HAVE_SNPRINTF 456 snprintf(errmsg,199,"init: open: %s", strerror(errno)); 457#else 458 sprintf(errmsg,"init: open: %s", strerror(errno)); 459#endif 460 continue; 461 } 462 463 464 /* try to find out the size */ 465 if(!sizetest) 466 tot_sectors = 0; 467 if(IS_SCSI(dev)) { 468 unsigned char cmd[10]; 469 unsigned char data[10]; 470 cmd[0] = SCSI_READ_CAPACITY; 471 memset ((void *) &cmd[2], 0, 8); 472 memset ((void *) &data[0], 137, 10); 473 scsi_cmd(get_fd(Stream), cmd, 10, SCSI_IO_READ, 474 data, 10, get_extra_data(Stream)); 475 476 tot_sectors = 1 + 477 (data[0] << 24) + 478 (data[1] << 16) + 479 (data[2] << 8) + 480 (data[3] ); 481 if(verbose) 482 printf("%d sectors in total\n", tot_sectors); 483 } 484 485#ifdef OS_linux 486 if (tot_sectors == 0) { 487 ioctl(get_fd(Stream), BLKGETSIZE, &tot_sectors); 488 } 489#endif 490 491 /* read the partition table */ 492 if (READS(Stream, (char *) buf, 0, 512) != 512 && !initialize){ 493#ifdef HAVE_SNPRINTF 494 snprintf(errmsg, 199, 495 "Error reading from '%s', wrong parameters?", 496 name); 497#else 498 sprintf(errmsg, 499 "Error reading from '%s', wrong parameters?", 500 name); 501#endif 502 continue; 503 } 504 if(verbose>=2) 505 print_sector("Read sector", buf, 512); 506 break; 507 } 508 509 /* print error msg if needed */ 510 if ( dev->drive == 0 ){ 511 FREE(&Stream); 512 fprintf(stderr,"%s: %s\n", argv[0],errmsg); 513 exit(1); 514 } 515 516 if((used_dev.sectors || used_dev.heads) && 517 (!used_dev.sectors || !used_dev.heads)) { 518 fprintf(stderr,"You should either indicate both the number of sectors and the number of heads,\n"); 519 fprintf(stderr," or none of them\n"); 520 exit(1); 521 } 522 523 if(initialize) { 524 if (bootSector) { 525 int fd; 526 fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE); 527 if (fd < 0) { 528 perror("open boot sector"); 529 exit(1); 530 } 531 read(fd, (char *) buf, 512); 532 } 533 memset((char *)(partTable+1), 0, 4*sizeof(*partTable)); 534 set_dword(((unsigned char*)buf)+510, 0xaa55); 535 } 536 537 /* check for boot signature, and place it if needed */ 538 if((buf[510] != 0x55) || (buf[511] != 0xaa)) { 539 fprintf(stderr,"Boot signature not set\n"); 540 fprintf(stderr, 541 "Use the -I flag to initialize the partition table, and set the boot signature\n"); 542 inconsistency = 1; 543 } 544 545 if(do_remove){ 546 if(!partTable[dev->partition].sys_ind) 547 fprintf(stderr, 548 "Partition for drive %c: does not exist\n", 549 drive); 550 if((partTable[dev->partition].sys_ind & 0x3f) == 5) { 551 fprintf(stderr, 552 "Partition for drive %c: may be an extended partition\n", 553 drive); 554 fprintf(stderr, 555 "Use the -f flag to remove it anyways\n"); 556 inconsistency = 1; 557 } 558 memset(&partTable[dev->partition], 0, sizeof(*partTable)); 559 } 560 561 if(create && partTable[dev->partition].sys_ind) { 562 fprintf(stderr, 563 "Partition for drive %c: already exists\n", drive); 564 fprintf(stderr, 565 "Use the -r flag to remove it before attempting to recreate it\n"); 566 } 567 568 569 /* find out number of heads and sectors, and whether there is 570 * any activated partition */ 571 has_activated = 0; 572 for(i=1; i<5; i++){ 573 if(!partTable[i].sys_ind) 574 continue; 575 576 if(partTable[i].boot_ind) 577 has_activated++; 578 579 /* set geometry from entry */ 580 if (!used_dev.heads) 581 used_dev.heads = head(partTable[i].end)+1; 582 if(!used_dev.sectors) 583 used_dev.sectors = sector(partTable[i].end); 584 if(i<dev->partition && !begin_set) 585 begin = END(partTable[i]); 586 if(i>dev->partition && !end_set && !size_set) { 587 end = BEGIN(partTable[i]); 588 end_set = 1; 589 } 590 } 591 592#ifdef OS_linux 593 if(!used_dev.sectors && !used_dev.heads) { 594 if(!IS_SCSI(dev)) { 595 struct hd_geometry geom; 596 if(ioctl(get_fd(Stream), HDIO_GETGEO, &geom) == 0) { 597 used_dev.heads = geom.heads; 598 used_dev.sectors = geom.sectors; 599 } 600 } 601 } 602#endif 603 604 if(!used_dev.sectors && !used_dev.heads) { 605 if(tot_sectors) 606 setsize0(tot_sectors,&dummy2,&used_dev.heads, 607 &used_dev.sectors); 608 else { 609 used_dev.heads = 64; 610 used_dev.sectors = 32; 611 } 612 } 613 614 if(verbose) 615 fprintf(stderr,"sectors: %d heads: %d %d\n", 616 used_dev.sectors, used_dev.heads, tot_sectors); 617 618 sec_per_cyl = used_dev.sectors * used_dev.heads; 619 if(create) { 620 if(!end_set && tot_sectors) { 621 end = tot_sectors - tot_sectors % sec_per_cyl; 622 end_set = 1; 623 } 624 625 /* if the partition starts right at the beginning of 626 * the disk, keep one track unused to allow place for 627 * the master boot record */ 628 if(!begin && !begin_set) 629 begin = used_dev.sectors; 630 if(!size_set && used_dev.tracks) { 631 size_set = 2; 632 length = sec_per_cyl * used_dev.tracks; 633 634 /* round the size in order to take 635 * into account any "hidden" sectors */ 636 637 /* do we anchor this at the beginning ?*/ 638 if(begin_set || dev->partition <= 2 || !end_set) 639 length -= begin % sec_per_cyl; 640 else if(end - length < begin) 641 /* truncate any overlap */ 642 length = end - begin; 643 } 644 if(size_set) { 645 if(!begin_set && dev->partition >2 && end_set) 646 begin = end - length; 647 else 648 end = begin + length; 649 } else if(!end_set) { 650 fprintf(stderr,"Unknown size\n"); 651 exit(1); 652 } 653 654 setBeginEnd(&partTable[dev->partition], begin, end, 655 used_dev.heads, used_dev.sectors, 656 !has_activated, type); 657 } 658 659 if(activate) { 660 if(!partTable[dev->partition].sys_ind) { 661 fprintf(stderr, 662 "Partition for drive %c: does not exist\n", 663 drive); 664 } else { 665 switch(activate) { 666 case 1: 667 partTable[dev->partition].boot_ind=0x80; 668 break; 669 case -1: 670 partTable[dev->partition].boot_ind=0x00; 671 break; 672 } 673 } 674 } 675 676 677 inconsistency |= consistencyCheck(partTable, doprint, verbose, 678 &has_activated, &last_end, &j, 679 &used_dev, dev->partition); 680 681 if(doprint && !inconsistency && partTable[dev->partition].sys_ind) { 682 printf("The following command will recreate the partition for drive %c:\n", 683 drive); 684 used_dev.tracks = 685 (_DWORD(partTable[dev->partition].nr_sects) + 686 (BEGIN(partTable[dev->partition]) % sec_per_cyl)) / 687 sec_per_cyl; 688 printf("mpartition -c -t %d -h %d -s %d -b %u %c:\n", 689 used_dev.tracks, used_dev.heads, used_dev.sectors, 690 BEGIN(partTable[dev->partition]), drive); 691 } 692 693 if(tot_sectors && last_end >tot_sectors) { 694 fprintf(stderr, 695 "Partition %d exceeds beyond end of disk\n", 696 j); 697 exit(1); 698 } 699 700 701 switch(has_activated) { 702 case 0: 703 fprintf(stderr, 704 "Warning: no active (bootable) partition present\n"); 705 break; 706 case 1: 707 break; 708 default: 709 fprintf(stderr, 710 "Warning: %d active (bootable) partitions present\n", 711 has_activated); 712 fprintf(stderr, 713 "Usually, a disk should have exactly one active partition\n"); 714 break; 715 } 716 717 if(inconsistency && !force) { 718 fprintf(stderr, 719 "inconsistency detected!\n" ); 720 if(dirty) 721 fprintf(stderr, 722 "Retry with the -f switch to go ahead anyways\n"); 723 exit(1); 724 } 725 726 if(dirty) { 727 /* write data back to the disk */ 728 if(verbose>=2) 729 print_sector("Writing sector", buf, 512); 730 if (WRITES(Stream, (char *) buf, 0, 512) != 512) { 731 fprintf(stderr,"Error writing partition table"); 732 exit(1); 733 } 734 if(verbose>=3) 735 print_sector("Sector written", buf, 512); 736 } 737 FREE(&Stream); 738 exit(0); 739} 740