Import TBBT (NFS trace replay).
[bluesky.git] / TBBT / trace_play / sfs_2_vld.c
diff --git a/TBBT/trace_play/sfs_2_vld.c b/TBBT/trace_play/sfs_2_vld.c
new file mode 100644 (file)
index 0000000..45e24d9
--- /dev/null
@@ -0,0 +1,1747 @@
+#ifndef lint
+static char sfs_c_vldSid[] = "@(#)sfs_2_vld.c 2.1     97/10/23";
+#endif
+
+/*
+ *   Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ *     All rights reserved.
+ *             Standard Performance Evaluation Corporation (SPEC)
+ *             6585 Merchant Place, Suite 100
+ *             Warrenton, VA 20187
+ *
+ *     This product contains benchmarks acquired from several sources who
+ *     understand and agree with SPEC's goal of creating fair and objective
+ *     benchmarks to measure computer performance.
+ *
+ *     This copyright notice is placed here only to protect SPEC in the
+ *     event the source is misused in any manner that is contrary to the
+ *     spirit, the goals and the intent of SPEC.
+ *
+ *     The source code is provided to the user or company under the license
+ *     agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ *                                                               *
+ *      Copyright 1991,1992  Legato Systems, Inc.                *
+ *      Copyright 1991,1992  Auspex Systems, Inc.                *
+ *      Copyright 1991,1992  Data General Corporation            *
+ *      Copyright 1991,1992  Digital Equipment Corporation       *
+ *      Copyright 1991,1992  Interphase Corporation              *
+ *      Copyright 1991,1992  Sun Microsystems, Inc.              *
+ *                                                               *
+ *****************************************************************/
+
+/*
+ * ------------------------------ sfs_c_vld.c ---------------------------
+ *
+ * Validation suite for sfs.
+ *
+ *.Exported_routines
+ *      void Validate_ops(int, char **)
+ *
+ *.Local_routines
+ *      void validate_init_rpc()
+ *      void validate_creation(void)
+ *      void validate_attributes(void)
+ *      void validate_read_write(void)
+ *      void validate_rename(void)
+ *      int compare_sattr(char *, char *, sattr *, fattr *)
+ *      int compare_fattr(char *, char *, fattr *, fattr *)
+ *      uint16_t sum(unsigned char *, uint_t)
+ *      void validate_remove(void)
+ *      void validate_cleanup(void)
+ *      void validate_exit(void)
+ *      void verror(int, ValMsgType, char *, ...)
+ *
+ *.Revision History
+ *      04-Dec-91       Keith           Define string.h for SYSV/SVR4.
+ *      25-Jun-91       Wiryaman        Created
+ */
+
+
+/*
+ * -------------------------  Include Files  -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/stat.h> 
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+extern struct hostent  *Server_hostent;
+
+/*
+ * -----------------------  External Definitions  -----------------------
+ */
+
+/* forward definitions for local routines */
+/*
+ * validate options
+ * BATCH        - do complete pass of validation, reporting errors if any
+ * VERBOSE      - prints step-by-step validation actions being performed
+ * INTERACTIVE  - VERBOSE and if any errors encountered, ask to continue
+ *                validation or not.
+ */
+#define VAL_BATCH       1
+#define VAL_VERBOSE     2
+#define VAL_INTERACTIVE 3
+
+typedef enum {
+        I = 1,
+        W = 2,
+        E = 3
+} ValMsgType;
+
+#define NUMREGFILES     7
+#define NUMDIRS         5
+#define NUMLINKS        5
+#define NUMSYMLINKS     5
+#define NUMFILES        NUMREGFILES + NUMDIRS + NUMLINKS + NUMSYMLINKS
+#define NUMFRAGS        8
+
+static void validate_init_rpc(void);
+static void validate_exit(void);
+static void validate_creation(void);
+static void validate_attributes(void);
+static void validate_read_write(void);
+static void validate_rename(void);
+static void validate_remove(void);
+static void validate_cleanup(void);
+static int compare_sattr(char *, char *, sattr *, fattr *);
+static int compare_fattr(char *, char *, fattr *, fattr *);
+static uint16_t sum(unsigned char *, uint_t);
+static void verror(int, ValMsgType, char *, ...);
+
+static void val_op_null(void);
+static void val_op_getattr(fhandle_t *, attrstat *);
+static void val_op_setattr(sattrargs *, attrstat *);
+static void val_op_lookup(diropargs *, diropres *);
+static void val_op_readlink(fhandle_t *, readlinkres *);
+static void val_op_read(readargs *, readres *);
+static void val_op_write(writeargs *, attrstat *);
+static void val_op_create(createargs *, diropres *);
+static void val_op_remove(diropargs *, nfsstat *);
+static void val_op_rename(renameargs *, nfsstat *);
+static void val_op_link(linkargs *, nfsstat *);
+static void val_op_symlink(symlinkargs *, nfsstat *);
+static void val_op_mkdir(mkdirargs *, diropres *);
+static void val_op_rmdir(diropargs *, nfsstat *);
+static void val_op_readdir(readdirargs *, readdirres *);
+static void val_op_statfs(fhandle_t *, statfsres *);
+static void create_tmp_handles(void);
+static void delete_tmp_handles(void);
+
+/*
+ * ----------------------  Static Declarations ----------------------
+ */
+
+int Validate;
+
+static int Validate_errors = 0;
+static char Testdirname[SFS_MAXPATHLEN];    /* test dir component name */
+
+/*
+ * ----------------------  SFS Validation Suite  ----------------------
+ */
+
+void
+Validate_ops(
+    int         argc,
+    char *      argv[])
+{
+    char *      valdir;
+    CLIENT *    mount_client_ptr;
+
+    if (argc > 1) {
+        verror(VAL_BATCH, E, "Can only validate one directory at a time.\n");
+        exit(1);
+    }
+
+    Num_io_files = NUMFILES;
+    Cur_uid = Real_uid;
+    nfs_version = NFS_VERSION;
+
+    if (argc == 0)
+        valdir = ".";
+    else
+        valdir = argv++[0];
+
+    (void) sprintf(Testdirname, "%s/validatedir", valdir);
+
+    do {
+        verror(VAL_BATCH, I, "validating sfs on \"%s\" directory ...\n",
+                valdir);
+
+        init_fileinfo();
+       create_tmp_handles();
+
+        /*
+         * need priv port to do following
+         */
+        mount_client_ptr = lad_getmnt_hand(valdir);
+        if (mount_client_ptr == NULL) {
+            exit(1);
+        }
+        validate_init_rpc();
+
+        /*
+         * should be all done doing priv port stuff
+         */
+        if (setuid(Real_uid) != 0) {
+           (void) fprintf(stderr,"%s: %s%s\n",
+                   sfs_Myname, "cannot perform setuid operation.\n",
+                   "Do `make install` as root.\n");
+        }
+
+        init_mount_point(0, valdir, mount_client_ptr);
+        verror(VAL_VERBOSE, I, "validating null operation ...\n");
+        val_op_null();
+
+        validate_creation();
+        validate_attributes();
+        validate_read_write();
+        validate_rename();
+        validate_remove();
+        argc--;
+        valdir = argv++[0];
+
+        /*
+         * Cleanup mount client handle
+         */
+        clnt_destroy(mount_client_ptr);
+
+       delete_tmp_handles();
+        validate_cleanup();
+
+    } while (argc > 0);
+
+    validate_exit();
+
+} /* Validate_ops */
+
+
+/*
+ * allocate and initialize client handles
+ */
+static void
+validate_init_rpc(void)
+{
+       NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+                                        (uint32_t) NFS_PROGRAM,
+                                        (uint32_t) NFS_VERSION,
+                                        RPC_ANYSOCK, &Nfs_timers[0]);
+       if (NFS_client == ((CLIENT *) NULL)) { 
+               verror(VAL_BATCH, E, "portmap/nfsd server not responding\n"); 
+               exit(1); 
+       }
+       NFS_client->cl_auth = authunix_create(lad_hostname, Real_uid,
+                                      Cur_gid, 0, NULL);
+} /* validate_init_rpc */
+
+
+static void
+validate_creation(void)
+{
+    int                 filenum;
+    int                 target_filenum;
+    diropargs           arglp;
+    createargs          argcr;
+    mkdirargs           argmk;
+    linkargs            argln;
+    symlinkargs         argsl;
+    char                sl_target_path[NFS_MAXPATHLEN];
+    diropres            reply;
+    readlinkres         rlreply;
+    char                sym_data[NFS_MAXPATHLEN];
+
+    for (filenum=0; filenum < NUMFILES ; filenum++) {
+
+        Cur_file_ptr = &Io_files[filenum];
+        sfs_gettime(&Cur_time);
+
+        if (filenum < NUMREGFILES) {
+
+            (void) sprintf(Cur_filename, Filespec, filenum);
+
+            /* regular file creation */
+            argcr.attributes.mode= (NFSMODE_REG | 0666);
+            argcr.attributes.uid = Cur_uid;
+            argcr.attributes.gid = Cur_gid;
+            argcr.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+            argcr.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+            argcr.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+            argcr.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+            argcr.attributes.size = 0;
+            (void) memmove((char *) &argcr.where.dir, (char *) &Export_dir.fh2,
+                       NFS_FHSIZE);
+            argcr.where.name = Cur_filename;
+
+            verror(VAL_VERBOSE, I, "validating create file %s ...\n",
+                                       Cur_filename);
+            val_op_create(&argcr, &reply);
+
+            if (reply.status == NFS_OK) {
+                Cur_file_ptr->state = Exists;
+                (void) memmove((char *) &Cur_file_ptr->fh2,
+                       (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE);
+                (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+                (void) memmove((char *) &Cur_file_ptr->attributes2,
+                               (char *) &reply.diropres_u.diropres.attributes,
+                               sizeof(Cur_file_ptr->attributes2));
+                (void) compare_sattr(Ops[CREATE].name, Io_files[filenum].file_name,
+                            &argcr.attributes, &Cur_file_ptr->attributes2);
+            } else {
+                Cur_file_ptr->state = Nonexistent;
+                errno = (int)reply.status;
+                verror(VAL_BATCH, E, "create %s failed: %m\n", Cur_filename);
+                /*
+                 * An error in file creation is fatal, because we use the
+                 * created files to validate the other operations.
+                 */
+                validate_exit();
+            }
+
+        } else if (filenum < NUMREGFILES + NUMDIRS) {
+
+            (void) sprintf(Cur_filename, Dirspec, filenum);
+
+            /* directory creation */
+            argmk.attributes.mode= (NFSMODE_DIR | 0777);
+            argmk.attributes.uid = Cur_uid;
+            argmk.attributes.gid = Cur_gid;
+            argmk.attributes.size = 0xFFFFFFFF;
+            argmk.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+            argmk.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+            argmk.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+            argmk.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+            (void) memmove((char *) &argmk.where.dir, (char *) &Export_dir.fh2,
+                       NFS_FHSIZE);
+            argmk.where.name = Cur_filename;
+
+            verror(VAL_VERBOSE, I, "validating mkdir %s ...\n", Cur_filename);
+            val_op_mkdir(&argmk, &reply);
+
+            if (reply.status == NFS_OK) {
+                Cur_file_ptr->state = Exists;
+                (void) memmove((char *) &Cur_file_ptr->fh2,
+                       (char *) &reply.diropres_u.diropres.file,
+                       NFS_FHSIZE);
+                (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+                (void) memmove((char *) &Cur_file_ptr->attributes2,
+                               (char *) &reply.diropres_u.diropres.attributes,
+                               sizeof(Cur_file_ptr->attributes2));
+                (void) compare_sattr(Ops[MKDIR].name, Io_files[filenum].file_name,
+                                &argmk.attributes, &Cur_file_ptr->attributes2);
+            } else {
+                Cur_file_ptr->state = Nonexistent;
+                verror(VAL_BATCH, W, "mkdir %s failed:%m\n", Cur_filename);
+            }
+
+        } else if(filenum < NUMREGFILES + NUMDIRS + NUMLINKS ) {
+
+            (void) sprintf(Cur_filename, Filespec, filenum);
+
+            /* hard link creation */
+            target_filenum = NUMFILES-NUMSYMLINKS-1-filenum;
+            (void) memmove((char *) &argln.from,
+                       (char *) &Io_files[target_filenum].fh2, NFS_FHSIZE);
+            (void) memmove((char *) &argln.to.dir, (char *) &Export_dir.fh2,
+                       NFS_FHSIZE);
+            argln.to.name = Cur_filename;
+
+            verror(VAL_VERBOSE, I, "validating link %s %s ...\n",
+                               Io_files[target_filenum].file_name, Cur_filename);
+            val_op_link(&argln, &reply.status);
+
+            if (reply.status == NFS_OK) {
+                Cur_file_ptr->state = Exists;
+                (void) memmove((char *) &Cur_file_ptr->fh2,
+                       (char *) &Io_files[target_filenum].fh2,
+                       NFS_FHSIZE);
+                (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+                Cur_file_ptr->attributes2 = Io_files[target_filenum].attributes2;
+                Io_files[target_filenum].attributes2.nlink++;
+                Cur_file_ptr->attributes2.nlink++;
+            } else {
+                Cur_file_ptr->state = Nonexistent;
+                verror(VAL_BATCH, W, "link %s failed: %m\n", Cur_filename);
+            }
+
+        } else {
+
+            (void) sprintf(Cur_filename, Symspec, filenum);
+
+            /* symbolic link creation */
+            target_filenum = NUMFILES-1-filenum;
+            (void) memmove((char *) &argsl.from.dir, (char *) &Export_dir.fh2,
+                       NFS_FHSIZE);
+            argsl.from.name = Cur_filename;
+            (void) sprintf(sl_target_path,
+                           "./%s", Io_files[target_filenum].file_name);
+            argsl.attributes.size = strlen(sl_target_path);
+            argsl.to = sl_target_path;
+            argsl.attributes.mode = (NFSMODE_LNK | 0777);
+            argsl.attributes.uid = Cur_uid;
+            argsl.attributes.gid = Cur_gid;
+            argsl.attributes.atime.seconds = (unsigned int)Cur_time.esec;
+            argsl.attributes.atime.useconds = (unsigned int)Cur_time.usec;
+            argsl.attributes.mtime.seconds = (unsigned int)Cur_time.esec;
+            argsl.attributes.mtime.useconds = (unsigned int)Cur_time.usec;
+
+            verror(VAL_VERBOSE, I, "validating symlink %s %s ...\n",
+                                       sl_target_path, Cur_filename);
+            val_op_symlink(&argsl, &reply.status);
+
+            if (reply.status == NFS_OK) {
+                Cur_file_ptr->state = Exists;
+
+                /* do a lookup to get file handle and attributes */
+                (void) memmove((char *) &arglp.dir, (char *) &Export_dir.fh2,
+                       NFS_FHSIZE);
+                arglp.name = Cur_filename;
+
+                val_op_lookup(&arglp, &reply);
+
+                if (reply.status == NFS_OK) {
+                    (void) memmove((char *) &Cur_file_ptr->fh2,
+                       (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE);
+                    (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+                    Cur_file_ptr->attributes2 =
+                                        reply.diropres_u.diropres.attributes;
+                    (void) compare_sattr(Ops[SYMLINK].name,
+                                        Io_files[filenum].file_name,
+                                        &argsl.attributes,
+                                        &Cur_file_ptr->attributes2);
+                } else {
+                    verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+                                               Cur_filename);
+                    continue;
+                }
+
+                /* validate readlink */
+                rlreply.readlinkres_u.data = sym_data;
+
+                verror(VAL_VERBOSE, I, "validating readlink %s ...\n",
+                                               Cur_filename);
+                val_op_readlink(&Cur_file_ptr->fh2, &rlreply);
+
+                if (rlreply.status == NFS_OK) {
+                    sym_data[rlreply.readlinkres_u.len] = '\0';
+                    if (strcmp(sl_target_path, sym_data)) {
+                        verror(VAL_BATCH, W,
+                            "readlink %s error, result = %s, should be %s\n",
+                            Cur_filename, rlreply.readlinkres_u.data,
+                            sl_target_path);
+                    }
+                } else {
+                    verror(VAL_BATCH, W, "readlink %s failed:%m\n",
+                            Cur_filename);
+                }
+
+            } else {
+                Cur_file_ptr->state = Nonexistent;
+                verror(VAL_BATCH, W, "symlink %s failed: %m\n",
+                        Cur_filename);
+            }
+        }
+    } /* end for each file */
+
+} /* validate_creation */
+
+
+static void
+validate_attributes(void)
+{
+    int                 filenum;
+    diropargs           arglp;
+    diropres            lreply;
+    fhandle_t           fh;
+    sattrargs           argsa;
+    attrstat            areply;
+
+    /* validate fsstat */
+
+    /* validate lookup */
+    for (filenum = 0; filenum < NUMFILES; filenum++) {
+        (void) memmove((char *) &arglp.dir, (char *) &Export_dir.fh2,
+                       NFS_FHSIZE);
+        arglp.name = Io_files[filenum].file_name;
+
+        verror(VAL_VERBOSE, I, "validating lookup %s ...\n",
+                Io_files[filenum].file_name);
+        val_op_lookup(&arglp, &lreply);
+
+        if (lreply.status == NFS_OK) {
+            if (memcmp((char *) &(Io_files[filenum].fh2),
+                     (char *) &(lreply.diropres_u.diropres.file),
+                        NFS_FHSIZE)) {
+                verror(VAL_BATCH, W, "lookup %s: file handle mismatch\n",
+                        Io_files[filenum].file_name);
+            }
+            (void) compare_fattr(Ops[LOOKUP].name, Io_files[filenum].file_name,
+                          &Io_files[filenum].attributes2,
+                          &lreply.diropres_u.diropres.attributes);
+        } else {
+            verror(VAL_BATCH, W, "lookup %s failed:%m\n",
+                    Io_files[filenum].file_name);
+        }
+    }
+
+    /* validate getattr */
+    for (filenum = 0; filenum < NUMFILES; filenum++) {
+        (void) memmove((char *) &fh, (char *) &Io_files[filenum].fh2,
+                       NFS_FHSIZE);
+
+        verror(VAL_VERBOSE, I, "validating getattr %s ...\n",
+                Io_files[filenum].file_name);
+        val_op_getattr(&fh, &areply);
+
+        if (areply.status == NFS_OK) {
+            (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+                          &Io_files[filenum].attributes2,
+                          &areply.attrstat_u.attributes);
+        } else {
+            verror(VAL_BATCH, W, "getattr %s failed: %m\n",
+                    Io_files[filenum].file_name);
+        }
+    }
+
+    /*validate setattr */
+    for (filenum = 0; filenum < NUMFILES; filenum++) {
+        sfs_gettime(&Cur_time);
+        if (filenum >= NUMREGFILES && filenum < NUMREGFILES + NUMDIRS)
+          argsa.attributes.mode= 0777;
+        else
+          argsa.attributes.mode= 0666;
+        argsa.attributes.uid  = 0xFFFFFFFF;
+        argsa.attributes.gid  = 0xFFFFFFFF;
+        argsa.attributes.size = 0xFFFFFFFF;
+        argsa.attributes.atime.seconds =  (unsigned int)Cur_time.esec;
+        argsa.attributes.atime.useconds = (unsigned int)Cur_time.usec;
+        argsa.attributes.mtime.seconds =  (unsigned int)Cur_time.esec;
+        argsa.attributes.mtime.useconds = (unsigned int)Cur_time.usec;
+        (void) memmove((char *) &argsa.file, (char *) &Io_files[filenum].fh2,
+                       NFS_FHSIZE);
+
+        verror(VAL_VERBOSE, I, "validating setattr %s ...\n",
+                Io_files[filenum].file_name);
+        val_op_setattr(&argsa, &areply);
+
+        if (areply.status == NFS_OK) {
+            if (argsa.attributes.mode != areply.attrstat_u.attributes.mode){
+                argsa.attributes.mode |=
+                        (Io_files[filenum].attributes2.mode & NFSMODE_FMT);
+                argsa.attributes.mode &= Io_files[filenum].attributes2.mode;
+            }
+            Io_files[filenum].attributes2 = areply.attrstat_u.attributes;
+            (void) compare_sattr(Ops[SETATTR].name, Io_files[filenum].file_name,
+                          &argsa.attributes, &areply.attrstat_u.attributes);
+
+            val_op_getattr(&argsa.file, &areply);
+
+            if (areply.status == NFS_OK) {
+                (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+                              &Io_files[filenum].attributes2,
+                              &areply.attrstat_u.attributes);
+            } else {
+                verror(VAL_BATCH, W, "getattr %s failed: %m\n",
+                        Io_files[filenum].file_name);
+            }
+        } else {
+            verror(VAL_BATCH, W, "setattr %s failed: %m\n",
+                    Io_files[filenum].file_name);
+        }
+    }
+
+} /* validate_attributes */
+
+
+static void
+validate_read_write(void)
+{
+    struct {
+        uint16_t  sum;                    /* checksum of data */
+        uint16_t  len;                    /* length of len and data */
+        char            data[DEFAULT_MAX_BUFSIZE - 2 * sizeof(uint16_t)];
+    } block;
+    writeargs           argwr;
+    attrstat            wrreply;
+    readargs            argrd;
+    readres             rdreply;
+    int                 maxblks;
+    int                 maxfiles;
+    uint_t             i;
+    int                 numfiles;
+    int                 filenum;
+    int                 blocknum;
+    readdirargs         argrdir;
+    readdirres          rdirreply;
+    int                 entry_cnt = 9;
+    entry               entry_stream[9];
+    char                name_stream[9 * SFS_MAXNAMLEN];
+
+    /* validate write */
+
+    /* get the maximum number of blocks sfs will write */
+    maxblks = Io_dist_ptr->max_bufs;
+    maxfiles = maxblks > NUMREGFILES ? NUMREGFILES : maxblks;
+
+    /* write maxblks - filenum + 1 blocks to each regular file */
+    argwr.offset = 0;
+    argwr.beginoffset = 0; /* unused */
+    argwr.totalcount = 0; /* unused */
+    argwr.data.data_val = (char *)&block;
+
+    for (blocknum = 0; blocknum <= maxblks ; blocknum++) {
+
+        for (i=0; i < sizeof(block.data); i++)
+            block.data[i] = (char)blocknum;
+
+        for (filenum=0; filenum < maxfiles; filenum++) {
+
+            /* Write fewer blocks to files with higher numbers. */
+            if (blocknum > (maxblks - filenum))
+                break;
+
+            /* set the length field */
+            if (blocknum == (maxblks - filenum)) {
+                block.len = ((maxfiles - filenum) *
+                            (Bytes_per_block/Kb_per_block)) - (sizeof(block.len)
+                             + sizeof(block.sum));
+            } else {
+                block.len = Bytes_per_block - (sizeof(block.len)
+                            + sizeof(block.sum));
+            }
+            block.sum = sum((unsigned char *) &block.len,
+                            block.len + sizeof(block.len));
+
+            (void) memmove((char *) &argwr.file,
+                       (char *) &Io_files[filenum].fh2, NFS_FHSIZE);
+            argwr.data.data_len = block.len +
+                                  sizeof(block.len) + sizeof(block.sum);
+
+            verror(VAL_VERBOSE, I,
+                    "validating write %d bytes @ offset %d to %s ...\n",
+                    argwr.data.data_len, argwr.offset,
+                    Io_files[filenum].file_name);
+
+            val_op_write(&argwr, &wrreply);
+
+            if (wrreply.status == NFS_OK) {
+                (void) compare_fattr(Ops[WRITE].name, Io_files[filenum].file_name,
+                              &Io_files[filenum].attributes2,
+                              &wrreply.attrstat_u.attributes);
+                Io_files[filenum].attributes2 = wrreply.attrstat_u.attributes;
+            } else {
+                verror(VAL_BATCH, W, "write %s failed: %m\n",
+                        Io_files[filenum].file_name);
+            }
+        }
+        argwr.offset += Bytes_per_block;
+    }
+
+    /* validate read */
+
+    for (filenum = 0; filenum < maxfiles; filenum++) {
+        (void) memmove((char *) &argrd.file, (char *) &Io_files[filenum].fh2,
+                       NFS_FHSIZE);
+        argrd.offset = 0;
+        argrd.count = 0;
+        rdreply.readres_u.reply.data.data_len = 0;
+        maxblks = Io_files[filenum].attributes2.size / Bytes_per_block;
+        for (blocknum = 0; blocknum <= maxblks; blocknum ++) {
+
+            if (argrd.count != rdreply.readres_u.reply.data.data_len) {
+                argrd.count -= rdreply.readres_u.reply.data.data_len;
+                rdreply.readres_u.reply.data.data_val = (char *)&block +
+                                        rdreply.readres_u.reply.data.data_len;
+                blocknum--;
+            } else {
+                if (blocknum < maxblks)
+                    argrd.count = Bytes_per_block;
+                else
+                    argrd.count = (maxfiles - filenum)
+                                  * (Bytes_per_block/Kb_per_block);
+                rdreply.readres_u.reply.data.data_val = (char *)&block;
+            }
+            argrd.totalcount = argrd.count; /* unused */
+
+            verror(VAL_VERBOSE, I,
+                   "validating read %d bytes @ offset %d from %s ...\n",
+                    argrd.count, argrd.offset,
+                    Io_files[filenum].file_name);
+
+            val_op_read(&argrd, &rdreply);
+
+            if (rdreply.status == NFS_OK) {
+                (void) compare_fattr(Ops[READ].name, Io_files[filenum].file_name,
+                              &Io_files[filenum].attributes2,
+                              &rdreply.readres_u.reply.attributes);
+                Io_files[filenum].attributes2 =
+                                        rdreply.readres_u.reply.attributes;
+                argrd.offset += rdreply.readres_u.reply.data.data_len;
+            } else {
+                verror(VAL_BATCH, W, "read %s failed: %m\n",
+                        Io_files[filenum].file_name);
+            }
+
+            if (argrd.count ==
+                (block.sum != sum((unsigned char *) &block.len,
+                                  block.len + sizeof(block.len)))) {
+                verror(VAL_BATCH, W, "read %s checksum mismatch\n",
+                        Io_files[filenum].file_name);
+            }
+        }
+    }
+
+    /* validate readdir */
+    numfiles = 0;
+
+    (void) memmove((char *) &argrdir.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+    (void) memset((char *) argrdir.cookie, '\0', NFS_COOKIESIZE);
+    argrdir.count = DEFAULT_MAX_BUFSIZE;
+
+    (void) memset((char *) &rdirreply, '\0', sizeof(rdirreply));
+    rdirreply.readdirres_u.reply.max_entries = entry_cnt;
+    rdirreply.readdirres_u.reply.entries = entry_stream;
+    for (i = 0; i < entry_cnt; i++) {
+        entry_stream[i].name = &name_stream[i * SFS_MAXNAMLEN];
+    }
+
+    do {
+        verror(VAL_VERBOSE, I, "validating readdir %d entries of %s ...\n",
+                rdirreply.readdirres_u.reply.max_entries, Testdirname);
+        val_op_readdir(&argrdir, &rdirreply);
+
+        if (rdirreply.status == NFS_OK) {
+            for (i = 0; i < rdirreply.readdirres_u.reply.max_entries; i++) {
+                numfiles++;
+                entry_stream[i].name[entry_stream[i].name_len] = '\0';
+                if (!entry_stream[i].valid) {
+                    verror(VAL_BATCH, W,
+                          "readdir %s error: entry %d (%s) is marked invalid\n",
+                            Testdirname, i, entry_stream[i].name);
+                }
+                if ((!strcmp(entry_stream[i].name, ".")) ||
+                    (!strcmp(entry_stream[i].name, "..")) ) {
+                    numfiles--;
+                    continue;
+                }
+                for (filenum = 0; filenum < NUMFILES; filenum++) {
+                    if (!strcmp(entry_stream[i].name, Io_files[filenum].file_name)) {
+                        if (entry_stream[i].fileid !=
+                                    Io_files[filenum].attributes2.fileid) {
+                            verror(VAL_BATCH, E,
+                                  "readdir %s error: file %s fileid mismatch\n",
+                                    Testdirname, entry_stream[i].name);
+
+                            verror(VAL_BATCH, W,
+                                   "   fileid: got = %d, original = %d\n",
+                                    entry_stream[i].fileid,
+                                   Io_files[filenum].attributes2.fileid);
+                        }
+                        break;
+                    }
+                }
+                if (filenum == NUMFILES) {
+                    verror(VAL_BATCH, W,
+               "readdir %s error: file \"%s\" was not created within sfs\n",
+                            Testdirname, entry_stream[i].name);
+                }
+            }
+
+            if (i < entry_cnt && entry_stream[i].valid) {
+                verror(VAL_BATCH, W,
+                        "readdir %s error: valid entries exceeded maximum\n",
+                        Testdirname);
+            }
+
+        } else {
+            verror(VAL_BATCH, W, "readdir %s failed: %m\n", Testdirname);
+        }
+
+        (void) memmove((char *)argrdir.cookie,
+        (char *)entry_stream[rdirreply.readdirres_u.reply.max_entries-1].cookie,
+                       NFS_COOKIESIZE);
+
+    } while (rdirreply.readdirres_u.reply.eof == 0);
+
+    if (numfiles != NUMFILES) {
+        verror(VAL_BATCH, W,
+                        "readdir %s error: the number of files found\n\
+does not match with the number of files created within sfs\n", Testdirname);
+    }
+
+} /* validate_read_write */
+
+
+static void
+validate_rename(void)
+{
+    renameargs  argrn;
+    nfsstat     rnreply;
+    int         filenum;
+    char        newname[SFS_MAXNAMLEN];
+    int         rncount = 0;
+
+    (void) memmove((char *) &argrn.from.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+    (void) memmove((char *) &argrn.to.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+
+    for (filenum=0; filenum < NUMFILES; filenum++) {
+        if (Io_files[filenum].state != Exists)
+            continue;
+
+        rncount++;
+        (void) sprintf(newname, "n%s", Io_files[filenum].file_name);
+        argrn.from.name = Io_files[filenum].file_name;
+        argrn.to.name = newname;
+
+        verror(VAL_VERBOSE, I, "validating rename %s %s ...\n",
+                argrn.from.name, argrn.to.name);
+
+        val_op_rename(&argrn, &rnreply);
+
+        if (rnreply == NFS_OK) {
+            (void) strcpy(Io_files[filenum].file_name, newname);
+        } else {
+            verror(VAL_BATCH, W, "rename %s to %s failed: %m\n",
+                    Io_files[filenum].file_name, newname);
+        }
+
+    }
+
+    if (!rncount) {
+        verror(VAL_BATCH, E, "validate_rename: no files renamed\n");
+        verror(VAL_BATCH, W, "    due to previous operation error\n");
+    }
+
+} /* validate_rename */
+
+
+static int
+compare_fattr(
+    char *      op,
+    char *      fname,
+    fattr *     attr1,
+    fattr *     attr2)
+{
+    int         ret = TRUE;
+    int         prev_warn = FALSE; /* -1 info warning */
+    int         flag_error = FALSE;
+
+    if (attr1->type != attr2->type) {
+        if (attr1->type == 0xFFFFFFFF) {
+            prev_warn = TRUE;
+            if (ret) {
+                verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                    fname, op);
+            }
+            verror(VAL_BATCH, I, "    type: current = %d, previous =  %d\n",
+                   attr2->type, attr1->type);
+            attr1->type = attr2->type;
+            ret = FALSE;
+        }
+        else {
+                if (ret) {
+                        verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                            fname, op);
+                }
+                verror(VAL_BATCH, E, "    type: current = %d, previous =  %d\n",
+                        attr2->type, attr1->type);
+                ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+    if ((attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) {
+        if (attr1->mode == (unsigned int)0xFFFFFFFF) {
+                prev_warn = TRUE;
+                if (ret) {
+                        verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                                fname, op);
+                }
+                verror(VAL_BATCH, I, "    mode: current = %7o, previous =  %7o\n",
+                        attr2->mode, attr1->mode);
+                attr1->mode =  attr2->mode;
+                ret = FALSE;
+        }
+        else {
+                if (ret) {
+                        verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                            fname, op);
+                }
+                verror(VAL_BATCH, E, "    mode: current = %d, previous =  %d\n",
+                        attr2->mode, attr1->mode);
+                ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+    if (attr1->nlink != attr2->nlink) {
+        if (attr1->nlink == (unsigned int)0xFFFFFFFF) {
+                prev_warn = TRUE;
+                if (ret) {
+                        verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                                fname, op);
+                }
+                verror(VAL_BATCH, I, "    nlink: current = %d, previous =  %d\n",
+                        attr2->nlink, attr1->nlink);
+                attr1->nlink =  attr2->nlink;
+                ret = FALSE;
+        }
+        else {
+                if (ret) {
+                        verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                            fname, op);
+                }
+                verror(VAL_BATCH, E, "    nlink: current = %d, previous =  %d\n",
+                        attr2->nlink, attr1->nlink);
+                ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+
+    /*
+     * Check for user "nobody", UID -2, which may be untranslated from
+     * sixteen-bit two's complement.
+     */
+    if (attr1->uid != attr2->uid && !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+        attr2->uid == 65534) && attr1->uid ==0)) {
+        if (attr1->uid == (unsigned int)0xFFFFFFFF) {
+                prev_warn = TRUE;
+                if (ret) {
+                        verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                                fname, op);
+                }
+                verror(VAL_BATCH, I, "    uid: current = %d, previous =  %d\n",
+                        attr2->uid, attr1->uid);
+                attr1->uid =  attr2->uid;
+                ret = FALSE;
+        }
+        else {
+                if (ret) {
+                        verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                            fname, op);
+                }
+                verror(VAL_BATCH, E, "    uid: current = %d, previous =  %d\n",
+                        attr2->uid, attr1->uid);
+                ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+    if (attr1->gid != attr2->gid && attr2->gid != 0) {
+/*
+    if (attr1->gid != attr2->gid) {
+*/
+        if (attr1->gid == (unsigned int)0xFFFFFFFF) {
+                prev_warn = TRUE;
+                if (ret) {
+                        verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                                fname, op);
+                }
+                verror(VAL_BATCH, I, "    gid: current = %d, previous =  %d\n",
+                        attr2->gid, attr1->gid);
+                attr1->gid =  attr2->gid;
+                ret = FALSE;
+        }
+        else {
+                if (ret) {
+                        verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                            fname, op);
+                }
+                verror(VAL_BATCH, E, "    gid: current = %d, previous =  %d\n",
+                        attr2->gid, attr1->gid);
+                ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+    if (attr1->size != attr2->size) {
+        if (strcmp(op, Ops[WRITE].name)) {
+            if (attr1->size == (unsigned int)0xFFFFFFFF) {
+                prev_warn = TRUE;
+                if (ret) {
+                        verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                                fname, op);
+                }
+                verror(VAL_BATCH, I, "    size: current = %d, previous =  %d\n",
+                        attr2->size, attr1->size);
+                attr1->size =  attr2->size;
+                ret = FALSE;
+            }
+            else {
+                if (ret) {
+                        verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                            fname, op);
+                }
+                verror(VAL_BATCH, E, "    size: current = %d, previous =  %d\n",
+                        attr2->size, attr1->size);
+                ret = FALSE;
+                flag_error = TRUE;
+            }
+        }
+    }
+
+    if (attr1->blocksize != attr2->blocksize) {
+        if (attr1->blocksize == (unsigned int)0xFFFFFFFF) {
+             prev_warn = TRUE;
+             if (ret) {
+                     verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                             fname, op);
+             }
+             verror(VAL_BATCH, I, "    blocksize: current = %d, previous =  %d\n",
+                     attr2->blocksize, attr1->blocksize);
+             attr1->blocksize =  attr2->blocksize;
+             ret = FALSE;
+        }
+        else {
+             if (ret) {
+                     verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                         fname, op);
+             }
+             verror(VAL_BATCH, E, "    blocksize: current = %d, previous =  %d\n",
+                     attr2->blocksize, attr1->blocksize);
+             ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+
+    /* compare rdev only if type == NFCHR or NFBLK */
+    if ((attr1->type == NFCHR || attr1->type == NFBLK) &&
+        attr1->rdev != attr2->rdev) {
+        if (attr1->rdev == (unsigned int)0xFFFFFFFF) {
+             prev_warn = TRUE;
+             if (ret) {
+                     verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                             fname, op);
+             }
+             verror(VAL_BATCH, I, "    rdev: current = %d, previous =  %d\n",
+                     attr2->rdev, attr1->rdev);
+             attr1->rdev =  attr2->rdev;
+             ret = FALSE;
+        }
+        else {
+             if (ret) {
+                     verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                         fname, op);
+             }
+             verror(VAL_BATCH, E, "    rdev: current = %d, previous =  %d\n",
+                     attr2->rdev, attr1->rdev);
+             ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+
+    /*
+     * The NFS specification does not require that the number of blocks
+     * associated with a file remain constant.  Certain file systems
+     * may pre-allocate more blocks than necessary then trim them
+     * back ("garbage collect") or even blow holes in files that have
+     * all zero blocks.
+     * We must check that we never get back -1.
+     */
+    if (attr1->blocks != attr2->blocks) {
+        if (strcmp(op, Ops[WRITE].name)) {
+            if (attr1->blocks == (unsigned int)0xFFFFFFFF) {
+                 prev_warn = TRUE;
+                 if (ret) {
+                     verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                             fname, op);
+                 }
+                verror(VAL_BATCH, I, "    blocks: current = %d, previous =  %d\n",
+                         attr2->blocks, attr1->blocks);
+                 attr1->blocks =  attr2->blocks;
+                 ret = FALSE;
+            }
+        }
+    }
+
+
+    if (attr1->fsid != attr2->fsid) {
+        if (attr1->fsid == (unsigned int)0xFFFFFFFF) {
+             prev_warn = TRUE;
+             if (ret) {
+                     verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                             fname, op);
+             }
+             verror(VAL_BATCH, I, "    fsid: current = %d, previous =  %d\n",
+                     attr2->fsid, attr1->fsid);
+             attr1->fsid =  attr2->fsid;
+             ret = FALSE;
+        }
+        else {
+             if (ret) {
+                     verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                         fname, op);
+             }
+             verror(VAL_BATCH, E, "    fsid: current = %d, previous =  %d\n",
+                     attr2->fsid, attr1->fsid);
+             ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+    if (attr1->fileid != attr2->fileid) {
+        if (attr1->fileid == (unsigned int)0xFFFFFFFF) {
+             prev_warn = TRUE;
+             if (ret) {
+                     verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+                             fname, op);
+             }
+             verror(VAL_BATCH, I, "    fileid: current = %d, previous =  %d\n",
+                     attr2->fileid, attr1->fileid);
+             attr1->fileid =  attr2->fileid;
+             ret = FALSE;
+        }
+        else {
+             if (ret) {
+                     verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                         fname, op);
+             }
+             verror(VAL_BATCH, E, "    fileid: current = %d, previous =  %d\n",
+                     attr2->fileid, attr1->fileid);
+             ret = FALSE;
+                flag_error = TRUE;
+        }
+    }
+
+    if (prev_warn) {
+        verror(VAL_BATCH, I,
+                "\n        Warning: the previous value of a field is -1,\n");
+        verror(VAL_BATCH, I,
+                "        this resulted from an unused field returned by\n");
+        verror(VAL_BATCH, I,
+                "        the previous operation on this file/directory.\n");
+        verror(VAL_BATCH, I,
+            "        The current value is now stored for future comparison\n\n");
+    }
+
+    if (flag_error)
+        verror(VAL_BATCH, W,"\n");
+
+    return(flag_error);
+
+} /* ckompare_fattr */
+
+
+static int
+compare_sattr(
+    char *      op,
+    char *      fname,
+    sattr *     attr1,
+    fattr *     attr2)
+{
+    int         ret = TRUE;
+    char        msg[80];
+
+    msg[0] = '\0';
+
+    if (attr1->mode != (unsigned int)0xFFFFFFFF &&
+        (attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) {
+        if (ret) {
+            verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                    fname, op);
+
+        }
+        verror(VAL_BATCH, E, "    mode: returned = %7o, specified = %7o\n",
+                attr2->mode, attr1->mode);
+        ret = FALSE;
+    }
+
+    /*
+     * Check for user "nobody", UID -2, which may be untranslated from
+     * sixteen-bit two's complement.
+     */
+    if (attr1->uid != (unsigned int)0xFFFFFFFF && attr1->uid != attr2->uid &&
+               !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+               attr2->uid == 65534) &&
+                       attr1->uid == 0)) {
+        if (ret) {
+            verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                    fname, op);
+        }
+        if (attr1->uid == 0)
+            (void) strcat(msg," (is root UID mapped to other UID?)");
+        verror(VAL_BATCH, E, "    uid: returned = %d, specified = %d %s\n",
+                attr2->uid, attr1->uid, msg);
+        ret = FALSE;
+    }
+    if (attr1->gid != (unsigned int)0xFFFFFFFF &&
+                       attr1->gid != attr2->gid &&
+                       attr2->gid != 0) {
+/*
+   if (attr1->gid != 0xFFFFFFFF && attr1->gid != attr2->gid) {
+*/
+        if (ret) {
+            verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                    fname, op);
+        }
+        verror(VAL_BATCH, E, "    gid: returned = %d, specified = %d\n",
+                attr2->gid, attr1->gid);
+        ret = FALSE;
+    }
+
+    if (attr1->size != (unsigned int)0xFFFFFFFF &&
+                       attr1->size != attr2->size) {
+        if (ret) {
+            verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                    fname, op);
+        }
+        verror(VAL_BATCH, E, "    size: returned = %d, specified = %d\n",
+                attr2->size, attr1->size);
+        ret = FALSE;
+    }
+
+    if (!ret)
+        verror(VAL_BATCH, W,"\n");
+
+    return(ret);
+
+} /* compare_sattr */
+
+
+/*
+ * Return the BSD checksum of buf[0..len-1]
+ */
+static uint16_t
+sum(
+    unsigned char *     buf,
+    uint_t              len)
+{
+    uint16_t      cksum;
+
+    cksum = 0;
+    for (; len--; buf++) {
+        if (cksum & 1)
+            cksum = (cksum >> 1) + 0x8000;
+        else
+            cksum >>= 1;
+        cksum += (uint16_t) *buf;
+        cksum &= 0xFFFF;
+    }
+    return(cksum);
+} /* sum */
+
+
+static void
+validate_remove(void)
+{
+    diropargs   args;
+    nfsstat     reply;
+    diropres    lreply;
+    int         filenum;
+    char *      op;
+
+    (void) memmove((char *) &args.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+
+    for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+        if (Io_files[filenum].state != Exists)
+            continue;
+
+        args.name = Io_files[filenum].file_name;
+
+        if (Io_files[filenum].attributes2.type == NFDIR) {
+            op = Ops[RMDIR].name;
+            verror(VAL_VERBOSE, I, "validating rmdir %s ...\n",
+                    args.name);
+            val_op_rmdir(&args, &reply);
+        } else {
+            op = Ops[REMOVE].name;
+            verror(VAL_VERBOSE, I, "validating remove %s ...\n",
+                    args.name);
+            val_op_remove(&args, &reply);
+        }
+
+        if (reply == NFS_OK) {
+            /* make sure the file is removed from the directory */
+            val_op_lookup(&args, &lreply);
+
+            if (lreply.status == NFSERR_NOENT) {
+                Io_files[filenum].state = Nonexistent;
+            } else if (lreply.status == NFS_OK) {
+                verror(VAL_BATCH, W, "%s %s: file not removed\n",
+                       op, Io_files[filenum].file_name);
+            } else {
+                verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+                       Io_files[filenum].file_name);
+
+            }
+        } else {
+            verror(VAL_BATCH, W, "%s %s failed: %m\n", op,
+                    Io_files[filenum].file_name);
+        }
+
+    }
+
+} /* validate_remove */
+
+
+static void
+validate_cleanup(void)
+{
+    free(Io_files);
+    free(Non_io_files);
+    free(Dirs);
+    free(Symlinks);
+    clnt_destroy(NFS_client);
+
+} /* validate_cleanup */
+
+
+static void
+validate_exit(void)
+{
+    if (!Validate_errors) {
+        verror(VAL_BATCH, I, "validation completed successfully.\n");
+        exit(0);
+    } else {
+        verror(VAL_BATCH, I, "validation terminated with errors\n");
+        exit(1);
+    }
+
+} /* validate_exit */
+
+
+/* PRINTFLIKE3 */
+static void
+verror(
+    int         opt,
+    ValMsgType  msgtype,
+    char *      fmt,
+    ...)
+{
+    va_list    ap;
+    char        buf[1024];
+    char *      bp = buf;
+    char *      fp;
+    int         repeat;
+    char *     sp;
+
+    va_start(ap, fmt);
+
+    /*
+     * Expand the "%m" format character into the descriptive string
+     * for the current value of errno.  Printf handles the other "%"
+     * formatting characters.
+     */
+    if (Validate >= opt) {
+        for (fp = fmt; *fp; fp++) {
+            if (*fp == '%' && fp[1] == 'm') {
+               if ((sp = strerror(errno)) == NULL) {
+                    (void) sprintf(bp, "unknown error %d", errno);
+                } else {
+                    (void) strcpy(bp, sp);
+                }
+                bp = buf + strlen(buf);
+                fp++;
+            } else {
+                *bp++ = *fp;
+            }
+        }
+        *bp = '\0';
+        (void) vprintf(buf, ap);
+    }
+    va_end(ap);
+
+    if (msgtype != I)
+        Validate_errors++;
+
+    if (msgtype == W && Validate == VAL_INTERACTIVE) {
+        repeat = 1;
+        while (repeat) {
+            char ans[80];
+
+            (void) fprintf(stderr, "continue? (y or n)  ");
+            if (!fgets(ans,80,stdin)) {
+                (void) fprintf(stderr, "\n");
+                continue;
+            }
+            if (ans[0] == 'n' || ans[0] == 'N') {
+                validate_exit();
+                exit(1);
+            } else if (ans[0] == 'y' || ans[0] == 'Y') {
+                repeat = 0;
+                break;
+            }
+        }
+    }
+
+} /* verror */
+
+
+static void
+val_op_null(void)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_NULL, 
+               xdr_void, (char *)0, xdr_void, (char *)0, 
+               Nfs_timers[Ops[NULLCALL].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "null");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_getattr(fhandle_t *args, attrstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_GETATTR, 
+           xdr_getattr, (char *)args, xdr_getattr, (char *)reply, 
+           Nfs_timers[Ops[GETATTR].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "getattr");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_setattr(sattrargs *args, attrstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR, 
+               xdr_setattr, (char *)args, xdr_setattr, (char *)reply, 
+               Nfs_timers[Ops[SETATTR].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "setattr");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_lookup(diropargs *args, diropres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP, 
+               xdr_lookup, (char *)args, xdr_lookup, (char *)reply, 
+               Nfs_timers[Ops[LOOKUP].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "lookup");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_readlink(fhandle_t *args, readlinkres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_READLINK, 
+               xdr_readlink, (char *)args, xdr_readlink, (char *)reply, 
+               Nfs_timers[Ops[READLINK].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "readlink");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_read(readargs *args, readres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_READ, 
+               xdr_read, (char *)args, xdr_read, (char *)reply, 
+               Nfs_timers[Ops[READ].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "read");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_write(writeargs *args, attrstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client,
+               NFSPROC_WRITE, xdr_write, (char *)args, xdr_write, (char *)reply,
+               Nfs_timers[Ops[WRITE].call_class]);
+
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+                   clnt_perror(NFS_client, "write");
+                   Validate_errors++;
+                   validate_exit();
+            }
+       }
+}
+
+static void
+val_op_create(createargs *args, diropres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client,
+           NFSPROC_CREATE, xdr_create, (char *)args, xdr_create, (char *)reply, 
+           Nfs_timers[Ops[CREATE].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "create");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_remove(diropargs *args, nfsstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client,
+               NFSPROC_REMOVE, xdr_remove, (char *)args, xdr_remove, (char *)reply, 
+               Nfs_timers[Ops[REMOVE].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "remove");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_rename(renameargs *args, nfsstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_RENAME, 
+               xdr_rename, (char *)args, xdr_rename, (char *)reply, 
+               Nfs_timers[Ops[RENAME].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "rename");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_link(linkargs *args, nfsstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_LINK, 
+               xdr_link, (char *)args, xdr_link, (char *)reply, 
+               Nfs_timers[Ops[LINK].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "link");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_symlink(symlinkargs *args, nfsstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK, 
+               xdr_symlink, (char *)args, xdr_symlink, (char *)reply, 
+               Nfs_timers[Ops[SYMLINK].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "symlink");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_mkdir(mkdirargs *args, diropres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR, 
+               xdr_mkdir, (char *)args, xdr_mkdir, (char *)reply, 
+               Nfs_timers[Ops[MKDIR].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "mkdir");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_rmdir(diropargs *args, nfsstat *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR, 
+               xdr_rmdir, (char *)args, xdr_rmdir, (char *)reply, 
+               Nfs_timers[Ops[RMDIR].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "rmdir");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_readdir(readdirargs *args, readdirres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_READDIR, 
+               xdr_readdir, (char *)args, xdr_readdir, (char *)reply, 
+               Nfs_timers[Ops[READDIR].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "readdir");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_statfs(fhandle_t *args, statfsres *reply)
+{
+       int rpc_stat;
+
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC_STATFS, 
+               xdr_statfs, (char *)args, xdr_statfs, (char *)reply, 
+               Nfs_timers[Ops[FSSTAT].call_class]);
+            if (rpc_stat == RPC_SUCCESS)
+                break;
+            if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "statfs");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+static void
+create_tmp_handles(void)
+{
+       int filenum;
+       for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+               if(Io_files[filenum].fh_data == (sfs_fh_data *)0)
+               {
+                       Io_files[filenum].fh_data = 
+                                       calloc(1,sizeof(sfs_fh_data));
+                       Io_files[filenum].attributes2.type = NFNON;
+                       Io_files[filenum].attributes3.type = NF3NON;
+               }
+       }
+}
+
+static void
+delete_tmp_handles()
+{
+       int filenum;
+       for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+               if(Io_files[filenum].fh_data != (sfs_fh_data *)0)
+               {
+                       free(Io_files[filenum].fh_data);
+                       Io_files[filenum].fh_data=(sfs_fh_data *)0;
+               }
+       }
+}
+
+
+
+
+
+
+
+