www.pudn.com > installboot.ZIP > installboot.c


/*	installboot 1.23 - Make a device bootable	Author: Kees J. Bot 
 *								21 Dec 1991 
 * 
 * Either make a device bootable or make an image from kernel, mm, fs, etc. 
 */ 
#define nil 0 
#define _POSIX_SOURCE	1 
#define _MINIX		1 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include "rawfs.h" 
#include "image.h" 
 
#define BOOTBLOCK	0	/* Of course */ 
#define SECTOR_SIZE	512	/* Disk sector size. */ 
#define RATIO		(BLOCK_SIZE/SECTOR_SIZE) 
#define SIGNATURE	0xAA55	/* Boot block signature. */ 
#define BOOT_MAX	64	/* Absolute maximum size of secondary boot */ 
#define JMP		0xEB	/* Opcode of a short jump */ 
#define JMPOFFM		0x01	/* Jump offset in normal master bootstrap */ 
#define JMPOFFE		0x02	/* Jump offset in extended bootstrap */ 
#define XOR		0x31	/* Jumping to an XOR instruction? */ 
#define SIGPOS		510	/* Where to put signature word. */ 
#define PARTPOS		446	/* Offset to the partition table in a master 
				 * boot block. 
				 */ 
 
#define between(a, c, z)	((unsigned) ((c) - (a)) <= ((z) - (a))) 
#define control(c)		between('\0', (c), '\37') 
 
#if !__minix_vmd 
#define cv64ul(i)	(i) 
#endif 
 
void report(char *label) 
/* installboot: label: No such file or directory */ 
{ 
	fprintf(stderr, "installboot: %s: %s\n", label, strerror(errno)); 
} 
 
void fatal(char *label) 
{ 
	report(label); 
	exit(1); 
} 
 
char *basename(char *name) 
/* Return the last component of name, stripping trailing slashes from name. 
 * Precondition: name != "/".  If name is prefixed by a label, then the 
 * label is copied to the basename too. 
 */ 
{ 
	static char base[IM_NAME_MAX]; 
	char *p, *bp= base; 
 
	if ((p= strchr(name, ':')) != nil) { 
		while (name <= p && bp < base + IM_NAME_MAX - 1) 
			*bp++ = *name++; 
	} 
	for (;;) { 
		if ((p= strrchr(name, '/')) == nil) { p= name; break; } 
		if (*++p != 0) break; 
		*--p= 0; 
	} 
	while (*p != 0 && bp < base + IM_NAME_MAX - 1) *bp++ = *p++; 
	*bp= 0; 
	return base; 
} 
 
void bread(FILE *f, char *name, void *buf, size_t len) 
/* Read len bytes.  Don't dare return without them. */ 
{ 
	if (len > 0 && fread(buf, len, 1, f) != 1) { 
		if (ferror(f)) fatal(name); 
		fprintf(stderr, "installboot: Unsuspected EOF on %s\n", name); 
		exit(1); 
	} 
} 
 
void bwrite(FILE *f, char *name, void *buf, size_t len) 
{ 
	if (len > 0 && fwrite(buf, len, 1, f) != 1) fatal(name); 
} 
 
long total_text= 0, total_data= 0, total_bss= 0; 
int making_image= 0; 
 
void read_header(int talk, char *proc, FILE *procf, struct image_header *ihdr) 
/* Read the a.out header of a program and check it.  If procf happens to be 
 * nil then the header is already in *image_hdr and need only be checked. 
 */ 
{ 
	int n, big= 0; 
	static int banner= 0; 
	struct exec *phdr= &ihdr->process; 
 
	if (procf == nil) { 
		/* Header already present. */ 
		n= phdr->a_hdrlen; 
	} else { 
		memset(ihdr, 0, sizeof(*ihdr)); 
 
		/* Put the basename of proc in the header. */ 
		strncpy(ihdr->name, basename(proc), IM_NAME_MAX); 
 
		/* Read the header. */ 
		n= fread(phdr, sizeof(char), A_MINHDR, procf); 
		if (ferror(procf)) fatal(proc); 
	} 
 
	if (n < A_MINHDR || BADMAG(*phdr)) { 
		fprintf(stderr, "installboot: %s is not an executable\n", proc); 
		exit(1); 
	} 
 
	/* Get the rest of the exec header. */ 
	if (procf != nil) { 
		bread(procf, proc, ((char *) phdr) + A_MINHDR, 
						phdr->a_hdrlen - A_MINHDR); 
	} 
 
	if (talk && !banner) { 
		printf("    text    data     bss     size\n"); 
		banner= 1; 
	} 
 
	if (talk) { 
		printf("%8ld%8ld%8ld%9ld  %s\n", 
			phdr->a_text, phdr->a_data, phdr->a_bss, 
			phdr->a_text + phdr->a_data + phdr->a_bss, proc); 
	} 
	total_text+= phdr->a_text; 
	total_data+= phdr->a_data; 
	total_bss+= phdr->a_bss; 
 
	if (phdr->a_cpu == A_I8086) { 
		long data= phdr->a_data + phdr->a_bss; 
 
		if (!(phdr->a_flags & A_SEP)) data+= phdr->a_text; 
 
		if (phdr->a_text >= 65536) big|= 1; 
		if (data >= 65536) big|= 2; 
	} 
	if (big) { 
		fprintf(stderr, 
			"%s will crash, %s%s%s segment%s larger then 64K\n", 
			proc, 
			big & 1 ? "text" : "", 
			big == 3 ? " and " : "", 
			big & 2 ? "data" : "", 
			big == 3 ? "s are" : " is"); 
	} 
} 
 
void padimage(char *image, FILE *imagef, int n) 
/* Add n zeros to image to pad it to a sector boundary. */ 
{ 
	while (n > 0) { 
		if (putc(0, imagef) == EOF) fatal(image); 
		n--; 
	} 
} 
 
#define align(n)	(((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1)) 
 
void copyexec(char *proc, FILE *procf, char *image, FILE *imagef, long n) 
/* Copy n bytes from proc to image padded to fill a sector. */ 
{ 
	int pad, c; 
 
	/* Compute number of padding bytes. */ 
	pad= align(n) - n; 
 
	while (n > 0) { 
		if ((c= getc(procf)) == EOF) { 
			if (ferror(procf)) fatal(proc); 
			fprintf(stderr,	"installboot: premature EOF on %s\n", 
									proc); 
			exit(1); 
		} 
		if (putc(c, imagef) == EOF) fatal(image); 
		n--; 
	} 
	padimage(image, imagef, pad); 
} 
 
void make_image(char *image, char **procv) 
/* Collect a set of files in an image, each "segment" is nicely padded out 
 * to SECTOR_SIZE, so it may be read from disk into memory without trickery. 
 */ 
{ 
	FILE *imagef, *procf; 
	char *proc, *file; 
	int procn; 
	struct image_header ihdr; 
	struct exec phdr; 
	struct stat st; 
 
	making_image= 1; 
 
	if ((imagef= fopen(image, "w")) == nil) fatal(image); 
 
	for (procn= 0; (proc= *procv++) != nil; procn++) { 
		/* Remove the label from the file name. */ 
		if ((file= strchr(proc, ':')) != nil) file++; else file= proc; 
 
		/* Real files please, may need to seek. */ 
		if (stat(file, &st) < 0 
			|| (errno= EISDIR, !S_ISREG(st.st_mode)) 
			|| (procf= fopen(file, "r")) == nil 
		) fatal(proc); 
 
		/* Read a.out header. */ 
		read_header(1, proc, procf, &ihdr); 
 
		/* Scratch. */ 
		phdr= ihdr.process; 
 
		/* The symbol table is always stripped off. */ 
		ihdr.process.a_syms= 0; 
		ihdr.process.a_flags &= ~A_NSYM; 
 
		/* Write header padded to fill a sector */ 
		bwrite(imagef, image, &ihdr, sizeof(ihdr)); 
 
		padimage(image, imagef, SECTOR_SIZE - sizeof(ihdr)); 
 
		/* A page aligned executable needs the header in text. */ 
		if (phdr.a_flags & A_PAL) { 
			rewind(procf); 
			phdr.a_text+= phdr.a_hdrlen; 
		} 
 
		/* Copy text and data of proc to image. */ 
		if (phdr.a_flags & A_SEP) { 
			/* Separate I&D: pad text & data separately. */ 
 
			copyexec(proc, procf, image, imagef, phdr.a_text); 
			copyexec(proc, procf, image, imagef, phdr.a_data); 
		} else { 
			/* Common I&D: keep text and data together. */ 
 
			copyexec(proc, procf, image, imagef, 
						phdr.a_text + phdr.a_data); 
		} 
 
		/* Done with proc. */ 
		(void) fclose(procf); 
	} 
	/* Done with image. */ 
 
	if (fclose(imagef) == EOF) fatal(image); 
 
	printf("  ------  ------  ------  -------\n"); 
	printf("%8ld%8ld%8ld%9ld  total\n", 
		total_text, total_data, total_bss, 
		total_text + total_data + total_bss); 
} 
 
void extractexec(FILE *imagef, char *image, FILE *procf, char *proc, 
						long count, off_t *alen) 
/* Copy a segment of an executable.  It is padded to a sector in image. */ 
{ 
	char buf[SECTOR_SIZE]; 
 
	while (count > 0) { 
		bread(imagef, image, buf, sizeof(buf)); 
		*alen-= sizeof(buf); 
 
		bwrite(procf, proc, buf, 
			count < sizeof(buf) ? (size_t) count : sizeof(buf)); 
		count-= sizeof(buf); 
	} 
} 
 
void extract_image(char *image) 
/* Extract the executables from an image. */ 
{ 
	FILE *imagef, *procf; 
	off_t len; 
	struct stat st; 
	struct image_header ihdr; 
	struct exec phdr; 
	char buf[SECTOR_SIZE]; 
 
	if (stat(image, &st) < 0) fatal(image); 
 
	/* Size of the image. */ 
	len= S_ISREG(st.st_mode) ? st.st_size : -1; 
 
	if ((imagef= fopen(image, "r")) == nil) fatal(image); 
 
	while (len != 0) { 
		/* Extract a program, first sector is an extended header. */ 
		bread(imagef, image, buf, sizeof(buf)); 
		len-= sizeof(buf); 
 
		memcpy(&ihdr, buf, sizeof(ihdr)); 
		phdr= ihdr.process; 
 
		/* Check header. */ 
		read_header(1, ihdr.name, nil, &ihdr); 
 
		if ((procf= fopen(ihdr.name, "w")) == nil) fatal(ihdr.name); 
 
		if (phdr.a_flags & A_PAL) { 
			/* A page aligned process contains a header in text. */ 
			phdr.a_text+= phdr.a_hdrlen; 
		} else { 
			bwrite(procf, ihdr.name, &ihdr.process, phdr.a_hdrlen); 
		} 
 
		/* Extract text and data segments. */ 
		if (phdr.a_flags & A_SEP) { 
			extractexec(imagef, image, procf, ihdr.name, 
						phdr.a_text, &len); 
			extractexec(imagef, image, procf, ihdr.name, 
						phdr.a_data, &len); 
		} else { 
			extractexec(imagef, image, procf, ihdr.name, 
				phdr.a_text + phdr.a_data, &len); 
		} 
 
		if (fclose(procf) == EOF) fatal(ihdr.name); 
	} 
} 
 
int rawfd;	/* File descriptor to open device. */ 
char *rawdev;	/* Name of device. */ 
 
void readblock(off_t blk, char *buf) 
/* For rawfs, so that it can read blocks. */ 
{ 
	int n; 
 
	if (lseek(rawfd, blk * BLOCK_SIZE, SEEK_SET) < 0 
		|| (n= read(rawfd, buf, BLOCK_SIZE)) < 0 
	) fatal(rawdev); 
 
	if (n < BLOCK_SIZE) { 
		fprintf(stderr, "installboot: unexpected EOF on %s\n", rawdev); 
		exit(1); 
	} 
} 
 
void writeblock(off_t blk, char *buf) 
/* Add a function to write blocks for local use. */ 
{ 
	if (lseek(rawfd, blk * BLOCK_SIZE, SEEK_SET) < 0 
		|| write(rawfd, buf, BLOCK_SIZE) < 0 
	) fatal(rawdev); 
} 
 
int raw_install(char *file, off_t *start, off_t *len) 
/* Copy bootcode or an image to the boot device at the given absolute disk 
 * block number.  This "raw" installation is used to place bootcode and 
 * image on a disk without a filesystem to make a simple boot disk.  Useful 
 * in automated scripts for J. Random User. 
 * Note: *len == 0 when an image is read.  It is set right afterwards. 
 */ 
{ 
	static char buf[BLOCK_SIZE];	/* Nonvolatile block buffer. */ 
	FILE *f; 
	off_t sec; 
	unsigned long devsize; 
	static int banner= 0; 
	struct partition entry; 
 
	/* See if the device has a maximum size. */ 
	devsize= -1; 
	if (ioctl(rawfd, DIOCGETP, &entry) == 0) devsize= cv64ul(entry.size); 
 
	if ((f= fopen(file, "r")) == nil) fatal(file); 
 
	/* Copy sectors from file onto the boot device. */ 
	sec= *start; 
	do { 
		int off= sec % RATIO; 
 
		if (fread(buf + off * SECTOR_SIZE, 1, SECTOR_SIZE, f) == 0) 
			break; 
 
		if (sec >= devsize) { 
			fprintf(stderr, 
			"installboot: %s can't be attached to %s\n", 
				file, rawdev); 
			return 0; 
		} 
 
		if (off == RATIO - 1) writeblock(sec / RATIO, buf); 
	} while (++sec != *start + *len); 
 
	if (ferror(f)) fatal(file); 
	(void) fclose(f); 
 
	/* Write a partial block, this may be the last image. */ 
	if (sec % RATIO != 0) writeblock(sec / RATIO, buf); 
 
	if (!banner) { 
		printf("  sector  length\n"); 
		banner= 1; 
	} 
	*len= sec - *start; 
	printf("%8ld%8ld  %s\n", *start, *len, file); 
	*start= sec; 
	return 1; 
} 
 
enum howto { FS, BOOT }; 
 
void make_bootable(enum howto how, char *device, char *bootblock, 
					char *bootcode, char **imagev) 
/* Install bootblock on the bootsector of device with the disk addresses to 
 * bootcode patched into the data segment of bootblock.  "How" tells if there 
 * should or shoudn't be a file system on the disk.  The images in the imagev 
 * vector are added to the end of the device. 
 */ 
{ 
	char buf[BLOCK_SIZE + 256], *adrp, *parmp; 
	struct fileaddr { 
		off_t	address; 
		int	count; 
	} bootaddr[BOOT_MAX + 1], *bap= bootaddr; 
	struct exec boothdr; 
	struct image_header dummy; 
	struct stat st; 
	ino_t ino; 
	off_t sector, max_sector; 
	FILE *bootf; 
	off_t addr, fssize, pos, len; 
	char *labels, *label, *image; 
	int nolabel; 
 
	/* Open device and set variables for readblock. */ 
	if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device); 
 
	/* Read and check the superblock. */ 
	fssize= r_super(); 
 
	switch (how) { 
	case FS: 
		if (fssize == 0) { 
			fprintf(stderr, 
				"installboot: %s is not a Minix file system\n", 
				device); 
			exit(1); 
		} 
		break; 
	case BOOT: 
		if (fssize != 0) { 
			int s; 
			printf("%s contains a file system!\n", device); 
			printf("Scribbling in 10 seconds"); 
			for (s= 0; s < 10; s++) { 
				fputc('.', stdout); 
				fflush(stdout); 
				sleep(1); 
			} 
			fputc('\n', stdout); 
		} 
		fssize= 1;	/* Just a boot block. */ 
	} 
 
	if (how == FS) { 
		/* See if the boot code can be found on the file system. */ 
		if ((ino= r_lookup(ROOT_INO, bootcode)) == 0) { 
			if (errno != ENOENT) fatal(bootcode); 
		} 
	} else { 
		/* Boot code must be attached at the end. */ 
		ino= 0; 
	} 
 
	if (ino == 0) { 
		/* For a raw installation, we need to copy the boot code onto 
		 * the device, so we need to look at the file to be copied. 
		 */ 
		if (stat(bootcode, &st) < 0) fatal(bootcode); 
 
		if ((bootf= fopen(bootcode, "r")) == nil) fatal(bootcode); 
	} else { 
		/* Boot code is present in the file system. */ 
		r_stat(ino, &st); 
 
		/* Get the header from the first block. */ 
		if ((addr= r_vir2abs((off_t) 0)) == 0) { 
			boothdr.a_magic[0]= !A_MAGIC0; 
		} else { 
			readblock(addr, buf); 
			memcpy(&boothdr, buf, sizeof(struct exec)); 
		} 
		bootf= nil; 
		dummy.process= boothdr; 
	} 
	/* See if it is an executable (read_header does the check). */ 
	read_header(0, bootcode, bootf, &dummy); 
	boothdr= dummy.process; 
 
	if (bootf != nil) fclose(bootf); 
 
	/* Get all the sector addresses of the secondary boot code. */ 
	max_sector= (boothdr.a_hdrlen + boothdr.a_text 
			+ boothdr.a_data + SECTOR_SIZE - 1) / SECTOR_SIZE; 
 
	if (max_sector > BOOT_MAX * RATIO) { 
		fprintf(stderr, "installboot: %s is way too big\n", bootcode); 
		exit(0); 
	} 
 
	/* Determine the addresses to the boot code to be patched into the 
	 * boot block. 
	 */ 
	bap->count= 0;	/* Trick to get the address recording going. */ 
 
	for (sector= 0; sector < max_sector; sector++) { 
		if (ino == 0) { 
			addr= fssize + (sector / RATIO); 
		} else 
		if ((addr= r_vir2abs(sector / RATIO)) == 0) { 
			fprintf(stderr, "installboot: %s has holes!\n", 
								bootcode); 
			exit(1); 
		} 
		addr= (addr * RATIO) + (sector % RATIO); 
 
		/* First address of the addresses array? */ 
		if (bap->count == 0) bap->address= addr; 
 
		/* Paste sectors together in a multisector read. */ 
		if (bap->address + bap->count == addr) 
			bap->count++; 
		else { 
			/* New address. */ 
			bap++; 
			bap->address= addr; 
			bap->count= 1; 
		} 
	} 
	(++bap)->count= 0;	/* No more. */ 
 
	/* Get the boot block and patch the pieces in. */ 
	readblock(BOOTBLOCK, buf); 
 
	if ((bootf= fopen(bootblock, "r")) == nil) fatal(bootblock); 
 
	read_header(0, bootblock, bootf, &dummy); 
	boothdr= dummy.process; 
 
	if (boothdr.a_text + boothdr.a_data + 
					 4 * (bap - bootaddr) + 1 > SIGPOS) { 
		fprintf(stderr, 
	"installboot: %s + addresses to %s don't fit in the boot sector\n", 
			bootblock, bootcode); 
		fprintf(stderr, 
		    "You can try copying/reinstalling %s to defragment it\n", 
			bootcode); 
		exit(1); 
	} 
 
	/* All checks out right.  Read bootblock into the boot block! */ 
	bread(bootf, bootblock, buf, boothdr.a_text + boothdr.a_data); 
	(void) fclose(bootf); 
 
	/* Patch the addresses in. */ 
	adrp= buf + (int) (boothdr.a_text + boothdr.a_data); 
	for (bap= bootaddr; bap->count != 0; bap++) { 
		*adrp++= bap->count; 
		*adrp++= (bap->address >>  0) & 0xFF; 
		*adrp++= (bap->address >>  8) & 0xFF; 
		*adrp++= (bap->address >> 16) & 0xFF; 
	} 
	/* Zero count stops bootblock's reading loop. */ 
	*adrp++= 0; 
 
	if (bap > bootaddr+1) { 
		printf("%s and %d addresses to %s patched into %s\n", 
			bootblock, (int)(bap - bootaddr), bootcode, device); 
	} 
 
	/* Boot block signature. */ 
	adrp= buf + SIGPOS; 
	*adrp++= (SIGNATURE >> 0) & 0xFF; 
	*adrp++= (SIGNATURE >> 8) & 0xFF; 
 
	/* Sector 2 of the boot block is used for boot parameters, initially 
	 * filled with null commands (newlines).  Initialize it only if 
	 * necessary. 
	 */ 
	for (parmp= buf + SECTOR_SIZE; parmp < buf + 2*SECTOR_SIZE; parmp++) { 
		if (*imagev != nil || (control(*parmp) && *parmp != '\n')) { 
			/* Param sector must be initialized. */ 
			memset(buf + SECTOR_SIZE, '\n', SECTOR_SIZE); 
			break; 
		} 
	} 
 
	/* Offset to the end of the file system to add boot code and images. */ 
	pos= fssize * RATIO; 
 
	if (ino == 0) { 
		/* Place the boot code onto the boot device. */ 
		len= max_sector; 
		if (!raw_install(bootcode, &pos, &len)) { 
			if (how == FS) { 
				fprintf(stderr, 
	"\t(Isn't there a copy of %s on %s that can be used?)\n", 
					bootcode, device); 
			} 
			exit(1); 
		} 
	} 
 
	parmp= buf + SECTOR_SIZE; 
	nolabel= 0; 
 
	if (how == BOOT) { 
		/* A boot only disk needs to have floppies swapped. */ 
		strcpy(parmp, "delay=swap\n"); 
		parmp+= strlen(parmp); 
	} 
 
	while ((labels= *imagev++) != nil) { 
		/* Place each kernel image on the boot device. */ 
 
		if ((image= strchr(labels, ':')) != nil) 
			*image++= 0; 
		else { 
			if (nolabel) { 
				fprintf(stderr, 
			    "installboot: Only one image can be the default\n"); 
				exit(1); 
			} 
			nolabel= 1; 
			image= labels; 
			labels= nil; 
		} 
		len= 0; 
		if (!raw_install(image, &pos, &len)) exit(1); 
 
		if (labels == nil) { 
			/* Let this image be the default. */ 
			sprintf(parmp, "image=%ld:%ld\n", pos-len, len); 
			parmp+= strlen(parmp); 
		} 
 
		while (labels != nil) { 
			/* Image is prefixed by a comma separated list of 
			 * labels.  Define functions to select label and image. 
			 */ 
			label= labels; 
			if ((labels= strchr(labels, ',')) != nil) *labels++ = 0; 
 
			sprintf(parmp, 
		"%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n", 
				label, 
				between('A', label[0], 'Z') 
					? label[0]-'A'+'a' : label[0], 
				label, pos-len, len, label); 
			parmp+= strlen(parmp); 
		} 
 
		if (parmp > buf + BLOCK_SIZE) { 
			fprintf(stderr, 
		"installboot: Out of parameter space, too many images\n"); 
			exit(1); 
		} 
	} 
	/* Install boot block. */ 
	writeblock((off_t) BOOTBLOCK, buf); 
 
	if (pos > fssize * RATIO) { 
		/* Tell the total size of the data on the device. */ 
		printf("%16ld  (%ld kb) total\n", pos, 
						(pos + RATIO - 1) / RATIO); 
	} 
} 
 
void install_master(char *device, char *masterboot, char *fix) 
/* Booting a hard disk is a two stage process:  The master bootstrap in sector 
 * 0 loads the bootstrap from sector 0 of the active partition which in turn 
 * starts the operating system.  This code installs such a master bootstrap 
 * on a hard disk.  If fix is non-null then the master bootstrap is locked 
 * into booting device /dev/hd'fix'. 
 */ 
{ 
	FILE *masf; 
	unsigned long size; 
	struct stat st; 
	char buf[BLOCK_SIZE]; 
 
	/* Open device. */ 
	if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device); 
 
	/* Open the master boot code. */ 
	if ((masf= fopen(masterboot, "r")) == nil) fatal(masterboot); 
 
	/* See if the user is cloning a device. */ 
	if (fstat(fileno(masf), &st) >=0 && S_ISBLK(st.st_mode)) 
		size= PARTPOS; 
	else { 
		/* Read and check header otherwise. */ 
		struct image_header ihdr; 
 
		read_header(1, masterboot, masf, &ihdr); 
		size= ihdr.process.a_text + ihdr.process.a_data; 
	} 
	if (size > PARTPOS) { 
		fprintf(stderr, "installboot: %s is too big\n", masterboot); 
		exit(1); 
	} 
	/* Read the master boot block, patch it, write. */ 
	readblock(BOOTBLOCK, buf); 
 
	(void) bread(masf, masterboot, buf, size); 
 
	if (fix != nil) { 
		/* Fixate partition to boot. */ 
		int device= 0, logical= 0; 
		char *pf= fix; 
 
		while (between('0', *pf, '9')) { 
			device= 10 * device + (*pf - '0'); 
			if (device >= 40) break; 
			pf++; 
		} 
		if (between('a', *pf, 'd')) { 
			logical= 1 + (*pf - 'a'); 
			pf++; 
		} 
		if (*pf != 0) { 
			fprintf(stderr, "installboot: bad fix key '%s'\n", fix); 
			exit(1); 
		} 
 
		if (buf[0] == (char) JMP && buf[1] == (char) JMPOFFM 
				&& buf[3] == (char) XOR && logical == 0) { 
			/* Minix masterboot; patch device number. */ 
			buf[2]= device; 
		} else 
		if (buf[0] == (char) JMP && buf[1] == (char) JMPOFFE 
				&& buf[4] == (char) XOR && logical != 0) { 
			/* Minix extboot; patch device and logical number. */ 
			buf[2]= device; 
			buf[3]= logical; 
		} else { 
			fprintf(stderr, 
				"installboot: can't put fix flag '%s' on %s\n", 
				fix, masterboot); 
			exit(1); 
		} 
	} 
 
	/* Install signature. */ 
	buf[SIGPOS+0]= (SIGNATURE >> 0) & 0xFF; 
	buf[SIGPOS+1]= (SIGNATURE >> 8) & 0xFF; 
 
	writeblock(BOOTBLOCK, buf); 
} 
 
void usage(void) 
{ 
	fprintf(stderr, 
		"Usage: installboot -i(mage) image kernel mm fs ... init\n"); 
	fprintf(stderr, 
		"       installboot -(e)x(tract) image\n"); 
	fprintf(stderr, 
		"       installboot -d(evice) device bootblock boot\n"); 
	fprintf(stderr, 
		"       installboot -b(oot) device bootblock boot image ...\n"); 
	fprintf(stderr, 
		"       installboot -m(aster) [fix] device masterboot\n"); 
	exit(1); 
} 
 
int isoption(char *option, char *test) 
/* Check if the option argument is equals "test".  Also accept -i as short 
 * for -image, and the special case -x for -extract. 
 */ 
{ 
	if (strcmp(option, test) == 0) return 1; 
	if (option[0] != '-' && strlen(option) != 2) return 0; 
	if (option[1] == test[1]) return 1; 
	if (option[1] == 'x' && test[1] == 'e') return 1; 
	return 0; 
} 
 
int main(int argc, char **argv) 
{ 
	if (argc < 2) usage(); 
 
	if (argc >= 4 && isoption(argv[1], "-image")) { 
		make_image(argv[2], argv + 3); 
	} else 
	if (argc == 3 && isoption(argv[1], "-extract")) { 
		extract_image(argv[2]); 
	} else 
	if (argc >= 5 && isoption(argv[1], "-device")) { 
		make_bootable(FS, argv[2], argv[3], argv[4], argv + 5); 
	} else 
	if (argc >= 6 && isoption(argv[1], "-boot")) { 
		make_bootable(BOOT, argv[2], argv[3], argv[4], argv + 5); 
	} else 
	if (argc == 4 && isoption(argv[1], "-master")) { 
		install_master(argv[2], argv[3], nil); 
	} else 
	if (argc == 5 && isoption(argv[1], "-master") 
				    && between('0', argv[2][0], '9')) { 
		install_master(argv[3], argv[4], argv[2]); 
	} else { 
		usage(); 
	} 
	exit(0); 
}