/*
 * preadwrite.c
 *
 * Read/write functionality tests for NASD drive
 * (also does setattr/getattr/create testing)
 *
 * (So really, this is the ubertester! Bow before it!)
 *
 * Author: Jim Zelenka
 * (Okay, you don't have to bow if you don't want to.)
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,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.
 */
/*
 * I changed my mind. Bow!
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_common.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_od.h> /* slurp in layout info */
#include <nasd/nasd_mem.h>

/*
 * !!! This test kind of assumes that it's running on a client with
 * 64-bit longs.
 */

char        *progname;

int          docreate = 0;
int          quiet_mode=0;


nasd_error_string_t error_text;
nasd_uint16 protection;
nasd_security_param_t  sec_param;
nasd_drive_handle_t    h;
nasd_identifier_t      nasdid;
char                  *server_name;
int                    partnum;
nasd_rpc_status_t      status;
nasd_cookie_t          cookie;
nasd_len_t             out_len;
nasd_status_t          rc;
int                    i, j, k, bad;
nasd_sec_keyring_t     keys;

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;

char *master_password;

#define LVL0_OFF_INC (NASD_OD_LVL0_POINTED*NASD_OD_BASIC_BLOCKSIZE)
#define LVL1_OFF_INC (NASD_OD_LVL1_POINTED*NASD_OD_BASIC_BLOCKSIZE)
#define LVL2_OFF_INC (NASD_OD_LVL2_POINTED*NASD_OD_BASIC_BLOCKSIZE)
#define LVL3_OFF_INC (NASD_OD_LVL3_POINTED*NASD_OD_BASIC_BLOCKSIZE)
#define LVL4_OFF_INC (NASD_OD_LVL4_POINTED*NASD_OD_BASIC_BLOCKSIZE)

nasd_offset_t lvl1_off = (LVL0_OFF_INC);
nasd_offset_t lvl2_off = (LVL0_OFF_INC+LVL1_OFF_INC);
nasd_offset_t lvl3_off = (LVL0_OFF_INC+LVL1_OFF_INC+LVL2_OFF_INC);
nasd_offset_t lvl4_off = (LVL0_OFF_INC+LVL1_OFF_INC+LVL2_OFF_INC+LVL3_OFF_INC);

#define BUFLEN  1024*1024
#define LBUFLEN BUFLEN/8
char           *buf;
nasd_uint64  *lbuf;
char           *buf2;
nasd_uint64  *lbuf2;
char           *buf3;
nasd_uint64  *lbuf3;
char           *buf4;
nasd_uint64  *lbuf4;
char           *buf5;
nasd_uint64  *lbuf5;

/* For security */
nasd_timespec_t tm;
/* timespec not equal */
#define TS_NEQ(_a_,_b_) (((_a_).ts_sec != (_b_).ts_sec) || ((_a_).ts_nsec != (_b_).ts_nsec))



void
do_getattr(
  int  docheck)
{
  nasd_p_getattr_dr_args_t args;
  nasd_p_getattr_dr_res_t res;
  nasd_rpc_status_t status;
  nasd_attribute_t *a;

  args.in_identifier = nasdid;
  args.in_partnum = partnum;

  nasd_cl_p_getattr_dr(h, cookie.key, &sec_param, &cookie.capability,
                       &args, &res, &status);
  rc = res.nasd_status;

  if (res.nasd_status || status) {
    fprintf(stderr, "GETATTR nasd_status=0x%x (%s) status=0x%x (%s)\n",
      res.nasd_status, nasd_error_string(res.nasd_status),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }
  if (!quiet_mode || rc || status)
    nasd_printstat(&res.out_attribute);

  if (docheck == 0)
    return;

  a = &res.out_attribute;
  if (TS_NEQ(a->object_create_time,a->object_modify_time)
    || TS_NEQ(a->object_create_time,a->attr_modify_time))
  {
    fprintf(stderr, "\nERROR\n");
    fprintf(stderr, "Object appears to have been modified after creation.\n");
    fprintf(stderr, "These tests require a newly-created object\n");
    exit(1);
  }

  if ((docreate == 0) && (a->object_len)) {
    fprintf(stderr, "\nERROR\n");
    fprintf(stderr, "Object appears to be non-zero length.\n");
    fprintf(stderr, "These tests require a zero-length object.\n");
    exit(1);
  }
}

void
do_write(
  nasd_identifier_t   nasdid,
  nasd_byte_t        *buf,
  nasd_offset_t       off,
  nasd_len_t          in_len,
  nasd_len_t         *out_len,
  nasd_status_t      *nasd_status,
  nasd_rpc_status_t  *op_status)
{
  nasd_p_smpl_op_dr_args_t wr_args;
  nasd_p_fastwrite_dr_res_t wr_res;

  wr_args.in_partnum = partnum;
  wr_args.in_identifier = nasdid;
  wr_args.in_offset = off;
  wr_args.in_len = in_len;
  nasd_cl_p_write_simple_dr(h, cookie.key, &sec_param, &cookie.capability,
                            &wr_args, buf, &wr_res, op_status);
  *out_len = wr_res.out_datalen;
  *nasd_status = wr_res.nasd_status;
}

void
do_read(
  nasd_identifier_t   nasdid,
  nasd_byte_t        *buf,
  nasd_offset_t       off,
  nasd_len_t          in_len,
  nasd_len_t         *out_len,
  nasd_status_t      *nasd_status,
  nasd_rpc_status_t  *op_status)
{
  nasd_p_smpl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;

  rd_args.in_partnum = partnum;
  rd_args.in_identifier = nasdid;
  rd_args.in_offset = off;
  rd_args.in_len = in_len;
  nasd_cl_p_read_simple_dr(h, cookie.key, &sec_param, &cookie.capability,
                           &rd_args, buf, &rd_res, op_status);
  *out_len = rd_res.out_datalen;
  *nasd_status = rd_res.nasd_status;
}

void
write_exact(
  nasd_identifier_t   nasdid,
  nasd_byte_t        *buf,
  nasd_offset_t       off,
  nasd_len_t          in_len)
{
  nasd_rpc_status_t status;
  nasd_len_t out_len;
  nasd_status_t rc;

  do_write(nasdid, buf, off, in_len, &out_len, &rc, &status);
  if (rc || status) {
    fprintf(stderr,
      "WRITE EXACT %d at %" NASD_64u_FMT " from 0x%lx ERROR: nasd_status=0x%x (%s) status=0x%x (%s)\n",
      in_len, off, (unsigned long)buf, rc, nasd_error_string(rc),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }
  if (out_len != in_len) {
    fprintf(stderr,
      "WRITE EXACT %d at %" NASD_64u_FMT " from 0x%lx ERROR: in_len=%d out_len=%d\n",
      in_len, off, (unsigned long)buf, in_len, out_len);
    exit(1);
  }
}

void
read_exact(
  nasd_identifier_t   nasdid,
  nasd_byte_t        *buf,
  nasd_offset_t       off,
  nasd_len_t          in_len)
{
  nasd_p_getattr_dr_args_t args;
  nasd_p_getattr_dr_res_t res;
  nasd_rpc_status_t status;
  nasd_len_t out_len;
  nasd_status_t rc;

  do_read(nasdid, buf, off, in_len, &out_len, &rc, &status);
  if (rc || status) {
    fprintf(stderr,
      "READ EXACT %d at %" NASD_64u_FMT " from 0x%lx ERROR: nasd_status=0x%x (%s) status=0x%x (%s)\n",
      in_len, off, (unsigned long)buf, rc, nasd_error_string(rc),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }
  if (out_len != in_len) {
    fprintf(stderr,
      "READ EXACT %d at %" NASD_64u_FMT " from 0x%lx ERROR: in_len=%d out_len=%d\n",
      in_len, off, (unsigned long)buf, in_len, out_len);

    args.in_identifier = nasdid;
    args.in_partnum = partnum;

    nasd_cl_p_getattr_dr(h, cookie.key, &sec_param, &cookie.capability,
                         &args, &res, &status);
    if (res.nasd_status || status) {
      fprintf(stderr, "GETATTR nasd_status=0x%x (%s) status=0x%x (%s)\n",
        res.nasd_status, nasd_error_string(res.nasd_status),
        status, nasd_cl_error_string(h, status, error_text));
      exit(1);
    }

    nasd_printstat(&res.out_attribute);

    fprintf(stderr, "READ EXACT ERROR TERMINATE\n");

    exit(1);
  }
}

void
read_some(
  nasd_identifier_t   nasdid,
  nasd_byte_t        *buf,
  nasd_offset_t       off,
  nasd_len_t          in_len,
  nasd_len_t         *out_len)
{
  nasd_rpc_status_t status;
  nasd_status_t rc;

  do_read(nasdid, buf, off, in_len, out_len, &rc, &status);
  if (rc || status) {
    fprintf(stderr,
      "READ SOME %d at %" NASD_64u_FMT " from 0x%lx ERROR: nasd_status=0x%x (%s) status=0x%x (%s)\n",
      in_len, off, (unsigned long)buf, rc, nasd_error_string(rc),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }
}

void
do_create()
{
  nasd_p_create_dr_args_t args;
  nasd_p_create_dr_res_t res;
  nasd_attribute_t *ap;
  nasd_security_param_t sp;

  printf("CREATE NEW OBJECT\n");

  bzero((char *)&args, sizeof(args));

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

  args.in_partnum = partnum;
 
  ap = &args.in_attribute;
  ap->object_len = 3000;
  /* Testing standard AV */ 

  args.in_attribute.av=7;
  args.in_fieldmask = (NASD_ATTR_OBJECT_LEN | NASD_ATTR_AV);
  nasd_cl_p_create_dr(h, keys.black_key, &sp, NULL, &args, &res, &status);

  if (status) {
    fprintf(stderr, "ERROR: status=0x%x (%s) nasd_status=0x%x (%s)\n",
      status, nasd_cl_error_string(h, status, error_text),
      res.nasd_status, nasd_error_string(res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (res.nasd_status) {
    fprintf(stderr, "NASD ERROR: 0x%x (%s)\n", res.nasd_status,
      nasd_error_string(res.nasd_status));
    fflush(stderr);
    exit(2);
  }
  nasdid = res.out_identifier;

  printf("New object identifier %" NASD_ID_FMT "\n\n", nasdid);
}

void
usage()
{
  int i;
  fprintf(stderr, "USAGE: %s [options] servername partnum master_password [nasdid]\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -c create new object\n");
  fprintf(stderr, "  -d use a daily key \n");
  fprintf(stderr, "  -k use kernel device\n");
  fprintf(stderr, "  -l use colocated drive\n");
  fprintf(stderr, "  -M use message queues\n");
  fprintf(stderr, "  -s security_level\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 */

  fflush(stderr);
  exit(1);
}

void
crtest()
{
  FILE *outf;

  printf("\nBEGIN CREATE TEST\n");
  outf = stdout;

  for(i=0;i<LBUFLEN;i++)
    lbuf[i] = i;
  lbuf[0] = nasd_int64cast(0x123456789abcdef0);

  printf("BEGIN READBACK: zero first chunk\n");

  do_read(nasdid, (nasd_byte_t *)buf2, 0, BUFLEN, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 3000) {
    fprintf(stderr, "out_len=%d expected %d\n", out_len, 3000);
    exit(1);
  }
  for(bad=i=0;i<3000;i++) {
    if (buf2[i]) {
      fprintf(stderr, "ERROR: buf2[%d]=%02x\n", i, buf2[i]&0xff);
      bad++;
      if (bad > 4) {
        fprintf(stderr, "Not showing further errors\n");
        break;
      }
    }
  }
  if (bad)
    exit(1);
  printf("\n");
}

void
lgbuftest(nasd_offset_t base_off)
{
  FILE *outf;

  printf("\nBEGIN LGBUF TESTS, BASE OFFSET %" NASD_64u_FMT "\n", base_off);
  outf = stdout;

  for(i=0;i<LBUFLEN;i++)
    lbuf[i] = i;
  lbuf[0] = nasd_int64cast(0x123456789abcdef0);
  
  printf("BEGIN WRITE: pattern first chunk\n");
  do_write(nasdid, (nasd_byte_t *)buf, base_off, BUFLEN, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != BUFLEN) {
    fprintf(stderr, "out_len=%d expected %d\n", out_len, BUFLEN);
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("NEW OBJECT STATE:\n");
  do_getattr(0);
  if (!quiet_mode || rc || status)
    printf("\n");

  printf("BEGIN READBACK: pattern first chunk\n");
  do_read(nasdid, (nasd_byte_t *)buf2, base_off, BUFLEN, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != BUFLEN) {
    fprintf(stderr, "out_len=%d expected %d\n", out_len, BUFLEN);
    exit(1);
  }
  if (bcmp(buf, buf2, BUFLEN)) {
    fprintf(stderr, "ERROR! Buffers do not match.\n");
    for(bad=i=0;i<LBUFLEN;i++) {
      if (lbuf[i] != lbuf2[i]) {
        bad++;
        fprintf(stderr, "Match error at i=%d 0x%" NASD_64x_FMT " -> 0x%" NASD_64x_FMT "\n",
          i, lbuf[i], lbuf2[i]);
        if (bad > 4) {
          fprintf(stderr, "Not showing further match errors\n");
          break;
        }
      }
    }
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("\n");
  do_getattr(0);
}

void
group3test(nasd_offset_t base_off)
{
  static int testno = 0;
  FILE *outf;

  testno++;
  printf("\nBEGIN GROUP3 TESTS, BASE OFFSET %" NASD_64u_FMT " TEST #%d\n", base_off, testno);
  outf = stdout;

  for(i=0;i<1024;i++) {
    lbuf[i] = i;
  }
  lbuf[0] = nasd_int64cast(0x123456789abcdef0);

  for(i=0;i<1024;i++) {
    lbuf4[i] = i;
    lbuf4[i] |= nasd_uint64cast(0x0f00000000000000);
  }
  lbuf4[0] = nasd_int64cast(0x0123456789abcdef);
  printf("\n");

  printf("BEGIN WRITE: pattern first block\n");
  do_write(nasdid, (nasd_byte_t *)buf, base_off, 8192, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 8192) {
    fprintf(stderr, "out_len=%d expected %d\n", out_len, 8192);
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("NEW OBJECT STATE:\n");
  do_getattr(0);
  if (!quiet_mode || rc || status)
    printf("\n");

  printf("BEGIN READBACK: pattern first block\n");
  do_read(nasdid, (nasd_byte_t *)buf2, base_off, 8192, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 8192) {
    fprintf(stderr, "out_len=%d expected 8192\n", out_len);
    exit(1);
  }
  if (bcmp(buf, buf2, 8192)) {
    fprintf(stderr, "ERROR! Buffers do not match.\n");
    for(bad=i=0;i<1024;i++) {
      if (lbuf[i] != lbuf2[i]) {
        bad++;
        fprintf(stderr, "Match error at i=%d 0x%" NASD_64x_FMT " -> 0x%" NASD_64x_FMT "\n",
          i, lbuf[i], lbuf2[i]);
        if (bad > 4) {
          fprintf(stderr, "Not showing further match errors\n");
          break;
        }
      }
    }
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("\n");

  printf("BEGIN READBACK: portion of first block\n");
  bzero((char *)buf3, BUFLEN);
  j = 27; k = 3;
  do_read(nasdid, (nasd_byte_t *)buf3, base_off+(j*8), k*8, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != k*8) {
    fprintf(stderr, "out_len=%d expected %d\n", out_len, k*8);
    exit(1);
  }
  for(bad=0,i=j;i<j+k;i++) {
    if (lbuf[i] != lbuf3[i-j]) {
      bad++;
      fprintf(stderr, "ERROR: lbuf[%d]=0x%" NASD_64x_FMT " -> lbuf3[%d]=0x%" NASD_64x_FMT "\n",
        i, lbuf[i], i-j, lbuf3[i-j]);
    }
  }
  if (bad)
    exit(1);
  if (!quiet_mode || rc || status)
    printf("\n");

  printf("BEGIN READBACK: read past end of first block (past end of file)\n");
  bzero((char *)buf3, BUFLEN);
  j = 1023; k = 5;
  do_read(nasdid, (nasd_byte_t *)buf3, base_off+(j*8), k*8, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 8) {
    fprintf(stderr, "out_len=%d expected %d\n", out_len, 8);
    exit(1);
  }
  if (lbuf[j] != lbuf3[0]) {
    bad++;
    fprintf(stderr, "ERROR: lbuf[%d]=0x%" NASD_64x_FMT " -> lbuf3[%d]=0x%" NASD_64x_FMT "\n",
      j, lbuf[j], 0, lbuf3[0]);
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("\n");

  printf("BEGIN WRITE: write third block (second zero-filled)\n");
  do_write(nasdid, (nasd_byte_t *)buf4, base_off+16384, 8192, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 8192) {
    printf("out_len=%d expected %d\n", out_len, 8192);
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("NEW OBJECT STATE:\n");
  do_getattr(0);
  if (!quiet_mode || rc || status)
    printf("\n");

  printf("BEGIN READBACK: pattern third block\n");
  do_read(nasdid, (nasd_byte_t *)buf5, base_off+16384, 8192, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 8192) {
    fprintf(stderr, "out_len=%d expected 8192\n", out_len);
    exit(1);
  }
  if (bcmp(buf4, buf5, 8192)) {
    fprintf(stderr, "ERROR! Buffers do not match.\n");
    for(bad=i=0;i<1024;i++) {
      if (lbuf4[i] != lbuf5[i]) {
        bad++;
        fprintf(stderr, "Match error at i=%d 0x%" NASD_64x_FMT " -> 0x%" NASD_64x_FMT "\n",
          i, lbuf4[i], lbuf5[i]);
        if (bad > 4) {
          fprintf(stderr, "Not showing further match errors\n");
          break;
        }
      }
    }
    exit(1);
  }
  if (!quiet_mode || rc || status)
    printf("\n");

  for(i=0;i<sizeof(buf3)/8;i++) {
    lbuf3[i] = 0xdeadbeefUL;
  }
  printf("BEGIN READBACK: first three blocks (second is zero-fill)\n");
  do_read(nasdid, (nasd_byte_t *)buf3, base_off, 24576, &out_len, &rc, &status);
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s) out_len=%" NASD_64u_FMT "\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text), (nasd_uint64)out_len);
  if (rc || status)
    exit(1);
  if (out_len != 24576) {
    fprintf(stderr, "out_len=%d expected 24576\n", out_len);
    exit(1);
  }
  if (bcmp(buf, buf3, 8192)) {
    fprintf(stderr, "ERROR! Block-1 buffers do not match.\n");
    for(bad=i=0;i<1024;i++) {
      if (lbuf[i] != lbuf3[i]) {
        bad++;
        fprintf(stderr, "Match error at i=%d %" NASD_64x_FMT " -> %" NASD_64x_FMT "\n",
          i, lbuf[i], lbuf3[i]);
        if (bad > 4) {
          fprintf(stderr, "Not showing further match errors\n");
          break;
        }
      }
    }
    exit(1);
  }
  for(bad=i=0;i<1024;i++) {
    if (lbuf3[i+1024]) {
      bad++;
      fprintf(stderr, "Zero-fill error at i=%d %" NASD_64x_FMT "\n", i, lbuf3[i]);
    }
    if (bad > 4) {
      fprintf(stderr, "Not showing further errors\n");
      break;
    }
  }
  if (bad)
    exit(1);
  if (bcmp(buf4, &buf3[16384], 8192)) {
    fprintf(stderr, "ERROR! Block-3 buffers do not match.\n");
    for(bad=i=0;i<1024;i++) {
      if (lbuf[i] != lbuf3[2048+i]) {
        bad++;
        fprintf(stderr, "Match error at i=%d %" NASD_64x_FMT " -> %" NASD_64x_FMT "\n",
          i, lbuf[i], lbuf3[i+2048]);
        if (bad > 4) {
          fprintf(stderr, "Not showing further match errors\n");
          break;
        }
      }
    }
    exit(1);
  }
  printf("\nCOMPLETE GROUP3 TESTS, BASE OFFSET %" NASD_64u_FMT " ",base_off);
  if (!quiet_mode || rc || status)
    printf("OBJECT STATE:\n");
  do_getattr(0);
  printf("\n");
}

void
setlen(
  nasd_identifier_t  nasdid,
  nasd_len_t         len)
{
  nasd_security_param_t sp;
  nasd_p_getattr_dr_args_t ga_args;
  nasd_p_getattr_dr_res_t ga_res;
  nasd_p_setattr_dr_args_t args;
  nasd_attribute_t *ina, *outa;
  nasd_p_setattr_dr_res_t res;
  nasd_fieldmask_t fm;
  FILE *outf;

  ina = &args.in_attribute;
  outa = &res.out_attribute;
  bzero((char *)&args, sizeof(nasd_p_create_dr_args_t));
  outf = stdout;

  fm = 0;

  fm |= NASD_ATTR_OBJECT_LEN;
  ina->object_len = len;

  args.in_identifier = nasdid;
  args.in_fieldmask = fm;
  args.in_partnum = partnum;
  args.in_guard = 0;

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

  nasd_cl_p_setattr_dr(h, keys.black_key, &sp, NULL, &args, &res, &status);
  if (res.nasd_status || status)
    outf = stderr;
  if (!quiet_mode || res.nasd_status || status) {
    fprintf(outf, "SA LEN %u RESULT: nasd_status 0x%x (%s) status 0x%x (%s)\n",
      len, res.nasd_status, nasd_error_string(res.nasd_status), status,
      nasd_cl_error_string(h, status, error_text));
  }
  if (res.nasd_status || status)
    exit(1);

  if (outa->object_len != len) {
    fprintf(stderr, "SA LEN %d got result length %" NASD_64u_FMT "\n",
      len, outa->object_len);
    exit(1);
  }

  ga_args.in_identifier = nasdid;
  ga_args.in_partnum = partnum;

  nasd_cl_p_getattr_dr(h, cookie.key, &sec_param, &cookie.capability,
                       &ga_args, &ga_res, &status);
  if (ga_res.nasd_status || status) {
    fprintf(stderr, "SA LEN %u RESULT: nasd_status=0x%x (%s) status=0x%x (%s)\n",
      len, ga_res.nasd_status, nasd_error_string(ga_res.nasd_status),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }

  outa = &ga_res.out_attribute;
  if (outa->object_len != len) {
    fprintf(stderr, "SA LEN %d got post-result length %" NASD_64u_FMT "!\n",
      len, outa->object_len);
    exit(1);
  }
}

void
smalltest(
  int  pass)
{
  nasd_len_t out_len;
  int i, nlen;
  FILE *outf;

  NASD_ASSERT((pass == 1) || (pass == 2) || (pass == 3));
  nlen = 0;

  printf("\nBEGIN SMALL PASS %d, NASDID 0x%" NASD_ID_FMT "\n", pass, nasdid);
  outf = stdout;
  bzero(buf, BUFLEN);
  bzero(buf2, BUFLEN);

  setlen(nasdid, 0);

  buf[7] = 'c';
  write_exact(nasdid, (nasd_byte_t *)&buf[7], 7, 1);

  read_exact(nasdid, (nasd_byte_t *)buf2, 1, 1);
  if (buf2[0]) {
    fprintf(stderr, "SMALL TEST ERROR: buf miscompare line %d\n", __LINE__);
    exit(1);
  }

  if (!quiet_mode) {
    fprintf(outf, "SMALL TEST: first read ok\n");
    fflush(outf);
  }

  read_some(nasdid, (nasd_byte_t *)buf2, 0, 16, &out_len);
  if (out_len != 8) {
    fprintf(stderr, "SMALL TEST ERROR: got %u bytes from readback, expected %u\n",
      out_len, 8);
    exit(1);
  }
  if (bcmp(buf, buf2, 8)) {
    fprintf(stderr, "SMALL TEST ERROR: buf miscompare line %d\n", __LINE__);
    exit(1);
  }
  for(i=0;i<7;i++) {
    buf[i] = i+1;
  }

  if (!quiet_mode) {
    fprintf(outf, "SMALL TEST: second read ok\n");
    fflush(outf);
  }

  write_exact(nasdid, (nasd_byte_t *)buf, 0, 7);
  read_exact(nasdid, (nasd_byte_t *)buf2, 0, 8);
  if (bcmp(buf, buf2, 8)) {
    fprintf(stderr, "SMALL TEST ERROR: buf miscompare line %d\n", __LINE__);
    exit(1);
  }

  if (!quiet_mode) {
    fprintf(outf, "SMALL TEST: third read ok\n");
    fflush(outf);
  }

  if (pass == 1) {
    nlen = 4096;
  }
  else if ((pass == 2) || (pass == 3)) {
    nlen = 8192;
  }
  else {
    NASD_PANIC();
  }

  setlen(nasdid, nlen);
  read_some(nasdid, (nasd_byte_t *)buf2, 0, 8192, &out_len);
  if (out_len != nlen) {
    fprintf(stderr, "SMALL TEST ERROR: got %u bytes from readback, expected %u\n",
      out_len, nlen);
    exit(1);
  }
  if (bcmp(buf, buf2, nlen)) {
    fprintf(stderr, "SMALL TEST ERROR: buf miscompare line %d\n", __LINE__);
    for(i=0;i<nlen;i++) {
      if (buf[i] != buf2[i]) {
        printf("  buf[%04d]=0x%02x buf2[%04d]=0x%02x\n", i, buf[i]&0xff, i, buf2[i]&0xff);
      }
    }
    exit(1);
  }
  for(i=0;i<2048;i++) {
    lbuf[i] = i;
  }

  if (!quiet_mode) {
    fprintf(outf, "SMALL TEST: fourth read ok\n");
    fflush(outf);
  }

  write_exact(nasdid, (nasd_byte_t *)buf, 0, 16384);
  read_some(nasdid, (nasd_byte_t *)buf2, 0, 24576, &out_len);
  if (out_len != 16384) {
    fprintf(stderr, "SMALL TEST ERROR: got %u bytes from readback, expected %u\n",
      out_len, 16384);
    exit(1);
  }
  if (bcmp(buf, buf2, 16384)) {
    fprintf(stderr, "SMALL TEST ERROR: buf miscompare line %d\n", __LINE__);
    exit(1);
  }

  if (!quiet_mode) {
    fprintf(outf, "SMALL TEST: fifth read ok\n");
    fflush(outf);
  }

  if ((pass == 1) || (pass == 2)) {
    setlen(nasdid, 0);
  }
  else {
    setlen(nasdid, 1);
    setlen(nasdid, 0);
  }

  if (!quiet_mode) {
    fprintf(outf, "SMALL TEST: successful\n");
    fflush(outf);
  }
}

void
sa_test(
  nasd_offset_t  newlen)
{
  nasd_security_param_t sp;
  nasd_p_setattr_dr_args_t args;
  nasd_attribute_t *ina, *outa;
  nasd_p_setattr_dr_res_t res;
  nasd_fieldmask_t fm;
  FILE *outf;

  printf("\nBEGIN SETATTR, NEW LENGTH %" NASD_64u_FMT ", NASDID 0x%" NASD_ID_FMT "\n",
    newlen, nasdid);
  outf = stdout;

  ina = &args.in_attribute;
  outa = &res.out_attribute;
  bzero((char *)&args,sizeof(nasd_p_create_dr_args_t));

  fm = 0;

  fm |= NASD_ATTR_OBJECT_LEN;
  ina->object_len = newlen;

  args.in_identifier = nasdid;
  args.in_fieldmask = fm;
  args.in_partnum = partnum;
  args.in_guard = 0;

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

  nasd_cl_p_setattr_dr(h, keys.black_key, &sp, NULL, &args, &res, &status);
  rc = res.nasd_status;
  if (rc || status)
    outf = stderr;
  if (!quiet_mode || rc || status)
    fprintf(outf, "RESULT: nasd_status 0x%x (%s) status 0x%x (%s)\n",
      rc, nasd_error_string(rc), status,
      nasd_cl_error_string(h, status, error_text));
  if (res.nasd_status || status)
    exit(1);
  if (!quiet_mode || rc || status) {
    printf("Out attribute:\n");
    nasd_printstat(outa);
    printf("\n");
  }
  printf("\nCOMPLETE SETATTR");
  
  if (!quiet_mode || rc || status)
    printf(" NEW ATTRIBUTES:\n");
  do_getattr(0);
}

void
flush_obj()
{
  nasd_p_flush_obj_dr_args_t args;
  nasd_p_flush_obj_dr_res_t res;
  nasd_rpc_status_t status;

  args.in_identifier = nasdid;
  args.in_partnum = partnum;

  printf("FLUSH OBJECT 0x%" NASD_ID_FMT "\n", nasdid);
 
  nasd_cl_p_flush_obj_dr(h, cookie.key, &sec_param, &cookie.capability,
                         &args, &res, &status);

  if (res.nasd_status || status) {
    fprintf(stderr, "FLUSH_OBJ nasd_status=0x%x (%s) status=0x%x (%s)\n",
      res.nasd_status, nasd_error_string(res.nasd_status),
      status, nasd_cl_error_string(h, status, error_text));
    exit(1);
  }
  printf("FLUSHED\n");
}

int
main(
  int     argc,
  char  **argv)
{
  char c;
  int sec_level=0;
  int use_daily=0;

  progname = argv[0];

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

  while (nasd_getopt(argc, argv, "cdklMqs:T", &c)) {
    switch(c) {
      case 'c':
        docreate = 1;
        break;
      case 'd':
        use_daily = 1;
        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 'l':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_COLOCATE;
        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;
      case 'q':
        quiet_mode = 1;
        break;
      case 's':
        if (sscanf(nasd_optarg, "%d", &sec_level) != 1) {
          usage();
        }
        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 */
      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];

  if (docreate == 0) {
    nasd_optind++;
    if (nasd_optind >= argc) {
      fprintf(stderr, "If -c is not used, a nasdid must be specified\n");
      usage();
    }
    rc = nasd_str_to_nasd_id(argv[nasd_optind], &nasdid);
    if (rc)
      usage();
  }

  nasd_optind++;
  if (nasd_optind < argc)
    usage();

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

  bzero((char *)&cookie, sizeof(cookie));

  NASD_Malloc(buf, BUFLEN, (char *));
  if (buf == NULL) {
    fprintf(stderr, "ERROR: not enough memory for tests\n");
    fflush(stderr);
    exit(1);
  }
  lbuf = (nasd_uint64 *)buf;

  NASD_Malloc(buf2, BUFLEN, (char *));
  if (buf2 == NULL) {
    fprintf(stderr, "ERROR: not enough memory for tests\n");
    fflush(stderr);
    exit(1);
  }
  lbuf2 = (nasd_uint64 *)buf2;

  NASD_Malloc(buf3, BUFLEN, (char *));
  if (buf3 == NULL) {
    fprintf(stderr, "ERROR: not enough memory for tests\n");
    fflush(stderr);
    exit(1);
  }
  lbuf3 = (nasd_uint64 *)buf3;

  NASD_Malloc(buf4, BUFLEN, (char *));
  if (buf4 == NULL) {
    fprintf(stderr, "ERROR: not enough memory for tests\n");
    fflush(stderr);
    exit(1);
  }
  lbuf4 = (nasd_uint64 *)buf4;

  NASD_Malloc(buf5, BUFLEN, (char *));
  if (buf5 == NULL) {
    fprintf(stderr, "ERROR: not enough memory for tests\n");
    fflush(stderr);
    exit(1);
  }
  lbuf5 = (nasd_uint64 *)buf5;

  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 error 0x%x (%s)\n",
      server_name, rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }

  rc = nasd_sec_seclevel_to_protection(sec_level, &protection);
  if(rc) {
    fprintf(stderr, "Unknown security level %d\n", sec_level);
    usage();
  }
  printf("Security Level %d   protection %d\n", sec_level, protection);

  /* Fill in capability and security param */
  nasd_sec_password_to_keys(master_password, partnum, &keys);
 
  if (docreate) {
    do_create();
  }

  if (!use_daily) {
    nasd_drive_handle_get_time(h, &tm);
    tm.ts_sec+=(60*60);
    nasd_sec_build_capability(partnum, nasdid,
                              (NASD_ACCESS_RIGHTS_GETATTR |
                               NASD_ACCESS_RIGHTS_READ |
                               NASD_ACCESS_RIGHTS_WRITE |
                               NASD_ACCESS_RIGHTS_FLUSH),
                              0, tm.ts_sec, protection,
                              NASD_BLACK_CAPABILITY,
                              (nasd_offset_t) 0,
                              (nasd_offset_t) lvl3_off*2,
                              7, keys.black_key,
                              &cookie);
    sec_param.type = NASD_BLACK_CAPABILITY;
  } else {
    sec_param.type = NASD_BLACK_KEY;
    /* this is a hack, but it allows the subroutines to pretend we're
       always using a capability and take the key from the (fake)
       cookie.  &cookie.capability arg will be ignored by the RPC
       layer since we specified a daily key in the security param. */
    bcopy(keys.black_key, cookie.key, sizeof(nasd_key_t));
  }
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;
  
  printf("BEGIN TESTS\n");
  if (!quiet_mode || rc || status)
    printf("INITIAL OBJECT STATE:\n");
  
  do_getattr(1);

  if (docreate) {
    crtest();
  }

  smalltest(1);
  smalltest(2);
  smalltest(3);

  setlen(nasdid, 3000);

  group3test(0);

  /* 40960 = 5 blocks in */
  group3test(40965);

  group3test(lvl1_off-8192);

  group3test(lvl2_off);

  group3test(lvl3_off-8192);

  lgbuftest(0);

  flush_obj();

  sa_test(0);

  group3test(0);

  /* 40960 = 5 blocks in */
  group3test(40965);

  group3test(lvl1_off-8192);

  group3test(lvl2_off);

  group3test(lvl3_off-8192);

  lgbuftest(0);

  flush_obj();

  fprintf(stderr, "Tests succeeded.\n");

  NASD_Free(buf, BUFLEN);
  NASD_Free(buf2, BUFLEN);
  NASD_Free(buf3, BUFLEN);
  NASD_Free(buf4, BUFLEN);
  NASD_Free(buf5, BUFLEN);

  nasd_unbind_drive(&h);
  nasd_cl_p_shutdown();

  exit(0);
}

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