/*
 * nasd_in.c
 *
 * Load data into a NASD drive
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_getopt.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_security.h>
#include <nasd/nasd_mem.h>

char *progname;

int                        binding_type;
int                        binding_args_len;
void                      *binding_args;
char                      *binding_port = NASD_PDRIVE_PORT;
nasd_drive_param_kpdev_t   kpdev_args;
int                        nondefault_binding = 0;
nasd_error_string_t error_text;
int partnum;

nasd_uint16                 protection;
int                         verbose;
nasd_drive_handle_t         h;
char                       *master_password;
nasd_error_string_t         err_str;
nasd_sec_keyring_t          keys;

#define DEFAULT_BUFLEN 131072
char *buf;
int buflen = DEFAULT_BUFLEN;

void
usage()
{
  int i;
  fprintf(stderr, "USAGE: %s [options] servername partnum master_password filename [filename [filename...]]\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -b buflen [default %d]\n", DEFAULT_BUFLEN);
  fprintf(stderr, "  -k use kernel device\n");
  fprintf(stderr, "  -M use message queues\n");
  fprintf(stderr, "  -s sec_level\n");
  fprintf(stderr, "  where sec_level is one of\n");
  for(i = 0; i <= NASD_MAX_SECURITY_LEVEL; i++) {
    fprintf(stderr, "     %d %s\n", i, nasd_sec_level_string(i));
  }
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
  fprintf(stderr, "  -T use DCE-TCP\n");
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
  fprintf(stderr, "  -v verbose\n");
  fflush(stderr);
  exit(1);
}

void
do_file(
  char  *filename)
{
  int no_close, fd, got, want, rc;
  nasd_rpc_status_t status;
  nasd_identifier_t nid;
  nasd_uint64 total_len;
  nasd_offset_t offset;
  nasd_timespec_t tm;
  char *say_filename;
  nasd_security_param_t sec_param;
  nasd_p_create_dr_args_t     cr_args;
  nasd_p_create_dr_res_t      cr_res;
  nasd_p_smpl_op_dr_args_t    wr_args;
  nasd_p_fastwrite_dr_res_t   wr_res;
  nasd_cookie_t               wr_cookie;

  total_len = 0;

  bzero((char *)&wr_args, sizeof(wr_args));
  bzero((char *)&cr_args, sizeof(cr_args));

  sec_param.type = NASD_BLACK_KEY;
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;

  cr_args.in_partnum = partnum;
  cr_args.in_fieldmask = NASD_ATTR_AV;
  cr_args.in_attribute.av = 7;

  /*
   * Open file
   */

  no_close = 0;
  if ((filename[0] == '-') && (filename[1] == '\0')) {
    no_close = 1;
    fd = fileno(stdin);
    say_filename = "stdin";
  }
  else {
    fd = open(filename, O_RDONLY);
    if (fd < 0) {
      perror("open");
      fprintf(stderr, "ERROR: could not open %s for reading\n", filename);
      fflush(stderr);
      exit(1);
    }
    say_filename = filename;
  }

  if (verbose) {
    printf("\nBegin processing %s\n", say_filename);
  }

  /*
   * Create NASD object
   */
  nasd_cl_p_create_dr(h, keys.black_key, &sec_param, NULL, &cr_args,
                      &cr_res, &status);

  if (status) {
    fprintf(stderr, "ERROR: creating object for %s, "
      "status=0x%x (%s) nasd_status=0x%x (%s)\n",
      say_filename,
      status, nasd_cl_error_string(h, status, err_str),
      cr_res.nasd_status, nasd_error_string(cr_res.nasd_status));
    fflush(stderr);
    exit(1);
  }

  if (cr_res.nasd_status) {
    fprintf(stderr, "ERROR: creating object for %s, "
      "nasd_status=0x%x (%s)\n",
      say_filename,
      cr_res.nasd_status, nasd_error_string(cr_res.nasd_status));
    fflush(stderr);
    exit(1);
  }

  nid = cr_res.out_identifier;

  if (verbose) {
    printf("New object is 0x%" NASD_ID_FMT "\n", nid);
  }

  wr_args.in_partnum = partnum;
  wr_args.in_identifier = nid;

  /* build the capability -- normally the file manager would do this. */
  nasd_drive_handle_get_time(h, &tm);
  tm.ts_sec += (60*60);
  nasd_sec_build_capability(wr_args.in_partnum,
                            wr_args.in_identifier, NASD_ACCESS_RIGHTS_WRITE,
                            0, tm.ts_sec, protection, NASD_BLACK_CAPABILITY,
                            0, 0, 7, keys.black_key, &wr_cookie);

  sec_param.type = NASD_BLACK_CAPABILITY;
  sec_param.partnum = wr_args.in_partnum;
  sec_param.actual_protection = protection;

  /*
   * Loop, reading file and writing into drive
   */
  offset = 0;
  do {
    /*
     * Read a chunk
     */
    rc = read(fd, buf, buflen);

    if (rc < 0) {
      perror("read");
      fprintf(stderr, "ERROR: could not read %s\n",
        say_filename);
      fflush(stderr);
      exit(1);
    }

    if (rc == 0)
      break;

    want = rc;

    if (verbose) {
      printf("read %d bytes from %s\n", want, say_filename);
    }

    /*
     * Write the chunk to the drive
     */
    for(got=0;got<want;) {
      wr_args.in_offset = offset;
      wr_args.in_len = want - got;

      nasd_cl_p_write_simple_dr(h, wr_cookie.key, &sec_param,
                                &wr_cookie.capability,
                                &wr_args, &buf[got], &wr_res, &status);

      if (status) {
        fprintf(stderr, "ERROR: writing 0x%" NASD_ID_FMT " from %s"
          ", status=0x%x (%s) nasd_status=0x%x (%s)\n",
          nid, say_filename,
          status, nasd_cl_error_string(h, status, err_str),
          wr_res.nasd_status, nasd_error_string(wr_res.nasd_status));
        fflush(stderr);
        exit(1);
      }

      if (wr_res.nasd_status) {
        fprintf(stderr, "ERROR: writing 0x%" NASD_ID_FMT " from %s"
          ", nasd_status=0x%x (%s)\n",
          nid, say_filename,
          wr_res.nasd_status, nasd_error_string(wr_res.nasd_status));
        fflush(stderr);
        exit(1);
      }

      if (verbose) {
        printf("wrote %d bytes to 0x%" NASD_ID_FMT "\n",
          wr_res.out_datalen, nid);
      }

      got += wr_res.out_datalen;
      offset += wr_res.out_datalen;
      total_len += wr_res.out_datalen;
    }

  } while(1);

  /*
   * Close file
   */

  if (no_close == 0) {
    close(fd);
  }

  printf("Wrote %s to 0x%" NASD_ID_FMT " (%" NASD_64u_FMT " total bytes)\n",
    say_filename, nid, total_len);
}

int
main(
  int     argc,
  char  **argv)
{
  char *server_name;
  nasd_status_t rc;
  char c;
  int i;
  int sec_level = 0;

  progname = argv[0];

  binding_type = NASD_BIND_DEFAULT;
  binding_args = NULL;
  binding_args_len = 0;

  while (nasd_getopt(argc, argv, "b:kMTs:v", &c)) {
    switch(c) {
      case 'b':
        if (sscanf(nasd_optarg, "%d", &buflen) != 1)
          usage();
        if (buflen < 1)
          usage();
        break;
      case 'k':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_KPDEV_DEFAULT;
        binding_args = &kpdev_args;
        binding_args_len = sizeof(kpdev_args);
        strcpy(kpdev_args.devname, "/dev/nasdkp0");
        break;
      case 'M':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_MSGQ;
        break;
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
      case 'T':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_DCE_DIRECT_TCP;
        break;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
      case 's':
        sec_level = atoi(nasd_optarg);
        break;
      case 'v':
        verbose = 1;
        break;
      default:
        fprintf(stderr, "Unknown option '%c'\n", nasd_optopt);
        usage();
    }
  }

  if (nasd_optind >= argc)
    usage();
  server_name = argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind >= argc)
    usage();
  if (sscanf(argv[nasd_optind], "%d", &partnum) != 1)
    usage();
  nasd_optind++;

  if (nasd_optind >= argc)
    usage();
  master_password=argv[nasd_optind];
  nasd_optind++;

  if (nasd_optind >= argc)
    usage();

  rc = nasd_sec_seclevel_to_protection(sec_level, &protection);
  if(rc) {
    fprintf(stderr, "ERROR: invalid security level %d\n", sec_level);
    usage();
  }

  nasd_sec_password_to_keys(master_password, partnum, &keys);

  rc = nasd_cl_p_init();
  if (rc) {
    fprintf(stderr, "ERROR: cannot init client library, rc=0x%x (%s)\n",
      rc, nasd_error_string(rc));
    exit(1);
  }

  NASD_Malloc(buf, buflen, (char *));
  if (buf == NULL) {
    fprintf(stderr, "ERROR: could not allocate %d bytes for buffer\n",
      buflen);
    fflush(stderr);
    exit(1);
  }

  rc = nasd_bind_to_drive(server_name, binding_port,
    binding_type, binding_args, binding_args_len, &h);
  if (rc) {
    fprintf(stderr, "ERROR: cannot bind to server %s\n", server_name);
    fflush(stderr);
    exit(1);
  }

  for(i=nasd_optind;i<argc;i++) {
    do_file(argv[i]);
  }

  NASD_Free(buf, buflen);

  rc = nasd_unbind_drive(&h);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_unbind_drive()\n",
      rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }

  nasd_cl_p_shutdown();

  exit(0);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
