1 /* Copyright 1997-2003,2005-2007,2009 Alain Knaff.
2 * This file is part of mtools.
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.
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.
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/>.
19 #define DONT_NEED_WAIT
21 #include "sysincludes.h"
28 #include "nameclash.h"
31 #include "partition.h"
34 #include "linux/hdreg.h"
36 #define _LINUX_STRING_H_
39 #undef _LINUX_STRING_H_
44 (sector(x)-1+(head(x)+cyl(x)*used_dev->heads)*used_dev->sectors)
47 static __inline__ void print_hsc(hsc *h)
49 printf(" h=%d s=%d c=%d\n",
50 head(*h), sector(*h), cyl(*h));
53 static void set_offset(hsc *h, int offset, int heads, int sectors)
55 int head, sector, cyl;
57 if(! heads || !sectors)
58 head = sector = cyl = 0; /* linear mode */
60 sector = offset % sectors;
61 offset = offset / sectors;
63 head = offset % heads;
65 if(cyl > 1023) cyl = 1023;
69 h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2);
73 void setBeginEnd(struct partition *partTable, int begin, int end,
74 int heads, int sectors, int activate, int type)
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);
81 partTable->boot_ind = 0x80;
83 partTable->boot_ind = 0;
86 type = 1; /* DOS 12-bit FAT */
87 else if(end-begin<32*2048)
88 type = 4; /* DOS 16-bit FAT, <32M */
90 type = 6; /* DOS 16-bit FAT >= 32M */
92 partTable->sys_ind = type;
95 int consistencyCheck(struct partition *partTable, int doprint, int verbose,
96 int *has_activated, unsigned int *last_end,
98 struct device *used_dev, int target_partition)
101 unsigned int inconsistency;
106 /* quick consistency check */
110 if(!partTable[i].sys_ind)
112 if(partTable[i].boot_ind)
115 (used_dev->heads != head(partTable[i].end)+1 ||
116 used_dev->sectors != sector(partTable[i].end))) ||
117 sector(partTable[i].start) != 1){
119 "Partition %d is not aligned\n",
125 *last_end > BEGIN(partTable[i])) {
127 "Partitions %d and %d badly ordered or overlapping\n",
132 *last_end = END(partTable[i]);
136 cyl(partTable[i].start) != 1023 &&
137 tolinear(partTable[i].start) != BEGIN(partTable[i])) {
139 "Start position mismatch for partition %d\n",
144 cyl(partTable[i].end) != 1023 &&
145 tolinear(partTable[i].end)+1 != END(partTable[i])) {
147 "End position mismatch for partition %d\n",
152 if(doprint && verbose) {
153 if(i==target_partition)
157 printf("Partition %d\n",i);
159 printf(" active=%x\n", partTable[i].boot_ind);
161 print_hsc(&partTable[i].start);
162 printf(" type=0x%x\n", partTable[i].sys_ind);
164 print_hsc(&partTable[i].end);
165 printf(" start=%d\n", BEGIN(partTable[i]));
166 printf(" nr=%d\n", _DWORD(partTable[i].nr_sects));
170 return inconsistency;
173 /* setsize function. Determines scsicam mapping if this cannot be inferred from
174 * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */
177 * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
178 * unsigned int *hds, unsigned int *secs);
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.
184 * Returns : -1 on failure, 0 on success.
194 * Information technology -
195 * SCSI-2 Common access method
196 * transport and SCSI interface module
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.
210 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
211 unsigned int *secs) {
213 unsigned long heads, sectors, cylinders, temp;
215 cylinders = 1024L; /* Set number of cylinders to max */
216 sectors = 62L; /* Maximize sectors per track */
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
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 */
231 if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */
233 *cyls = (unsigned int) cylinders; /* Stuff return values */
234 *secs = (unsigned int) sectors;
235 *hds = (unsigned int) heads;
239 static void setsize0(unsigned long capacity,unsigned int *cyls,
240 unsigned int *hds, unsigned int *secs)
244 /* 1. First try "Megabyte" sizes */
245 if(capacity < 1024 * 2048 && !(capacity % 1024)) {
246 *cyls = capacity >> 11;
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;
264 static void usage(int ret) NORETURN;
265 static void usage(int ret)
268 "Mtools version %s, dated %s\n", mversion, mdate);
270 "Usage: %s [-pradcv] [-I] [-B bootsect-template] [-s sectors] "
272 "[-h heads] [-T type] [-b begin] [-l length] "
273 "drive\n", progname);
277 void mpartition(int argc, char **argv, int dummy)
292 unsigned int tot_sectors=0;
297 unsigned int last_end = 0;
299 int has_activated = 0;
305 int open2flags = NO_OFFSET;
308 struct device used_dev;
309 int argtracks, argheads, argsectors;
311 char drive, name[EXPAND_BUF];
312 unsigned char buf[512];
313 struct partition *partTable=(struct partition *)(buf+ 0x1ae);
322 /* get command line options */
323 if(helpFlag(argc, argv))
325 while ((c = getopt(argc, argv, "i:adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) {
328 set_cmd_line_image(optarg, 0);
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;
353 /* could be abused to nuke all other
355 open2flags |= NO_PRIV;
365 /* could be abused to "manually" create
366 * extended partitions */
367 open2flags |= NO_PRIV;
368 type = strtoul(optarg,0,0);
372 argtracks = atoi(optarg);
375 argheads = atoi(optarg);
378 argsectors = atoi(optarg);
382 /* could be abused by creating overlapping
383 * partitions and other such Snafu */
384 open2flags |= NO_PRIV;
393 /* could be abused to create partitions
394 * extending beyond the actual size of the
396 open2flags |= NO_PRIV;
397 tot_sectors = strtoul(optarg,0,0);
402 begin = atoi(optarg);
406 length = atoi(optarg);
414 if (argc - optind != 1 ||
415 !argv[optind][0] || argv[optind][1] != ':')
418 drive = toupper(argv[optind][0]);
420 /* check out a drive whose letter and parameters match */
421 sprintf(errmsg, "Drive '%c:' not supported", drive);
423 for(dev=devices;dev->drive;dev++) {
428 if (dev->drive != drive)
430 if (dev->partition < 1 || dev->partition > 4) {
432 "Drive '%c:' is not a partition",
438 SET_INT(used_dev.tracks, argtracks);
439 SET_INT(used_dev.heads, argheads);
440 SET_INT(used_dev.sectors, argsectors);
442 expand(dev->name, name);
444 mode = dirty ? O_RDWR : O_RDONLY;
448 #ifdef USING_NEW_VOLD
449 strcpy(name, getVoldName(dev, name));
451 Stream = SimpleFileOpen(&used_dev, dev, name, mode,
452 errmsg, open2flags, 1, 0);
456 snprintf(errmsg,199,"init: open: %s", strerror(errno));
458 sprintf(errmsg,"init: open: %s", strerror(errno));
464 /* try to find out the size */
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));
482 printf("%d sectors in total\n", tot_sectors);
486 if (tot_sectors == 0) {
487 ioctl(get_fd(Stream), BLKGETSIZE, &tot_sectors);
491 /* read the partition table */
492 if (READS(Stream, (char *) buf, 0, 512) != 512 && !initialize){
494 snprintf(errmsg, 199,
495 "Error reading from '%s', wrong parameters?",
499 "Error reading from '%s', wrong parameters?",
505 print_sector("Read sector", buf, 512);
509 /* print error msg if needed */
510 if ( dev->drive == 0 ){
512 fprintf(stderr,"%s: %s\n", argv[0],errmsg);
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");
526 fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE);
531 if(read(fd, (char *) buf, 512) < 512) {
536 memset((char *)(partTable+1), 0, 4*sizeof(*partTable));
537 set_word(((unsigned char*)buf)+510, 0xaa55);
540 /* check for boot signature, and place it if needed */
541 if((buf[510] != 0x55) || (buf[511] != 0xaa)) {
542 fprintf(stderr,"Boot signature not set\n");
544 "Use the -I flag to initialize the partition table, and set the boot signature\n");
549 if(!partTable[dev->partition].sys_ind)
551 "Partition for drive %c: does not exist\n",
553 if((partTable[dev->partition].sys_ind & 0x3f) == 5) {
555 "Partition for drive %c: may be an extended partition\n",
558 "Use the -f flag to remove it anyways\n");
561 memset(&partTable[dev->partition], 0, sizeof(*partTable));
564 if(create && partTable[dev->partition].sys_ind) {
566 "Partition for drive %c: already exists\n", drive);
568 "Use the -r flag to remove it before attempting to recreate it\n");
572 /* find out number of heads and sectors, and whether there is
573 * any activated partition */
576 if(!partTable[i].sys_ind)
579 if(partTable[i].boot_ind)
582 /* set geometry from entry */
584 used_dev.heads = head(partTable[i].end)+1;
585 if(!used_dev.sectors)
586 used_dev.sectors = sector(partTable[i].end);
587 if(i<dev->partition && !begin_set)
588 begin = END(partTable[i]);
589 if(i>dev->partition && !end_set && !size_set) {
590 end = BEGIN(partTable[i]);
596 if(!used_dev.sectors && !used_dev.heads) {
598 struct hd_geometry geom;
599 if(ioctl(get_fd(Stream), HDIO_GETGEO, &geom) == 0) {
600 used_dev.heads = geom.heads;
601 used_dev.sectors = geom.sectors;
607 if(!used_dev.sectors && !used_dev.heads) {
609 setsize0(tot_sectors,&dummy2,&used_dev.heads,
613 used_dev.sectors = 32;
618 fprintf(stderr,"sectors: %d heads: %d %d\n",
619 used_dev.sectors, used_dev.heads, tot_sectors);
621 sec_per_cyl = used_dev.sectors * used_dev.heads;
623 if(!end_set && tot_sectors) {
624 end = tot_sectors - tot_sectors % sec_per_cyl;
628 /* if the partition starts right at the beginning of
629 * the disk, keep one track unused to allow place for
630 * the master boot record */
631 if(!begin && !begin_set)
632 begin = used_dev.sectors;
633 if(!size_set && used_dev.tracks) {
635 length = sec_per_cyl * used_dev.tracks;
637 /* round the size in order to take
638 * into account any "hidden" sectors */
640 /* do we anchor this at the beginning ?*/
641 if(begin_set || dev->partition <= 2 || !end_set)
642 length -= begin % sec_per_cyl;
643 else if(end - length < begin)
644 /* truncate any overlap */
645 length = end - begin;
648 if(!begin_set && dev->partition >2 && end_set)
649 begin = end - length;
651 end = begin + length;
652 } else if(!end_set) {
653 fprintf(stderr,"Unknown size\n");
657 setBeginEnd(&partTable[dev->partition], begin, end,
658 used_dev.heads, used_dev.sectors,
659 !has_activated, type);
663 if(!partTable[dev->partition].sys_ind) {
665 "Partition for drive %c: does not exist\n",
670 partTable[dev->partition].boot_ind=0x80;
673 partTable[dev->partition].boot_ind=0x00;
680 inconsistency |= consistencyCheck(partTable, doprint, verbose,
681 &has_activated, &last_end, &j,
682 &used_dev, dev->partition);
684 if(doprint && !inconsistency && partTable[dev->partition].sys_ind) {
685 printf("The following command will recreate the partition for drive %c:\n",
688 (_DWORD(partTable[dev->partition].nr_sects) +
689 (BEGIN(partTable[dev->partition]) % sec_per_cyl)) /
691 printf("mpartition -c -t %d -h %d -s %d -b %u %c:\n",
692 used_dev.tracks, used_dev.heads, used_dev.sectors,
693 BEGIN(partTable[dev->partition]), drive);
696 if(tot_sectors && last_end >tot_sectors) {
698 "Partition %d exceeds beyond end of disk\n",
704 switch(has_activated) {
707 "Warning: no active (bootable) partition present\n");
713 "Warning: %d active (bootable) partitions present\n",
716 "Usually, a disk should have exactly one active partition\n");
720 if(inconsistency && !force) {
722 "inconsistency detected!\n" );
725 "Retry with the -f switch to go ahead anyways\n");
730 /* write data back to the disk */
732 print_sector("Writing sector", buf, 512);
733 if (WRITES(Stream, (char *) buf, 0, 512) != 512) {
734 fprintf(stderr,"Error writing partition table");
738 print_sector("Sector written", buf, 512);