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