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