/* $Id: rospeed.C,v 1.4 2004/02/12 02:51:06 fubob Exp $ */

/* Use this program to benchmark the speed of crypto on
   typical SFSRO structures (SHA1 time, SHA1 of inode time, etc).
*/

/*
 *
 * Copyright (C) 2000 Kevin Fu (fubob@mit.edu)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "sfsrodb.h"

bool opaque_directory = false;

u_int64_t start_tm, end_tm, duration_tm, i;  

char IV[SFSRO_IVSIZE];
sfs_hash fh;
struct itimerval itv;
size_t calllen;
char *callbuf;
sfsro1_signed_fsinfo info;
char eightkbuf[8192];
char shabuf[20];

inline u_int64_t
get_time ()
{
  timeval tv;
  gettimeofday (&tv, NULL);
  return (u_int64_t) tv.tv_sec * 1000000 + tv.tv_usec;
}


void
handler_sha (int foo)
{
  end_tm = get_time();

  warn << "SHA1 on 8KB blocks\n";
  warn << "Time spent:      " << end_tm - start_tm << " usec\n";
  warn << "Hash/sec: " << i*1000000/(end_tm - start_tm) << "\n\n";
  exit (0);
}

void
start_sha ()
{ 
  signal (SIGALRM, handler_sha);
  signal (SIGINT,  handler_sha);
  signal (SIGTERM, handler_sha);

  
  start_tm = get_time();
  for (i=0; 1; i++) {
    sha1_hash (shabuf, eightkbuf, 8192);
  }
}

void
handler_sigverify (int foo)
{
  end_tm = get_time();

  warn << "SFSRO sig verification\n (RW) of typical fsinfo\n";
  warn << "Time spent:      " << end_tm - start_tm << " usec\n";
  warn << "Sig Verification/sec: " << i*1000000/(end_tm - start_tm) << "\n\n";
  exit (0);
}

void
start_sigverify ()
{ 
  signal (SIGALRM, handler_sigverify);
  signal (SIGINT,  handler_sigverify);
  signal (SIGTERM, handler_sigverify);

  sfs_sig sig;
  ptr<rabin_priv> sk;

  str seckeyfile = "/disk/pu0/fubob/inplace/sfsro/server/rodb/sfs_host_key";

  str key = file2wstr (seckeyfile);
  if (!key) {
    warn << seckeyfile << ": " << strerror (errno) << "\n";
    fatal ("errors!\n");
  }
  else if (!(sk = import_rabin_priv (key, NULL))) {
    warn << "could not decode " << seckeyfile << "\n";
    warn << key << "\n";
    fatal ("errors!\n");
  }

  sig = sk->sign (str ((char *) &info, sizeof (info)));

  start_tm = get_time();
  for (i=0; 1; i++) {
    sfscrypt.verify (sk->n, sig, xdr2str (info));
  }
 
}


void
handler_sigcreate (int foo)
{
  end_tm = get_time();

  warn << "SFSRO sig creation\n (RW) of typical fsinfo\n";
  warn << "Time spent:      " << end_tm - start_tm << " usec\n";
  warn << "Sig Creation/sec: " << i*1000000/(end_tm - start_tm) << "\n\n";
  exit (0);
}

void
start_sigcreate ()
{ 
  signal (SIGALRM, handler_sigcreate);
  signal (SIGINT,  handler_sigcreate);
  signal (SIGTERM, handler_sigcreate);

  sfs_sig sig;
  ptr<rabin_priv> sk;

  str seckeyfile = "/disk/pu0/fubob/inplace/sfsro/server/rodb/sfs_host_key";

  str key = file2wstr (seckeyfile);
  if (!key) {
    warn << seckeyfile << ": " << strerror (errno) << "\n";
    fatal ("errors!\n");
  }
  else if (!(sk = import_rabin_priv (key, NULL))) {
    warn << "could not decode " << seckeyfile << "\n";
    warn << key << "\n";
    fatal ("errors!\n");
  }

  start_tm = get_time();
  for (i=0; 1; i++) {
    sig = sk->sign (str ((char *) &info, sizeof (info)));
  } 
}


void
handler_fhverify (int foo)
{
  end_tm = get_time();

  warn << "SFSRO file handle verification\n (SHA1 hash) of typical inode w/o indirect blocks\n";
  warn << "Time spent:      " << end_tm - start_tm << " usec\n";
  warn << "FH Verification/sec: " << i*1000000/(end_tm - start_tm) << "\n\n";
  exit (0);
}


void
start_fhverify ()
{ 
  signal (SIGALRM, handler_fhverify);
  signal (SIGINT,  handler_fhverify);
  signal (SIGTERM, handler_fhverify);

  create_sfsrofh (IV, SFSRO_IVSIZE,
		  &fh, callbuf, calllen);

  start_tm = get_time();
  for (i=0; 1; i++) {
    verify_sfsrofh (IV, SFSRO_IVSIZE, &fh, callbuf, calllen);
  }
}

void
handler_fhcreate (int foo)
{
  end_tm = get_time();

  warn << "SFSRO file handle creation\n (SHA1 hash) of typical inode w/o indirect blocks\n";
  warn << "Time spent:      " << end_tm - start_tm << " usec\n";
  warn << "FH Creation/sec: " << i*1000000/(end_tm - start_tm) << "\n\n";


  exit (0);
}

void
start_fhcreate () 
{
  signal (SIGALRM, handler_fhcreate);
  signal (SIGINT,  handler_fhcreate);
  signal (SIGTERM, handler_fhcreate);

  start_tm = get_time();
  for (i=0; 1; i++) {
    create_sfsrofh (IV, SFSRO_IVSIZE,
		    &fh, callbuf, calllen);
  }
}


void
sfsrodb_setinode (const struct stat *st, sfsro_inode *inode)
{

  /*
     SFSRO has implied read-access by all.  We only care
     whether the file is a non-directory executable.  Everything
     else is synthesised by sfsrocd.

   */

  ftypero t;
  if (S_ISREG (st->st_mode)) {
    t = ((st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ?
		 SFSROREG_EXEC : SFSROREG);
  }
  else if (S_ISDIR (st->st_mode))
    t = opaque_directory ? SFSRODIR_OPAQ : SFSRODIR;
  else if (S_ISLNK (st->st_mode))
    t = SFSROLNK;
  else {
    warn << "Non-supported file type " << st->st_mode << "\n";
    exit (1);
  }

  inode->set_type (t);

  if (inode->type == SFSROLNK) {
    rpc_clear (*inode->lnk);

    inode->lnk->nlink = st->st_nlink; // XXX bogus! cannot rely on this number

#ifdef SFS_HAVE_STAT_ST_ATIMESPEC
    inode->lnk->mtime.seconds = st->st_mtimespec.tv_sec;
    inode->lnk->mtime.nseconds = st->st_mtimespec.tv_nsec;
    inode->lnk->ctime.seconds = st->st_ctimespec.tv_sec;
    inode->lnk->ctime.nseconds = st->st_ctimespec.tv_nsec;
#else
    inode->lnk->mtime.seconds = st->st_mtime;
    inode->lnk->mtime.nseconds = 0;
    inode->lnk->ctime.seconds = st->st_ctime;
    inode->lnk->ctime.nseconds = 0;
#endif /* SFS_HAVE_ST_ATIMESPEC */

  } else {
    rpc_clear (*inode->reg);

    inode->reg->nlink = st->st_nlink;  // XXX bogus! cannot rely on this number
    inode->reg->size = 0;
    inode->reg->used = 0;
    
#ifdef SFS_HAVE_STAT_ST_ATIMESPEC
    inode->reg->mtime.seconds = st->st_mtimespec.tv_sec;
    inode->reg->mtime.nseconds = st->st_mtimespec.tv_nsec;
    inode->reg->ctime.seconds = st->st_ctimespec.tv_sec;
    inode->reg->ctime.nseconds = st->st_ctimespec.tv_nsec;
#else
    inode->reg->mtime.seconds = st->st_mtime;
    inode->reg->mtime.nseconds = 0;
    inode->reg->ctime.seconds = st->st_ctime;
    inode->reg->ctime.nseconds = 0;
#endif /* SFS_HAVE_ST_ATIMESPEC */

    // Special for benchmark
    inode->reg->direct.setsize (SFSRO_NDIR);
    bzero (inode->reg->direct.base (), SFSRO_NDIR * sizeof (sfs_hash));

  }

}

int
main (int argc, char **argv)
{
  setprogname (argv[0]);

  if (argc != 3) {
    warn << "Usage: " << argv[0] << " <duration in sec> <which test>\n";
    warn << "0 == fh creation\n";
    warn << "1 == fh verification\n";
    warn << "2 == signature creation\n";
    warn << "3 == signature verification\n";
    warn << "4 == SHA1 on 8KB blocks\n";
    exit (-1);

  }

  duration_tm = atoi (argv[1]);
  char *path = argv[0];

  memset (IV, 0xCA, SFSRO_IVSIZE);

  memset (eightkbuf, 0xFE, 8192);

  sfsro_data dat (SFSRO_INODE);
  calllen = 0;
  callbuf = NULL;
  xdrsuio x (XDR_ENCODE);

  struct stat st;
  if (lstat (path, &st) < 0) {
    warn << "lstat failed on " << path << "\n";
    exit (-1);
  }
  sfsro_inode inode;
  sfsrodb_setinode (&st, &inode);

  *dat.inode = inode;
  if (xdr_sfsro_data (x.xdrp (), &dat)) {
    calllen = x.uio ()->resid ();
    callbuf = suio_flatten (x.uio ());
  }

  // fake fsinfo
  info.type = SFS_ROFSINFO;
  info.start = time(NULL);
  info.duration = 86400;
  memcpy (info.iv.base (), &IV[0], SFSRO_IVSIZE);
  create_sfsrofh (IV, SFSRO_IVSIZE,
		  &fh, callbuf, calllen);
  info.rootfh =  info.fhdb = fh ;

  // Done with setup

  bzero (&itv, sizeof (itv));
  itv.it_value.tv_sec  = duration_tm;
  int err;
  if ((err = setitimer (ITIMER_REAL, &itv, NULL)) != 0) {
    warn << "setitimer failed " << strerror (errno) << "\n";
    exit (-1);
  }

  switch (atoi (argv[2])) {
  case 0: 
    start_fhcreate ();
    break;
  case 1:
    start_fhverify ();
    break;
  case 2:
    start_sigcreate ();
    break;
  case 3:
    start_sigverify ();
    break;
  case 4:
    start_sha ();
    break;
  }

}
