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"
27 #include "nameclash.h"
29 #include "partition.h"
30 #include "open_image.h"
34 #include "linux/hdreg.h"
38 static void set_offset(hsc *h, unsigned long offset,
39 uint16_t heads, uint16_t sectors)
41 uint16_t head, sector;
44 if(! heads || !sectors)
45 head = sector = cyl = 0; /* linear mode */
47 sector = offset % sectors;
48 offset = offset / sectors;
50 head = offset % heads;
51 offset = offset / heads;
55 cyl = (uint16_t) offset;
57 if(head > UINT8_MAX) {
58 /* sector or head out of range => linear mode */
59 head = sector = cyl = 0;
61 h->head = (uint8_t) head;
62 h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2);
66 void setBeginEnd(struct partition *partTable,
67 uint32_t begin, uint32_t end,
68 uint16_t iheads, uint16_t isectors,
69 int activate, uint8_t type, unsigned int fat_bits)
71 uint8_t heads, sectors;
73 if(iheads > UINT8_MAX) {
75 "Too many heads for partition: %d\n",
79 heads=(uint8_t) iheads;
80 if(isectors > UINT8_MAX) {
82 "Too many sectors for partition: %d\n",
86 sectors=(uint8_t) isectors;
88 set_offset(&partTable->start, begin, heads, sectors);
89 set_offset(&partTable->end, end-1, heads, sectors);
90 set_dword(partTable->start_sect, begin);
91 set_dword(partTable->nr_sects, end-begin);
93 partTable->boot_ind = 0x80;
95 partTable->boot_ind = 0;
99 * Fat bits unknown / not specified. We look
100 * at size to get a rough estimate what FAT
101 * bits are used. Note: this is only an
102 * estimate, the precise calculation would
103 * involve the number of clusters, which is
104 * not necessarily known here.
106 /* cc977219 would have a cutoff number of 32680,
107 * corresponding to a FAT12 partition with 4K
108 * clusters, however other information hints that
109 * only partitions with less than 4096 sectors are
117 /* Description of various partition types in
118 * https://en.wikipedia.org/wiki/Partition_type#List_of_partition_IDs
120 * https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc977219(v=technet.10)
123 /* FAT 32 partition. For now, we disregard the
124 * possibility of FAT 32 CHS partitions */
125 type = 0x0C; /* Win95 FAT32, LBA */
126 else if (end < 65536) {
127 /* FAT 12 or FAT 16 partitions which fit entirely below
129 /* The 32M restriction doesn't apply to logical
130 partitions within an extended partition, but for the
131 moment mpartition only makes primary partitions */
133 /* FAT 12 partition */
134 type = 0x01; /* DOS FAT12, CHS */
135 else if (fat_bits == 16)
136 /* FAT 16 partition */
137 type = 0x04; /* DOS FAT16, CHS */
138 } else if (end < sectors * heads * 1024u)
139 /* FAT 12 or FAT16 partition above the 32M
140 * mark but below the 1024 cylinder mark.
141 * Indeed, there can be no CHS partition
142 * beyond 1024 cylinders */
143 type = 0x06; /* DOS BIG FAT16 or FAT12, CHS */
145 type = 0x0E; /* Win95 BIG FAT16, LBA */
147 partTable->sys_ind = type;
151 /* setsize function. Determines scsicam mapping if this cannot be inferred from
152 * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */
155 * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
156 * unsigned int *hds, unsigned int *secs);
158 * Purpose : to determine a near-optimal int 0x13 mapping for a
159 * SCSI disk in terms of lost space of size capacity, storing
160 * the results in *cyls, *hds, and *secs.
162 * Returns : -1 on failure, 0 on success.
172 * Information technology -
173 * SCSI-2 Common access method
174 * transport and SCSI interface module
178 * setsize() converts a read capacity value to int 13h
179 * head-cylinder-sector requirements. It minimizes the value for
180 * number of heads and maximizes the number of cylinders. This
181 * will support rather large disks before the number of heads
182 * will not fit in 4 bits (or 6 bits). This algorithm also
183 * minimizes the number of sectors that will be unused at the end
184 * of the disk while allowing for very large disks to be
185 * accommodated. This algorithm does not use physical geometry.
188 static int setsize(unsigned long capacity,unsigned int *cyls,
189 uint16_t *hds, uint16_t *secs) {
191 unsigned long heads, sectors, cylinders, temp;
193 cylinders = 1024L; /* Set number of cylinders to max */
194 sectors = 62L; /* Maximize sectors per track */
196 temp = cylinders * sectors; /* Compute divisor for heads */
197 heads = capacity / temp; /* Compute value for number of heads */
198 if (capacity % temp) { /* If no remainder, done! */
199 heads++; /* Else, increment number of heads */
200 temp = cylinders * heads; /* Compute divisor for sectors */
201 sectors = capacity / temp; /* Compute value for sectors per
203 if (capacity % temp) { /* If no remainder, done! */
204 sectors++; /* Else, increment number of sectors */
205 temp = heads * sectors; /* Compute divisor for cylinders */
206 cylinders = capacity / temp;/* Compute number of cylinders */
209 if (cylinders == 0) rv=-1;/* Give error if 0 cylinders */
211 *cyls = (unsigned int) cylinders; /* Stuff return values */
212 *secs = (uint16_t) sectors;
213 *hds = (uint16_t) heads;
217 static void setsize0(uint32_t capacity,unsigned int *cyls,
218 uint16_t *hds, uint16_t *secs)
222 /* 1. First try "Megabyte" sizes */
223 if(capacity < 1024 * 2048 && !(capacity % 1024)) {
224 *cyls = capacity >> 11;
230 /* then try scsicam's size */
231 r = setsize(capacity,cyls,hds,secs);
232 if(r || *hds > 255 || *secs > 63) {
233 /* scsicam failed. Do megabytes anyways */
234 *cyls = capacity >> 11;
242 static void usage(int ret) NORETURN;
243 static void usage(int ret)
246 "Mtools version %s, dated %s\n", mversion, mdate);
248 "Usage: %s [-pradcv] [-I] [-B bootsect-template] [-s sectors] "
250 "[-h heads] [-T type] [-b begin] [-l length] "
251 "drive\n", progname);
255 void mpartition(int argc, char **argv, int dummy UNUSEDP) NORETURN;
256 void mpartition(int argc, char **argv, int dummy UNUSEDP)
263 uint16_t sec_per_cyl;
268 unsigned int length = 0;
272 uint32_t tot_sectors=0;
273 /* Needs to be long due to BLKGETSIZE ioctl */
280 int has_activated = 0;
282 unsigned int begin=0;
288 struct device used_dev;
289 unsigned int argtracks;
290 uint16_t argheads, argsectors;
292 char drive, name[EXPAND_BUF];
293 unsigned char buf[512];
294 struct partition *partTable=(struct partition *)(buf+ 0x1ae);
298 struct partition *tpartition;
304 /* get command line options */
305 if(helpFlag(argc, argv))
307 while ((c = getopt(argc, argv, "i:adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) {
312 set_cmd_line_image(optarg);
318 /* no privs, as it could be abused to
319 * make other partitions unbootable, or
320 * to boot a rogue kernel from this one */
321 open2flags |= NO_PRIV;
337 /* could be abused to nuke all other
339 open2flags |= NO_PRIV;
349 /* could be abused to "manually" create
350 * extended partitions */
351 open2flags |= NO_PRIV;
352 type = strtou8(optarg, &endptr, 0);
356 argtracks = atoui(optarg);
359 argheads = atou16(optarg);
362 argsectors = atou16(optarg);
366 /* could be abused by creating overlapping
367 * partitions and other such Snafu */
368 open2flags |= NO_PRIV;
377 begin = strtoui(optarg, &endptr, 0);
381 length = parseSize(optarg);
387 check_number_parse_errno((char)c, optarg, endptr);
390 if (argc - optind != 1 ||
391 !argv[optind][0] || argv[optind][1] != ':')
394 drive = ch_toupper(argv[optind][0]);
396 /* check out a drive whose letter and parameters match */
397 sprintf(errmsg, "Drive '%c:' not supported", drive);
399 for(dev=devices;dev->drive;dev++) {
404 if (dev->drive != drive)
406 if (dev->partition < 1 || dev->partition > 4) {
408 "Drive '%c:' is not a partition",
414 SET_INT(used_dev.tracks, argtracks);
415 SET_INT(used_dev.heads, argheads);
416 SET_INT(used_dev.sectors, argsectors);
418 expand(dev->name, name);
420 mode = dirty ? O_RDWR : O_RDONLY;
424 #ifdef USING_NEW_VOLD
425 strcpy(name, getVoldName(dev, name));
427 Stream = OpenImage(&used_dev, dev, name, mode, errmsg,
428 open2flags | SKIP_PARTITION | ALWAYS_GET_GEOMETRY,
429 mode, NULL, NULL, NULL);
433 snprintf(errmsg,sizeof(errmsg)-1,
434 "init: open: %s", strerror(errno));
436 sprintf(errmsg,"init: open: %s", strerror(errno));
441 tot_sectors = used_dev.tot_sectors;
443 /* read the partition table */
444 if (PREADS(Stream, (char *) buf, 0, 512) != 512 && !initialize){
446 snprintf(errmsg, sizeof(errmsg)-1,
447 "Error reading from '%s', wrong parameters?",
451 "Error reading from '%s', wrong parameters?",
457 print_sector("Read sector", buf, 512);
461 /* print error msg if needed */
462 if ( dev->drive == 0 ){
464 fprintf(stderr,"%s: %s\n", argv[0],errmsg);
468 if((used_dev.sectors || used_dev.heads) &&
469 (!used_dev.sectors || !used_dev.heads)) {
470 fprintf(stderr,"You should either indicate both the number of sectors and the number of heads,\n");
471 fprintf(stderr," or none of them\n");
478 fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE);
483 if(read(fd, (char *) buf, 512) < 512) {
488 memset((char *)(partTable+1), 0, 4*sizeof(*partTable));
489 set_word(((unsigned char*)buf)+510, 0xaa55);
492 /* check for boot signature, and place it if needed */
493 if((buf[510] != 0x55) || (buf[511] != 0xaa)) {
494 fprintf(stderr,"Boot signature not set\n");
496 "Use the -I flag to initialize the partition table, and set the boot signature\n");
500 tpartition=&partTable[dev->partition];
502 if(!tpartition->sys_ind)
504 "Partition for drive %c: does not exist\n",
506 if((tpartition->sys_ind & 0x3f) == 5) {
508 "Partition for drive %c: may be an extended partition\n",
511 "Use the -f flag to remove it anyways\n");
514 memset(tpartition, 0, sizeof(*tpartition));
517 if(create && tpartition->sys_ind) {
519 "Partition for drive %c: already exists\n", drive);
521 "Use the -r flag to remove it before attempting to recreate it\n");
524 /* if number of heads and sectors not known yet, set "reasonable"
526 compute_lba_geom_from_tot_sectors(&used_dev);
528 /* find out whether there is any activated partition. Moreover
529 * if no offset of a partition to be created have been
530 * specificed, find out whether it may be placed between the
531 * preceding and following partition already existing */
534 struct partition *partition=&partTable[i];
535 if(!partition->sys_ind)
538 if(partition->boot_ind)
541 if(i<dev->partition && !begin_set)
542 begin = END(partition);
543 if(i>dev->partition && !end_set && !size_set) {
544 end = BEGIN(partition);
549 if(!used_dev.sectors && !used_dev.heads) {
551 setsize0((uint32_t)tot_sectors,&dummy2,&used_dev.heads,
555 used_dev.sectors = 32;
560 fprintf(stderr,"sectors: %d heads: %d %u\n",
561 used_dev.sectors, used_dev.heads, tot_sectors);
563 sec_per_cyl = used_dev.sectors * used_dev.heads;
565 unsigned int overlap;
566 if(!end_set && !size_set && tot_sectors) {
567 end = tot_sectors - tot_sectors % sec_per_cyl;
571 /* if the partition starts right at the beginning of
572 * the disk, keep one track unused to allow place for
573 * the master boot record */
574 if(!begin && !begin_set)
575 begin = used_dev.sectors ? used_dev.sectors : 2048;
577 /* Do not try to align partitions (other than first) on track
578 * boundaries here: apparently this was a thing of the past */
581 end = begin + length;
582 } else if(!end_set) {
583 fprintf(stderr,"Unknown size\n");
587 /* Make sure partition boundaries are correctly ordered
590 fprintf(stderr, "Begin larger than end\n");
594 /* Check whether new partition doesn't overlap with
595 * any of those already in place */
596 if((overlap=findOverlap(partTable, 4, begin, end))) {
598 "Partition would overlap with partition %d\n",
603 setBeginEnd(tpartition, begin, end,
604 used_dev.heads, used_dev.sectors,
605 !has_activated, type,
610 if(!tpartition->sys_ind) {
612 "Partition for drive %c: does not exist\n",
617 tpartition->boot_ind=0x80;
620 tpartition->boot_ind=0x00;
626 inconsistency |= consistencyCheck(partTable, doprint, verbose,
627 &has_activated, tot_sectors,
628 &used_dev, dev->partition);
630 switch(has_activated) {
633 "Warning: no active (bootable) partition present\n");
639 "Warning: %d active (bootable) partitions present\n",
642 "Usually, a disk should have exactly one active partition\n");
646 if(inconsistency && !force) {
648 "inconsistency detected!\n" );
651 "Retry with the -f switch to go ahead anyways\n");
656 if(doprint && tpartition->sys_ind) {
657 printf("The following command will recreate the partition for drive %c:\n",
660 (DWORD(tpartition->nr_sects) +
661 (BEGIN(tpartition) % sec_per_cyl)) /
663 printf("mpartition -c -b %d -l %d -t %d -h %d -s %d -b %u %c:\n",
664 BEGIN(tpartition), PART_SIZE(tpartition),
665 used_dev.tracks, used_dev.heads, used_dev.sectors,
666 BEGIN(tpartition), drive);
670 /* write data back to the disk */
672 print_sector("Writing sector", buf, 512);
673 if (PWRITES(Stream, (char *) buf, 0, 512) != 512) {
674 fprintf(stderr,"Error writing partition table");
678 print_sector("Sector written", buf, 512);