view insertsums/insertsums.c @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
line wrap: on
line source

/*
 * AnaGram, A System for Syntax Directed Programming
 * Copyright 2006 David A. Holland. All Rights Reserved.
 * See the file COPYING for license and usage terms.
 */

/*
 * insertsums - patch checksum data block into binary
 * usage: insertsums sums.dat sums.ctl
 *
 * The patch area is a region of size TARGETSIZE that the
 * checksum program finds for us. We check to make sure it
 * contains the right magic string before patching over it,
 * just in case.
 *
 * Everything is XOR'd with PADBYTE as something of a paranoia
 * measure against viruses.
 *
 * XXX the definitions of TARGETSIZE and TARGETLABEL should 
 * be shared with the AG code. PADBYTE too.
 *
 * This module is new code as of June 2006 and should be much more
 * portable than the old stuff.
 */

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifndef O_BINARY
#define O_BINARY 0
#define O_TEXT   0
#endif

#define TARGETSIZE	512
#define TARGETLABEL	"Checksum data:\n"
#define PADBYTE		0xae


static char info[TARGETSIZE];
static const char *me;

static const char label[] = TARGETLABEL;

////////////////////////////////////////////////////////////

static void setme(const char *av0) {
  me = strrchr(av0, '/');
  if (me) {
    me++;
  }
  else {
    me = av0;
  }
}

static void die(const char *fmt, ...) {
  va_list ap;
  fprintf(stderr, "%s: ", me);
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  fputc('\n', stderr);
  exit(EXIT_FAILURE);
}

////////////////////////////////////////////////////////////

static int isnumber(const char *s) {
  size_t i;
  for (i=0; s[i]; i++) {
    if (!isdigit((unsigned char) s[i])) {
      return 0;
    }
  }
  // protect against possible overflow, just in case
  return i<10;
}

////////////////////////////////////////////////////////////

static void putsums(const char *file, long offset) {
  char buf[TARGETSIZE];
  ssize_t len;
  int fd;

  fd = open(file, O_RDWR);
  if (fd<0) {
    die("%s: %s", file, strerror(errno));
  }
  if (lseek(fd, offset, SEEK_SET)<0) {
    die("%s: lseek: %s", file, strerror(errno));
  }
  len = read(fd, buf, sizeof(buf));
  if (len < 0) {
    die("%s: read: %s", file, strerror(errno));
  }
  if ((size_t)len != TARGETSIZE) {
    die("%s: read: short count (past EOF?)", file);
  }

  if (memcmp(buf, label, strlen(label))!=0) {
    die("%s: patch area label is missing", file);
  }

  if (lseek(fd, offset, SEEK_SET)<0) {
    die("%s: lseek: %s", file, strerror(errno));
  }
  len = write(fd, info, sizeof(info));
  if (len < 0) {
    die("%s: write: %s", file, strerror(errno));
  }
  if ((size_t)len != TARGETSIZE) {
    die("%s: write: short count", file);
  }
  if (close(fd)<0) {
    die("%s: close: %s", file, strerror(errno));
  }
  //printf("%s: patched %s\n", me, file);
}

static void storesums(const char *ctlfile) {
  char buf[128];
  FILE *f;
  int lineno = 0;

  f = fopen(ctlfile, "rt");
  if (!f) {
    die("%s: fopen failed", ctlfile);
  }

  while (fgets(buf, sizeof(buf), f)) {
    /* format is: pathname offset */
    char *words[3], *s;
    int nwords = 0;
    lineno++;
    if (*buf=='#') {
      continue;
    }
    for (s = strtok(buf, " \t\r\n"); s; s = strtok(NULL, " \t\r\n")) {
      if (nwords >= 3) {
	break;
      }
      words[nwords++] = s;
    }
    if (nwords != 2 || !isnumber(words[1])) {
      die("%s: Invalid line %d", ctlfile, lineno);
    }
    putsums(words[0], atol(words[1]));
  }
  if (ferror(f)) {
    die("%s: read error", ctlfile);
  }
  fclose(f);
}

////////////////////////////////////////////////////////////

static void hidesums(void) {
  size_t i;
  for (i=strlen(label); i<sizeof(info); i++) {
    info[i] = (char) (PADBYTE ^ (unsigned char)info[i]);
  }
}

////////////////////////////////////////////////////////////

static void loadsums(const char *sumpath) {
  ssize_t readlen;
  size_t loadpos, maxlen, infolen;
  int fd;

  assert(strlen(label) < TARGETSIZE);
  strcpy(info, label);
  loadpos = strlen(info);
  maxlen = sizeof(info) - loadpos;

  fd = open(sumpath, O_TEXT|O_RDONLY);
  if (fd < 0) {
    die("%s: %s", sumpath, strerror(errno));
  }

  readlen = read(fd, info+loadpos, maxlen);
  if (readlen < 0) {
    die("%s: read: %s", sumpath, strerror(errno));
  }
  if (readlen == 0) {
    die("%s: read: empty file?", sumpath);
  }
  close(fd);

  infolen = loadpos+readlen;
  if (infolen >= sizeof(info)) {
    die("%s: Too much information!", sumpath); /* :-) */
  }

  //printf("%s: %lu bytes prepared\n", me, (unsigned long) infolen);

  while (infolen < sizeof(info)) {
    info[infolen++] = 0;
  }
}

////////////////////////////////////////////////////////////

static void usage(void) {
  die("Usage: %s sums.dat sums.ctl", me);
}

int main(int argc, char *argv[]) {
  setme(argv[0]);
  if (argc != 3) {
    usage();
  }

  loadsums(argv[1]);
  hidesums();
  storesums(argv[2]);

  return EXIT_SUCCESS;
}