Example #1
0
/**
 * @brief Check whether it is aout format
 * @param vp[in]  pointer inode for reading exec file
 * @return 0 on success
 */
static int aout_check_binfmt(struct nucleos_binprm *param, struct vnode *vp)
{
	/* Read the header and check the magic number.  The standard MINIX header
	 * is defined in <nucleos/a.out.h>.  It consists of 8 chars followed by 6 longs.
	 * Then come 4 more longs that are not used here.
	 *	Byte 0: magic number 0x01
	 *	Byte 1: magic number 0x03
	 *	Byte 2: normal = 0x10 (not checked, 0 is OK), separate I/D = 0x20
	 *	Byte 3: CPU type, Intel 16 bit = 0x04, Intel 32 bit = 0x10,
	 *            Motorola = 0x0B, Sun SPARC = 0x17
	 *	Byte 4: Header length = 0x20
	 *	Bytes 5-7 are not used.
	 *
	 *	Now come the 6 longs
	 *	Bytes  8-11: size of text segments in bytes
	 *	Bytes 12-15: size of initialized data segment in bytes
	 *	Bytes 16-19: size of bss in bytes
	 *	Bytes 20-23: program entry point
	 *	Bytes 24-27: total memory allocated to program (text, data + stack)
	 *	Bytes 28-31: size of symbol table in bytes
	 * The longs are represented in a machine dependent order,
	 * little-endian on the 8088, big-endian on the 68000.
	 * The header is followed directly by the text and data segments, and the 
	 * symbol table (if any). The sizes are given in the header. Only the 
	 * text and data segments are copied into memory by exec. The header is 
	 * used here only. The symbol table is for the benefit of a debugger and 
	 * is ignored here.
	 */
	off_t pos;
	int err;
	u64_t new_pos;
	unsigned int cum_io_incr;
	struct exec hdr;

	/* Read from the start of the file */
	pos = 0;

	/* Issue request */
	err = req_readwrite(vp->v_fs_e, vp->v_inode_nr, cvul64(pos), READING, 
			  VFS_PROC_NR, (char*)&hdr, sizeof(struct exec), &new_pos, &cum_io_incr);

	if (err) {
		app_err("Can't read the file header\n");
		return -1;
	}

	/* Interpreted script? */
	if (((char*)&hdr)[0] == '#' && ((char*)&hdr)[1] == '!' && vp->v_size >= 2) {
		return ESCRIPT;
	}

	if (vp->v_size < A_MINHDR)
		return -1;

	if (BADMAG(hdr))
		return -1;

#ifdef CONFIG_X86_32
	if (hdr.a_cpu != A_I8086 && hdr.a_cpu != A_I80386)
		return -1;
#endif

	if ((hdr.a_flags & ~(A_NSYM | A_EXEC)) != 0)
		return -1;

	memcpy(param->buf, &hdr, hdr.a_hdrlen);
	param->vp = vp;

	return BINFMT_AOUT;
}
Example #2
0
int main(int argc, char *argv[])
{

    int a_out_fd;
    struct stat statbuf;
    char *a_out;
    struct exec *header;
    /*short img_ar[12]; */
    ImgHeader imgHeader;
    FILE *img_fd;
    /* Embedded file info */
    char aflName[20];		/* Name of file containing list of added files */
    char afName[20];		/* Name of embedded file from .afl file */
    int af;			/* general purpose integer for add files routines */
    FILE *afl_fd;		/* The .afl FILE */
    int af_fd;			/* Each embedded file */
    struct stat afstatbuf;	/* Each embedded file */
    unsigned short embeddedTotalLength, embeddedOffset;
    int afLen, numEmbeddedFiles;


    /* Default values for the configurable options */
    unsigned short HeapParas = 128;
    unsigned short StackParas = 64;	/* Is this the default SIBO SDK uses? */
    unsigned short InitialIP = 0;
    unsigned short Priority = 128;
    unsigned short CodeVersion = 8204;
    int verbose = FALSE;

    int i;

    if (argc < 3)
	goto usage;

    if ((a_out_fd = open(argv[1], O_RDONLY | O_BINARY)) < 0) {
	fprintf(stderr, "imgconv: Couldn't open input file %s\n", argv[1]);
	exit(1);
    }

    if (fstat(a_out_fd, &statbuf) < 0) {
	fprintf(stderr, "imgconv: Couldn't fstat input file %s: ", argv[1]);
	perror("");
	exit(1);
    }

    if ((a_out = malloc(statbuf.st_size)) == NULL) {
	fprintf(stderr,
		"imgconv: Couldn't allocate %ld bytes to read input file %s\n",
		statbuf.st_size, argv[1]);
	exit(1);
    }

    if (statbuf.st_size != read(a_out_fd, a_out, statbuf.st_size)) {
	fprintf(stderr, "imgconv: Couldn't read %ld bytes of input file %s: ",
		statbuf.st_size, argv[1]);
	perror("");
	exit(1);
    }
#ifdef MSDOS
    if (NULL == (img_fd = fopen(argv[2], "wb"))) {
#else
    if (NULL == (img_fd = fopen(argv[2], "w"))) {
#endif
	fprintf(stderr, "imgconv: Couldn't open %s for writing\n", argv[1]);
	exit(1);
    }

    /* Handle any extra command line options */
    for (i = 3; i < argc; i++) {
	if (argv[i][0] != '-') {
	    fprintf(stderr, "imgconv: %s is not a - option\n", argv[i]);
	    goto usage;
	}
	switch (tolower(argv[i][1])) {
	case 'c':		/* Specify code version */
	    if (sscanf(&argv[i][2], "%hu", &CodeVersion) != 1)
		goto usage;
	    break;
	case 'h':		/* Specify heap paragraphs */
	    if (sscanf(&argv[i][2], "%hu", &HeapParas) != 1)
		goto usage;
	    break;
	case 'i':		/* Specify initial IP */
	    if (sscanf(&argv[i][2], "%hu", &InitialIP) != 1)
		goto usage;
	    break;
	case 'p':		/* Specify priority */
	    if (sscanf(&argv[i][2], "%hu", &Priority) != 1)
		goto usage;
	    break;
	case 's':		/* Specify stack paragraphs */
	    if (sscanf(&argv[i][2], "%hu", &StackParas) != 1)
		goto usage;
	    break;
	case 'v':		/* Verbose */
	    verbose = TRUE;
	    break;
	default:
	    fprintf(stderr, "imgconv: unknown option %s\n", argv[i]);
	    goto usage;
	}
    }

    header = (struct exec *) a_out;

    if (BADMAG(*header)) {
	fprintf(stderr, "imgconv: Bad Magic in input file %s\n", argv[1]);
	exit(1);
    }
    if (verbose)
	printf("%s info:\nText 0x%04lx, Data 0x%04lx, BSS 0x%04lx\n",
	       argv[1], header->a_text, header->a_data, header->a_bss);

    a_out += header->a_hdrlen;


    /* Do we have an add file list? (.afl file) Try the base name of the
     * .img file, replacing .img with .afl... */
    strcpy(aflName, argv[2]);
    for (af = 0; af < 16 && aflName[af] != '.'; af++);
    strcpy(aflName + af, ".afl");

    embeddedTotalLength = numEmbeddedFiles = 0;

#ifdef MSDOS
    if (NULL != (afl_fd = fopen(aflName, "rb"))) {
#else
    if (NULL != (afl_fd = fopen(aflName, "r"))) {
#endif
	if (verbose)
	    printf("Adding embedded files from list held in %s\n", aflName);


	for (af = 0; af < MaxAddFiles; af++) {

	    embeddedFiles[af].length = 0;
	    embeddedFiles[af].data = NULL;

	    /* What is the name of this embedded file? */
	    if (fgets(afName, 20, afl_fd) == NULL)
		continue;

	    afLen = strlen(afName);

	    if (afLen < 2)
		continue;

	    if (afName[afLen - 1] == '\n')
		afName[afLen - 1] = '\0';

	    if ((af_fd = open(afName, O_RDONLY | O_BINARY)) < 0) {
		fprintf(stderr, "imgconv: Couldn't open embedded file %s\n",
			afName);
		exit(1);
	    }

	    if (fstat(af_fd, &afstatbuf) < 0) {
		fprintf(stderr, "imgconv: Couldn't fstat embedded file %s: ",
			afName);
		perror("");
		exit(1);
	    }

	    /* Embedded files can be zero length */
	    embeddedFiles[af].length = afstatbuf.st_size;
	    if (embeddedFiles[af].length > 0) {

		/* Allocate storage space */
		if ((embeddedFiles[af].data = malloc(embeddedFiles[af].length))
		    == NULL) {
		    fprintf(stderr,
			    "imgconv: Couldn't allocate %d bytes to read embedded file %s\n",
			    embeddedFiles[af].length, afName);
		    exit(1);
		}

		if (embeddedFiles[af].length !=
		    read(af_fd, embeddedFiles[af].data,
			 embeddedFiles[af].length)) {
		    fprintf(stderr,
			    "imgconv: Couldn't read %d bytes of embedded file %s: ",
			    embeddedFiles[af].length, afName);
		    perror("");
		    exit(1);
		}
	    }
	    close(af_fd);
	    numEmbeddedFiles++;
	    embeddedTotalLength += embeddedFiles[af].length;
	    if (verbose)
		printf("Read in %d bytes from %s\n", embeddedFiles[af].length,
		       afName);
	}
	if (verbose)
	    printf("Embedded files take up %d bytes\n", embeddedTotalLength);
    }


    /* Olaf's original .img header...
     * fwrite( "ImageFileType**", 16, 1, img_fd);
     * img_ar[ 0] = 8207;
     * img_ar[ 1] = 64;
     * img_ar[ 2] = header->a_text / 16;
     * img_ar[ 3] = 0;
     * img_ar[ 4] = 1000;  ** Stack Size **
     * img_ar[ 5] = header->a_data / 16;
     * img_ar[ 6] = 128;
     * img_ar[ 7] = header->a_data;
     * img_ar[ 8] = chksum( (short *) a_out, header->a_text);
     * img_ar[ 9]  = chksum( (short *)(a_out+header->a_text), header->a_data);
     * img_ar[ 10] = 128;  ** Start up Priority **
     * img_ar[ 11] = 8204; ** Version ?? **
     */

    strcpy(imgHeader.Signature, "ImageFileType**");
    imgHeader.ImageVersion = 0x200f;
    imgHeader.HeaderSizeBytes = sizeof(ImgHeader) + embeddedTotalLength;
    imgHeader.CodeParas = header->a_text / 16;
    imgHeader.InitialIP = InitialIP;
    imgHeader.StackParas = StackParas;
    imgHeader.DataParas = (header->a_data + header->a_bss + 15) / 16;
    imgHeader.HeapParas = HeapParas;
    imgHeader.InitialisedData = header->a_data;
    imgHeader.CodeCheckSum = chksum((unsigned char *) a_out, header->a_text);
    imgHeader.DataCheckSum =
	chksum((unsigned char *) (a_out + header->a_text), header->a_data);
    imgHeader.CodeVersion = CodeVersion;
    imgHeader.Priority = Priority;

    embeddedOffset = embeddedTotalLength > 0 ? sizeof(ImgHeader) : 0;

    for (i = 0; i < MaxAddFiles; i++) {
	imgHeader.Add[i].offset = i < numEmbeddedFiles ? embeddedOffset : 0;
	imgHeader.Add[i].length =
	    i < numEmbeddedFiles ? embeddedFiles[i].length : 0;
	embeddedOffset += embeddedFiles[i].length;
    }

    imgHeader.DylCount = 0;
    imgHeader.DylTableOffsetLo = 0;
    imgHeader.DylTableOffsetHi = 0;
    imgHeader.Spare = 0;

    /* Olaf missed the DylTableOffset off this, methinks...
     * fwrite( img_ar, 12, 2, img_fd);
     * for (i=0 ; i < 12; i++)
     *   img_ar[i] = 0;
     * fwrite( img_ar, 12, 2, img_fd);*/

    /* Write out the header structure */
    fwrite((char *) &imgHeader, sizeof(imgHeader), 1, img_fd);

    /* Write out the embedded files, if any */
    for (i = 0; i < MaxAddFiles; i++)
	if (embeddedFiles[i].length > 0) {
	    fwrite((char *) embeddedFiles[i].data, embeddedFiles[i].length, 1,
		   img_fd);
	    free(embeddedFiles[i].data);
	}

    /* Write out the code segment */
    fwrite(a_out, header->a_text, 1, img_fd);

    /* Write out the initialised data segment */
    fwrite(a_out + header->a_text, header->a_data, 1, img_fd);

    fclose(img_fd);
    close(a_out_fd);
    exit(0);

  usage:			/* Vile, nasty hack: I love it :) */
    fprintf(stderr, "usage: imgconv a.out-file .img-file [options]\n\n");
    fprintf(stderr,
	    "imgconv converts an executable file in a.out format (as produced\n");
    fprintf(stderr,
	    "by the bcc toolset) into a Psion-compatible .img file.\n\n");
    fprintf(stderr,
	    "These options can be passed to this program with the -Y\n");
    fprintf(stderr, "option of bcc, e.g. bcc prog.c -o prog.img -Y-p50\n\n");
    fprintf(stderr,
	    "options: all <num>'s are in decimal, defaults in brackets\n");
    fprintf(stderr, " -C<num>   Specify code version number        (8204)\n");
    fprintf(stderr, " -H<num>   Specify number of heap paragraphs  (128)\n");
    fprintf(stderr, " -I<num>   Specify initial value for IP       (0)\n");
    fprintf(stderr, " -P<num>   Specify priority                   (128)\n");
    fprintf(stderr, " -S<num>   Specify number of stack paragraphs (64)\n");
    fprintf(stderr,
	    " -V        Verbose mode: display code stats   (Don't)\n\n");
    fprintf(stderr, "Up to four files may be embedded inside the resultant\n");
    fprintf(stderr,
	    ".img file by placing their filenames on separate lines\n");
    fprintf(stderr,
	    "in a .afl file. This .afl file must have the same base\n");
    fprintf(stderr,
	    "name as the .img file. e.g. Create a file fred.afl containing:\n");
    fprintf(stderr, "  fred.pic\n  fred.rsc\n  fred.shd\n");
    fprintf(stderr, "To have these files added to the fred.img file.\n");
    exit(1);
}
Example #3
0
void exec_image(char *image)
/* Get a Minix image into core, patch it up and execute. */
{
	int i;
	struct image_header hdr;
	char *buf;
	u32_t vsec, addr, limit, aout, n, totalmem = 0;
	struct process *procp;		/* Process under construction. */
	long a_text, a_data, a_bss, a_stack;
	int banner= 0;
	long processor= a2l(b_value("processor"));
	u16_t kmagic, mode;
	char *console;
	char params[SECTOR_SIZE];
	extern char *sbrk(int);
	char *verb;

	/* The stack is pretty deep here, so check if heap and stack collide. */
	(void) sbrk(0);

	if ((verb= b_value(VERBOSEBOOTVARNAME)) != nil)
		verboseboot = a2l(verb);

	printf("\nLoading ");
	pretty_image(image);
	printf(".\n");

	vsec= 0;			/* Load this sector from image next. */
	addr= mem[0].base;		/* Into this memory block. */
	limit= mem[0].base + mem[0].size;
	if (limit > caddr) limit= caddr;

	/* Allocate and clear the area where the headers will be placed. */
	aout = (limit -= PROCESS_MAX * A_MINHDR);

	/* Clear the area where the headers will be placed. */
	raw_clear(aout, PROCESS_MAX * A_MINHDR);

	/* Read the many different processes: */
	for (i= 0; vsec < image_size; i++) {
		u32_t startaddr;
		startaddr = addr;
		if (i == PROCESS_MAX) {
			printf("There are more then %d programs in %s\n",
				PROCESS_MAX, image);
			errno= 0;
			return;
		}
		procp= &process[i];

		/* Read header. */
		DEBUGEXTRA(("Reading header... "));
		for (;;) {
			if ((buf= get_sector(vsec++)) == nil) return;

			memcpy(&hdr, buf, sizeof(hdr));

			if (BADMAG(hdr.process)) { errno= ENOEXEC; return; }

			/* Check the optional label on the process. */
			if (selected(hdr.name)) break;

			/* Bad label, skip this process. */
			vsec+= proc_size(&hdr);
		}
		DEBUGEXTRA(("done\n"));

		/* Sanity check: an 8086 can't run a 386 kernel. */
		if (hdr.process.a_cpu == A_I80386 && processor < 386) {
			printf("You can't run a 386 kernel on this 80%ld\n",
				processor);
			errno= 0;
			return;
		}

		/* Get the click shift from the kernel text segment. */
		if (i == KERNEL_IDX) {
			if (!get_clickshift(vsec, &hdr)) return;
			addr= align(addr, click_size);

			/* big kernels must be loaded into extended memory */
			if (k_flags & K_KHIGH) {
				addr= mem[1].base;
				limit= mem[1].base + mem[1].size;
			}
		}

		/* Save a copy of the header for the kernel, with a_syms
		 * misused as the address where the process is loaded at.
		 */
		DEBUGEXTRA(("raw_copy(0x%x, 0x%lx, 0x%x)... ", 
			aout + i * A_MINHDR, mon2abs(&hdr.process), A_MINHDR));
		hdr.process.a_syms= addr;
		raw_copy(aout + i * A_MINHDR, mon2abs(&hdr.process), A_MINHDR);
		DEBUGEXTRA(("done\n"));

		if (!banner) {
			DEBUGBASIC(("     cs       ds     text     data      bss"));
			if (k_flags & K_CHMEM) DEBUGBASIC(("    stack"));
			DEBUGBASIC(("\n"));
			banner= 1;
		}

		/* Segment sizes. */
		DEBUGEXTRA(("a_text=0x%lx; a_data=0x%lx; a_bss=0x%lx; a_flags=0x%x)\n",
			hdr.process.a_text, hdr.process.a_data, 
			hdr.process.a_bss, hdr.process.a_flags));

		a_text= hdr.process.a_text;
		a_data= hdr.process.a_data;
		a_bss= hdr.process.a_bss;
		if (k_flags & K_CHMEM) {
			a_stack= hdr.process.a_total - a_data - a_bss;
			if (!(hdr.process.a_flags & A_SEP)) a_stack-= a_text;
		} else {
			a_stack= 0;
		}

		/* Collect info about the process to be. */
		procp->cs= addr;

		/* Process may be page aligned so that the text segment contains
		 * the header, or have an unmapped zero page against vaxisms.
		 */
		procp->entry= hdr.process.a_entry;
		if (hdr.process.a_flags & A_PAL) a_text+= hdr.process.a_hdrlen;
		if (hdr.process.a_flags & A_UZP) procp->cs-= click_size;

		/* Separate I&D: two segments.  Common I&D: only one. */
		if (hdr.process.a_flags & A_SEP) {
			/* Read the text segment. */
			DEBUGEXTRA(("get_segment(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n",
				vsec, a_text, addr, limit));
			if (!get_segment(&vsec, &a_text, &addr, limit)) return;
			DEBUGEXTRA(("get_segment done vsec=0x%lx a_text=0x%lx "
				"addr=0x%lx\n", 
				vsec, a_text, addr));

			/* The data segment follows. */
			procp->ds= addr;
			if (hdr.process.a_flags & A_UZP) procp->ds-= click_size;
			procp->data= addr;
		} else {
			/* Add text to data to form one segment. */
			procp->data= addr + a_text;
			procp->ds= procp->cs;
			a_data+= a_text;
		}

		/* Read the data segment. */
		DEBUGEXTRA(("get_segment(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", 
			vsec, a_data, addr, limit));
		if (!get_segment(&vsec, &a_data, &addr, limit)) return;
		DEBUGEXTRA(("get_segment done vsec=0x%lx a_data=0x%lx "
			"addr=0x%lx\n", 
			vsec, a_data, addr));

		/* Make space for bss and stack unless... */
		if (i != KERNEL_IDX && (k_flags & K_CLAIM)) a_bss= a_stack= 0;

		DEBUGBASIC(("%07lx  %07lx %8ld %8ld %8ld",
			procp->cs, procp->ds, hdr.process.a_text,
			hdr.process.a_data, hdr.process.a_bss));
		if (k_flags & K_CHMEM) DEBUGBASIC((" %8ld", a_stack));

		/* Note that a_data may be negative now, but we can look at it
		 * as -a_data bss bytes.
		 */

		/* Compute the number of bss clicks left. */
		a_bss+= a_data;
		n= align(a_bss, click_size);
		a_bss-= n;

		/* Zero out bss. */
		DEBUGEXTRA(("\nraw_clear(0x%lx, 0x%lx); limit=0x%lx... ", addr, n, limit));
		if (addr + n > limit) { errno= ENOMEM; return; }
		raw_clear(addr, n);
		DEBUGEXTRA(("done\n"));
		addr+= n;

		/* And the number of stack clicks. */
		a_stack+= a_bss;
		n= align(a_stack, click_size);
		a_stack-= n;

		/* Add space for the stack. */
		addr+= n;

		/* Process endpoint. */
		procp->end= addr;

		if (verboseboot >= VERBOSEBOOT_BASIC)
			printf("  %s\n", hdr.name);
		else {
			u32_t mem;
			mem = addr-startaddr;
			printf("%s ", hdr.name);
			totalmem += mem;
		}

		if (i == 0 && (k_flags & (K_HIGH | K_KHIGH)) == K_HIGH) {
			/* Load the rest in extended memory. */
			addr= mem[1].base;
			limit= mem[1].base + mem[1].size;
		}
	}

	if (verboseboot < VERBOSEBOOT_BASIC)
		printf("(%dk)\n", totalmem/1024);

	if ((n_procs= i) == 0) {
		printf("There are no programs in %s\n", image);
		errno= 0;
		return;
	}

	/* Check the kernel magic number. */
	raw_copy(mon2abs(&kmagic), 
		process[KERNEL_IDX].data + MAGIC_OFF, sizeof(kmagic));
	if (kmagic != KERNEL_D_MAGIC) {
		printf("Kernel magic number is incorrect (0x%x@0x%lx)\n", 
			kmagic, process[KERNEL_IDX].data + MAGIC_OFF);
		errno= 0;
		return;
	}

	/* Patch sizes, etc. into kernel data. */
	DEBUGEXTRA(("patch_sizes()... "));
	patch_sizes();
	DEBUGEXTRA(("done\n"));

#if !DOS
	if (!(k_flags & K_MEML)) {
		/* Copy the a.out headers to the old place. */
		raw_copy(HEADERPOS, aout, PROCESS_MAX * A_MINHDR);
	}
#endif

	/* Run the trailer function just before starting Minix. */
	DEBUGEXTRA(("run_trailer()... "));
	if (!run_trailer()) { errno= 0; return; }
	DEBUGEXTRA(("done\n"));

	/* Translate the boot parameters to what Minix likes best. */
	DEBUGEXTRA(("params2params(0x%x, 0x%x)... ", params, sizeof(params)));
	if (!params2params(params, sizeof(params))) { errno= 0; return; }
	DEBUGEXTRA(("done\n"));

	/* Set the video to the required mode. */
	if ((console= b_value("console")) == nil || (mode= a2x(console)) == 0) {
		mode= strcmp(b_value("chrome"), "color") == 0 ? COLOR_MODE :
								MONO_MODE;
	}
	DEBUGEXTRA(("set_mode(%d)... ", mode));
	set_mode(mode);
	DEBUGEXTRA(("done\n"));

	/* Close the disk. */
	DEBUGEXTRA(("dev_close()... "));
	(void) dev_close();
	DEBUGEXTRA(("done\n"));

	/* Minix. */
	DEBUGEXTRA(("minix(0x%lx, 0x%lx, 0x%lx, 0x%x, 0x%x, 0x%lx)\n", 
		process[KERNEL_IDX].entry, process[KERNEL_IDX].cs,
		process[KERNEL_IDX].ds, params, sizeof(params), aout));
	minix(process[KERNEL_IDX].entry, process[KERNEL_IDX].cs,
			process[KERNEL_IDX].ds, params, sizeof(params), aout);

	if (!(k_flags & K_BRET)) {
		extern u32_t reboot_code;
		raw_copy(mon2abs(params), reboot_code, sizeof(params));
	}
	parse_code(params);

	/* Return from Minix.  Things may have changed, so assume nothing. */
	fsok= -1;
	errno= 0;

	/* Read leftover character, if any. */
	scan_keyboard();

	/* Restore screen contents. */
	restore_screen();
}
Example #4
0
void copylink(char *source, char *dest, int mode, int owner, int group)
{
	struct stat sst, dst;
	int sfd, dfd, n;
	int r, same= 0, change= 0, docopy= 1;
	char buf[4096];
#	define hdr ((struct exec *) buf)
	pid_t pid;
	int status;

	/* Source must exist as a plain file, dest may exist as a plain file. */

	if (stat(source, &sst) < 0) { report(source); return; }

	if (mode == -1) {
		mode= sst.st_mode & 07777;
		if (!lflag || cflag) {
			mode|= 0444;
			if (mode & 0111) mode|= 0111;
		}
	}
	if (owner == -1) owner= sst.st_uid;
	if (group == -1) group= sst.st_gid;

	if (!S_ISREG(sst.st_mode)) {
		fprintf(stderr, "install: %s is not a regular file\n", source);
		excode= 1;
		return;
	}
	r= stat(dest, &dst);
	if (r < 0) {
		if (errno != ENOENT) { report(dest); return; }
	} else {
		if (!S_ISREG(dst.st_mode)) {
			fprintf(stderr, "install: %s is not a regular file\n",
									dest);
			excode= 1;
			return;
		}

		/* Are the files the same? */
		if (sst.st_dev == dst.st_dev && sst.st_ino == dst.st_ino) {
			if (!lflag && cflag) {
				fprintf(stderr,
				"install: %s and %s are the same, can't copy\n",
					source, dest);
				excode= 1;
				return;
			}
			same= 1;
		}
	}

	if (lflag && !same) {
		/* Try to link the files. */

		if (r >= 0 && unlink(dest) < 0) {
			report(dest); return;
		}

		if (link(source, dest) >= 0) {
			docopy= 0;
		} else {
			if (!cflag || errno != EXDEV) {
				fprintf(stderr,
					"install: can't link %s to %s: %s\n",
					source, dest, strerror(errno));
				excode= 1;
				return;
			}
		}
	}

	if (docopy && !same) {
		/* Copy the files, stripping if necessary. */
		long count= LONG_MAX;
		int first= 1;

		if ((sfd= open(source, O_RDONLY)) < 0) {
			report(source); return;
		}

		/* Open for write is less simple, its mode may be 444. */
		dfd= open(dest, O_WRONLY|O_CREAT|O_TRUNC, mode | 0600);
		if (dfd < 0 && errno == EACCES) {
			(void) chmod(dest, mode | 0600);
			dfd= open(dest, O_WRONLY|O_TRUNC);
		}
		if (dfd < 0) {
			report(dest);
			close(sfd);
			return;
		}

		pid= 0;
		while (count > 0 && (n= read(sfd, buf, sizeof(buf))) > 0) {
			if (first && n >= A_MINHDR && !BADMAG(*hdr)) {
				if (strip) {
					count= hdr->a_hdrlen
						+ hdr->a_text + hdr->a_data;
#ifdef A_NSYM
					hdr->a_flags &= ~A_NSYM;
#endif
					hdr->a_syms= 0;
				}
				if (stack != -1 && setstack(hdr)) change= 1;

				if (compress != nil) {
					/* Write first #! line. */
					(void) write(dfd, zcat, strlen(zcat));

					/* Put a compressor in between. */
					if ((pid= filter(dfd, compress)) < 0) {
						close(sfd);
						close(dfd);
						return;
					}
					change= 1;
				}
			}
			if (count < n) n= count;

			if (write(dfd, buf, n) < 0) {
				report(dest);
				close(sfd);
				close(dfd);
				if (pid != 0) (void) waitpid(pid, nil, 0);
				return;
			}
			count-= n;
			first= 0;
		}
		if (n < 0) report(source);
		close(sfd);
		close(dfd);
		if (pid != 0 && waitpid(pid, &status, 0) < 0 || status != 0) {
			excode= 1;
			return;
		}
		if (n < 0) return;
	} else {
		if (stack != -1) {
			/* The file has been linked into place.  Set the
			 * stack size.
			 */
			if ((dfd= open(dest, O_RDWR)) < 0) {
				report(dest);
				return;
			}

			if ((n= read(dfd, buf, sizeof(*hdr))) < 0) {
				report(dest); return;
			}

			if (n >= A_MINHDR && !BADMAG(*hdr) && setstack(hdr)) {
				if (lseek(dfd, (off_t) 0, SEEK_SET) == -1
					|| write(dfd, buf, n) < 0
				) {
					report(dest);
					close(dfd);
					return;
				}
				change= 1;
			}
			close(dfd);
		}
	}

	if (stat(dest, &dst) < 0) { report(dest); return; }

	if ((dst.st_mode & 07777) != mode) {
		if (chmod(dest, mode) < 0) { report(dest); return; }
	}
	if (dst.st_uid != owner || dst.st_gid != group) {
		if (chown(dest, owner, group) < 0 && errno != EPERM) {
			report(dest); return;
		}
		/* Set the mode again, chown may have wrecked it. */
		(void) chmod(dest, mode);
	}
	if (!change) {
		struct utimbuf ubuf;

		ubuf.actime= dst.st_atime;
		ubuf.modtime= sst.st_mtime;

		if (utime(dest, &ubuf) < 0 && errno != EPERM) {
			report(dest); return;
		}
	}
}