/* makehole.c - original by HJ Lu */

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <a.out.h>

#define	BUFSIZE	1024
#undef DEBUG

void usage(char *name, char *message)
{
    if (message)
	fprintf(stderr, "%s: %s\n", name, message);

    if (errno)
	perror(name);

    fprintf(stderr, "Usage:%s Imagefile\n", name);
    exit(1);
}

int ishole(char *buf, int size)
{
    int i;

    for (i = 0; i < size; i++)
	if (buf[i])
	    return 0;

    return 1;
}

void main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    char *tmp_file;
    int fdin, fdout;
    int ret;
    int abs_offset;
    int hole;
    struct exec *header = (struct exec *) buf;

#ifndef DEBUG
    if (geteuid()) {
	fprintf(stderr, "%s: must be root to run!\n", *argv);
	exit(1);
    }
#endif

    switch (argc) {
    case 2:
	break;
    default:
	usage(*argv, NULL);
    }

    errno = 0;

    tmp_file = tempnam(getwd("."), "hole");
    if (tmp_file == NULL) {
	usage(*argv, "Unable to get a temparory image filename!");
    }
#ifdef DEBUG
    else {
	fprintf(stderr, "Temparory image file: %s\n", tmp_file);
    }
#endif

    errno = 0;
    fdin = open(argv[1], O_RDONLY);
    if (fdin == -1) {
	usage(*argv, "unable to open file.");
    }
    fprintf(stderr, "Making holes in %s...\n", argv[1]);

    errno = 0;

    if ((ret = read(fdin, header, BUFSIZE)) != BUFSIZE
	|| N_MAGIC(*header) != ZMAGIC) {
	usage(*argv, "file must be pure executable.");
    }

    fdout = creat(tmp_file, 0555);
    if (fdout == -1) {
	perror("Unable to create the temparory image file!");
	exit(1);
    }
    if (write(fdout, header, ret) != ret) {
	perror("Fail to write header to the temparory image file!");
	unlink(tmp_file);
	exit(1);
    }
    abs_offset = ret;
    hole = 0;
    while ((ret = read(fdin, buf, BUFSIZE)) > 0) {
	abs_offset += ret;
	if (ishole(buf, ret)) {
#ifdef DEBUG
	    fprintf(stderr, "There is a %d byte hole from 0x%x to 0x%x.\n", ret, abs_offset - ret, abs_offset);
#endif
	    hole += ret;
	    if (lseek(fdout, abs_offset, SEEK_SET) != abs_offset) {
		perror("Fail to make a hole in the temparory image file!");
		unlink(tmp_file);
		exit(1);
	    }
	} else {
#ifdef DEBUG
	    fprintf(stderr, "Writing %d bytes from 0x%x to 0x%x.\n", ret, abs_offset - ret, abs_offset);
#endif
	    if (write(fdout, buf, ret) != ret) {
		perror("Fail to write the temparory image file!");
		unlink(tmp_file);
		exit(1);
	    }
	}
    }

    if (ftruncate(fdout, abs_offset)) {
	perror("Fail to truncate the temparory image file!");
	unlink(tmp_file);
	exit(1);
    }
    close(fdout);
    close(fdin);

    if (rename(tmp_file, argv[1])) {
	perror("Fail to rename the temparory image file to the old image file!");
	unlink(tmp_file);
	exit(1);
    }
    fprintf(stderr, "There are %d byte holes out of %d bytes in `%s'.\n", hole, abs_offset, argv[1]);
}
