Import TBBT (NFS trace replay).
[bluesky.git] / TBBT / trace_play / sfs_3_vld.c
diff --git a/TBBT/trace_play/sfs_3_vld.c b/TBBT/trace_play/sfs_3_vld.c
new file mode 100644 (file)
index 0000000..8ede891
--- /dev/null
@@ -0,0 +1,2126 @@
+#ifndef lint
+static char sfs_3_vldSid[] = "@(#)sfs_3_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_3_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 *, sattr3 *, fattr3 *)
+ *      int compare_fattr(char *, char *, fattr3 *, fattr3 *)
+ *      uint16_t sum(unsigned char *, int)
+ *      void validate_remove(void)
+ *      void validate_cleanup(void)
+ *      void validate_exit(void)
+ *      void verror(int, ValMsgType, char *, ...)
+ *
+ *.Revision History
+ *     11-Jul-94       ChakChung Ng    Created
+ */
+
+
+/*
+ * -------------------------  Include Files  -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.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    21
+#define NUMDIRS                10
+#define NUMLINKS       10
+#define NUMSYMLINKS    10
+#define NUMFILES       NUMREGFILES + NUMDIRS + NUMLINKS + NUMSYMLINKS
+#define NUMFRAGS       8
+
+static void val_op_null(void);
+static void val_op_getattr(GETATTR3args *, GETATTR3res *);
+static void val_op_setattr(SETATTR3args *, SETATTR3res *);
+static void val_op_lookup(LOOKUP3args *, LOOKUP3res *);
+static void val_op_access(ACCESS3args *, ACCESS3res *);
+static void val_op_readlink(READLINK3args *, READLINK3res *);
+static void val_op_read(READ3args *, READ3res *);
+static void val_op_write(WRITE3args *, WRITE3res *);
+static void val_op_create(CREATE3args *, CREATE3res *);
+static void val_op_mkdir(MKDIR3args *, MKDIR3res *);
+static void val_op_symlink(SYMLINK3args *, SYMLINK3res *);
+static void val_op_mknod(MKNOD3args *, MKNOD3res *);
+static void val_op_remove(REMOVE3args *, REMOVE3res *);
+static void val_op_rmdir(RMDIR3args *, RMDIR3res *);
+static void val_op_rename(RENAME3args *, RENAME3res *);
+static void val_op_link(LINK3args *, LINK3res *);
+static void val_op_readdir(READDIR3args *, READDIR3res *);
+static void val_op_readdirplus(READDIRPLUS3args *, READDIRPLUS3res *);
+static void val_op_fsstat(FSSTAT3args *, FSSTAT3res *);
+static void val_op_fsinfo(FSINFO3args *, FSINFO3res *);
+static void val_op_pathconf(PATHCONF3args *, PATHCONF3res *);
+static void val_op_commit(COMMIT3args *, COMMIT3res *);
+
+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 *, sattr3 *, fattr3 *);
+static int compare_fattr(char *, char *, fattr3 *, fattr3 *);
+static uint16_t sum(unsigned char *, int);
+static void verror(int, ValMsgType, char *, ...);
+static void create_3tmp_handles(void);
+static void delete_3tmp_handles(void);
+
+/*
+ * ----------------------  Static Declarations ----------------------
+ */
+
+int Validate;
+
+static int Validate_errors = 0;
+static char Testdirname[SFS_MAXPATHLEN];    /* test dir component name */
+/*
+ * packed structure to keep track of file status
+ */
+struct sfs_fileinfo {
+    int file_found:1,   /* file has been found */
+        file_is_dup:1,  /* file has a duplicate */
+        pad:30;         /* pad the rest */
+};
+
+typedef struct sfs_fileinfo sfs_fileinfo;
+/*
+ * This vector is used for readdirplus validation currently, but could be
+ * extended to keep track of other interesting pieces of information.
+ */
+static sfs_fileinfo check_files[NUMFILES];
+
+/*
+ * ----------------------  SFS Validation Suite  ----------------------
+ */
+
+/*
+ * XXXXX Must make sure that we validate that all servers return back
+ * XXXXX All optional values
+ */
+void
+Validate_ops(
+    int         argc,
+    char *      argv[])
+{
+    char *      valdir;
+    CLIENT *    mount_client_ptr;
+    int i;
+
+    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_V3;
+
+    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_3tmp_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);
+
+        /*
+         * initialize the check_file array
+         */
+        (void) memset((void *) check_files, '\0', sizeof(check_files));
+
+       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_3tmp_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_V3,
+                                        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;
+    CREATE3args                argcr;
+    CREATE3res         repcr;
+    MKDIR3args         argmk;
+    MKDIR3res          repmk;
+    LINK3args          argln;
+    LINK3res           repln;
+    SYMLINK3args       argsl;
+    SYMLINK3res                repsl;
+    LOOKUP3args                arglp;
+    LOOKUP3res         replp;
+    READLINK3args      argrl;
+    READLINK3res       reprl;
+    char                sl_target_path[NFS_MAXPATHLEN];
+    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 */
+           (void) memmove((char *) &argcr.where.dir, (char *) &Export_dir.fh3,
+                               sizeof (nfs_fh3));
+           argcr.where.name = Cur_filename;
+           argcr.how.mode = UNCHECKED;
+           argcr.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+           argcr.how.createhow3_u.obj_attributes.mode.mode =
+                                                       (NFSMODE_REG | 0666);
+           argcr.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+           argcr.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+           argcr.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+           argcr.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+           argcr.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+           argcr.how.createhow3_u.obj_attributes.atime.atime.seconds =
+                                                       Cur_time.esec;
+           argcr.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+                                                       Cur_time.usec * 1000;
+           argcr.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+           argcr.how.createhow3_u.obj_attributes.mtime.mtime.seconds =
+                                                       Cur_time.esec;
+           argcr.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+                                                       Cur_time.usec * 1000;
+           argcr.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+           argcr.how.createhow3_u.obj_attributes.size.size._p._u =
+                                                       (uint32_t) 0;
+           argcr.how.createhow3_u.obj_attributes.size.size._p._l =
+                                                       (uint32_t) 0;
+
+           (void) memset((char *) &repcr.resok.obj.handle, '\0',
+                                                       sizeof (nfs_fh3));
+           verror(VAL_VERBOSE, I, "validating create file %s ...\n",
+                   Cur_filename);
+           val_op_create(&argcr, &repcr);
+
+           if (repcr.status == NFS3_OK) {
+               Cur_file_ptr->state = Exists;
+               (void) memmove((char *) &Cur_file_ptr->fh3,
+                               (char *) &repcr.resok.obj.handle,
+                               sizeof (nfs_fh3));
+               (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+               (void) memmove((char *) &Cur_file_ptr->attributes3,
+                               (char *) &repcr.resok.obj_attributes.attr,
+                               sizeof(Cur_file_ptr->attributes3));
+               (void) compare_sattr(Ops[CREATE].name, Io_files[filenum].file_name,
+                                       &argcr.how.createhow3_u.obj_attributes,
+                                       &Cur_file_ptr->attributes3);
+           } else {
+               Cur_file_ptr->state = Nonexistent;
+               errno = (int)repcr.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 - NUMREGFILES);
+
+           /* directory creation */
+           (void) memmove((char *) &argmk.where.dir, (char *) &Export_dir.fh3,
+                       sizeof (nfs_fh3));
+           argmk.where.name = Cur_filename;
+           argmk.attributes.mode.set_it = TRUE;
+           argmk.attributes.mode.mode = (NFSMODE_DIR | 0777);
+           argmk.attributes.uid.set_it = TRUE;
+           argmk.attributes.uid.uid = Cur_uid;
+           argmk.attributes.gid.set_it = TRUE;
+           argmk.attributes.gid.gid = Cur_gid;
+           argmk.attributes.size.set_it = FALSE;
+           argmk.attributes.size.size._p._u = (uint32_t) 0;
+           argmk.attributes.size.size._p._l = (uint32_t) 0;
+           argmk.attributes.atime.set_it = TRUE;
+           argmk.attributes.atime.atime.seconds = Cur_time.esec;
+           argmk.attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+           argmk.attributes.mtime.set_it = TRUE;
+           argmk.attributes.mtime.mtime.seconds = Cur_time.esec;
+           argmk.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+
+           (void) memset((char *) &repmk.resok.obj.handle, '\0', sizeof (nfs_fh3));
+           verror(VAL_VERBOSE, I, "validating mkdir %s ...\n", Cur_filename);
+           val_op_mkdir(&argmk, &repmk);
+
+           if (repmk.status == NFS3_OK) {
+               Cur_file_ptr->state = Exists;
+               (void) memmove((char *) &Cur_file_ptr->fh3,
+                               (char *) &repmk.resok.obj.handle,
+                               sizeof (nfs_fh3));
+               (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+               (void) memmove((char *) &Cur_file_ptr->attributes3,
+                               (char *) &repmk.resok.obj_attributes.attr,
+                               sizeof(Cur_file_ptr->attributes3));
+               (void) compare_sattr(Ops[MKDIR].name, Io_files[filenum].file_name,
+                               &argmk.attributes, &Cur_file_ptr->attributes3);
+           } 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 - NUMDIRS);
+
+           /* hard link creation */
+           target_filenum = NUMFILES-NUMSYMLINKS-1-filenum;
+           (void) memmove((char *) &argln.file,
+                       (char *) &Io_files[target_filenum].fh3,
+                       sizeof (nfs_fh3));
+           (void) memmove((char *) &argln.link.dir, (char *) &Export_dir.fh3,
+                       sizeof (nfs_fh3));
+           argln.link.name = Cur_filename;
+
+           verror(VAL_VERBOSE, I, "validating link %s %s ...\n",
+                   Io_files[target_filenum].file_name, Cur_filename);
+           val_op_link(&argln, &repln);
+
+           if (repln.status == NFS3_OK) {
+               Cur_file_ptr->state = Exists;
+               (void) memmove((char *) &Cur_file_ptr->fh3,
+                               (char *) &Io_files[target_filenum].fh3,
+                               sizeof (nfs_fh3));
+               (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+               Cur_file_ptr->attributes3 = Io_files[target_filenum].attributes3;
+               Io_files[target_filenum].attributes3.nlink++;
+               Cur_file_ptr->attributes3.nlink++;
+           } else {
+               Cur_file_ptr->state = Nonexistent;
+               verror(VAL_BATCH, W, "link %s failed: %m\n", Cur_filename);
+               exit(1);
+           }
+
+       } else {
+
+           (void) sprintf(Cur_filename, Symspec, filenum -
+                                       (NUMREGFILES + NUMDIRS + NUMLINKS));
+
+           /* symbolic link creation */
+           target_filenum = NUMFILES-1-filenum;
+           (void) memmove((char *) &argsl.where.dir, (char *) &Export_dir.fh3,
+                       sizeof (nfs_fh3));
+           argsl.where.name = Cur_filename;
+           (void) sprintf(sl_target_path,
+                          "./%s", Io_files[target_filenum].file_name);
+           argsl.symlink.symlink_attributes.size.set_it = TRUE;
+           argsl.symlink.symlink_attributes.size.size._p._u =
+                                       (uint32_t) 0;
+           argsl.symlink.symlink_attributes.size.size._p._l =
+                                       (uint32_t) strlen(sl_target_path);
+           argsl.symlink.symlink_data = sl_target_path;
+           argsl.symlink.symlink_attributes.mode.set_it = TRUE;
+           argsl.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777);
+           argsl.symlink.symlink_attributes.uid.set_it = TRUE;
+           argsl.symlink.symlink_attributes.uid.uid = Cur_uid;
+           argsl.symlink.symlink_attributes.gid.set_it = TRUE;
+           argsl.symlink.symlink_attributes.gid.gid = Cur_gid;
+           argsl.symlink.symlink_attributes.atime.set_it = TRUE;
+           argsl.symlink.symlink_attributes.atime.atime.seconds =
+                                                       Cur_time.esec;
+           argsl.symlink.symlink_attributes.atime.atime.nseconds =
+                                                       Cur_time.usec * 1000;
+           argsl.symlink.symlink_attributes.mtime.set_it = TRUE;
+           argsl.symlink.symlink_attributes.mtime.mtime.seconds =
+                                                       Cur_time.esec;
+           argsl.symlink.symlink_attributes.mtime.mtime.nseconds =
+                                                       Cur_time.usec * 1000;
+
+           verror(VAL_VERBOSE, I, "validating symlink %s %s ...\n",
+                   sl_target_path, Cur_filename);
+           val_op_symlink(&argsl, &repsl);
+
+           if (repsl.status == NFS3_OK) {
+               Cur_file_ptr->state = Exists;
+
+               /* do a lookup to get file handle and attributes */
+               (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3,
+                               sizeof (nfs_fh3));
+               arglp.what.name = Cur_filename;
+               (void) memset((char *) &replp.resok.object, '\0', sizeof (nfs_fh3));
+
+               val_op_lookup(&arglp, &replp);
+
+               if (replp.status == NFS3_OK) {
+                   (void) memmove((char *) &Cur_file_ptr->fh3,
+                               (char *) &replp.resok.object,
+                               sizeof (nfs_fh3));
+                   (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+                   (void) memmove((char *) &Cur_file_ptr->attributes3,
+                               (char *) &replp.resok.obj_attributes.attr,
+                               sizeof (Cur_file_ptr->attributes3));
+                   (void) compare_sattr(Ops[SYMLINK].name,
+                                       Io_files[filenum].file_name,
+                                       &argsl.symlink.symlink_attributes,
+                                       &Cur_file_ptr->attributes3);
+               } else {
+                   verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+                           Cur_filename);
+                   continue;
+               }
+
+               /* validate readlink */
+               (void) memmove((char *) &argrl.symlink,
+                               (char *) &Cur_file_ptr->fh3,
+                               sizeof (nfs_fh3));
+               reprl.resok.data = sym_data;
+
+               verror(VAL_VERBOSE, I, "validating readlink %s ...\n",
+                       Cur_filename);
+               val_op_readlink(&argrl, &reprl);
+
+               if (reprl.status == NFS3_OK) {
+                   if (strcmp(sl_target_path, sym_data)) {
+                       verror(VAL_BATCH, W,
+                           "readlink %s error, result = %s, should be %s\n",
+                           Cur_filename, reprl.resok.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;
+    LOOKUP3args                arglp;
+    LOOKUP3res         replp;
+    GETATTR3args       argga;
+    GETATTR3res                repga;
+    SETATTR3args       argsa;
+    SETATTR3res                repsa;
+
+    /* validate fsstat */
+
+    /* validate lookup */
+    for (filenum = 0; filenum < NUMFILES; filenum++) {
+       (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3,
+                       sizeof (nfs_fh3));
+       arglp.what.name = Io_files[filenum].file_name;
+       (void) memset((char *) &replp.resok.object, '\0', sizeof (nfs_fh3));
+
+       verror(VAL_VERBOSE, I, "validating lookup %s ...\n",
+               Io_files[filenum].file_name);
+       val_op_lookup(&arglp, &replp);
+
+       if (replp.status == NFS3_OK) {
+           if (memcmp((char *) &(Io_files[filenum].fh3),
+                    (char *) &(replp.resok.object), sizeof (nfs_fh3))) {
+               verror(VAL_BATCH, W, "lookup %s: file handle mismatch\n",
+                               Io_files[filenum].file_name);
+               exit(1);
+           }
+           (void) compare_fattr(Ops[LOOKUP].name, Io_files[filenum].file_name,
+                                       &Io_files[filenum].attributes3,
+                                       &replp.resok.obj_attributes.attr);
+       } 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 *) &argga.object,
+                       (char *) &Io_files[filenum].fh3,
+                       sizeof (nfs_fh3));
+
+       verror(VAL_VERBOSE, I, "validating getattr %s ...\n",
+               Io_files[filenum].file_name);
+       val_op_getattr(&argga, &repga);
+
+       if (repga.status == NFS3_OK) {
+           (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+                         &Io_files[filenum].attributes3,
+                         &repga.resok.obj_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);
+       (void) memmove((char *) &argsa.object,
+                       (char *) &Io_files[filenum].fh3,
+                       sizeof (nfs_fh3));
+       argsa.new_attributes.mode.set_it = TRUE;
+       if (filenum >= NUMREGFILES && filenum < NUMREGFILES + NUMDIRS)
+         argsa.new_attributes.mode.mode = 0777;
+       else
+         argsa.new_attributes.mode.mode = 0666;
+       argsa.new_attributes.uid.set_it = FALSE;
+       argsa.new_attributes.uid.uid = 0;
+       argsa.new_attributes.gid.set_it = FALSE;
+       argsa.new_attributes.gid.gid = 0;
+       argsa.new_attributes.size.set_it = FALSE;
+       argsa.new_attributes.size.size._p._u = 0;
+       argsa.new_attributes.size.size._p._l = 0;
+       argsa.new_attributes.atime.set_it = TRUE;
+       argsa.new_attributes.atime.atime.seconds = Cur_time.esec;
+       argsa.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+       argsa.new_attributes.mtime.set_it = TRUE;
+       argsa.new_attributes.mtime.mtime.seconds = Cur_time.esec;
+       argsa.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+       argsa.guard.check = FALSE;
+
+       verror(VAL_VERBOSE, I, "validating setattr %s ...\n",
+                               Io_files[filenum].file_name);
+       val_op_setattr(&argsa, &repsa);
+
+       if (repsa.status == NFS3_OK) {
+           (void) memmove((char *) &Io_files[filenum].attributes3,
+                               (char *) &repsa.resok.obj_wcc.after.attr,
+                               sizeof (Io_files[filenum].attributes3));
+           (void) compare_sattr(Ops[SETATTR].name, Io_files[filenum].file_name,
+                       &argsa.new_attributes, &repsa.resok.obj_wcc.after.attr);
+
+           (void) memmove((char *) &argga.object,
+                               (char *) &Io_files[filenum].fh3,
+                               sizeof (nfs_fh3));
+
+           val_op_getattr(&argga, &repga);
+
+           if (repga.status == NFS3_OK) {
+               (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+                                               &Io_files[filenum].attributes3,
+                                               &repga.resok.obj_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);
+           exit(1);
+       }
+    }
+
+} /* validate_attributes */
+
+#define WAITFOREOF 1
+/*
+ * need to check if readdirplus returns a reasonable amount of data.
+ */
+static void
+val_rddirplus_retsize(uint32_t dircount, uint32_t maxcount,
+                      READDIRPLUS3resok *rp)
+{
+    entryplus3 *esp;
+    static int eof_wait = 0;
+    int i;
+    int size = 0;
+    int tsize = 0;
+    int msize = 0;
+    double mpcnt = 0;
+
+    esp = rp->reply.entries;
+
+    for (i = 0; i < rp->count; i++) {
+            size += sizeof(esp[i].fileid);
+            size += strlen(esp[i].name) * sizeof(char);
+            size += sizeof(esp[i].cookie);
+            tsize += sizeof(esp[i].name_attributes);
+            tsize += sizeof(esp[i].name_handle.handle_follows);
+            tsize += esp[i].name_handle.handle.fh3_length * sizeof(char);
+    }
+
+    msize = size + tsize;
+    mpcnt = (double) msize / (double) maxcount * 100;
+
+    if (rp->reply.eof) {
+        verror(VAL_VERBOSE, I, "readdirplus on %s returned EOF.\n"
+    "\treceived %d bytes of directory information and %d bytes including\n"
+    "\tpost op attributes and filehandle.\n",
+               Testdirname, size, msize);
+    } else if (mpcnt < 80) {
+       eof_wait++;
+        if (eof_wait > WAITFOREOF) {
+            verror(VAL_BATCH, E,
+    "readdirplus on %s did not return a reasonable amount of data.\n"
+    "\treceived %d bytes. should receive close to %d bytes.\n",
+                  Testdirname, msize, maxcount);
+       } else {
+           verror(VAL_VERBOSE, I, "readdirplus on %s did not return EOF.\n"
+    "\treceived %d bytes of directory information and %d bytes including\n"
+    "\tpost op attributes and filehandle.\n",
+                  Testdirname, size, msize);
+       }
+    } else {
+        verror(VAL_VERBOSE, I, "readdirplus on %s did not return EOF.\n"
+    "\treceived %d bytes of directory information and %d bytes including\n"
+    "\tpost op attributes and filehandle.\n",
+               Testdirname, size, msize);
+    }    
+}
+
+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;
+    WRITE3args         argwr;
+    WRITE3res          repwr;
+    READ3args          argrd;
+    READ3res           reprd;
+    READDIR3args       argdr;
+    READDIR3res                repdr;
+    READDIRPLUS3args    argdrp;
+    READDIRPLUS3res     repdrp;
+    int                 maxblks;
+    int                 maxfiles;
+    int                 i;
+    int                 numfiles;
+    int                 filenum;
+    int                 blocknum;
+    entry3              entry_stream[SFS_MAXDIRENTS];
+    entryplus3          entryplus_stream[SFS_MAXDIRENTS];
+
+    /* 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._p._u = argwr.offset._p._l = (uint32_t) 0;
+    argwr.stable = FILE_SYNC;
+    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));
+               /*
+                 * XXX - write kludge.
+                 *
+                 * Writes must be less than 8K in
+                 * size or else the checksum will incur a buffer overflow
+                 */
+                block.len = (block.len % DEFAULT_MAX_BUFSIZE) -
+                        (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,
+                           (int)(block.len + sizeof(block.len)));
+
+           (void) memmove((char *) &argwr.file,
+                               (char *) &Io_files[filenum].fh3,
+                               sizeof (nfs_fh3));
+           argwr.data.data_len = block.len +
+                                 sizeof(block.len) + sizeof(block.sum);
+           argwr.count = argwr.data.data_len;
+
+           verror(VAL_VERBOSE, I,
+                   "validating write %d bytes @ offset %lu to %s ...\n",
+                   argwr.data.data_len, argwr.offset._p._l,
+                   Io_files[filenum].file_name);
+
+           val_op_write(&argwr, &repwr);
+
+           if (repwr.status == NFS3_OK) {
+               (void) compare_fattr(Ops[WRITE].name, Io_files[filenum].file_name,
+                             &Io_files[filenum].attributes3,
+                             &repwr.resok.file_wcc.after.attr);
+               Io_files[filenum].attributes3 = repwr.resok.file_wcc.after.attr;
+                /*
+                 * XXX-bookeeping kludge.
+                 *
+                 * Need to update hardlink attributes, so readdirplus vali-
+                 * dation doesn't barf.  This is necessary because setattr was
+                 * validated on all the test files and the attributes in
+                 * Io_files[] were updated accordingly.  Since the write
+                 * op has been validated on just the regular files, we have to
+                 * make sure that the corresponding indexes in Io_files[] that
+                 * point to the hard links reflect the current file attributes.
+                 */
+                if (filenum < NUMLINKS) {
+                    Io_files[NUMREGFILES+NUMDIRS+NUMLINKS-1-filenum].attributes3 = Io_files[filenum].attributes3;
+                }
+
+           } else {
+               verror(VAL_BATCH, W, "write %s failed: %m\n",
+                       Io_files[filenum].file_name);
+           }
+       }
+       argwr.offset._p._l += Bytes_per_block;
+    }
+
+    /* validate read */
+
+    for (filenum = 0; filenum < maxfiles; filenum++) {
+       (void) memmove((char *) &argrd.file,
+                       (char *) &Io_files[filenum].fh3,
+                       sizeof (nfs_fh3));
+       argrd.offset._p._u = argrd.offset._p._l = (uint32_t) 0;
+       argrd.count = 0;
+       reprd.resok.data.data_len = 0;
+       maxblks = Io_files[filenum].attributes3.size._p._l / Bytes_per_block;
+       for (blocknum = 0; blocknum <= maxblks; blocknum ++) {
+
+           if (argrd.count != reprd.resok.data.data_len) {
+               argrd.count -= reprd.resok.data.data_len;
+               reprd.resok.data.data_val = (char *)&block +
+                                       reprd.resok.data.data_len;
+               blocknum--;
+           } else {
+               if (blocknum < maxblks)
+                   argrd.count = Bytes_per_block;
+               else
+                   argrd.count = (maxfiles - filenum)
+                                 * (Bytes_per_block/Kb_per_block);
+               reprd.resok.data.data_val = (char *)&block;
+           }
+
+           verror(VAL_VERBOSE, I,
+                  "validating read %lu bytes @ offset %lu from %s ...\n",
+                   argrd.count, argrd.offset._p._l,
+                   Io_files[filenum].file_name);
+
+           val_op_read(&argrd, &reprd);
+
+           if (reprd.status == NFS3_OK) {
+               (void) compare_fattr(Ops[READ].name, Io_files[filenum].file_name,
+                             &Io_files[filenum].attributes3,
+                             &reprd.resok.file_attributes.attr);
+               Io_files[filenum].attributes3 = reprd.resok.file_attributes.attr;
+               argrd.offset._p._l += reprd.resok.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,
+                                 (int)(block.len + sizeof(block.len)))))) {
+               verror(VAL_BATCH, W, "read %s checksum mismatch\n",
+                       Io_files[filenum].file_name);
+           }
+       }
+    }
+
+    /* validate readdir */
+    (void) memmove((char *) &argdr.dir, (char *) &Export_dir.fh3,
+                       sizeof (nfs_fh3));
+    argdr.cookie._p._l = argdr.cookie._p._u = (uint32_t) 0;
+    (void) memset((char *) argdr.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+    argdr.count = DEFAULT_MAX_BUFSIZE;
+
+    do {
+        (void) memset((char *) entry_stream, '\0', sizeof (entry3) * SFS_MAXDIRENTS);
+        (void) memset((char *) &repdr, '\0', sizeof(repdr));
+        repdr.resok.count = SFS_MAXDIRENTS;
+        repdr.resok.reply.entries = entry_stream;
+       verror(VAL_VERBOSE, I, "validating readdir on %s ...\n", Testdirname);
+       val_op_readdir(&argdr, &repdr);
+
+
+       if (repdr.status == NFS3_OK) {
+            for (i = 0; i < repdr.resok.count; i++) {
+                for (filenum = 0; filenum < NUMFILES; filenum++) {
+                    if (!strcmp(entry_stream[i].name, Io_files[filenum].file_name)) {                        if (entry_stream[i].fileid._p._l !=
+                                    Io_files[filenum].attributes3.fileid._p._l) {
+                            verror(VAL_BATCH, E,
+                                "readdir %s error: file %s fileid mismatch\n",
+                                Testdirname, entry_stream[i].name);
+                            verror(VAL_BATCH, W,
+                                "   fileid: got = %lu, original = %lu\n",
+                                entry_stream[i].fileid._p._l,
+                                Io_files[filenum].attributes3.fileid._p._l);
+                        }
+                        /*
+                         * mark file as either found or dup
+                         */
+                        if (check_files[filenum].file_found &&
+                            !check_files[filenum].file_is_dup)
+                                check_files[filenum].file_is_dup = 1;
+                        else
+                                check_files[filenum].file_found = 1;
+                        break;
+                    }
+                }
+            }
+        } else {
+            verror(VAL_BATCH, W, "readdir %s failed: %m\n", Testdirname);
+        }
+
+       argdr.cookie = entry_stream[repdr.resok.count-1].cookie;
+
+    } while (repdr.resok.reply.eof == 0);
+
+    /*
+     * check if any known files have not been found
+     */
+    for (i = 0; i < NUMFILES; i++) {
+        if (!check_files[i].file_found)
+                verror(VAL_BATCH, E,
+                       "readdir %s error: file %s not found\n",
+                       Testdirname, Io_files[i].file_name);
+        else {
+                if (check_files[i].file_is_dup)
+                    verror(VAL_BATCH, E,
+              "readdir %s error: file %s returned more than once\n",
+                           Testdirname, Io_files[i].file_name);
+        }
+    }
+
+    /* validate readdirplus */
+    (void) memset((void *) check_files, '\0', sizeof(check_files));
+
+    argdrp.cookie._p._l = argdrp.cookie._p._u = (uint32_t) 0;
+    (void) memmove((char *) &argdrp.dir, (char *) &Export_dir.fh3,
+                   sizeof (nfs_fh3));
+    (void) memset((char *) argdrp.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+    /*
+     * We validate readdirplus with dircount and maxcount both set at 8K.
+     * This is not the most efficient way, but this is how readdirplus is
+     * called in SFS.  With the numbers set as such, maxcount becomes the
+     * bottleneck and we will not get 8K worth of directory info.  What we
+     * should get is 8K worth which includes directory info plus post_op_
+     * attributes and filehandle.
+     */
+    argdrp.dircount = DEFAULT_MAX_BUFSIZE;
+    argdrp.maxcount = DEFAULT_MAX_BUFSIZE;
+    do {
+        (void) memset((char *) entryplus_stream, '\0',
+                      sizeof (entryplus_stream));
+
+        (void) memset((char *) &repdrp, '\0', sizeof(repdrp));
+        repdrp.resok.count = SFS_MAXDIRENTS;
+        repdrp.resok.reply.entries = entryplus_stream;
+
+        verror(VAL_VERBOSE, I, "validating readdirplus on %s ...\n",
+                Testdirname);
+        val_op_readdirplus(&argdrp, &repdrp);
+
+        if (repdrp.status == NFS3_OK) {
+            verror(VAL_VERBOSE, I, "readdirplus found %d entries in %s...\n",
+                   repdrp.resok.count, Testdirname);
+            val_rddirplus_retsize(argdrp.dircount, argdrp.maxcount,
+                                  &repdrp.resok);
+            for (i = 0; i < repdrp.resok.count; i++) {
+                for (filenum = 0; filenum < NUMFILES; filenum++) {
+                    if (!strcmp(entryplus_stream[i].name,
+                                Io_files[filenum].file_name)) {
+                        if (entryplus_stream[i].fileid._p._l !=
+                            Io_files[filenum].attributes3.fileid._p._l) {
+                            verror(VAL_BATCH, E,
+                        "readdirplus %s error: file %s fileid mismatch\n",
+                                   Testdirname, entryplus_stream[i].name);
+                            verror(VAL_BATCH, W,
+                                "   fileid: got = %lu, original = %lu\n",
+                                   entryplus_stream[i].fileid._p._l,
+                                   Io_files[filenum].attributes3.fileid._p._l);
+                        }
+
+                        /*
+                         * mark file as either found or dup
+                         */
+                        if (check_files[filenum].file_found &&
+                            !check_files[filenum].file_is_dup)
+                                check_files[filenum].file_is_dup = 1;
+                        else
+                                check_files[filenum].file_found = 1;
+
+                       /*
+                         * check to make sure post op attributes and
+                         * file handle are returned.
+                         */
+                        if (!entryplus_stream[i].name_attributes.attributes)
+                                verror(VAL_BATCH, E,
+                "readdirplus %s warning: did not receive post op attributes for file %s\n\n",
+                                       Testdirname, entryplus_stream[i].name);
+                        else
+                            (void) compare_fattr(Ops[READDIRPLUS].name,
+                                                 Io_files[filenum].file_name,
+                                                 &Io_files[filenum].attributes3,                                                 &entryplus_stream[i].name_attributes.attr);
+
+                        if (!entryplus_stream[i].name_handle.handle_follows)
+                                verror(VAL_BATCH, E,
+                "readdirplus %s warning: did not receive file handle for file %s\n\n",
+                                       Testdirname, entryplus_stream[i].name);
+                        else
+                            if (memcmp((void *) &Io_files[filenum].fh3.fh3_u.data,
+                                       (void *) &entryplus_stream[i].name_handle.handle.fh3_u.data, Io_files[filenum].fh3.fh3_length) != 0)
+                                   verror(VAL_BATCH, E,
+                "readdirplus %s error: file %s, filehandles do not match\n\n",
+                                          Testdirname, entryplus_stream[i].name);
+                        break;
+                    }
+                }
+            }
+        } else {
+            verror(VAL_BATCH, W, "readdirplus %s failed: %m\n", Testdirname);
+        }
+        argdrp.cookie = entryplus_stream[repdrp.resok.count-1].cookie;
+
+    } while (repdrp.resok.reply.eof == 0);
+    /*
+     * check if any known files have not been found
+     */
+    for (i = 0; i < NUMFILES; i++) {
+        if (!check_files[i].file_found)
+                verror(VAL_BATCH, E,
+                       "readdirplus %s error: file %s not found\n",
+                       Testdirname, Io_files[i].file_name);
+        else {
+                if (check_files[i].file_is_dup)
+                    verror(VAL_BATCH, E,
+              "readdirplus %s error: file %s returned more than once\n",
+                           Testdirname, Io_files[i].file_name);
+        }
+    }   
+} /* validate_read_write */
+
+
+static void
+validate_rename(void)
+{
+    RENAME3args argrn;
+    RENAME3res  reprn;
+    int         filenum;
+    char        newname[SFS_MAXNAMLEN];
+    int         rncount = 0;
+
+    (void) memmove((char *) &argrn.from.dir, (char *) &Export_dir.fh3,
+                       sizeof (nfs_fh3));
+    (void) memmove((char *) &argrn.to.dir, (char *) &Export_dir.fh3,
+               sizeof (nfs_fh3));
+
+    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, &reprn);
+
+       if (reprn.status == NFS3_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,
+    fattr3 *     attr1,
+    fattr3 *     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 = %7lo, previous =  %7lo\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 = %7lo, previous =  %7lo\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 = %lu, previous =  %lu\n",
+                       attr2->nlink, attr1->nlink);
+               ret = FALSE;
+               attr1->nlink =  attr2->nlink;
+       }
+       else {
+               if (ret) {
+                       verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                           fname, op);
+               }
+               verror(VAL_BATCH, E,
+                       "    nlink: current = %lu, previous =  %lu\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 = %lu, previous =  %lu\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 = %lu, previous =  %lu\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 = %lu, previous =  %lu\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 = %lu, previous =  %lu\n",
+                       attr2->gid, attr1->gid);
+               ret = FALSE;
+               flag_error = TRUE;
+       }
+    }
+
+    if (attr1->size._p._l != attr2->size._p._l) {
+       if (strcmp(op, Ops[WRITE].name)) {
+           if (attr1->size._p._l == (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 = %lu, previous =  %lu\n",
+                       attr2->size._p._l, attr1->size._p._l);
+               attr1->size._p._l =  attr2->size._p._l;
+               ret = FALSE;
+           }
+           else {
+               if (ret) {
+                       verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                           fname, op);
+               }
+               verror(VAL_BATCH, E,
+                       "    size: current = %lu, previous =  %lu\n",
+                       attr2->size._p._l, attr1->size._p._l);
+               ret = FALSE;
+               flag_error = TRUE;
+           }
+       }
+    }
+
+    /* compare rdev only if type == NFCHR or NFBLK */
+    if ((attr1->type == NF3CHR || attr1->type == NF3BLK) &&
+                       (attr1->rdev.specdata1 != attr2->rdev.specdata1 ||
+                       attr1->rdev.specdata2 != attr2->rdev.specdata2)) {
+       if (attr1->rdev.specdata1 == (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 = %lu %lu, previous =  %lu %lu\n",
+                       attr2->rdev.specdata1, attr2->rdev.specdata2,
+                       attr1->rdev.specdata1, attr1->rdev.specdata2);
+            attr1->rdev.specdata1 =  attr2->rdev.specdata1;
+            attr1->rdev.specdata2 =  attr2->rdev.specdata2;
+            ret = FALSE;
+       }
+       else {
+            if (ret) {
+                    verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                        fname, op);
+            }
+            verror(VAL_BATCH, E,
+                       "    rdev: current = %lu %lu, previous =  %lu %lu\n",
+                       attr2->rdev.specdata1, attr2->rdev.specdata2,
+                       attr1->rdev.specdata1, attr1->rdev.specdata2);
+            ret = FALSE;
+            flag_error = TRUE;
+       }
+    }
+
+    if (attr1->fsid._p._l != attr2->fsid._p._l) {
+       if (attr1->fsid._p._l == (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 = %lu, previous =  %lu\n",
+                    attr2->fsid._p._l, attr1->fsid._p._l);
+            attr1->fsid._p._l =  attr2->fsid._p._l;
+            ret = FALSE;
+       }
+       else {
+            if (ret) {
+                    verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                        fname, op);
+            }
+            verror(VAL_BATCH, E, "    fsid: current = %lu, previous =  %lu\n",
+                    attr2->fsid._p._l, attr1->fsid._p._l);
+            ret = FALSE;
+               flag_error = TRUE;
+       }
+    }
+
+    if (attr1->fileid._p._l != attr2->fileid._p._l) {
+       if (attr1->fileid._p._l == (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 = %lu, previous =  %lu\n",
+                       attr2->fileid._p._l, attr1->fileid._p._l);
+            attr1->fileid._p._l =  attr2->fileid._p._l;
+            ret = FALSE;
+       }
+       else {
+            if (ret) {
+                    verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                        fname, op);
+            }
+            verror(VAL_BATCH, E,
+                       "    fileid: current = %lu, previous =  %lu\n",
+                    attr2->fileid._p._l, attr1->fileid._p._l);
+            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 */
+
+
+uint32_t sattr_types[8] =      {
+                         0000000,              /* NF3NON */
+                         0100000,              /* NF3REG */
+                         0040000,              /* NF3DIR */
+                         0060000,              /* NF3BLK */
+                         0020000,              /* NF3CHR */
+                         0120000,              /* NF3LNK */
+                         0140000,              /* NF3SOCK */
+                         0010000 };            /* NF3FIFO */
+static int
+compare_sattr(
+    char *      op,
+    char *      fname,
+    sattr3 *    attr1,
+    fattr3 *    attr2)
+{
+    int         ret = TRUE;
+    char        msg[80];
+
+    msg[0] = '\0';
+
+#ifdef notyet
+    if (attr1->mode.mode.set_it == TRUE &&
+       (((attr1->mode.mode & NFSTYPE_FMT) != sattr_types[attr2->type]) &&
+       ((attr1->mode.mode & NFSTYPE_FMT) != sattr_types[0]) ||
+       ((attr1->mode.mode & NFSMODE_FMT) != (attr2->mode & NFSMODE_FMT)))) {
+       if (ret) {
+           verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                                               fname, op);
+
+       }
+       verror(VAL_BATCH, E, "    mode: returned = %7o, specified = %7o\n",
+                               attr1->mode.mode, attr2->mode);
+       ret = FALSE;
+    }
+#endif
+
+    /*
+     * Check for user "nobody", UID -2, which may be untranslated from
+     * sixteen-bit two's complement.
+     */
+    if (attr1->uid.set_it == TRUE && attr1->uid.uid != attr2->uid &&
+                        !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+                                               attr2->uid == 65534) &&
+                                               attr1->uid.uid == 0)) {
+       if (ret) {
+           verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                                               fname, op);
+       }
+       if (attr1->uid.uid == 0)
+           (void) strcat(msg," (is root UID mapped to other UID?)");
+       verror(VAL_BATCH, E, "    uid: returned = %lu, specified = %lu %s\n",
+                               attr2->uid, attr1->uid.uid, msg);
+       ret = FALSE;
+    }
+
+    if (attr1->gid.set_it == TRUE && attr1->gid.gid != attr2->gid &&
+                                                       attr2->gid != 0) {
+/*
+   if (attr1->gid.set_it == TRUE && attr1->gid != attr2->gid) {
+*/
+       if (ret) {
+           verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                   fname, op);
+       }
+       verror(VAL_BATCH, E, "    gid: returned = %lu, specified = %lu\n",
+               attr2->gid, attr1->gid.gid);
+       ret = FALSE;
+    }
+
+    if (attr1->size.set_it == TRUE &&
+                               attr1->size.size._p._l != attr2->size._p._l) {
+       if (ret) {
+           verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+                   fname, op);
+       }
+       verror(VAL_BATCH, E, "    size: returned = %lu, specified = %lu\n",
+               attr2->size._p._l, attr1->size.size._p._l);
+       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,
+    int                 len)
+{
+    uint16_t      cksum;
+
+    cksum = 0;
+    for (; len--; buf++) {
+       if (cksum & 01)
+           cksum = (cksum >> 1) + 0x8000;
+       else
+           cksum >>= 1;
+       cksum += (uint16_t) *buf;
+       cksum &= 0xFFFF;
+    }
+    return(cksum);
+
+} /* sum */
+
+
+static void
+validate_remove(void)
+{
+    REMOVE3args        argrm;
+    REMOVE3res reprm;
+    RMDIR3args argrd;
+    RMDIR3res  reprd;
+    LOOKUP3args        arglp;
+    LOOKUP3res replp;
+    nfsstat3   reply;
+    int         filenum;
+    char *      op;
+
+    for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+       if (Io_files[filenum].state != Exists)
+           continue;
+
+       if (Io_files[filenum].attributes3.type == NF3DIR) {
+           op = Ops[RMDIR].name;
+           verror(VAL_VERBOSE, I, "validating rmdir %s ...\n",
+                   Io_files[filenum].file_name);
+
+           (void) memmove((char *) &argrd.object.dir,
+                               (char *) &Export_dir.fh3,
+                               sizeof (nfs_fh3));
+           argrd.object.name = Io_files[filenum].file_name;
+           val_op_rmdir(&argrd, &reprd);
+           reply = reprd.status;
+       } else {
+           op = Ops[REMOVE].name;
+           verror(VAL_VERBOSE, I, "validating remove %s ...\n",
+                   Io_files[filenum].file_name);
+
+           (void) memmove((char *) &argrm.object.dir, (char *) &Export_dir.fh3,
+                               sizeof (nfs_fh3));
+           argrm.object.name = Io_files[filenum].file_name;
+           val_op_remove(&argrm, &reprm);
+           reply = reprm.status;
+       }
+
+       if (reply == NFS3_OK) {
+           /* make sure the file is removed from the directory */
+           (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3,
+                               sizeof (nfs_fh3));
+           arglp.what.name = Io_files[filenum].file_name;
+           val_op_lookup(&arglp, &replp);
+
+           if (replp.status == NFS3ERR_NOENT) {
+               Io_files[filenum].state = Nonexistent;
+           } else if (replp.status == NFS3_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;
+    char *      sp;
+    int         repeat;
+
+    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) vfprintf(stderr, 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, NFSPROC3_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(GETATTR3args *args, GETATTR3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR, 
+               xdr_GETATTR3args, (char *)args, xdr_GETATTR3res, (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(SETATTR3args *args, SETATTR3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR, 
+               xdr_SETATTR3args, (char *)args, xdr_SETATTR3res, (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(LOOKUP3args *args, LOOKUP3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, 
+               xdr_LOOKUP3args, (char *)args, xdr_LOOKUP3res, (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_access(ACCESS3args *args, ACCESS3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS, 
+               xdr_ACCESS3args, (char *)args, xdr_ACCESS3res, (char *)reply, 
+               Nfs_timers[Ops[ACCESS].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "access");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_readlink(READLINK3args *args, READLINK3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK, 
+               xdr_READLINK3args, (char *)args, xdr_READLINK3res, (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(READ3args *args, READ3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_READ, 
+               xdr_READ3args, (char *)args, xdr_READ3res, (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(WRITE3args *args, WRITE3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE, 
+               xdr_WRITE3args, (char *)args, xdr_WRITE3res, (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(CREATE3args *args, CREATE3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE, 
+               xdr_CREATE3args, (char *)args, xdr_CREATE3res, (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_mkdir(MKDIR3args *args, MKDIR3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR, 
+               xdr_MKDIR3args, (char *)args, xdr_MKDIR3res, (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_symlink(SYMLINK3args *args, SYMLINK3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK, 
+               xdr_SYMLINK3args, (char *)args, xdr_SYMLINK3res, (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_mknod(MKNOD3args *args, MKNOD3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD, 
+               xdr_MKNOD3args, (char *)args, xdr_MKNOD3res, (char *)reply, 
+               Nfs_timers[Ops[MKNOD].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "mknod");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_remove(REMOVE3args *args, REMOVE3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE, 
+               xdr_REMOVE3args, (char *)args, xdr_REMOVE3res, (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_rmdir(RMDIR3args *args, RMDIR3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR, 
+               xdr_RMDIR3args, (char *)args, xdr_RMDIR3res, (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_rename(RENAME3args *args, RENAME3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, 
+               xdr_RENAME3args, (char *)args, xdr_RENAME3res, (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(LINK3args *args, LINK3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK, 
+               xdr_LINK3args, (char *)args, xdr_LINK3res, (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_readdir(READDIR3args *args, READDIR3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR, 
+               xdr_READDIR3args, (char *)args, xdr_READDIR3res, (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_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client,
+               NFSPROC3_READDIRPLUS, xdr_READDIRPLUS3args, (char *)args, 
+               xdr_READDIRPLUS3res, (char *)reply, 
+               Nfs_timers[Ops[READDIRPLUS].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client,
+                   "readdirplus");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_fsstat(FSSTAT3args *args, FSSTAT3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT, 
+               xdr_FSSTAT3args, (char *)args, xdr_FSSTAT3res, (char *)reply, 
+               Nfs_timers[Ops[FSSTAT].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "fsstat");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_fsinfo(FSINFO3args *args, FSINFO3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO, 
+               xdr_FSINFO3args, (char *)args, xdr_FSINFO3res, (char *)reply, 
+               Nfs_timers[Ops[FSINFO].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "fsinfo");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_pathconf(PATHCONF3args *args, PATHCONF3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF, 
+               xdr_PATHCONF3args, (char *)args, xdr_PATHCONF3res, (char *)reply, 
+               Nfs_timers[Ops[PATHCONF].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "pathconf");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+val_op_commit(COMMIT3args *args, COMMIT3res *reply)
+{
+       int rpc_stat;
+       /* CONSTCOND */
+       while (1) {
+           rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT, 
+               xdr_COMMIT3args, (char *)args, xdr_COMMIT3res, (char *)reply, 
+               Nfs_timers[Ops[COMMIT].call_class]);
+           if (rpc_stat == RPC_SUCCESS)
+               break;
+           if (rpc_stat != RPC_TIMEDOUT) {
+               clnt_perror(NFS_client, "commit");
+               Validate_errors++;
+               validate_exit();
+           }
+       }
+}
+
+static void
+create_3tmp_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_3tmp_handles(void)
+{
+       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;
+               }
+       }
+}
+/* sfs_3_vld.c */