Imported Upstream version 4.0.18
[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 "msdos.h"
23 #include "mtools.h"
24 #include "mainloop.h"
25 #include "fsP.h"
26 #include "file.h"
27 #include "plain_io.h"
28 #include "nameclash.h"
29 #include "buffer.h"
30 #include "scsi.h"
31 #include "partition.h"
32
33 #ifdef OS_linux
34 #include "linux/hdreg.h"
35
36 #define _LINUX_STRING_H_
37 #define kdev_t int
38 #include "linux/fs.h"
39 #undef _LINUX_STRING_H_
40
41 #endif
42
43 #define tolinear(x) \
44 (sector(x)-1+(head(x)+cyl(x)*used_dev->heads)*used_dev->sectors)
45
46
47 static __inline__ void print_hsc(hsc *h)
48 {
49         printf(" h=%d s=%d c=%d\n", 
50                head(*h), sector(*h), cyl(*h));
51 }
52
53 static void set_offset(hsc *h, int offset, int heads, int sectors)
54 {
55         int head, sector, cyl;
56
57         if(! heads || !sectors)
58                 head = sector = cyl = 0; /* linear mode */
59         else {
60                 sector = offset % sectors;
61                 offset = offset / sectors;
62
63                 head = offset % heads;
64                 cyl = offset / heads;
65                 if(cyl > 1023) cyl = 1023;
66         }
67
68         h->head = head;
69         h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2);
70         h->cyl = cyl & 0xff;
71 }
72
73 void setBeginEnd(struct partition *partTable, int begin, int end,
74                  int heads, int sectors, int activate, int type)
75 {
76         set_offset(&partTable->start, begin, heads, sectors);
77         set_offset(&partTable->end, end-1, heads, sectors);
78         set_dword(partTable->start_sect, begin);
79         set_dword(partTable->nr_sects, end-begin);
80         if(activate)
81                 partTable->boot_ind = 0x80;
82         else
83                 partTable->boot_ind = 0;
84         if(!type) {
85                 if(end-begin < 4096)
86                         type = 1; /* DOS 12-bit FAT */
87                 else if(end-begin<32*2048)
88                         type = 4; /* DOS 16-bit FAT, <32M */
89                 else
90                         type = 6; /* DOS 16-bit FAT >= 32M */
91         }
92         partTable->sys_ind = type;
93 }
94
95 int consistencyCheck(struct partition *partTable, int doprint, int verbose,
96                      int *has_activated, unsigned int *last_end,
97                      unsigned int *j, 
98                      struct device *used_dev, int target_partition)
99 {
100         unsigned int i;
101         unsigned int inconsistency;
102         
103         *j = 0;
104         *last_end = 1;
105
106         /* quick consistency check */
107         inconsistency = 0;
108         *has_activated = 0;
109         for(i=1; i<5; i++){
110                 if(!partTable[i].sys_ind)
111                         continue;
112                 if(partTable[i].boot_ind)
113                         (*has_activated)++;
114                 if((used_dev && 
115                     (used_dev->heads != head(partTable[i].end)+1 ||
116                      used_dev->sectors != sector(partTable[i].end))) ||
117                    sector(partTable[i].start) != 1){
118                         fprintf(stderr,
119                                 "Partition %d is not aligned\n",
120                                 i);
121                         inconsistency=1;
122                 }
123                 
124                 if(*j && 
125                    *last_end > BEGIN(partTable[i])) {
126                         fprintf(stderr,
127                                 "Partitions %d and %d badly ordered or overlapping\n",
128                                 *j,i);
129                         inconsistency=1;
130                 }
131                         
132                 *last_end = END(partTable[i]);
133                 *j = i;
134
135                 if(used_dev &&
136                    cyl(partTable[i].start) != 1023 &&
137                    tolinear(partTable[i].start) != BEGIN(partTable[i])) {
138                         fprintf(stderr,
139                                 "Start position mismatch for partition %d\n",
140                                 i);
141                         inconsistency=1;
142                 }
143                 if(used_dev &&
144                    cyl(partTable[i].end) != 1023 &&
145                    tolinear(partTable[i].end)+1 != END(partTable[i])) {
146                         fprintf(stderr,
147                                 "End position mismatch for partition %d\n",
148                                 i);
149                         inconsistency=1;
150                 }
151
152                 if(doprint && verbose) {
153                         if(i==target_partition)
154                                 putchar('*');
155                         else
156                                 putchar(' ');
157                         printf("Partition %d\n",i);
158
159                         printf("  active=%x\n", partTable[i].boot_ind);
160                         printf("  start:");
161                         print_hsc(&partTable[i].start);
162                         printf("  type=0x%x\n", partTable[i].sys_ind);
163                         printf("  end:");
164                         print_hsc(&partTable[i].end);
165                         printf("  start=%d\n", BEGIN(partTable[i]));
166                         printf("  nr=%d\n", _DWORD(partTable[i].nr_sects));
167                         printf("\n");
168                 }
169         }
170         return inconsistency;
171 }
172
173 /* setsize function.  Determines scsicam mapping if this cannot be inferred from
174  * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */
175
176 /*
177  * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
178  *      unsigned int *hds, unsigned int *secs);
179  *
180  * Purpose : to determine a near-optimal int 0x13 mapping for a
181  *      SCSI disk in terms of lost space of size capacity, storing
182  *      the results in *cyls, *hds, and *secs.
183  *
184  * Returns : -1 on failure, 0 on success.
185  *
186  * Extracted from
187  *
188  * WORKING                                                    X3T9.2
189  * DRAFT                                                        792D
190  *
191  *
192  *                                                        Revision 6
193  *                                                         10-MAR-94
194  * Information technology -
195  * SCSI-2 Common access method
196  * transport and SCSI interface module
197  * 
198  * ANNEX A :
199  *
200  * setsize() converts a read capacity value to int 13h
201  * head-cylinder-sector requirements. It minimizes the value for
202  * number of heads and maximizes the number of cylinders. This
203  * will support rather large disks before the number of heads
204  * will not fit in 4 bits (or 6 bits). This algorithm also
205  * minimizes the number of sectors that will be unused at the end
206  * of the disk while allowing for very large disks to be
207  * accommodated. This algorithm does not use physical geometry. 
208  */
209
210 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
211     unsigned int *secs) { 
212     unsigned int rv = 0; 
213     unsigned long heads, sectors, cylinders, temp; 
214
215     cylinders = 1024L;                  /* Set number of cylinders to max */ 
216     sectors = 62L;                      /* Maximize sectors per track */ 
217
218     temp = cylinders * sectors;         /* Compute divisor for heads */ 
219     heads = capacity / temp;            /* Compute value for number of heads */
220     if (capacity % temp) {              /* If no remainder, done! */ 
221         heads++;                        /* Else, increment number of heads */ 
222         temp = cylinders * heads;       /* Compute divisor for sectors */ 
223         sectors = capacity / temp;      /* Compute value for sectors per
224                                                track */ 
225         if (capacity % temp) {          /* If no remainder, done! */ 
226             sectors++;                  /* Else, increment number of sectors */ 
227             temp = heads * sectors;     /* Compute divisor for cylinders */
228             cylinders = capacity / temp;/* Compute number of cylinders */ 
229         } 
230     } 
231     if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */ 
232
233     *cyls = (unsigned int) cylinders;   /* Stuff return values */ 
234     *secs = (unsigned int) sectors; 
235     *hds  = (unsigned int) heads; 
236     return(rv); 
237
238
239 static void setsize0(unsigned long capacity,unsigned int *cyls,
240                      unsigned int *hds, unsigned int *secs)
241 {
242         int r;
243
244         /* 1. First try "Megabyte" sizes */
245         if(capacity < 1024 * 2048 && !(capacity % 1024)) {
246                 *cyls = capacity >> 11;
247                 *hds  = 64;
248                 *secs = 32;
249                 return;
250         }
251
252         /* then try scsicam's size */
253         r = setsize(capacity,cyls,hds,secs);
254         if(r || *hds > 255 || *secs > 63) {
255                 /* scsicam failed. Do megabytes anyways */
256                 *cyls = capacity >> 11;
257                 *hds  = 64;
258                 *secs = 32;
259                 return;
260         }
261 }
262
263
264 static void usage(int ret) NORETURN;
265 static void usage(int ret)
266 {
267         fprintf(stderr, 
268                 "Mtools version %s, dated %s\n", mversion, mdate);
269         fprintf(stderr, 
270                 "Usage: %s [-pradcv] [-I] [-B bootsect-template] [-s sectors] "
271                         "[-t cylinders] "
272                 "[-h heads] [-T type] [-b begin] [-l length] "
273                 "drive\n", progname);
274         exit(ret);
275 }
276
277 void mpartition(int argc, char **argv, int dummy)
278 {
279         Stream_t *Stream;
280         unsigned int dummy2;
281
282         unsigned int i,j;
283
284         int sec_per_cyl;
285         int doprint = 0;
286         int verbose = 0;
287         int create = 0;
288         int force = 0;
289         int length = 0;
290         int do_remove = 0;
291         int initialize = 0;
292         unsigned int tot_sectors=0;
293         int type = 0;
294         int begin_set = 0;
295         int size_set = 0;
296         int end_set = 0;
297         unsigned int last_end = 0;
298         int activate = 0;
299         int has_activated = 0;
300         int inconsistency=0;
301         int begin=0;
302         int end=0;
303         int sizetest=0;
304         int dirty = 0;
305         int open2flags = NO_OFFSET;
306         
307         int c;
308         struct device used_dev;
309         int argtracks, argheads, argsectors;
310
311         char drive, name[EXPAND_BUF];
312         unsigned char buf[512];
313         struct partition *partTable=(struct partition *)(buf+ 0x1ae);
314         struct device *dev;
315         char errmsg[200];
316         char *bootSector=0;
317
318         argtracks = 0;
319         argheads = 0;
320         argsectors = 0;
321
322         /* get command line options */
323         if(helpFlag(argc, argv))
324                 usage(0);
325         while ((c = getopt(argc, argv, "i:adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) {
326                 switch (c) {
327                         case 'i':
328                                 set_cmd_line_image(optarg);
329                                 break;
330                         case 'B':
331                                 bootSector = optarg;
332                                 break;
333                         case 'a':
334                                 /* no privs, as it could be abused to
335                                  * make other partitions unbootable, or
336                                  * to boot a rogue kernel from this one */
337                                 open2flags |= NO_PRIV;
338                                 activate = 1;
339                                 dirty = 1;
340                                 break;
341                         case 'd':
342                                 activate = -1;
343                                 dirty = 1;
344                                 break;
345                         case 'p':
346                                 doprint = 1;
347                                 break;
348                         case 'r':
349                                 do_remove = 1;
350                                 dirty = 1;
351                                 break;
352                         case 'I':
353                                 /* could be abused to nuke all other 
354                                  * partitions */
355                                 open2flags |= NO_PRIV;
356                                 initialize = 1;
357                                 dirty = 1;
358                                 break;
359                         case 'c':
360                                 create = 1;
361                                 dirty = 1;
362                                 break;
363
364                         case 'T':
365                                 /* could be abused to "manually" create
366                                  * extended partitions */
367                                 open2flags |= NO_PRIV;
368                                 type = strtoul(optarg,0,0);
369                                 break;
370
371                         case 't':
372                                 argtracks = atoi(optarg);
373                                 break;
374                         case 'h':
375                                 argheads = atoi(optarg);
376                                 break;
377                         case 's':
378                                 argsectors = atoi(optarg);
379                                 break;
380
381                         case 'f':
382                                 /* could be abused by creating overlapping
383                                  * partitions and other such Snafu */
384                                 open2flags |= NO_PRIV;
385                                 force = 1;
386                                 break;
387
388                         case 'v':
389                                 verbose++;
390                                 break;
391                         case 'S':
392                                 /* testing only */
393                                 /* could be abused to create partitions
394                                  * extending beyond the actual size of the
395                                  * device */
396                                 open2flags |= NO_PRIV;
397                                 tot_sectors = strtoul(optarg,0,0);
398                                 sizetest = 1;
399                                 break;
400                         case 'b':
401                                 begin_set = 1;
402                                 begin = atoi(optarg);
403                                 break;
404                         case 'l':
405                                 size_set = 1;
406                                 length = atoi(optarg);
407                                 break;
408
409                         default:
410                                 usage(1);
411                 }
412         }
413
414         if (argc - optind != 1 ||
415             !argv[optind][0] || argv[optind][1] != ':')
416                 usage(1);
417         
418         drive = toupper(argv[optind][0]);
419
420         /* check out a drive whose letter and parameters match */       
421         sprintf(errmsg, "Drive '%c:' not supported", drive);
422         Stream = 0;
423         for(dev=devices;dev->drive;dev++) {
424                 int mode ;
425
426                 FREE(&(Stream));
427                 /* drive letter */
428                 if (dev->drive != drive)
429                         continue;
430                 if (dev->partition < 1 || dev->partition > 4) {
431                         sprintf(errmsg, 
432                                 "Drive '%c:' is not a partition", 
433                                 drive);
434                         continue;
435                 }
436                 used_dev = *dev;
437
438                 SET_INT(used_dev.tracks, argtracks);
439                 SET_INT(used_dev.heads, argheads);
440                 SET_INT(used_dev.sectors, argsectors);
441                 
442                 expand(dev->name, name);
443
444                 mode = dirty ? O_RDWR : O_RDONLY;
445                 if(initialize)
446                         mode |= O_CREAT;
447
448 #ifdef USING_NEW_VOLD
449                 strcpy(name, getVoldName(dev, name));
450 #endif
451                 Stream = SimpleFileOpen(&used_dev, dev, name, mode, 
452                                         errmsg, open2flags, 1, 0);
453
454                 if (!Stream) {
455 #ifdef HAVE_SNPRINTF
456                         snprintf(errmsg,199,"init: open: %s", strerror(errno));
457 #else
458                         sprintf(errmsg,"init: open: %s", strerror(errno));
459 #endif
460                         continue;
461                 }                       
462
463
464                 /* try to find out the size */
465                 if(!sizetest)
466                         tot_sectors = 0;
467                 if(IS_SCSI(dev)) {
468                         unsigned char cmd[10];
469                         unsigned char data[10];
470                         cmd[0] = SCSI_READ_CAPACITY;
471                         memset ((void *) &cmd[2], 0, 8);
472                         memset ((void *) &data[0], 137, 10);
473                         scsi_cmd(get_fd(Stream), cmd, 10, SCSI_IO_READ,
474                                  data, 10, get_extra_data(Stream));
475                         
476                         tot_sectors = 1 +
477                                 (data[0] << 24) +
478                                 (data[1] << 16) +
479                                 (data[2] <<  8) +
480                                 (data[3]      );
481                         if(verbose)
482                                 printf("%d sectors in total\n", tot_sectors);
483                 }
484
485 #ifdef OS_linux
486                 if (tot_sectors == 0) {
487                         ioctl(get_fd(Stream), BLKGETSIZE, &tot_sectors);
488                 }
489 #endif
490
491                 /* read the partition table */
492                 if (READS(Stream, (char *) buf, 0, 512) != 512 && !initialize){
493 #ifdef HAVE_SNPRINTF
494                         snprintf(errmsg, 199,
495                                 "Error reading from '%s', wrong parameters?",
496                                 name);
497 #else
498                         sprintf(errmsg,
499                                 "Error reading from '%s', wrong parameters?",
500                                 name);
501 #endif
502                         continue;
503                 }
504                 if(verbose>=2)
505                         print_sector("Read sector", buf, 512);
506                 break;
507         }
508
509         /* print error msg if needed */ 
510         if ( dev->drive == 0 ){
511                 FREE(&Stream);
512                 fprintf(stderr,"%s: %s\n", argv[0],errmsg);
513                 exit(1);
514         }
515
516         if((used_dev.sectors || used_dev.heads) &&
517            (!used_dev.sectors || !used_dev.heads)) {
518                 fprintf(stderr,"You should either indicate both the number of sectors and the number of heads,\n");
519                 fprintf(stderr," or none of them\n");
520                 exit(1);
521         }
522
523         if(initialize) {
524                 if (bootSector) {
525                         int fd;
526                         fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE);
527                         if (fd < 0) {
528                                 perror("open MBR");
529                                 exit(1);
530                         }
531                         if(read(fd, (char *) buf, 512) < 512) {
532                                 perror("read MBR");
533                                 exit(1);
534                         }
535                 }
536                 memset((char *)(partTable+1), 0, 4*sizeof(*partTable));
537                 set_word(((unsigned char*)buf)+510, 0xaa55);
538         }
539
540         /* check for boot signature, and place it if needed */
541         if((buf[510] != 0x55) || (buf[511] != 0xaa)) {
542                 fprintf(stderr,"Boot signature not set\n");
543                 fprintf(stderr,
544                         "Use the -I flag to initialize the partition table, and set the boot signature\n");
545                 inconsistency = 1;
546         }
547         
548         if(do_remove){
549                 if(!partTable[dev->partition].sys_ind)
550                         fprintf(stderr,
551                                 "Partition for drive %c: does not exist\n",
552                                 drive);
553                 if((partTable[dev->partition].sys_ind & 0x3f) == 5) {
554                         fprintf(stderr,
555                                 "Partition for drive %c: may be an extended partition\n",
556                                 drive);
557                         fprintf(stderr,
558                                 "Use the -f flag to remove it anyways\n");
559                         inconsistency = 1;
560                 }
561                 memset(&partTable[dev->partition], 0, sizeof(*partTable));
562         }
563
564         if(create && partTable[dev->partition].sys_ind) {
565                 fprintf(stderr,
566                         "Partition for drive %c: already exists\n", drive);
567                 fprintf(stderr,
568                         "Use the -r flag to remove it before attempting to recreate it\n");
569         }
570
571
572         /* find out number of heads and sectors, and whether there is
573         * any activated partition */
574         has_activated = 0;
575         for(i=1; i<5; i++){
576                 if(!partTable[i].sys_ind)
577                         continue;
578                 
579                 if(partTable[i].boot_ind)
580                         has_activated++;
581
582                 /* set geometry from entry */
583                 if (!used_dev.heads)
584                         used_dev.heads = head(partTable[i].end)+1;
585                 if(!used_dev.sectors)
586                         used_dev.sectors = sector(partTable[i].end);
587                 if(i<dev->partition && !begin_set)
588                         begin = END(partTable[i]);
589                 if(i>dev->partition && !end_set && !size_set) {
590                         end = BEGIN(partTable[i]);
591                         end_set = 1;
592                 }
593         }
594
595 #ifdef OS_linux
596         if(!used_dev.sectors && !used_dev.heads) {
597                 if(!IS_SCSI(dev)) {
598                         struct hd_geometry geom;
599                         if(ioctl(get_fd(Stream), HDIO_GETGEO, &geom) == 0) {
600                                 used_dev.heads = geom.heads;
601                                 used_dev.sectors = geom.sectors;
602                         }
603                 }
604         }
605 #endif
606
607         if(!used_dev.sectors && !used_dev.heads) {
608                 if(tot_sectors)
609                         setsize0(tot_sectors,&dummy2,&used_dev.heads,
610                                          &used_dev.sectors);
611                 else {
612                         used_dev.heads = 64;
613                         used_dev.sectors = 32;
614                 }
615         }
616
617         if(verbose)
618                 fprintf(stderr,"sectors: %d heads: %d %d\n",
619                         used_dev.sectors, used_dev.heads, tot_sectors);
620
621         sec_per_cyl = used_dev.sectors * used_dev.heads;
622         if(create) {
623                 if(!end_set && tot_sectors) {
624                         end = tot_sectors - tot_sectors % sec_per_cyl;
625                         end_set = 1;
626                 }
627                 
628                 /* if the partition starts right at the beginning of
629                  * the disk, keep one track unused to allow place for
630                  * the master boot record */
631                 if(!begin && !begin_set)
632                         begin = used_dev.sectors;
633                 if(!size_set && used_dev.tracks) {
634                         size_set = 2;
635                         length = sec_per_cyl * used_dev.tracks;
636
637                         /*  round the size in order to take
638                          * into account any "hidden" sectors */
639
640                         /* do we anchor this at the beginning ?*/
641                         if(begin_set || dev->partition <= 2 || !end_set)
642                                 length -= begin % sec_per_cyl;
643                         else if(end - length < begin)
644                                 /* truncate any overlap */
645                                 length = end - begin;
646                 }
647                 if(size_set) {
648                         if(!begin_set && dev->partition >2 && end_set)
649                                 begin = end - length;
650                         else
651                                 end = begin + length;
652                 } else if(!end_set) {
653                         fprintf(stderr,"Unknown size\n");
654                         exit(1);
655                 }
656
657                 setBeginEnd(&partTable[dev->partition], begin, end,
658                                         used_dev.heads, used_dev.sectors, 
659                                         !has_activated, type);
660         }
661
662         if(activate) {
663                 if(!partTable[dev->partition].sys_ind) {
664                         fprintf(stderr,
665                                 "Partition for drive %c: does not exist\n",
666                                 drive);
667                 } else {
668                         switch(activate) {
669                                 case 1:
670                                         partTable[dev->partition].boot_ind=0x80;
671                                         break;
672                                 case -1:
673                                         partTable[dev->partition].boot_ind=0x00;
674                                         break;
675                         }
676                 }
677         }
678
679
680         inconsistency |= consistencyCheck(partTable, doprint, verbose,
681                                           &has_activated, &last_end, &j,
682                                           &used_dev, dev->partition);
683
684         if(doprint && !inconsistency && partTable[dev->partition].sys_ind) {
685                 printf("The following command will recreate the partition for drive %c:\n", 
686                        drive);
687                 used_dev.tracks = 
688                         (_DWORD(partTable[dev->partition].nr_sects) +
689                          (BEGIN(partTable[dev->partition]) % sec_per_cyl)) / 
690                         sec_per_cyl;
691                 printf("mpartition -c -t %d -h %d -s %d -b %u %c:\n",
692                        used_dev.tracks, used_dev.heads, used_dev.sectors,
693                        BEGIN(partTable[dev->partition]), drive);
694         }
695
696         if(tot_sectors && last_end >tot_sectors) {
697                 fprintf(stderr,
698                         "Partition %d exceeds beyond end of disk\n",
699                         j);
700                 exit(1);
701         }
702
703         
704         switch(has_activated) {
705                 case 0:
706                         fprintf(stderr,
707                                 "Warning: no active (bootable) partition present\n");
708                         break;
709                 case 1:
710                         break;
711                 default:
712                         fprintf(stderr,
713                                 "Warning: %d active (bootable) partitions present\n",
714                                 has_activated);
715                         fprintf(stderr,
716                                 "Usually, a disk should have exactly one active partition\n");
717                         break;
718         }
719         
720         if(inconsistency && !force) {
721                 fprintf(stderr,
722                         "inconsistency detected!\n" );
723                 if(dirty)
724                         fprintf(stderr,
725                                 "Retry with the -f switch to go ahead anyways\n");
726                 exit(1);
727         }
728
729         if(dirty) {
730                 /* write data back to the disk */
731                 if(verbose>=2)
732                         print_sector("Writing sector", buf, 512);
733                 if (WRITES(Stream, (char *) buf, 0, 512) != 512) {
734                         fprintf(stderr,"Error writing partition table");
735                         exit(1);
736                 }
737                 if(verbose>=3)
738                         print_sector("Sector written", buf, 512);
739         }
740         FREE(&Stream);
741         exit(0);
742 }