/*

 * Boot installator.

 * Author: Serge Vakulenko, <vak@kiae.su>

 */



#include <stdio.h>

#include <fcntl.h>

#include <dos.h>



#define BOOTFILE	"boot.bin"

#define SAVEFILE	"bootsav.bin"

#define SAVE2FILE	"boot2sav.bin"



#define PartAddr        0x1be	/* Offset to partition table */

#define ValidationAddr  0x1fe	/* Offset to validation bytes */

#define MAGIC		0xaa55	/* Validation tag */



#define DISK1		0

#define DISK2		1

#define READ		0

#define WRITE		1



typedef struct PartitionEntry {

	unsigned char	bootIndicator;

	unsigned char	beginHead;

	unsigned char	beginSector;

	unsigned char	beginCyl;

	unsigned char	systemId;

	unsigned char	endHead;

	unsigned char	endSector;

	unsigned char	endCyl;

	unsigned short	relSectorLow;

	unsigned short	relSectorHigh;

	unsigned short	numSectorsLow;

	unsigned short	numSectorsHigh;

} PartitionEntry;



unsigned char bootRecord [512];

unsigned char bootProg [512];



char head1 [] =	"�������������������������������������������������������������������������Ŀ\n";

char head2 [] =	"� �Partition�Can �Boot�   Beginning   �    Ending     � Relative�Number of�\n";

char head3 [] =	"�N�  Type   �Boot�Part�Head  Cyl  Sect�Head  Cyl  Sect� Sectors � Sectors �\n";

char head4 [] =	"�������������������������������������������������������������������������Ĵ\n";

char mid1 []  = "�%d�%-9.9s�%-4.4s�%-4.4s�%3u �%4u �%3u �%3u �%4u �%3u �%8lu �%8lu �\n";

char mid2 []  =	"�������������������������������������������������������������������������Ĵ\n";

char foot []  =	"���������������������������������������������������������������������������\n";



void printtable (disk)

{

	PartitionEntry *part = (PartitionEntry *) (bootRecord + PartAddr);

	int i, cb;

	long relSectors;

	long numSectors;

	char *typeString, block [512];



	printf ("\n");

	printf (head1);

	printf (head2);

	printf (head3);

	printf (head4);



	for (i=0; i<4; ++i) {

		switch (part->systemId) {

		default:	typeString = "  ?????";   cb = 1; break;

		case 0:		typeString = "  empty";   cb = 0; break;

		case 1:		typeString = "  dos-12";  cb = 1; break;

		case 2:		typeString = "  xenix";   cb = 1; break;

		case 3:		typeString = "xenix usr"; cb = 0; break;

		case 4:		typeString = "  dos-16";  cb = 1; break;

		case 5:		typeString = "  extend";  cb = 0; break;

		case 6:		typeString = "  bigdos";  cb = 1; break;

		case 7:		typeString = "  hpfs";    cb = 1; break;

		case 0x75:	typeString = "  pcix";    cb = 1; break;

		case 0xdb:	typeString = "  cp/m";    cb = 1; break;

		case 0xff:	typeString = "  bbt";     cb = 0; break;

		case 0x08:	typeString = "  aix";     cb = 0; break;

		case 0x09:	typeString = " coherent"; cb = 1; break;

		case 0x0A:	typeString = "  os/2";    cb = 1; break;

		case 0x10:	typeString = "  opus";    cb = 1; break;

		case 0x40:	typeString = "venix 286"; cb = 1; break;

		case 0x50:

		case 0x51:	typeString = "  dm";      cb = 1; break;

		case 0x52:	typeString = "microport"; cb = 1; break;

		case 0x56:	typeString = "  gb";      cb = 1; break;

		case 0x61:

		case 0xE1:

		case 0xE3:

		case 0xE4:

		case 0xF1:

		case 0xF4:	typeString = "  speed";   cb = 1; break;

		case 0x63:	typeString = "  unix";    cb = 1; break;

		case 0x64:	typeString = "novell286"; cb = 1; break;

		case 0x65:	typeString = "novell386"; cb = 1; break;

		case 0x80:	typeString = "old minix"; cb = 1; break;

		case 0x81:	typeString = "  minix";   cb = 1; break;

		case 0x82:	typeString = "  linux";   cb = 1; break;

		case 0x93:	typeString = "  amoeba";  cb = 1; break;

		case 0x94:	typeString = "amoebaBBT"; cb = 0; break;

		case 0xA5:	typeString = "  386bsd";  cb = 1; break;

		case 0x9f:

		case 0xB7:	typeString = "  bsdi";    cb = 1; break;

		case 0xB8:	typeString = "bsdi swap"; cb = 0; break;

		case 0xF2:	typeString = " dos sec";  cb = 1; break;

		}

		relSectors = part->relSectorLow |

			(unsigned long) part->relSectorHigh<<16;

		numSectors = part->numSectorsLow |

			(unsigned long) part->numSectorsHigh<<16;



		if (cb && (bootio (disk, READ, part->beginHead, part->beginCyl,

		    part->beginSector, block) != 0 ||

		    *(short *) &block [ValidationAddr] != (short) MAGIC))

			cb = 0;



		printf (mid1, i+1, typeString, cb ? " yes" : " no",

			(part->bootIndicator & 0x80) ? " yes" : " no",

			part->beginHead, part->beginCyl |

			part->beginSector<<2 & 0x300,

			part->beginSector & 077, part->endHead,

			part->endCyl | part->endSector<<2 & 0x300,

			part->endSector & 077, relSectors, numSectors);

		if (i < 3)

			printf (mid2);

		++part;

	}

	printf (foot);

}



int yes ()

{

	char reply [80];



	printf (" (yes/no) ");

	fgets (reply, sizeof (reply), stdin);

	return (!stricmp (reply, "y\n") || !stricmp (reply, "yes\n"));

}



int install (disk, file, savefile)

char *file, *savefile;

{

	int rc, fd;



	rc = bootio (disk, READ, 0, 0, 1, bootRecord);

	if (rc) {

		fprintf (stderr, "Error %d reading boot record from disk %d\n",

			rc, disk);

		return (-1);

	}

	if (*(short *) &bootRecord [ValidationAddr] != (short) MAGIC) {

		fprintf (stderr, "Bad master boot record on disk %d!\n",

			disk);

		return (-1);

	}



	printtable (disk);



	fd = open (file, O_RDONLY|O_BINARY);

	if (fd < 0) {

		fprintf (stderr, "Cannot read file %s\n", file);

		return (-1);

	}

	if (read (fd, bootProg, 512) != 512) {

		fprintf (stderr, "Error reading %s\n", file);

		return (-1);

	}

	close (fd);

	if (*(short *) &bootProg [ValidationAddr] != (short) MAGIC) {

		fprintf (stderr, "Bad boot image in file %s\n", file);

		return (-1);

	}



	printf ("\nAre you sure you want to install new boot on disk %d?",

		disk);

	if (! yes ())

		return (0);



	close (creat (savefile, 0664));

	fd = open (savefile, O_WRONLY|O_BINARY);

	if (fd < 0) {

		fprintf (stderr, "Cannot write to file %s\n", savefile);

		return (-1);

	}

	if (write (fd, bootRecord, 512) != 512) {

		fprintf (stderr, "Error writing to %s\n", savefile);

		return (-1);

	}

	close (fd);



	memcpy (bootRecord, bootProg, PartAddr);



	rc = bootio (disk, WRITE, 0, 0, 1, bootRecord);

	if (rc) {

		fprintf (stderr, "Error %d updating boot record on disk %d\n",

			rc, disk);

		return (-1);

	}

	printf ("New boot record successfully installed.\n");

	return (0);

}



bootio (disk, op, head, cyl, sect, buf)

char *buf;

{

	return (biosdisk (op==WRITE ? 3 : 2, disk==DISK1 ? 0x80 : 0x81,

		head, cyl, sect, 1, buf));

}



int main (argc, argv)

char **argv;

{

	char *bootfile;

	int disk2present;



	printf ("Boot installer, version 1.7, Copyright (C) Serge Vakulenko\n\n");



	printf ("This utility allows you to install new boot program on your\n");

	printf ("hard disk.  The usage is:\n\n");

	printf ("        bootinst [bootfile]\n\n");

	printf ("It installs boot block from bootfile (default boot.bin) to the first\n");

	printf ("drive and, if second drive is present, to the second drive.\n");

	printf ("Old boot sectors are saved to bootsav.bin and boot2sav.bin respectively.\n");

	printf ("\nYou can copy and distribute this software free of charge,\n");

	printf ("provided that sources are included.  No warranty, of course.\n\n");



	if (argc > 3)

		return (-1);



	bootfile = argc>1 ? argv[1] : BOOTFILE;

	if (access (bootfile, 4) < 0) {

		perror (bootfile);

		return (-1);

	}



	/*

	 * Is second disk present?  Try to read boot sector,

	 * check that it is bootable and has valid partition table.

	 */

	disk2present = (bootio (DISK2, READ, 0, 0, 1, bootRecord) == 0 &&

		*(short *) &bootRecord [ValidationAddr] == (short) MAGIC &&

		(bootRecord [PartAddr] == 0 || bootRecord [PartAddr] == 0x80) &&

		(bootRecord [PartAddr+16] == 0 || bootRecord [PartAddr+16] == 0x80) &&

		(bootRecord [PartAddr+32] == 0 || bootRecord [PartAddr+32] == 0x80) &&

		(bootRecord [PartAddr+48] == 0 || bootRecord [PartAddr+48] == 0x80));



	printf ("Continue with installation?");

	if (! yes ())

		return (0);



	install (DISK1, bootfile, SAVEFILE);



	if (disk2present)

		install (DISK2, bootfile, SAVE2FILE);



	return (0);

}