Imported Upstream version 4.0.43
[platform/upstream/mtools.git] / mpartition.c
1 /*  Copyright 1997-2003,2005-2007,2009 Alain Knaff.
2  *  This file is part of mtools.
3  *
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.
8  *
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.
13  *
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/>.
16  *
17  * mformat.c
18  */
19 #define DONT_NEED_WAIT
20
21 #include "sysincludes.h"
22 #include "mtools.h"
23 #include "fsP.h"
24 #include "file.h"
25 #include "plain_io.h"
26 #include "nameclash.h"
27 #include "buffer.h"
28 #include "partition.h"
29 #include "open_image.h"
30 #include "lba.h"
31
32 #ifdef OS_linux
33 #include "linux/hdreg.h"
34 #include "linux/fs.h"
35 #endif
36
37 static void set_offset(hsc *h, unsigned long offset,
38                        uint16_t heads, uint16_t sectors)
39 {
40         uint16_t head, sector;
41         unsigned int cyl;
42
43         if(! heads || !sectors)
44                 head = sector = cyl = 0; /* linear mode */
45         else {
46                 sector = offset % sectors;
47                 offset = offset / sectors;
48
49                 head = offset % heads;
50                 offset = offset / heads;
51                 if(offset > 1023)
52                         cyl = 1023;
53                 else
54                         cyl = (uint16_t) offset;
55         }
56         if(head > UINT8_MAX) {
57                 /* sector or head out of range => linear mode */
58                 head = sector = cyl = 0;
59         }
60         h->head = (uint8_t) head;
61         h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2);
62         h->cyl = cyl & 0xff;
63 }
64
65 void setBeginEnd(struct partition *partTable,
66                  uint32_t begin, uint32_t end,
67                  uint16_t iheads, uint16_t isectors,
68                  int activate, uint8_t type, unsigned int fat_bits)
69 {
70         uint8_t heads, sectors;
71
72         if(iheads > UINT8_MAX) {
73                 fprintf(stderr,
74                         "Too many heads for partition: %d\n",
75                         iheads);
76                 exit(1);
77         }
78         heads=(uint8_t) iheads;
79         if(isectors > UINT8_MAX) {
80                 fprintf(stderr,
81                         "Too many sectors for partition: %d\n",
82                         isectors);
83                 exit(1);
84         }
85         sectors=(uint8_t) isectors;
86
87         set_offset(&partTable->start, begin, heads, sectors);
88         set_offset(&partTable->end, end-1, heads, sectors);
89         set_dword(partTable->start_sect, begin);
90         set_dword(partTable->nr_sects, end-begin);
91         if(activate)
92                 partTable->boot_ind = 0x80;
93         else
94                 partTable->boot_ind = 0;
95         if(!type) {
96                 if (fat_bits == 0) {
97                         /**
98                          * Fat bits unknown / not specified. We look
99                          * at size to get a rough estimate what FAT
100                          * bits are used.  Note: this is only an
101                          * estimate, the precise calculation would
102                          * involve the number of clusters, which is
103                          * not necessarily known here.
104                          */
105                         /* cc977219 would have a cutoff number of 32680,
106                          * corresponding to a FAT12 partition with 4K
107                          * clusters, however other information hints that
108                          * only partitions with less than 4096 sectors are
109                          * considered */
110                         if(end-begin < 4096)
111                                 fat_bits = 12;
112                         else
113                                 fat_bits = 16;
114                 }
115
116                 /* Description of various partition types in
117                  * https://en.wikipedia.org/wiki/Partition_type#List_of_partition_IDs
118                  * and
119                  * https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc977219(v=technet.10)
120                  */
121                 if (fat_bits == 32)
122                         /* FAT 32 partition. For now, we disregard the
123                          * possibility of FAT 32 CHS partitions */
124                         type = 0x0C; /* Win95 FAT32, LBA */
125                 else if (end < 65536) {
126                         /* FAT 12 or FAT 16 partitions which fit entirely below
127                            the 32M mark */
128                         /* The 32M restriction doesn't apply to logical
129                            partitions within an extended partition, but for the
130                            moment mpartition only makes primary partitions */
131                         if(fat_bits == 12)
132                                 /* FAT 12 partition */
133                                 type = 0x01; /* DOS FAT12, CHS */
134                         else if (fat_bits == 16)
135                                 /* FAT 16 partition */
136                                 type = 0x04; /* DOS FAT16, CHS */
137                 } else if (end <  sectors * heads * 1024u)
138                         /* FAT 12 or FAT16 partition above the 32M
139                          * mark but below the 1024 cylinder mark.
140                          * Indeed, there can be no CHS partition
141                          * beyond 1024 cylinders */
142                         type = 0x06; /* DOS BIG FAT16 or FAT12, CHS */
143                 else
144                         type = 0x0E; /* Win95 BIG FAT16, LBA */
145         }
146         partTable->sys_ind = type;
147 }
148
149
150 /* setsize function.  Determines scsicam mapping if this cannot be inferred from
151  * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */
152
153 /*
154  * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
155  *      unsigned int *hds, unsigned int *secs);
156  *
157  * Purpose : to determine a near-optimal int 0x13 mapping for a
158  *      SCSI disk in terms of lost space of size capacity, storing
159  *      the results in *cyls, *hds, and *secs.
160  *
161  * Returns : -1 on failure, 0 on success.
162  *
163  * Extracted from
164  *
165  * WORKING                                                    X3T9.2
166  * DRAFT                                                        792D
167  *
168  *
169  *                                                        Revision 6
170  *                                                         10-MAR-94
171  * Information technology -
172  * SCSI-2 Common access method
173  * transport and SCSI interface module
174  *
175  * ANNEX A :
176  *
177  * setsize() converts a read capacity value to int 13h
178  * head-cylinder-sector requirements. It minimizes the value for
179  * number of heads and maximizes the number of cylinders. This
180  * will support rather large disks before the number of heads
181  * will not fit in 4 bits (or 6 bits). This algorithm also
182  * minimizes the number of sectors that will be unused at the end
183  * of the disk while allowing for very large disks to be
184  * accommodated. This algorithm does not use physical geometry.
185  */
186
187 static int setsize(unsigned long capacity,unsigned int *cyls,
188                    uint16_t *hds,  uint16_t *secs) {
189     int rv = 0;
190     unsigned long heads, sectors, cylinders, temp;
191
192     cylinders = 1024L;                  /* Set number of cylinders to max */
193     sectors = 62L;                      /* Maximize sectors per track */
194
195     temp = cylinders * sectors;         /* Compute divisor for heads */
196     heads = capacity / temp;            /* Compute value for number of heads */
197     if (capacity % temp) {              /* If no remainder, done! */
198         heads++;                        /* Else, increment number of heads */
199         temp = cylinders * heads;       /* Compute divisor for sectors */
200         sectors = capacity / temp;      /* Compute value for sectors per
201                                                track */
202         if (capacity % temp) {          /* If no remainder, done! */
203             sectors++;                  /* Else, increment number of sectors */
204             temp = heads * sectors;     /* Compute divisor for cylinders */
205             cylinders = capacity / temp;/* Compute number of cylinders */
206         }
207     }
208     if (cylinders == 0) rv=-1;/* Give error if 0 cylinders */
209
210     *cyls = (unsigned int) cylinders;   /* Stuff return values */
211     *secs = (uint16_t) sectors;
212     *hds  = (uint16_t) heads;
213     return(rv);
214 }
215
216 static void setsize0(uint32_t capacity,unsigned int *cyls,
217                      uint16_t *hds, uint16_t *secs)
218 {
219         int r;
220
221         /* 1. First try "Megabyte" sizes */
222         if(capacity < 1024 * 2048 && !(capacity % 1024)) {
223                 *cyls = capacity >> 11;
224                 *hds  = 64;
225                 *secs = 32;
226                 return;
227         }
228
229         /* then try scsicam's size */
230         r = setsize(capacity,cyls,hds,secs);
231         if(r || *hds > 255 || *secs > 63) {
232                 /* scsicam failed. Do megabytes anyways */
233                 *cyls = capacity >> 11;
234                 *hds  = 64;
235                 *secs = 32;
236                 return;
237         }
238 }
239
240
241 static void usage(int ret) NORETURN;
242 static void usage(int ret)
243 {
244         fprintf(stderr,
245                 "Mtools version %s, dated %s\n", mversion, mdate);
246         fprintf(stderr,
247                 "Usage: %s [-pradcv] [-I] [-B bootsect-template] [-s sectors] "
248                         "[-t cylinders] "
249                 "[-h heads] [-T type] [-b begin] [-l length] "
250                 "drive\n", progname);
251         exit(ret);
252 }
253
254 void mpartition(int argc, char **argv, int dummy UNUSEDP) NORETURN;
255 void mpartition(int argc, char **argv, int dummy UNUSEDP)
256 {
257         Stream_t *Stream;
258         unsigned int dummy2;
259
260         unsigned int i;
261
262         uint16_t sec_per_cyl;
263         int doprint = 0;
264         int verbose = 0;
265         int create = 0;
266         int force = 0;
267         unsigned int length = 0;
268         int do_remove = 0;
269         int initialize = 0;
270
271         uint32_t tot_sectors=0;
272         /* Needs to be long due to BLKGETSIZE ioctl */
273
274         uint8_t type = 0;
275         int begin_set = 0;
276         int size_set = 0;
277         int end_set = 0;
278         int activate = 0;
279         int has_activated = 0;
280         int inconsistency=0;
281         unsigned int begin=0;
282         unsigned int end=0;
283         int dirty = 0;
284         int open2flags = 0;
285
286         int c;
287         struct device used_dev;
288         unsigned int argtracks;
289         uint16_t argheads, argsectors;
290
291         char drive, name[EXPAND_BUF];
292         unsigned char buf[512];
293         struct partition *partTable=(struct partition *)(buf+ 0x1ae);
294         struct device *dev;
295         char errmsg[2100];
296         char *bootSector=0;
297         struct partition *tpartition;
298
299         argtracks = 0;
300         argheads = 0;
301         argsectors = 0;
302
303         /* get command line options */
304         if(helpFlag(argc, argv))
305                 usage(0);
306         while ((c = getopt(argc, argv, "i:adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) {
307                 char *endptr=NULL;
308                 errno=0;
309                 switch (c) {
310                         case 'i':
311                                 set_cmd_line_image(optarg);
312                                 break;
313                         case 'B':
314                                 bootSector = optarg;
315                                 break;
316                         case 'a':
317                                 /* no privs, as it could be abused to
318                                  * make other partitions unbootable, or
319                                  * to boot a rogue kernel from this one */
320                                 open2flags |= NO_PRIV;
321                                 activate = 1;
322                                 dirty = 1;
323                                 break;
324                         case 'd':
325                                 activate = -1;
326                                 dirty = 1;
327                                 break;
328                         case 'p':
329                                 doprint = 1;
330                                 break;
331                         case 'r':
332                                 do_remove = 1;
333                                 dirty = 1;
334                                 break;
335                         case 'I':
336                                 /* could be abused to nuke all other
337                                  * partitions */
338                                 open2flags |= NO_PRIV;
339                                 initialize = 1;
340                                 dirty = 1;
341                                 break;
342                         case 'c':
343                                 create = 1;
344                                 dirty = 1;
345                                 break;
346
347                         case 'T':
348                                 /* could be abused to "manually" create
349                                  * extended partitions */
350                                 open2flags |= NO_PRIV;
351                                 type = strtou8(optarg, &endptr, 0);
352                                 break;
353
354                         case 't':
355                                 argtracks = atoui(optarg);
356                                 break;
357                         case 'h':
358                                 argheads = atou16(optarg);
359                                 break;
360                         case 's':
361                                 argsectors = atou16(optarg);
362                                 break;
363
364                         case 'f':
365                                 /* could be abused by creating overlapping
366                                  * partitions and other such Snafu */
367                                 open2flags |= NO_PRIV;
368                                 force = 1;
369                                 break;
370
371                         case 'v':
372                                 verbose++;
373                                 break;
374                         case 'b':
375                                 begin_set = 1;
376                                 begin = strtoui(optarg, &endptr, 0);
377                                 break;
378                         case 'l':
379                                 size_set = 1;
380                                 length = parseSize(optarg);
381                                 break;
382
383                         default:
384                                 usage(1);
385                 }
386                 check_number_parse_errno((char)c, optarg, endptr);
387         }
388
389         if (argc - optind != 1 ||
390             !argv[optind][0] || argv[optind][1] != ':')
391                 usage(1);
392
393         drive = ch_toupper(argv[optind][0]);
394
395         /* check out a drive whose letter and parameters match */
396         sprintf(errmsg, "Drive '%c:' not supported", drive);
397         Stream = 0;
398         for(dev=devices;dev->drive;dev++) {
399                 int mode ;
400
401                 FREE(&(Stream));
402                 /* drive letter */
403                 if (dev->drive != drive)
404                         continue;
405                 if (dev->partition < 1 || dev->partition > 4) {
406                         sprintf(errmsg,
407                                 "Drive '%c:' is not a partition",
408                                 drive);
409                         continue;
410                 }
411                 used_dev = *dev;
412
413                 SET_INT(used_dev.tracks, argtracks);
414                 SET_INT(used_dev.heads, argheads);
415                 SET_INT(used_dev.sectors, argsectors);
416
417                 expand(dev->name, name);
418
419                 mode = dirty ? O_RDWR : O_RDONLY;
420                 if(initialize)
421                         mode |= O_CREAT;
422
423 #ifdef USING_NEW_VOLD
424                 strcpy(name, getVoldName(dev, name));
425 #endif
426                 Stream = OpenImage(&used_dev, dev, name, mode, errmsg,
427                                    open2flags | SKIP_PARTITION | ALWAYS_GET_GEOMETRY,
428                                    mode, NULL, NULL, NULL);
429
430                 if (!Stream) {
431 #ifdef HAVE_SNPRINTF
432                         snprintf(errmsg,sizeof(errmsg)-1,
433                                  "init: open: %s", strerror(errno));
434 #else
435                         sprintf(errmsg,"init: open: %s", strerror(errno));
436 #endif
437                         continue;
438                 }
439
440                 tot_sectors = used_dev.tot_sectors;
441
442                 /* read the partition table */
443                 if (PREADS(Stream, (char *) buf, 0, 512) != 512 && !initialize){
444 #ifdef HAVE_SNPRINTF
445                         snprintf(errmsg, sizeof(errmsg)-1,
446                                 "Error reading from '%s', wrong parameters?",
447                                 name);
448 #else
449                         sprintf(errmsg,
450                                 "Error reading from '%s', wrong parameters?",
451                                 name);
452 #endif
453                         continue;
454                 }
455                 if(verbose>=2)
456                         print_sector("Read sector", buf, 512);
457                 break;
458         }
459
460         /* print error msg if needed */
461         if ( dev->drive == 0 ){
462                 FREE(&Stream);
463                 fprintf(stderr,"%s: %s\n", argv[0],errmsg);
464                 exit(1);
465         }
466
467         if((used_dev.sectors || used_dev.heads) &&
468            (!used_dev.sectors || !used_dev.heads)) {
469                 fprintf(stderr,"You should either indicate both the number of sectors and the number of heads,\n");
470                 fprintf(stderr," or none of them\n");
471                 exit(1);
472         }
473
474         if(initialize) {
475                 if (bootSector) {
476                         int fd;
477                         fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE);
478                         if (fd < 0) {
479                                 perror("open MBR");
480                                 exit(1);
481                         }
482                         if(read(fd, (char *) buf, 512) < 512) {
483                                 perror("read MBR");
484                                 exit(1);
485                         }
486                 }
487                 memset((char *)(partTable+1), 0, 4*sizeof(*partTable));
488                 set_word(((unsigned char*)buf)+510, 0xaa55);
489         }
490
491         /* check for boot signature, and place it if needed */
492         if((buf[510] != 0x55) || (buf[511] != 0xaa)) {
493                 fprintf(stderr,"Boot signature not set\n");
494                 fprintf(stderr,
495                         "Use the -I flag to initialize the partition table, and set the boot signature\n");
496                 inconsistency = 1;
497         }
498
499         tpartition=&partTable[dev->partition];
500         if(do_remove){
501                 if(!tpartition->sys_ind)
502                         fprintf(stderr,
503                                 "Partition for drive %c: does not exist\n",
504                                 drive);
505                 if((tpartition->sys_ind & 0x3f) == 5) {
506                         fprintf(stderr,
507                                 "Partition for drive %c: may be an extended partition\n",
508                                 drive);
509                         fprintf(stderr,
510                                 "Use the -f flag to remove it anyways\n");
511                         inconsistency = 1;
512                 }
513                 memset(tpartition, 0, sizeof(*tpartition));
514         }
515
516         if(create && tpartition->sys_ind) {
517                 fprintf(stderr,
518                         "Partition for drive %c: already exists\n", drive);
519                 fprintf(stderr,
520                         "Use the -r flag to remove it before attempting to recreate it\n");
521         }
522
523         /* if number of heads and sectors not known yet, set "reasonable"
524          * defaults */
525         compute_lba_geom_from_tot_sectors(&used_dev);
526
527         /* find out whether there is any activated partition. Moreover
528          * if no offset of a partition to be created have been
529          * specificed, find out whether it may be placed between the
530          * preceding and following partition already existing */
531         has_activated = 0;
532         for(i=1; i<5; i++){
533                 struct partition *partition=&partTable[i];
534                 if(!partition->sys_ind)
535                         continue;
536
537                 if(partition->boot_ind)
538                         has_activated++;
539
540                 if(i<dev->partition && !begin_set)
541                         begin = END(partition);
542                 if(i>dev->partition && !end_set && !size_set) {
543                         end = BEGIN(partition);
544                         end_set = 1;
545                 }
546         }
547
548         if(!used_dev.sectors && !used_dev.heads) {
549                 if(tot_sectors) {
550                         setsize0((uint32_t)tot_sectors,&dummy2,&used_dev.heads,
551                                  &used_dev.sectors);
552                 } else {
553                         used_dev.heads = 64;
554                         used_dev.sectors = 32;
555                 }
556         }
557
558         if(verbose)
559                 fprintf(stderr,"sectors: %d heads: %d %u\n",
560                         used_dev.sectors, used_dev.heads, tot_sectors);
561
562         sec_per_cyl = used_dev.sectors * used_dev.heads;
563         if(create) {
564                 unsigned int overlap;
565                 if(!end_set && !size_set && tot_sectors) {
566                         end = tot_sectors - tot_sectors % sec_per_cyl;
567                         end_set = 1;
568                 }
569
570                 /* if the partition starts right at the beginning of
571                  * the disk, keep one track unused to allow place for
572                  * the master boot record */
573                 if(!begin && !begin_set)
574                         begin = used_dev.sectors ? used_dev.sectors : 2048;
575
576                 /* Do not try to align  partitions (other than first) on track
577                  * boundaries here: apparently this was a thing of the past */
578
579                 if(size_set) {
580                         end = begin + length;
581                 } else if(!end_set) {
582                         fprintf(stderr,"Unknown size\n");
583                         exit(1);
584                 }
585
586                 /* Make sure partition boundaries are correctly ordered
587                  * (end > begin) */
588                 if(begin >= end) {
589                         fprintf(stderr, "Begin larger than end\n");
590                         exit(1);
591                 }
592
593                 /* Check whether new partition doesn't overlap with
594                  * any of those already in place */
595                 if((overlap=findOverlap(partTable, 4, begin, end))) {
596                         fprintf(stderr,
597                                 "Partition would overlap with partition %d\n",
598                                 overlap);
599                         exit(1);
600                 }
601
602                 setBeginEnd(tpartition, begin, end,
603                             used_dev.heads, used_dev.sectors,
604                             !has_activated, type,
605                             abs(dev->fat_bits));
606         }
607
608         if(activate) {
609                 if(!tpartition->sys_ind) {
610                         fprintf(stderr,
611                                 "Partition for drive %c: does not exist\n",
612                                 drive);
613                 } else {
614                         switch(activate) {
615                                 case 1:
616                                         tpartition->boot_ind=0x80;
617                                         break;
618                                 case -1:
619                                         tpartition->boot_ind=0x00;
620                                         break;
621                         }
622                 }
623         }
624
625         inconsistency |= consistencyCheck(partTable, doprint, verbose,
626                                           &has_activated, tot_sectors,
627                                           &used_dev, dev->partition);
628
629         switch(has_activated) {
630                 case 0:
631                         fprintf(stderr,
632                                 "Warning: no active (bootable) partition present\n");
633                         break;
634                 case 1:
635                         break;
636                 default:
637                         fprintf(stderr,
638                                 "Warning: %d active (bootable) partitions present\n",
639                                 has_activated);
640                         fprintf(stderr,
641                                 "Usually, a disk should have exactly one active partition\n");
642                         break;
643         }
644
645         if(inconsistency && !force) {
646                 fprintf(stderr,
647                         "inconsistency detected!\n" );
648                 if(dirty) {
649                         fprintf(stderr,
650                                 "Retry with the -f switch to go ahead anyways\n");
651                         exit(1);
652                 }
653         }
654
655         if(doprint && tpartition->sys_ind) {
656                 printf("The following command will recreate the partition for drive %c:\n",
657                        drive);
658                 used_dev.tracks =
659                         (DWORD(tpartition->nr_sects) +
660                          (BEGIN(tpartition) % sec_per_cyl)) /
661                         sec_per_cyl;
662                 printf("mpartition -c -b %d -l %d -t %d -h %d -s %d -b %u %c:\n",
663                        BEGIN(tpartition), PART_SIZE(tpartition),
664                        used_dev.tracks, used_dev.heads, used_dev.sectors,
665                        BEGIN(tpartition), drive);
666         }
667
668         if(dirty) {
669                 /* write data back to the disk */
670                 if(verbose>=2)
671                         print_sector("Writing sector", buf, 512);
672                 if (PWRITES(Stream, (char *) buf, 0, 512) != 512) {
673                         fprintf(stderr,"Error writing partition table");
674                         exit(1);
675                 }
676                 if(verbose>=3)
677                         print_sector("Sector written", buf, 512);
678         }
679         FREE(&Stream);
680         exit(0);
681 }