summaryrefslogtreecommitdiff
path: root/mpartition.c
diff options
context:
space:
mode:
Diffstat (limited to 'mpartition.c')
-rw-r--r--mpartition.c742
1 files changed, 742 insertions, 0 deletions
diff --git a/mpartition.c b/mpartition.c
new file mode 100644
index 0000000..24ac840
--- /dev/null
+++ b/mpartition.c
@@ -0,0 +1,742 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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, int 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, int begin, int end,
+ int heads, int sectors, int activate, int type)
+{
+ 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(end-begin < 4096)
+ type = 1; /* DOS 12-bit FAT */
+ else if(end-begin<32*2048)
+ type = 4; /* DOS 16-bit FAT, <32M */
+ else
+ type = 6; /* DOS 16-bit FAT >= 32M */
+ }
+ 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)
+{
+ unsigned 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,unsigned int *hds,
+ unsigned int *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,
+ unsigned int *hds, unsigned int *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)
+{
+ 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;
+ 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;
+ int begin=0;
+ 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[200];
+ 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) {
+ 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 = strtoul(optarg,0,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 = strtoul(optarg,0,0);
+ sizetest = 1;
+ break;
+ case 'b':
+ begin_set = 1;
+ begin = atoi(optarg);
+ break;
+ case 'l':
+ size_set = 1;
+ length = atoi(optarg);
+ break;
+
+ default:
+ usage(1);
+ }
+ }
+
+ if (argc - optind != 1 ||
+ !argv[optind][0] || argv[optind][1] != ':')
+ usage(1);
+
+ drive = 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,199,"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, 199,
+ "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(i<dev->partition && !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);
+ }
+
+ 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);
+}