Imported Upstream version 4.0.43
[platform/upstream/mtools.git] / mpartition.c
index 4d20ef0..56e2b6a 100644 (file)
 #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"
+#include "open_image.h"
+#include "lba.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)
+static void set_offset(hsc *h, unsigned long offset,
+                      uint16_t heads, uint16_t sectors)
 {
-       int head, sector, cyl;
+       uint16_t head, sector;
+       unsigned int cyl;
 
        if(! heads || !sectors)
                head = sector = cyl = 0; /* linear mode */
@@ -61,20 +47,43 @@ static void set_offset(hsc *h, int offset, int heads, int sectors)
                offset = offset / sectors;
 
                head = offset % heads;
-               cyl = offset / heads;
-               if(cyl > 1023) cyl = 1023;
+               offset = offset / heads;
+               if(offset > 1023)
+                       cyl = 1023;
+               else
+                       cyl = (uint16_t) offset;
        }
-
-       h->head = head;
+       if(head > UINT8_MAX) {
+               /* sector or head out of range => linear mode */
+               head = sector = cyl = 0;
+       }
+       h->head = (uint8_t) head;
        h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2);
        h->cyl = cyl & 0xff;
 }
 
 void setBeginEnd(struct partition *partTable,
-                unsigned int begin, unsigned int end,
-                unsigned int heads, unsigned int sectors,
-                int activate, int type, int fat_bits)
+                uint32_t begin, uint32_t end,
+                uint16_t iheads, uint16_t isectors,
+                int activate, uint8_t type, unsigned int fat_bits)
 {
+       uint8_t heads, sectors;
+
+       if(iheads > UINT8_MAX) {
+               fprintf(stderr,
+                       "Too many heads for partition: %d\n",
+                       iheads);
+               exit(1);
+       }
+       heads=(uint8_t) iheads;
+       if(isectors > UINT8_MAX) {
+               fprintf(stderr,
+                       "Too many sectors for partition: %d\n",
+                       isectors);
+               exit(1);
+       }
+       sectors=(uint8_t) isectors;
+
        set_offset(&partTable->start, begin, heads, sectors);
        set_offset(&partTable->end, end-1, heads, sectors);
        set_dword(partTable->start_sect, begin);
@@ -125,7 +134,7 @@ void setBeginEnd(struct partition *partTable,
                        else if (fat_bits == 16)
                                /* FAT 16 partition */
                                type = 0x04; /* DOS FAT16, CHS */
-               } else if (end <  sectors * heads * 1024)
+               } else if (end <  sectors * heads * 1024u)
                        /* FAT 12 or FAT16 partition above the 32M
                         * mark but below the 1024 cylinder mark.
                         * Indeed, there can be no CHS partition
@@ -137,83 +146,6 @@ void setBeginEnd(struct partition *partTable,
        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 ;-) */
@@ -254,7 +186,7 @@ int consistencyCheck(struct partition *partTable, int doprint, int verbose,
 
 static int setsize(unsigned long capacity,unsigned int *cyls,
                   uint16_t *hds,  uint16_t *secs) {
-    unsigned int rv = 0;
+    int rv = 0;
     unsigned long heads, sectors, cylinders, temp;
 
     cylinders = 1024L;                 /* Set number of cylinders to max */
@@ -273,15 +205,15 @@ static int setsize(unsigned long capacity,unsigned int *cyls,
            cylinders = capacity / temp;/* Compute number of cylinders */
        }
     }
-    if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */
+    if (cylinders == 0) rv=-1;/* Give error if 0 cylinders */
 
     *cyls = (unsigned int) cylinders;  /* Stuff return values */
-    *secs = (unsigned int) sectors;
-    *hds  = (unsigned int) heads;
+    *secs = (uint16_t) sectors;
+    *hds  = (uint16_t) heads;
     return(rv);
 }
 
-static void setsize0(unsigned long capacity,unsigned int *cyls,
+static void setsize0(uint32_t capacity,unsigned int *cyls,
                     uint16_t *hds, uint16_t *secs)
 {
        int r;
@@ -325,9 +257,9 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
        Stream_t *Stream;
        unsigned int dummy2;
 
-       unsigned int i,j;
+       unsigned int i;
 
-       int sec_per_cyl;
+       uint16_t sec_per_cyl;
        int doprint = 0;
        int verbose = 0;
        int create = 0;
@@ -335,24 +267,26 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
        unsigned int length = 0;
        int do_remove = 0;
        int initialize = 0;
-       unsigned int tot_sectors=0;
-       int type = 0;
+
+       uint32_t tot_sectors=0;
+       /* Needs to be long due to BLKGETSIZE ioctl */
+
+       uint8_t 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 open2flags = 0;
 
        int c;
        struct device used_dev;
-       int argtracks, argheads, argsectors;
+       unsigned int argtracks;
+       uint16_t argheads, argsectors;
 
        char drive, name[EXPAND_BUF];
        unsigned char buf[512];
@@ -360,6 +294,7 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
        struct device *dev;
        char errmsg[2100];
        char *bootSector=0;
+       struct partition *tpartition;
 
        argtracks = 0;
        argheads = 0;
@@ -369,6 +304,8 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
        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);
@@ -411,17 +348,17 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
                                /* could be abused to "manually" create
                                 * extended partitions */
                                open2flags |= NO_PRIV;
-                               type = strtoi(optarg,0,0);
+                               type = strtou8(optarg, &endptr, 0);
                                break;
 
                        case 't':
-                               argtracks = atoi(optarg);
+                               argtracks = atoui(optarg);
                                break;
                        case 'h':
-                               argheads = atoi(optarg);
+                               argheads = atou16(optarg);
                                break;
                        case 's':
-                               argsectors = atoi(optarg);
+                               argsectors = atou16(optarg);
                                break;
 
                        case 'f':
@@ -434,27 +371,19 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
                        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,0,0);
-                               sizetest = 1;
-                               break;
                        case 'b':
                                begin_set = 1;
-                               begin = strtoui(optarg, NULL, 10);
+                               begin = strtoui(optarg, &endptr, 0);
                                break;
                        case 'l':
                                size_set = 1;
-                               length = strtoui(optarg, NULL, 10);
+                               length = parseSize(optarg);
                                break;
 
                        default:
                                usage(1);
                }
+               check_number_parse_errno((char)c, optarg, endptr);
        }
 
        if (argc - optind != 1 ||
@@ -494,8 +423,9 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
 #ifdef USING_NEW_VOLD
                strcpy(name, getVoldName(dev, name));
 #endif
-               Stream = SimpleFileOpen(&used_dev, dev, name, mode,
-                                       errmsg, open2flags, 1, 0);
+               Stream = OpenImage(&used_dev, dev, name, mode, errmsg,
+                                  open2flags | SKIP_PARTITION | ALWAYS_GET_GEOMETRY,
+                                  mode, NULL, NULL, NULL);
 
                if (!Stream) {
 #ifdef HAVE_SNPRINTF
@@ -507,36 +437,10 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
                        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
+               tot_sectors = used_dev.tot_sectors;
 
                /* read the partition table */
-               if (READS(Stream, (char *) buf, 0, 512) != 512 && !initialize){
+               if (PREADS(Stream, (char *) buf, 0, 512) != 512 && !initialize){
 #ifdef HAVE_SNPRINTF
                        snprintf(errmsg, sizeof(errmsg)-1,
                                "Error reading from '%s', wrong parameters?",
@@ -592,12 +496,13 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
                inconsistency = 1;
        }
 
+       tpartition=&partTable[dev->partition];
        if(do_remove){
-               if(!partTable[dev->partition].sys_ind)
+               if(!tpartition->sys_ind)
                        fprintf(stderr,
                                "Partition for drive %c: does not exist\n",
                                drive);
-               if((partTable[dev->partition].sys_ind & 0x3f) == 5) {
+               if((tpartition->sys_ind & 0x3f) == 5) {
                        fprintf(stderr,
                                "Partition for drive %c: may be an extended partition\n",
                                drive);
@@ -605,69 +510,59 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
                                "Use the -f flag to remove it anyways\n");
                        inconsistency = 1;
                }
-               memset(&partTable[dev->partition], 0, sizeof(*partTable));
+               memset(tpartition, 0, sizeof(*tpartition));
        }
 
-       if(create && partTable[dev->partition].sys_ind) {
+       if(create && tpartition->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");
        }
 
+       /* if number of heads and sectors not known yet, set "reasonable"
+        * defaults */
+       compute_lba_geom_from_tot_sectors(&used_dev);
 
-       /* find out number of heads and sectors, and whether there is
-       * any activated partition */
+       /* find out whether there is any activated partition. Moreover
+        * if no offset of a partition to be created have been
+        * specificed, find out whether it may be placed between the
+        * preceding and following partition already existing */
        has_activated = 0;
        for(i=1; i<5; i++){
-               if(!partTable[i].sys_ind)
+               struct partition *partition=&partTable[i];
+               if(!partition->sys_ind)
                        continue;
 
-               if(partTable[i].boot_ind)
+               if(partition->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]);
+                       begin = END(partition);
                if(i>dev->partition && !end_set && !size_set) {
-                       end = BEGIN(partTable[i]);
+                       end = BEGIN(partition);
                        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,
+               if(tot_sectors) {
+                       setsize0((uint32_t)tot_sectors,&dummy2,&used_dev.heads,
                                 &used_dev.sectors);
-               else {
+               else {
                        used_dev.heads = 64;
                        used_dev.sectors = 32;
                }
        }
 
        if(verbose)
-               fprintf(stderr,"sectors: %d heads: %d %d\n",
+               fprintf(stderr,"sectors: %d heads: %d %u\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) {
+               unsigned int overlap;
+               if(!end_set && !size_set && tot_sectors) {
                        end = tot_sectors - tot_sectors % sec_per_cyl;
                        end_set = 1;
                }
@@ -676,79 +571,61 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
                 * 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;
-               }
+                       begin = used_dev.sectors ? used_dev.sectors : 2048;
+
+               /* Do not try to align  partitions (other than first) on track
+                * boundaries here: apparently this was a thing of the past */
+
                if(size_set) {
-                       if(!begin_set && dev->partition >2 && end_set)
-                               begin = end - length;
-                       else
-                               end = begin + length;
+                       end = begin + length;
                } else if(!end_set) {
                        fprintf(stderr,"Unknown size\n");
                        exit(1);
                }
 
-               setBeginEnd(&partTable[dev->partition], begin, end,
+               /* Make sure partition boundaries are correctly ordered
+                * (end > begin) */
+               if(begin >= end) {
+                       fprintf(stderr, "Begin larger than end\n");
+                       exit(1);
+               }
+
+               /* Check whether new partition doesn't overlap with
+                * any of those already in place */
+               if((overlap=findOverlap(partTable, 4, begin, end))) {
+                       fprintf(stderr,
+                               "Partition would overlap with partition %d\n",
+                               overlap);
+                       exit(1);
+               }
+
+               setBeginEnd(tpartition, begin, end,
                            used_dev.heads, used_dev.sectors,
                            !has_activated, type,
-                           dev->fat_bits);
+                           abs(dev->fat_bits));
        }
 
        if(activate) {
-               if(!partTable[dev->partition].sys_ind) {
+               if(!tpartition->sys_ind) {
                        fprintf(stderr,
                                "Partition for drive %c: does not exist\n",
                                drive);
                } else {
                        switch(activate) {
                                case 1:
-                                       partTable[dev->partition].boot_ind=0x80;
+                                       tpartition->boot_ind=0x80;
                                        break;
                                case -1:
-                                       partTable[dev->partition].boot_ind=0x00;
+                                       tpartition->boot_ind=0x00;
                                        break;
                        }
                }
        }
 
-
        inconsistency |= consistencyCheck(partTable, doprint, verbose,
-                                         &has_activated, &last_end, &j,
+                                         &has_activated, tot_sectors,
                                          &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,
@@ -768,17 +645,31 @@ void mpartition(int argc, char **argv, int dummy UNUSEDP)
        if(inconsistency && !force) {
                fprintf(stderr,
                        "inconsistency detected!\n" );
-               if(dirty)
+               if(dirty) {
                        fprintf(stderr,
                                "Retry with the -f switch to go ahead anyways\n");
-               exit(1);
+                       exit(1);
+               }
+       }
+
+       if(doprint && tpartition->sys_ind) {
+               printf("The following command will recreate the partition for drive %c:\n",
+                      drive);
+               used_dev.tracks =
+                       (DWORD(tpartition->nr_sects) +
+                        (BEGIN(tpartition) % sec_per_cyl)) /
+                       sec_per_cyl;
+               printf("mpartition -c -b %d -l %d -t %d -h %d -s %d -b %u %c:\n",
+                      BEGIN(tpartition), PART_SIZE(tpartition),
+                      used_dev.tracks, used_dev.heads, used_dev.sectors,
+                      BEGIN(tpartition), drive);
        }
 
        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) {
+               if (PWRITES(Stream, (char *) buf, 0, 512) != 512) {
                        fprintf(stderr,"Error writing partition table");
                        exit(1);
                }