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