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
47static __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
53static 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
73void 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
95int 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
210static 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
239static 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
264static void usage(int ret) NORETURN;
265static 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
277void 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, 0);
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 boot sector");
529				exit(1);
530			}
531			read(fd, (char *) buf, 512);
532		}
533		memset((char *)(partTable+1), 0, 4*sizeof(*partTable));
534		set_dword(((unsigned char*)buf)+510, 0xaa55);
535	}
536
537	/* check for boot signature, and place it if needed */
538	if((buf[510] != 0x55) || (buf[511] != 0xaa)) {
539		fprintf(stderr,"Boot signature not set\n");
540		fprintf(stderr,
541			"Use the -I flag to initialize the partition table, and set the boot signature\n");
542		inconsistency = 1;
543	}
544
545	if(do_remove){
546		if(!partTable[dev->partition].sys_ind)
547			fprintf(stderr,
548				"Partition for drive %c: does not exist\n",
549				drive);
550		if((partTable[dev->partition].sys_ind & 0x3f) == 5) {
551			fprintf(stderr,
552				"Partition for drive %c: may be an extended partition\n",
553				drive);
554			fprintf(stderr,
555				"Use the -f flag to remove it anyways\n");
556			inconsistency = 1;
557		}
558		memset(&partTable[dev->partition], 0, sizeof(*partTable));
559	}
560
561	if(create && partTable[dev->partition].sys_ind) {
562		fprintf(stderr,
563			"Partition for drive %c: already exists\n", drive);
564		fprintf(stderr,
565			"Use the -r flag to remove it before attempting to recreate it\n");
566	}
567
568
569	/* find out number of heads and sectors, and whether there is
570	* any activated partition */
571	has_activated = 0;
572	for(i=1; i<5; i++){
573		if(!partTable[i].sys_ind)
574			continue;
575
576		if(partTable[i].boot_ind)
577			has_activated++;
578
579		/* set geometry from entry */
580		if (!used_dev.heads)
581			used_dev.heads = head(partTable[i].end)+1;
582		if(!used_dev.sectors)
583			used_dev.sectors = sector(partTable[i].end);
584		if(i<dev->partition && !begin_set)
585			begin = END(partTable[i]);
586		if(i>dev->partition && !end_set && !size_set) {
587			end = BEGIN(partTable[i]);
588			end_set = 1;
589		}
590	}
591
592#ifdef OS_linux
593	if(!used_dev.sectors && !used_dev.heads) {
594		if(!IS_SCSI(dev)) {
595			struct hd_geometry geom;
596			if(ioctl(get_fd(Stream), HDIO_GETGEO, &geom) == 0) {
597				used_dev.heads = geom.heads;
598				used_dev.sectors = geom.sectors;
599			}
600		}
601	}
602#endif
603
604	if(!used_dev.sectors && !used_dev.heads) {
605		if(tot_sectors)
606			setsize0(tot_sectors,&dummy2,&used_dev.heads,
607					 &used_dev.sectors);
608		else {
609			used_dev.heads = 64;
610			used_dev.sectors = 32;
611		}
612	}
613
614	if(verbose)
615		fprintf(stderr,"sectors: %d heads: %d %d\n",
616			used_dev.sectors, used_dev.heads, tot_sectors);
617
618	sec_per_cyl = used_dev.sectors * used_dev.heads;
619	if(create) {
620		if(!end_set && tot_sectors) {
621			end = tot_sectors - tot_sectors % sec_per_cyl;
622			end_set = 1;
623		}
624
625		/* if the partition starts right at the beginning of
626		 * the disk, keep one track unused to allow place for
627		 * the master boot record */
628		if(!begin && !begin_set)
629			begin = used_dev.sectors;
630		if(!size_set && used_dev.tracks) {
631			size_set = 2;
632			length = sec_per_cyl * used_dev.tracks;
633
634			/*  round the size in order to take
635			 * into account any "hidden" sectors */
636
637			/* do we anchor this at the beginning ?*/
638			if(begin_set || dev->partition <= 2 || !end_set)
639				length -= begin % sec_per_cyl;
640			else if(end - length < begin)
641				/* truncate any overlap */
642				length = end - begin;
643		}
644		if(size_set) {
645			if(!begin_set && dev->partition >2 && end_set)
646				begin = end - length;
647			else
648				end = begin + length;
649		} else if(!end_set) {
650			fprintf(stderr,"Unknown size\n");
651			exit(1);
652		}
653
654		setBeginEnd(&partTable[dev->partition], begin, end,
655					used_dev.heads, used_dev.sectors,
656					!has_activated, type);
657	}
658
659	if(activate) {
660		if(!partTable[dev->partition].sys_ind) {
661			fprintf(stderr,
662				"Partition for drive %c: does not exist\n",
663				drive);
664		} else {
665			switch(activate) {
666				case 1:
667					partTable[dev->partition].boot_ind=0x80;
668					break;
669				case -1:
670					partTable[dev->partition].boot_ind=0x00;
671					break;
672			}
673		}
674	}
675
676
677	inconsistency |= consistencyCheck(partTable, doprint, verbose,
678					  &has_activated, &last_end, &j,
679					  &used_dev, dev->partition);
680
681	if(doprint && !inconsistency && partTable[dev->partition].sys_ind) {
682		printf("The following command will recreate the partition for drive %c:\n",
683		       drive);
684		used_dev.tracks =
685			(_DWORD(partTable[dev->partition].nr_sects) +
686			 (BEGIN(partTable[dev->partition]) % sec_per_cyl)) /
687			sec_per_cyl;
688		printf("mpartition -c -t %d -h %d -s %d -b %u %c:\n",
689		       used_dev.tracks, used_dev.heads, used_dev.sectors,
690		       BEGIN(partTable[dev->partition]), drive);
691	}
692
693	if(tot_sectors && last_end >tot_sectors) {
694		fprintf(stderr,
695			"Partition %d exceeds beyond end of disk\n",
696			j);
697		exit(1);
698	}
699
700
701	switch(has_activated) {
702		case 0:
703			fprintf(stderr,
704				"Warning: no active (bootable) partition present\n");
705			break;
706		case 1:
707			break;
708		default:
709			fprintf(stderr,
710				"Warning: %d active (bootable) partitions present\n",
711				has_activated);
712			fprintf(stderr,
713				"Usually, a disk should have exactly one active partition\n");
714			break;
715	}
716
717	if(inconsistency && !force) {
718		fprintf(stderr,
719			"inconsistency detected!\n" );
720		if(dirty)
721			fprintf(stderr,
722				"Retry with the -f switch to go ahead anyways\n");
723		exit(1);
724	}
725
726	if(dirty) {
727		/* write data back to the disk */
728		if(verbose>=2)
729			print_sector("Writing sector", buf, 512);
730		if (WRITES(Stream, (char *) buf, 0, 512) != 512) {
731			fprintf(stderr,"Error writing partition table");
732			exit(1);
733		}
734		if(verbose>=3)
735			print_sector("Sector written", buf, 512);
736	}
737	FREE(&Stream);
738	exit(0);
739}
740