/* Copyright 1997-2003,2005-2007,2009 Alain Knaff. * This file is part of mtools. * * Mtools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Mtools is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Mtools. If not, see . * * mformat.c */ #define DONT_NEED_WAIT #include "sysincludes.h" #include "msdos.h" #include "mtools.h" #include "mainloop.h" #include "fsP.h" #include "file.h" #include "plain_io.h" #include "nameclash.h" #include "buffer.h" #include "scsi.h" #include "partition.h" #ifdef OS_linux #include "linux/hdreg.h" #define _LINUX_STRING_H_ #define kdev_t int #include "linux/fs.h" #undef _LINUX_STRING_H_ #endif #define tolinear(x) \ (sector(x)-1+(head(x)+cyl(x)*used_dev->heads)*used_dev->sectors) static __inline__ void print_hsc(hsc *h) { printf(" h=%d s=%d c=%d\n", head(*h), sector(*h), cyl(*h)); } static void set_offset(hsc *h, unsigned long offset, int heads, int sectors) { int head, sector, cyl; if(! heads || !sectors) head = sector = cyl = 0; /* linear mode */ else { sector = offset % sectors; offset = offset / sectors; head = offset % heads; cyl = offset / heads; if(cyl > 1023) cyl = 1023; } h->head = head; h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2); h->cyl = cyl & 0xff; } void setBeginEnd(struct partition *partTable, unsigned long begin, unsigned long end, unsigned int heads, unsigned int sectors, int activate, int type, int fat_bits) { set_offset(&partTable->start, begin, heads, sectors); set_offset(&partTable->end, end-1, heads, sectors); set_dword(partTable->start_sect, begin); set_dword(partTable->nr_sects, end-begin); if(activate) partTable->boot_ind = 0x80; else partTable->boot_ind = 0; if(!type) { if (fat_bits == 0) { /** * Fat bits unknown / not specified. We look * at size to get a rough estimate what FAT * bits are used. Note: this is only an * estimate, the precise calculation would * involve the number of clusters, which is * not necessarily known here. */ /* cc977219 would have a cutoff number of 32680, * corresponding to a FAT12 partition with 4K * clusters, however other information hints that * only partitions with less than 4096 sectors are * considered */ if(end-begin < 4096) fat_bits = 12; else fat_bits = 16; } /* Description of various partition types in * https://en.wikipedia.org/wiki/Partition_type#List_of_partition_IDs * and * https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc977219(v=technet.10) */ if (fat_bits == 32) /* FAT 32 partition. For now, we disregard the * possibility of FAT 32 CHS partitions */ type = 0x0C; /* Win95 FAT32, LBA */ else if (end < 65536) { /* FAT 12 or FAT 16 partitions which fit entirely below the 32M mark */ /* The 32M restriction doesn't apply to logical partitions within an extended partition, but for the moment mpartition only makes primary partitions */ if(fat_bits == 12) /* FAT 12 partition */ type = 0x01; /* DOS FAT12, CHS */ else if (fat_bits == 16) /* FAT 16 partition */ type = 0x04; /* DOS FAT16, CHS */ } else if (end < sectors * heads * 1024) /* FAT 12 or FAT16 partition above the 32M * mark but below the 1024 cylinder mark. * Indeed, there can be no CHS partition * beyond 1024 cylinders */ type = 0x06; /* DOS BIG FAT16 or FAT12, CHS */ else type = 0x0E; /* Win95 BIG FAT16, LBA */ } partTable->sys_ind = type; } int consistencyCheck(struct partition *partTable, int doprint, int verbose, int *has_activated, unsigned int *last_end, unsigned int *j, struct device *used_dev, int target_partition) { int i; unsigned int inconsistency; *j = 0; *last_end = 1; /* quick consistency check */ inconsistency = 0; *has_activated = 0; for(i=1; i<5; i++){ if(!partTable[i].sys_ind) continue; if(partTable[i].boot_ind) (*has_activated)++; if((used_dev && (used_dev->heads != head(partTable[i].end)+1 || used_dev->sectors != sector(partTable[i].end))) || sector(partTable[i].start) != 1){ fprintf(stderr, "Partition %d is not aligned\n", i); inconsistency=1; } if(*j && *last_end > BEGIN(partTable[i])) { fprintf(stderr, "Partitions %d and %d badly ordered or overlapping\n", *j,i); inconsistency=1; } *last_end = END(partTable[i]); *j = i; if(used_dev && cyl(partTable[i].start) != 1023 && tolinear(partTable[i].start) != BEGIN(partTable[i])) { fprintf(stderr, "Start position mismatch for partition %d\n", i); inconsistency=1; } if(used_dev && cyl(partTable[i].end) != 1023 && tolinear(partTable[i].end)+1 != END(partTable[i])) { fprintf(stderr, "End position mismatch for partition %d\n", i); inconsistency=1; } if(doprint && verbose) { if(i==target_partition) putchar('*'); else putchar(' '); printf("Partition %d\n",i); printf(" active=%x\n", partTable[i].boot_ind); printf(" start:"); print_hsc(&partTable[i].start); printf(" type=0x%x\n", partTable[i].sys_ind); printf(" end:"); print_hsc(&partTable[i].end); printf(" start=%d\n", BEGIN(partTable[i])); printf(" nr=%d\n", _DWORD(partTable[i].nr_sects)); printf("\n"); } } return inconsistency; } /* setsize function. Determines scsicam mapping if this cannot be inferred from * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */ /* * Function : static int setsize(unsigned long capacity,unsigned int *cyls, * unsigned int *hds, unsigned int *secs); * * Purpose : to determine a near-optimal int 0x13 mapping for a * SCSI disk in terms of lost space of size capacity, storing * the results in *cyls, *hds, and *secs. * * Returns : -1 on failure, 0 on success. * * Extracted from * * WORKING X3T9.2 * DRAFT 792D * * * Revision 6 * 10-MAR-94 * Information technology - * SCSI-2 Common access method * transport and SCSI interface module * * ANNEX A : * * setsize() converts a read capacity value to int 13h * head-cylinder-sector requirements. It minimizes the value for * number of heads and maximizes the number of cylinders. This * will support rather large disks before the number of heads * will not fit in 4 bits (or 6 bits). This algorithm also * minimizes the number of sectors that will be unused at the end * of the disk while allowing for very large disks to be * accommodated. This algorithm does not use physical geometry. */ static int setsize(unsigned long capacity,unsigned int *cyls, uint16_t *hds, uint16_t *secs) { unsigned int rv = 0; unsigned long heads, sectors, cylinders, temp; cylinders = 1024L; /* Set number of cylinders to max */ sectors = 62L; /* Maximize sectors per track */ temp = cylinders * sectors; /* Compute divisor for heads */ heads = capacity / temp; /* Compute value for number of heads */ if (capacity % temp) { /* If no remainder, done! */ heads++; /* Else, increment number of heads */ temp = cylinders * heads; /* Compute divisor for sectors */ sectors = capacity / temp; /* Compute value for sectors per track */ if (capacity % temp) { /* If no remainder, done! */ sectors++; /* Else, increment number of sectors */ temp = heads * sectors; /* Compute divisor for cylinders */ cylinders = capacity / temp;/* Compute number of cylinders */ } } if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */ *cyls = (unsigned int) cylinders; /* Stuff return values */ *secs = (unsigned int) sectors; *hds = (unsigned int) heads; return(rv); } static void setsize0(unsigned long capacity,unsigned int *cyls, uint16_t *hds, uint16_t *secs) { int r; /* 1. First try "Megabyte" sizes */ if(capacity < 1024 * 2048 && !(capacity % 1024)) { *cyls = capacity >> 11; *hds = 64; *secs = 32; return; } /* then try scsicam's size */ r = setsize(capacity,cyls,hds,secs); if(r || *hds > 255 || *secs > 63) { /* scsicam failed. Do megabytes anyways */ *cyls = capacity >> 11; *hds = 64; *secs = 32; return; } } static void usage(int ret) NORETURN; static void usage(int ret) { fprintf(stderr, "Mtools version %s, dated %s\n", mversion, mdate); fprintf(stderr, "Usage: %s [-pradcv] [-I] [-B bootsect-template] [-s sectors] " "[-t cylinders] " "[-h heads] [-T type] [-b begin] [-l length] " "drive\n", progname); exit(ret); } void mpartition(int argc, char **argv, int dummy UNUSEDP) NORETURN; void mpartition(int argc, char **argv, int dummy UNUSEDP) { Stream_t *Stream; unsigned int dummy2; unsigned int i,j; int sec_per_cyl; int doprint = 0; int verbose = 0; int create = 0; int force = 0; unsigned int length = 0; int do_remove = 0; int initialize = 0; unsigned int tot_sectors=0; int type = 0; int begin_set = 0; int size_set = 0; int end_set = 0; unsigned int last_end = 0; int activate = 0; int has_activated = 0; int inconsistency=0; unsigned int begin=0; unsigned int end=0; int sizetest=0; int dirty = 0; int open2flags = NO_OFFSET; int c; struct device used_dev; int argtracks, argheads, argsectors; char drive, name[EXPAND_BUF]; unsigned char buf[512]; struct partition *partTable=(struct partition *)(buf+ 0x1ae); struct device *dev; char errmsg[2100]; char *bootSector=0; argtracks = 0; argheads = 0; argsectors = 0; /* get command line options */ if(helpFlag(argc, argv)) usage(0); while ((c = getopt(argc, argv, "i:adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) { char *endptr=NULL; errno=0; switch (c) { case 'i': set_cmd_line_image(optarg); break; case 'B': bootSector = optarg; break; case 'a': /* no privs, as it could be abused to * make other partitions unbootable, or * to boot a rogue kernel from this one */ open2flags |= NO_PRIV; activate = 1; dirty = 1; break; case 'd': activate = -1; dirty = 1; break; case 'p': doprint = 1; break; case 'r': do_remove = 1; dirty = 1; break; case 'I': /* could be abused to nuke all other * partitions */ open2flags |= NO_PRIV; initialize = 1; dirty = 1; break; case 'c': create = 1; dirty = 1; break; case 'T': /* could be abused to "manually" create * extended partitions */ open2flags |= NO_PRIV; type = strtoi(optarg, &endptr, 0); break; case 't': argtracks = atoi(optarg); break; case 'h': argheads = atoi(optarg); break; case 's': argsectors = atoi(optarg); break; case 'f': /* could be abused by creating overlapping * partitions and other such Snafu */ open2flags |= NO_PRIV; force = 1; break; case 'v': verbose++; break; case 'S': /* testing only */ /* could be abused to create partitions * extending beyond the actual size of the * device */ open2flags |= NO_PRIV; tot_sectors = strtoui(optarg, &endptr, 0); sizetest = 1; break; case 'b': begin_set = 1; begin = strtoui(optarg, &endptr, 0); break; case 'l': size_set = 1; length = strtoui(optarg, &endptr, 0); break; default: usage(1); } check_number_parse_errno(c, optarg, endptr); } if (argc - optind != 1 || !argv[optind][0] || argv[optind][1] != ':') usage(1); drive = ch_toupper(argv[optind][0]); /* check out a drive whose letter and parameters match */ sprintf(errmsg, "Drive '%c:' not supported", drive); Stream = 0; for(dev=devices;dev->drive;dev++) { int mode ; FREE(&(Stream)); /* drive letter */ if (dev->drive != drive) continue; if (dev->partition < 1 || dev->partition > 4) { sprintf(errmsg, "Drive '%c:' is not a partition", drive); continue; } used_dev = *dev; SET_INT(used_dev.tracks, argtracks); SET_INT(used_dev.heads, argheads); SET_INT(used_dev.sectors, argsectors); expand(dev->name, name); mode = dirty ? O_RDWR : O_RDONLY; if(initialize) mode |= O_CREAT; #ifdef USING_NEW_VOLD strcpy(name, getVoldName(dev, name)); #endif Stream = SimpleFileOpen(&used_dev, dev, name, mode, errmsg, open2flags, 1, 0); if (!Stream) { #ifdef HAVE_SNPRINTF snprintf(errmsg,sizeof(errmsg)-1, "init: open: %s", strerror(errno)); #else sprintf(errmsg,"init: open: %s", strerror(errno)); #endif continue; } /* try to find out the size */ if(!sizetest) tot_sectors = 0; if(IS_SCSI(dev)) { unsigned char cmd[10]; unsigned char data[10]; cmd[0] = SCSI_READ_CAPACITY; memset ((void *) &cmd[2], 0, 8); memset ((void *) &data[0], 137, 10); scsi_cmd(get_fd(Stream), cmd, 10, SCSI_IO_READ, data, 10, get_extra_data(Stream)); tot_sectors = 1 + (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + (data[3] ); if(verbose) printf("%d sectors in total\n", tot_sectors); } #ifdef OS_linux if (tot_sectors == 0) { ioctl(get_fd(Stream), BLKGETSIZE, &tot_sectors); } #endif /* read the partition table */ if (READS(Stream, (char *) buf, 0, 512) != 512 && !initialize){ #ifdef HAVE_SNPRINTF snprintf(errmsg, sizeof(errmsg)-1, "Error reading from '%s', wrong parameters?", name); #else sprintf(errmsg, "Error reading from '%s', wrong parameters?", name); #endif continue; } if(verbose>=2) print_sector("Read sector", buf, 512); break; } /* print error msg if needed */ if ( dev->drive == 0 ){ FREE(&Stream); fprintf(stderr,"%s: %s\n", argv[0],errmsg); exit(1); } if((used_dev.sectors || used_dev.heads) && (!used_dev.sectors || !used_dev.heads)) { fprintf(stderr,"You should either indicate both the number of sectors and the number of heads,\n"); fprintf(stderr," or none of them\n"); exit(1); } if(initialize) { if (bootSector) { int fd; fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE); if (fd < 0) { perror("open MBR"); exit(1); } if(read(fd, (char *) buf, 512) < 512) { perror("read MBR"); exit(1); } } memset((char *)(partTable+1), 0, 4*sizeof(*partTable)); set_word(((unsigned char*)buf)+510, 0xaa55); } /* check for boot signature, and place it if needed */ if((buf[510] != 0x55) || (buf[511] != 0xaa)) { fprintf(stderr,"Boot signature not set\n"); fprintf(stderr, "Use the -I flag to initialize the partition table, and set the boot signature\n"); inconsistency = 1; } if(do_remove){ if(!partTable[dev->partition].sys_ind) fprintf(stderr, "Partition for drive %c: does not exist\n", drive); if((partTable[dev->partition].sys_ind & 0x3f) == 5) { fprintf(stderr, "Partition for drive %c: may be an extended partition\n", drive); fprintf(stderr, "Use the -f flag to remove it anyways\n"); inconsistency = 1; } memset(&partTable[dev->partition], 0, sizeof(*partTable)); } if(create && partTable[dev->partition].sys_ind) { fprintf(stderr, "Partition for drive %c: already exists\n", drive); fprintf(stderr, "Use the -r flag to remove it before attempting to recreate it\n"); } /* find out number of heads and sectors, and whether there is * any activated partition */ has_activated = 0; for(i=1; i<5; i++){ if(!partTable[i].sys_ind) continue; if(partTable[i].boot_ind) has_activated++; /* set geometry from entry */ if (!used_dev.heads) used_dev.heads = head(partTable[i].end)+1; if(!used_dev.sectors) used_dev.sectors = sector(partTable[i].end); if(ipartition && !begin_set) begin = END(partTable[i]); if(i>dev->partition && !end_set && !size_set) { end = BEGIN(partTable[i]); end_set = 1; } } #ifdef OS_linux if(!used_dev.sectors && !used_dev.heads) { if(!IS_SCSI(dev)) { struct hd_geometry geom; if(ioctl(get_fd(Stream), HDIO_GETGEO, &geom) == 0) { used_dev.heads = geom.heads; used_dev.sectors = geom.sectors; } } } #endif if(!used_dev.sectors && !used_dev.heads) { if(tot_sectors) setsize0(tot_sectors,&dummy2,&used_dev.heads, &used_dev.sectors); else { used_dev.heads = 64; used_dev.sectors = 32; } } if(verbose) fprintf(stderr,"sectors: %d heads: %d %d\n", used_dev.sectors, used_dev.heads, tot_sectors); sec_per_cyl = used_dev.sectors * used_dev.heads; if(create) { if(!end_set && tot_sectors) { end = tot_sectors - tot_sectors % sec_per_cyl; end_set = 1; } /* if the partition starts right at the beginning of * the disk, keep one track unused to allow place for * the master boot record */ if(!begin && !begin_set) begin = used_dev.sectors; if(!size_set && used_dev.tracks) { size_set = 2; length = sec_per_cyl * used_dev.tracks; /* round the size in order to take * into account any "hidden" sectors */ /* do we anchor this at the beginning ?*/ if(begin_set || dev->partition <= 2 || !end_set) length -= begin % sec_per_cyl; else if(end - length < begin) /* truncate any overlap */ length = end - begin; } if(size_set) { if(!begin_set && dev->partition >2 && end_set) begin = end - length; else end = begin + length; } else if(!end_set) { fprintf(stderr,"Unknown size\n"); exit(1); } setBeginEnd(&partTable[dev->partition], begin, end, used_dev.heads, used_dev.sectors, !has_activated, type, dev->fat_bits); } if(activate) { if(!partTable[dev->partition].sys_ind) { fprintf(stderr, "Partition for drive %c: does not exist\n", drive); } else { switch(activate) { case 1: partTable[dev->partition].boot_ind=0x80; break; case -1: partTable[dev->partition].boot_ind=0x00; break; } } } inconsistency |= consistencyCheck(partTable, doprint, verbose, &has_activated, &last_end, &j, &used_dev, dev->partition); if(doprint && !inconsistency && partTable[dev->partition].sys_ind) { printf("The following command will recreate the partition for drive %c:\n", drive); used_dev.tracks = (_DWORD(partTable[dev->partition].nr_sects) + (BEGIN(partTable[dev->partition]) % sec_per_cyl)) / sec_per_cyl; printf("mpartition -c -t %d -h %d -s %d -b %u %c:\n", used_dev.tracks, used_dev.heads, used_dev.sectors, BEGIN(partTable[dev->partition]), drive); } if(tot_sectors && last_end >tot_sectors) { fprintf(stderr, "Partition %d exceeds beyond end of disk\n", j); exit(1); } switch(has_activated) { case 0: fprintf(stderr, "Warning: no active (bootable) partition present\n"); break; case 1: break; default: fprintf(stderr, "Warning: %d active (bootable) partitions present\n", has_activated); fprintf(stderr, "Usually, a disk should have exactly one active partition\n"); break; } if(inconsistency && !force) { fprintf(stderr, "inconsistency detected!\n" ); if(dirty) fprintf(stderr, "Retry with the -f switch to go ahead anyways\n"); exit(1); } if(dirty) { /* write data back to the disk */ if(verbose>=2) print_sector("Writing sector", buf, 512); if (WRITES(Stream, (char *) buf, 0, 512) != 512) { fprintf(stderr,"Error writing partition table"); exit(1); } if(verbose>=3) print_sector("Sector written", buf, 512); } FREE(&Stream); exit(0); }