/* 
 * Copyright (c) Secure Software Solutions, 2002.
 * Licenced under the GNU Public License
 */

#define RAND_PASS(fd, sz, err) if(random_pass(fd, sz, err)) { \
                                  errno = saved_errno; fclose(randfile); \
                                  *err = 3; return; }
#define PATT_PASS(fd, pt, l, s) if(pattern_pass(fd, pt, l, s)) {\
                                  errno = saved_errno; fclose(randfile); \
                                  *err = 4; return; }

#define BUFSIZE 4096

void randbuf(unsigned char * buf, size_t sz, int *error)
{
  /* Hook up to /dev/urandom or another cryptographically strong
   * PRNG seeded with sufficient entroy!
   */
#error "You must implement randbuf()"
}

static int
random_pass(int fd, int sz, int *err)
{
  int             i;
  char            buf[BUFSIZE];
  int		  error;

  if (lseek(fd, 0, SEEK_SET) != 0)
  {
    return -1;
  }
  for (i = 0; i < sz / BUFSIZE; i++)
  {
    randbuf(buf, BUFSIZE, &error);

    while (write(fd, buf, BUFSIZE) == -1)
    {
      if (errno != EINTR)
      {
	return -1;
      }
    }
  }
  randbuf(buf, sz % BUFSIZE, &error);

  while (write(fd, buf, sz % BUFSIZE) == -1)
  {
    if (errno != EINTR)
    {
      return -1;
    }
  }
  return 0;
}

/* num is expected to be non-zero. */
static int
pattern_pass(int fd, char *s, unsigned int num, int fsz)
{
  int             i;

  if (lseek(fd, 0, SEEK_SET) != 0)
  {
    return -1;
  }
  for (i = 0; i < fsz / num; i++)
  {
  write:
    switch (write(fd, s, num))
    {
    case -1:
      if (errno == EINTR)
      {
	goto write;
      }
      return -1;
    case 0:
      return 0;
    }
  }
  for (i = 0; i < fsz % num; i++)
  {
    while (write(fd, &s[i], 1) == -1)
    {
      if (errno != EINTR)
      {
	return -1;
      }
    }
  }
  return 0;
}

void
fd_wipe(int fd, int *err)
{
  static char     single_pats[] = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
    0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
    0xee, 0xff
  };
  static char     pat1[] = { 0x92, 0x49, 0x24 };
  static char     pat2[] = { 0x49, 0x24, 0x92 };
  static char     pat3[] = { 0x24, 0x92, 0x49 };
  static char     pat4[] = { 0x6d, 0xb6, 0xdb };
  static char     pat5[] = { 0xb6, 0xdb, 0x6d };
  static char     pat6[] = { 0xdb, 0x6d, 0xb6 };
  int             i, fsz, saved_errno;
  FILE           *randfile;

  *err = 0;
  
  if (*err)
  {
    return;
  }
  saved_errno = errno;

  fsz = lseek(fd, 0, SEEK_END);
  switch (fsz)
  {
  case -1:
    errno = saved_errno;
    fclose(randfile);
    *err = 2;
    return;
  case 0:
    fclose(randfile);
    return;
  }

  for (i = 0; i < 4; i++)
  {
    RAND_PASS(fd, fsz, err);
  }
  PATT_PASS(fd, &(single_pats[5]), 1, fsz);
  PATT_PASS(fd, &(single_pats[10]), 1, fsz);
  PATT_PASS(fd, pat1, 3, fsz);
  PATT_PASS(fd, pat2, 3, fsz);
  PATT_PASS(fd, pat3, 3, fsz);
  for (i = 0; i < sizeof(single_pats); i++)
  {
    PATT_PASS(fd, &single_pats[i], 1, fsz);
  }
  PATT_PASS(fd, pat1, 3, fsz);
  PATT_PASS(fd, pat2, 3, fsz);
  PATT_PASS(fd, pat3, 3, fsz);
  PATT_PASS(fd, pat4, 3, fsz);
  PATT_PASS(fd, pat5, 3, fsz);
  PATT_PASS(fd, pat6, 3, fsz);
  for (i = 0; i < 4; i++)
  {
    RAND_PASS(fd, fsz, err);
  }
  fclose(randfile);
  return;
}

void
file_wipe(FILE * f, int *err)
{
  return fd_wipe(fileno(f), err);
}
