]> git.michaelhowe.org Git - packages/o/openafs.git/commitdiff
Copy volser in from 1.3.64 as there were merge problems
authorSam Hartman <hartmans@debian.org>
Fri, 21 May 2004 04:01:53 +0000 (04:01 +0000)
committerSam Hartman <hartmans@debian.org>
Fri, 21 May 2004 04:01:53 +0000 (04:01 +0000)
and no changes needed to be saved.

24 files changed:
src/volser/.cvsignore [new file with mode: 0644]
src/volser/Makefile.in [new file with mode: 0644]
src/volser/NTMakefile [new file with mode: 0644]
src/volser/common.c [new file with mode: 0644]
src/volser/dump.h [new file with mode: 0644]
src/volser/dumpstuff.c [new file with mode: 0644]
src/volser/lockdata.h [new file with mode: 0644]
src/volser/lockprocs.c [new file with mode: 0644]
src/volser/physio.c [new file with mode: 0644]
src/volser/restorevol.c [new file with mode: 0644]
src/volser/vol-dump.c [new file with mode: 0644]
src/volser/vol.h [new file with mode: 0644]
src/volser/volerr.et [new file with mode: 0644]
src/volser/volint.xg [new file with mode: 0644]
src/volser/volmain.c [new file with mode: 0644]
src/volser/volprocs.c [new file with mode: 0644]
src/volser/volser.p.h [new file with mode: 0644]
src/volser/volser_prototypes.h [new file with mode: 0644]
src/volser/volserver.rc [new file with mode: 0644]
src/volser/voltrans.c [new file with mode: 0644]
src/volser/vos.c [new file with mode: 0644]
src/volser/vos.rc [new file with mode: 0644]
src/volser/vsprocs.c [new file with mode: 0644]
src/volser/vsutils.c [new file with mode: 0644]

diff --git a/src/volser/.cvsignore b/src/volser/.cvsignore
new file mode 100644 (file)
index 0000000..8d0ac86
--- /dev/null
@@ -0,0 +1,11 @@
+AFS_component_version_number.c
+Makefile
+restorevol
+volerr.c
+volint.cs.c
+volint.h
+volint.ss.c
+volint.xdr.c
+volser.h
+volserver
+vos
diff --git a/src/volser/Makefile.in b/src/volser/Makefile.in
new file mode 100644 (file)
index 0000000..7833d78
--- /dev/null
@@ -0,0 +1,211 @@
+# Copyright 2000, International Business Machines Corporation and others.
+# All Rights Reserved.
+# 
+# This software has been released under the terms of the IBM Public
+# License.  For details, see the LICENSE file in the top-level source
+# directory or online at http://www.openafs.org/dl/license10.html
+
+srcdir=@srcdir@
+include @TOP_OBJDIR@/src/config/Makefile.config
+HELPER_SPLINT=@HELPER_SPLINT@
+
+
+VINCLS=${TOP_INCDIR}/afs/partition.h ${TOP_INCDIR}/afs/volume.h \
+       ${TOP_INCDIR}/afs/vlserver.h vol.h dump.h volser.h  lockdata.h
+
+RINCLS=${TOP_INCDIR}/rx/rx.h ${TOP_INCDIR}/rx/xdr.h \
+       ${TOP_INCDIR}/afs/keys.h ${TOP_INCDIR}/afs/cellconfig.h \
+       ${TOP_INCDIR}/ubik.h ${TOP_INCDIR}/afs/cmd.h
+
+INTINCLS=volint.h volser.h 
+
+LIBS=\
+       ${TOP_LIBDIR}/libaudit.a \
+       ${TOP_LIBDIR}/vlib.a \
+       ${TOP_LIBDIR}/libacl.a \
+       ${TOP_LIBDIR}/libsys.a \
+       ${TOP_LIBDIR}/libvldb.a \
+       ${TOP_LIBDIR}/libubik.a \
+       ${TOP_LIBDIR}/libauth.a \
+       ${TOP_LIBDIR}/libcmd.a \
+       ${TOP_LIBDIR}/librxkad.a \
+       ${TOP_LIBDIR}/libdes.a \
+       ${TOP_LIBDIR}/librxstat.a \
+       ${TOP_LIBDIR}/librx.a \
+       ${TOP_LIBDIR}/liblwp.a \
+       ${TOP_LIBDIR}/libsys.a \
+       ${TOP_LIBDIR}/libcom_err.a \
+       ${TOP_LIBDIR}/libkauth.a \
+       ${TOP_LIBDIR}/libusd.a \
+       ${TOP_LIBDIR}/util.a
+
+VOLDUMP_LIBS = \
+       ../vol/ihandle.o \
+       ../vol/physio.o \
+       ../vol/vlib.a \
+       ${TOP_LIBDIR}/libcmd.a \
+       ${TOP_LIBDIR}/util.a \
+       ${TOP_LIBDIR}/libsys.a \
+       ${TOP_LIBDIR}/libdir.a \
+       ${TOP_LIBDIR}/liblwp.a  \
+       ${TOP_LIBDIR}/libacl.a
+
+VSOBJS=vsprocs.o vsutils.o lockprocs.o volint.xdr.o volerr.o 
+SOBJS=volmain.o volprocs.o physio.o common.o voltrans.o volerr.o \
+ volint.cs.o dumpstuff.o volint.ss.o volint.xdr.o
+
+all: volserver vos restorevol voldump \
+       ${TOP_INCDIR}/afs/volser.h \
+       ${TOP_INCDIR}/afs/volint.h \
+       ${TOP_LIBDIR}/libvolser.a
+
+restorevol: restorevol.c
+       ${CC} ${CFLAGS} -o restorevol ${srcdir}/restorevol.c \
+               ${TOP_LIBDIR}/libcmd.a ${TOP_LIBDIR}/util.a ${XLIBS}
+
+vos: vos.o ${VSOBJS} libvolser.a ${LIBS}
+       ${CC} ${LDFLAGS} -o vos vos.o $(VSOBJS) libvolser.a ${LIBS} ${XLIBS}
+
+volserver: $(SOBJS) $(LIBS) ${TOP_LIBDIR}/libdir.a
+       ${CC} ${DBUG} -o volserver $(SOBJS) ${TOP_LIBDIR}/libdir.a \
+               ${LDFLAGS} $(LIBS) ${XLIBS}
+
+voldump: vol-dump.o ${VOLDUMP_LIBS}
+       ${CC} ${CFLAGS} -o voldump vol-dump.o ${VOLDUMP_LIBS} ${XLIBS}
+
+libvolser.a: volint.cs.o  $(VSOBJS) volint.ss.o AFS_component_version_number.o
+       -$(RM) -f $@
+       $(AR) crv $@ volint.cs.o $(VSOBJS) volint.ss.o AFS_component_version_number.o 
+       $(RANLIB) $@
+
+volser.h volerr.c: volerr.et volser.p.h
+       $(RM) -f volser.h volerr.c
+       ${COMPILE_ET} -p ${srcdir} volerr -h volser
+
+volint.cs.c: volint.xg
+       ${RXGEN} -x -C -o $@ ${srcdir}/volint.xg
+
+volint.ss.c: volint.xg
+       ${RXGEN} -x -S -o $@ ${srcdir}/volint.xg
+
+volint.xdr.c: volint.xg
+       ${RXGEN} -x -c -o $@ ${srcdir}/volint.xg
+
+volint.h: volint.xg
+       ${RXGEN} -x -h -o $@ ${srcdir}/volint.xg
+
+volint.cs.c: volint.h
+volint.ss.c: volint.h
+volint.xdr.c: volint.h
+
+#
+# Dependencies
+#
+volint.cs.o: volint.cs.c ${INTINCLS}
+volint.ss.o: volint.ss.c ${INTINCLS}
+volint.xdr.o: volint.xdr.c ${INTINCLS}
+vsutils.o: vsutils.c ${VINCLS} ${RINCLS} ${INTINCLS}
+volmain.o: volmain.c ${VINCLS} ${RINCLS} AFS_component_version_number.c
+volprocs.o: volprocs.c ${VINCLS} ${RINCLS} ${INTINCLS}
+dumpstuff.o: dumpstuff.c ${VINCLS} ${RINCLS} ${INTINCLS}
+voldump.o: voldump.c ${VINCLS} ${RINCLS}
+vos.o: vos.c ${VINCLS} ${RINCLS} ${INTINCLS} AFS_component_version_number.c
+vsprocs.o: vsprocs.c ${VINCLS} ${RINCLS} ${INTINCLS}
+physio.o: physio.c ${VINCLS}
+common.o: common.c ${VINCLS}
+lockprocs.o: lockprocs.c ${VINCLS} ${INTINCLS} ${RINCLS}
+
+#
+# Installation targets
+#
+install: \
+       ${DESTDIR}${sbindir}/restorevol \
+       ${DESTDIR}${sbindir}/voldump \
+       ${DESTDIR}${includedir}/afs/volser.h \
+       ${DESTDIR}${includedir}/afs/volint.h \
+       ${DESTDIR}${sbindir}/vos \
+       ${DESTDIR}${afssrvsbindir}/vos \
+       ${DESTDIR}${afssrvlibexecdir}/volserver \
+       ${DESTDIR}${libdir}/afs/libvolser.a
+
+
+${DEST}/include/afs/volser.h: volser.h
+       ${INSTALL} $? $@
+
+${DEST}/include/afs/volint.h: volint.h
+       ${INSTALL} $? $@
+
+${DEST}/etc/restorevol: restorevol
+       ${INSTALL} $? $@
+
+${DEST}/etc/voldump: voldump
+       ${INSTALL} $? $@
+
+${DEST}/etc/vos ${DEST}/root.server/usr/afs/bin/vos: vos
+       ${INSTALL} $? $@
+
+${DEST}/lib/afs/libvolser.a: libvolser.a
+       ${INSTALL} $? $@
+
+${DEST}/root.server/usr/afs/bin/volserver: volserver
+       ${INSTALL} $? $@
+
+#
+# Misc targets
+# 
+clean:
+       $(RM) -f *.o *.a core volserver volint.ss.c volint.cs.c volint.h \
+       volint.xdr.c vos volser.h volerr.c AFS_component_version_number.c restorevol voldump
+
+include ../config/Makefile.version
+
+${DESTDIR}${sbindir}/restorevol: restorevol
+       ${INSTALL} $? $@
+
+${DESTDIR}${sbindir}/voldump: voldump
+       ${INSTALL} $? $@
+
+${DESTDIR}${includedir}/afs/volser.h: volser.h
+       ${INSTALL} $? $@
+
+${TOP_INCDIR}/afs/volser.h: volser.h
+       ${INSTALL} $? $@
+
+${DESTDIR}${includedir}/afs/volint.h: volint.h
+       ${INSTALL} $? $@
+
+${TOP_INCDIR}/afs/volint.h: volint.h
+       ${INSTALL} $? $@
+
+${DESTDIR}${sbindir}/vos: vos
+       ${INSTALL} $? $@
+
+${DESTDIR}${afssrvsbindir}/vos: vos
+       ${INSTALL} $? $@
+
+${DESTDIR}${afssrvlibexecdir}/volserver: volserver
+       ${INSTALL} $? $@
+
+${DESTDIR}${libdir}/afs/libvolser.a: libvolser.a
+       ${INSTALL} $? $@
+
+${TOP_LIBDIR}/libvolser.a: libvolser.a
+       ${INSTALL} $? $@
+
+dest: \
+       ${DEST}/etc/restorevol \
+       ${DEST}/etc/voldump \
+       ${DEST}/include/afs/volser.h \
+       ${DEST}/include/afs/volint.h \
+       ${DEST}/etc/vos \
+       ${DEST}/root.server/usr/afs/bin/vos \
+       ${DEST}/root.server/usr/afs/bin/volserver \
+       ${DEST}/lib/afs/libvolser.a
+
+
+check-splint::
+       sh $(HELPER_SPLINT) $(CFLAGS) \
+           vos.c restorevol.c \
+           vsprocs.c vsutils.c lockprocs.c volerr.c \
+           volmain.c volprocs.c physio.c common.c voltrans.c \
+           dumpstuff.c
diff --git a/src/volser/NTMakefile b/src/volser/NTMakefile
new file mode 100644 (file)
index 0000000..ff0e914
--- /dev/null
@@ -0,0 +1,168 @@
+# Copyright 2000, International Business Machines Corporation and others.
+# All Rights Reserved.
+# 
+# This software has been released under the terms of the IBM Public
+# License.  For details, see the LICENSE file in the top-level source
+# directory or online at http://www.openafs.org/dl/license10.html
+
+RELDIR=volser
+!INCLUDE ..\config\NTMakefile.$(SYS_NAME)
+!INCLUDE ..\config\NTMakefile.version
+
+############################################################################
+# Definitions for installing header files
+
+INCFILEDIR = $(DESTDIR)\include\afs  # header file install directory
+
+INCFILES = \
+       $(INCFILEDIR)\volser_prototypes.h \
+       $(INCFILEDIR)\volser.h \
+       $(INCFILEDIR)\volint.h
+
+
+LOCAL_INCFILES = \
+       volser.h \
+       volint.h
+
+############################################################################
+# Build volser library.
+
+LIBFILE = $(DESTDIR)\lib\afs\afsvolser.lib
+
+LIBOBJS =\
+       $(OUT)\lockprocs.obj \
+       $(OUT)\volerr.obj \
+       $(OUT)\volint.cs.obj \
+       $(OUT)\volint.ss.obj \
+       $(OUT)\volint.xdr.obj \
+       $(OUT)\vsprocs.obj \
+       $(OUT)\vsutils.obj \
+       $(OUT)\AFS_component_version_number.obj 
+
+$(LIBFILE): $(LIBOBJS)
+       $(LIBARCH)
+
+
+############################################################################
+# External libraries
+
+EXEC_LIBS = \
+       $(DESTDIR)\lib\afs\afscmd.lib \
+       $(DESTDIR)\lib\afs\afsvol.lib \
+       $(DESTDIR)\lib\afs\afsutil.lib \
+       $(DESTDIR)\lib\afs\afsdir.lib \
+       $(DESTDIR)\lib\afs\afsvol.lib \
+       $(DESTDIR)\lib\afs\afsaudit.lib \
+       $(DESTDIR)\lib\afs\afsauth.lib \
+       $(DESTDIR)\lib\afs\afsvldb.lib \
+       $(DESTDIR)\lib\afs\afskauth.lib \
+       $(DESTDIR)\lib/afs/afscom_err.lib \
+       $(DESTDIR)\lib\afs\afsusd.lib \
+       $(DESTDIR)\lib\afsrxkad.lib \
+       $(DESTDIR)\lib\afsrxstat.lib \
+       $(DESTDIR)\lib\afsdes.lib \
+       $(DESTDIR)\lib\afsrx.lib \
+       $(DESTDIR)\lib\afslwp.lib \
+       $(DESTDIR)\lib\afs\afsacl.lib \
+       $(DESTDIR)\lib\afs\afsreg.lib \
+       $(DESTDIR)\lib\afs\afseventlog.lib \
+     $(DESTDIR)\lib\cm_dns.obj
+
+
+############################################################################
+# Build volserver
+
+VOLSERVER_EXEFILE = $(DESTDIR)\root.server\usr\afs\bin\volserver.exe
+
+VOLSERVER_EXEOBJS = \
+       $(OUT)\common.obj \
+       $(OUT)\dumpstuff.obj \
+       $(OUT)\physio.obj \
+       $(OUT)\volerr.obj \
+       $(OUT)\volint.cs.obj \
+       $(OUT)\volint.ss.obj \
+       $(OUT)\volint.xdr.obj \
+       $(OUT)\volmain.obj \
+       $(OUT)\volprocs.obj \
+       $(OUT)\voltrans.obj \
+       $(OUT)\volserver.res
+       
+
+VOLSERVER_EXELIBS = \
+       $(DESTDIR)\lib\afs\afsdir.lib \
+       $(DESTDIR)\lib\afs\afsprocmgmt.lib
+
+$(VOLSERVER_EXEFILE): $(VOLSERVER_EXEOBJS) $(VOLSERVER_EXELIBS) $(EXEC_LIBS)
+       $(EXECONLINK)
+       $(EXEPREP) 
+
+############################################################################
+# Build vos
+
+RS_VOS_EXEFILE = $(DESTDIR)\root.server\usr\afs\bin\vos.exe
+CL_VOS_EXEFILE = $(DESTDIR)\etc\vos.exe
+
+VOS_EXEOBJS = \
+       $(OUT)\vos.obj \
+       $(OUT)\vsprocs.obj \
+       $(OUT)\vsutils.obj \
+       $(OUT)\lockprocs.obj \
+       $(OUT)\volint.xdr.obj \
+       $(OUT)\volerr.obj \
+       $(OUT)\vos.res
+
+VOS_EXELIBS = \
+       $(DESTDIR)\lib\afsubik.lib \
+       $(DESTDIR)\lib\afs\afsvolser.lib \
+       $(DESTDIR)\lib\afs\afsprocmgmt.lib \
+       $(DESTDIR)\lib\afs\afspioctl.lib
+
+$(RS_VOS_EXEFILE): $(VOS_EXEOBJS) $(VOS_EXELIBS) $(EXEC_LIBS)
+       $(EXECONLINK)
+       $(EXEPREP) 
+
+$(CL_VOS_EXEFILE): $(RS_VOS_EXEFILE)
+       $(COPY)  $** $@
+
+############################################################################
+# Generate versioninfo resources
+$(OUT)\volserver.res: AFS_component_version_number.h
+
+$(OUT)\vos.res: AFS_component_version_number.h
+
+############################################################################
+# Definitions for generating files via RXGEN
+
+$(INCFILES):$$(@F)
+        $(COPY)  $** $(INCFILEDIR)\.
+
+volint.h volint.cs.c volint.ss.c volint.xdr.c: volint.xg
+       $(RXGEN) -x $**
+
+
+############################################################################
+# Definitions for generating files via COMPILE_ET
+
+volser.h volerr.c: volerr.et volser.p.h
+       $(DEL) volerr.c volser.h
+       $(COMPILE_ET) volerr -h volser
+
+
+
+
+############################################################################
+# Install target; primary makefile target
+
+install: $(LOCAL_INCFILES) $(LIBFILE) $(VOLSERVER_EXEFILE) $(CL_VOS_EXEFILE) \
+               $(INCFILES)
+
+
+############################################################################
+# Local clean target; augments predefined clean target
+
+clean::
+       $(DEL) volerr.c volser.h $(INCFILES)
+       $(DEL) volint.cs.c volint.h volint.ss.c volint.xdr.c volser.h
+
+mkdir:
+       
diff --git a/src/volser/common.c b/src/volser/common.c
new file mode 100644 (file)
index 0000000..1af526e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/common.c,v 1.10 2003/11/15 04:59:16 shadow Exp $");
+
+#include <stdio.h>
+#include <afs/afsutil.h>
+#include <afs/com_err.h>
+
+#ifndef AFS_PTHREAD_ENV
+/*@printflike@*/ void
+Log(const char *format, ...)
+{
+    va_list args;
+
+    va_start(args, format);
+    vViceLog(0, (format, args));
+    va_end(args);
+}
+#endif
+
+void
+LogError(afs_int32 errcode)
+{
+    ViceLog(0,
+           ("%s: %s\n", error_table_name(errcode), error_message(errcode)));
+}
+
+#ifndef AFS_PTHREAD_ENV
+/*@printflike@*/ void
+Abort(const char *format, ...)
+{
+    va_list args;
+
+    ViceLog(0, ("Program aborted: "));
+    va_start(args, format);
+    vViceLog(0, (format, args));
+    va_end(args);
+    abort();
+}
+#endif
+
+void
+InitErrTabs(void)
+{
+#ifndef AFS_PTHREAD_ENV
+    initialize_KA_error_table();
+    initialize_RXK_error_table();
+    initialize_KTC_error_table();
+    initialize_ACFG_error_table();
+    initialize_CMD_error_table();
+    initialize_VL_error_table();
+    initialize_VOLS_error_table();
+#endif
+    return;
+}
diff --git a/src/volser/dump.h b/src/volser/dump.h
new file mode 100644 (file)
index 0000000..349eb72
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+       System:         Volser
+       Module:         dump.h
+       Institution:    The Information Technology Center, Carnegie-Mellon University
+
+ */
+
+#define DUMPVERSION    1
+
+#define DUMPENDMAGIC   0x3A214B6E
+#define DUMPBEGINMAGIC 0xB3A11322
+
+#define D_DUMPHEADER   1
+#define D_VOLUMEHEADER  2
+#define D_VNODE                3
+#define D_DUMPEND      4
+
+#define D_MAX          20
+
+#define MAXDUMPTIMES   50
+
+/* DumpHeader:
+   Each {from,to} pair of time values gives a span of time covered by this dump.
+   Merged dumps may have multiple pairs if there are dumps missing from the merge */
+
+struct DumpHeader {
+    afs_int32 version;
+    VolumeId volumeId;
+    char volumeName[VNAMESIZE];
+    int nDumpTimes;            /* Number of pairs */
+    struct {
+       afs_int32 from, to;
+    } dumpTimes[MAXDUMPTIMES];
+};
+
+
+/* Some handshaking constants for volume move */
+#define SHAKE1 "x"
+#define SHAKE2 "y"
+#define SHAKE3 "z"
+#define SHAKE4 "P"
+#define SHAKE5 "Q"
+#define SHAKE_ABORT    "!"
diff --git a/src/volser/dumpstuff.c b/src/volser/dumpstuff.c
new file mode 100644 (file)
index 0000000..83562b2
--- /dev/null
@@ -0,0 +1,1637 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/dumpstuff.c,v 1.25 2003/11/23 04:53:44 jaltman Exp $");
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#else
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#include <sys/stat.h>
+#ifdef AFS_PTHREAD_ENV
+#include <assert.h>
+#else /* AFS_PTHREAD_ENV */
+#include <afs/assert.h>
+#endif /* AFS_PTHREAD_ENV */
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <afs/afsint.h>
+#include <afs/nfs.h>
+#include <afs/errors.h>
+#include <lock.h>
+#include <lwp.h>
+#include <afs/ihandle.h>
+#include <afs/vnode.h>
+#include <afs/volume.h>
+#include <afs/partition.h>
+#include "dump.h"
+#include <afs/fssync.h>
+#include <afs/acl.h>
+#include "volser.h"
+#include "volint.h"
+
+#ifndef AFS_NT40_ENV
+#ifdef O_LARGEFILE
+#define afs_stat       stat64
+#define afs_fstat      fstat64
+#else /* !O_LARGEFILE */
+#define afs_stat       stat
+#define afs_fstat      fstat
+#endif /* !O_LARGEFILE */
+#endif /* !AFS_NT40_ENV */
+
+/*@printflike@*/ extern void Log(const char *format, ...);
+
+extern int DoLogging;
+
+/* This iod stuff is a silly little package to emulate the old qi_in stuff, which
+   emulated the stdio stuff.  There is a big assumption here, that the
+   rx_Read will never be called directly, by a routine like readFile, when
+   there is an old character that was pushed back with iod_ungetc.  This
+   is really bletchy, and is here for compatibility only.  Eventually,
+   we should define a volume format that doesn't require
+   the pushing back of characters (i.e. characters should not double both
+   as an end marker and a begin marker) */
+struct iod {
+    struct rx_call *call;      /* call to which to write, might be an array */
+    int device;                        /* dump device ID for volume */
+    int parentId;              /* dump parent ID for volume */
+    struct DiskPartition *dumpPartition;       /* Dump partition. */
+    struct rx_call **calls;    /* array of pointers to calls */
+    int ncalls;                        /* how many calls/codes in array */
+    int *codes;                        /* one return code for each call */
+    char haveOldChar;          /* state for pushing back a character */
+    char oldChar;
+};
+
+
+/* Forward Declarations */
+static int DumpDumpHeader(register struct iod *iodp, register Volume * vp,
+                         afs_int32 fromtime);
+static int DumpPartial(register struct iod *iodp, register Volume * vp,
+                      afs_int32 fromtime, int dumpAllDirs);
+static int DumpVnodeIndex(register struct iod *iodp, Volume * vp,
+                         VnodeClass class, afs_int32 fromtime,
+                         int forcedump);
+static int DumpVnode(register struct iod *iodp, struct VnodeDiskObject *v,
+                    int volid, int vnodeNumber, int dumpEverything);
+static int ReadDumpHeader(register struct iod *iodp, struct DumpHeader *hp);
+static int ReadVnodes(register struct iod *iodp, Volume * vp, int incremental,
+                     afs_int32 * Lbuf, afs_int32 s1, afs_int32 * Sbuf,
+                     afs_int32 s2, afs_int32 delo);
+static afs_fsize_t volser_WriteFile(int vn, struct iod *iodp,
+                                   FdHandle_t * handleP, int tag,
+                                   Error * status);
+
+static int SizeDumpDumpHeader(register struct iod *iodp, register Volume * vp,
+                             afs_int32 fromtime,
+                             register struct volintSize *size);
+static int SizeDumpPartial(register struct iod *iodp, register Volume * vp,
+                          afs_int32 fromtime, int dumpAllDirs,
+                          register struct volintSize *size);
+static int SizeDumpVnodeIndex(register struct iod *iodp, Volume * vp,
+                             VnodeClass class, afs_int32 fromtime,
+                             int forcedump,
+                             register struct volintSize *size);
+static int SizeDumpVnode(register struct iod *iodp, struct VnodeDiskObject *v,
+                        int volid, int vnodeNumber, int dumpEverything,
+                        register struct volintSize *size);
+
+static void
+iod_Init(register struct iod *iodp, register struct rx_call *call)
+{
+    iodp->call = call;
+    iodp->haveOldChar = 0;
+    iodp->ncalls = 1;
+    iodp->calls = (struct rx_call **)0;
+}
+
+static void
+iod_InitMulti(struct iod *iodp, struct rx_call **calls, int ncalls,
+             int *codes)
+{
+
+    iodp->calls = calls;
+    iodp->haveOldChar = 0;
+    iodp->ncalls = ncalls;
+    iodp->codes = codes;
+    iodp->call = (struct rx_call *)0;
+}
+
+/* N.B. iod_Read doesn't check for oldchar (see previous comment) */
+#define iod_Read(iodp, buf, nbytes) rx_Read((iodp)->call, buf, nbytes)
+
+/* For the single dump case, it's ok to just return the "bytes written"
+ * that rx_Write returns, since all the callers of iod_Write abort when
+ * the returned value is less than they expect.  For the multi dump case,
+ * I don't think we want half the replicas to go bad just because one 
+ * connection timed out, but if they all time out, then we should give up. 
+ */
+static int
+iod_Write(struct iod *iodp, char *buf, int nbytes)
+{
+    int code, i;
+    int one_success = 0;
+
+    assert((iodp->call && iodp->ncalls == 1 && !iodp->calls)
+          || (!iodp->call && iodp->ncalls >= 1 && iodp->calls));
+
+    if (iodp->call) {
+       code = rx_Write(iodp->call, buf, nbytes);
+       return code;
+    }
+
+    for (i = 0; i < iodp->ncalls; i++) {
+       if (iodp->calls[i] && !iodp->codes[i]) {
+           code = rx_Write(iodp->calls[i], buf, nbytes);
+           if (code != nbytes) {       /* everything gets merged into a single error */
+               iodp->codes[i] = VOLSERDUMPERROR;       /* but that's exactly what the */
+           } /* standard dump does, anyways */
+           else {
+               one_success = TRUE;
+           }
+       }
+    }                          /* for all calls */
+
+    if (one_success)
+       return nbytes;
+    else
+       return 0;
+}
+
+static void
+iod_ungetc(struct iod *iodp, int achar)
+{
+    iodp->oldChar = achar;
+    iodp->haveOldChar = 1;
+}
+
+static int
+iod_getc(register struct iod *iodp)
+{
+    unsigned char t;
+
+    if (iodp->haveOldChar) {
+       iodp->haveOldChar = 0;
+       return iodp->oldChar;
+    }
+    if (iod_Read(iodp, &t, 1) == 1)
+       return t;
+    return EOF;
+}
+
+static int
+ReadShort(register struct iod *iodp, register unsigned short *sp)
+{
+    register b1, b0;
+    b1 = iod_getc(iodp);
+    b0 = iod_getc(iodp);
+    *sp = (b1 << 8) | b0;
+    return b0 != EOF;
+}
+
+static int
+ReadInt32(register struct iod *iodp, afs_uint32 * lp)
+{
+    afs_uint32 register b3, b2, b1, b0;
+    b3 = iod_getc(iodp);
+    b2 = iod_getc(iodp);
+    b1 = iod_getc(iodp);
+    b0 = iod_getc(iodp);
+    *lp = (((((b3 << 8) | b2) << 8) | b1) << 8) | b0;
+    return b0 != EOF;
+}
+
+static void
+ReadString(register struct iod *iodp, register char *to, register int maxa)
+{
+    register int c;
+    while (maxa--) {
+       if ((*to++ = iod_getc(iodp)) == 0)
+           break;
+    }
+    if (to[-1]) {
+       while ((c = iod_getc(iodp)) && c != EOF);
+       to[-1] = 0;
+    }
+}
+
+static void
+ReadByteString(register struct iod *iodp, register byte * to,
+              register int size)
+{
+    while (size--)
+       *to++ = iod_getc(iodp);
+}
+
+static int
+ReadVolumeHeader(register struct iod *iodp, VolumeDiskData * vol)
+{
+    register tag;
+    afs_uint32 trash;
+    memset(vol, 0, sizeof(*vol));
+    while ((tag = iod_getc(iodp)) > D_MAX && tag != EOF) {
+       switch (tag) {
+       case 'i':
+           if (!ReadInt32(iodp, &vol->id))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'v':
+           if (!ReadInt32(iodp, &trash))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'n':
+           ReadString(iodp, vol->name, sizeof(vol->name));
+           /*this means the name of the retsored volume could be possibly different. In conjunction with SAFSVolSignalRestore */
+           break;
+       case 's':
+           vol->inService = iod_getc(iodp);
+           break;
+       case 'b':
+           vol->blessed = iod_getc(iodp);
+           break;
+       case 'u':
+           if (!ReadInt32(iodp, &vol->uniquifier))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 't':
+           vol->type = iod_getc(iodp);
+           break;
+       case 'p':
+           if (!ReadInt32(iodp, &vol->parentId))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'c':
+           if (!ReadInt32(iodp, &vol->cloneId))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'q':
+           if (!ReadInt32(iodp, (afs_uint32 *) & vol->maxquota))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'm':
+           if (!ReadInt32(iodp, (afs_uint32 *) & vol->minquota))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'd':
+           if (!ReadInt32(iodp, (afs_uint32 *) & vol->diskused))
+               return VOLSERREAD_DUMPERROR;    /* Bogus:  should calculate this */
+           break;
+       case 'f':
+           if (!ReadInt32(iodp, (afs_uint32 *) & vol->filecount))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'a':
+           if (!ReadInt32(iodp, &vol->accountNumber))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'o':
+           if (!ReadInt32(iodp, &vol->owner))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'C':
+           if (!ReadInt32(iodp, &vol->creationDate))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'A':
+           if (!ReadInt32(iodp, &vol->accessDate))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'U':
+           if (!ReadInt32(iodp, &vol->updateDate))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'E':
+           if (!ReadInt32(iodp, &vol->expirationDate))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'B':
+           if (!ReadInt32(iodp, &vol->backupDate))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'O':
+           ReadString(iodp, vol->offlineMessage,
+                      sizeof(vol->offlineMessage));
+           break;
+       case 'M':
+           /*
+            * Detailed volume statistics are never stored in dumps,
+            * so we just restore either the null string if this volume
+            * had already been set to store statistics, or the old motd
+            * contents otherwise.  It doesn't matter, since this field
+            * will soon get initialized anyway.
+            */
+           ReadString(iodp, (char *)(vol->stat_reads), VMSGSIZE);
+           break;
+       case 'W':{
+               unsigned short length;
+               int i;
+               afs_uint32 data;
+               if (!ReadShort(iodp, &length))
+                   return VOLSERREAD_DUMPERROR;
+               for (i = 0; i < length; i++) {
+                   if (!ReadInt32(iodp, &data))
+                       return VOLSERREAD_DUMPERROR;
+                   if (i < sizeof(vol->weekUse) / sizeof(vol->weekUse[0]))
+                       vol->weekUse[i] = data;
+               }
+               break;
+           }
+       case 'D':
+           if (!ReadInt32(iodp, &vol->dayUseDate))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       case 'Z':
+           if (!ReadInt32(iodp, (afs_uint32 *) & vol->dayUse))
+               return VOLSERREAD_DUMPERROR;
+           break;
+       }
+    }
+    iod_ungetc(iodp, tag);
+    return 0;
+}
+
+static int
+DumpTag(register struct iod *iodp, register int tag)
+{
+    char p;
+
+    p = tag;
+    return ((iod_Write(iodp, &p, 1) == 1) ? 0 : VOLSERDUMPERROR);
+
+}
+
+static int
+DumpByte(register struct iod *iodp, char tag, byte value)
+{
+    char tbuffer[2];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    *p = value;
+    return ((iod_Write(iodp, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR);
+}
+
+#define putint32(p, v)  *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v
+#define putshort(p, v) *p++ = v>>8, *p++ = v
+
+static int
+DumpDouble(register struct iod *iodp, char tag, register afs_uint32 value1,
+          register afs_uint32 value2)
+{
+    char tbuffer[9];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    putint32(p, value1);
+    putint32(p, value2);
+    return ((iod_Write(iodp, tbuffer, 9) == 9) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpInt32(register struct iod *iodp, char tag, register afs_uint32 value)
+{
+    char tbuffer[5];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    putint32(p, value);
+    return ((iod_Write(iodp, tbuffer, 5) == 5) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpArrayInt32(register struct iod *iodp, char tag,
+              register afs_uint32 * array, register int nelem)
+{
+    char tbuffer[4];
+    register afs_uint32 v;
+    int code = 0;
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    putshort(p, nelem);
+    code = iod_Write(iodp, tbuffer, 3);
+    if (code != 3)
+       return VOLSERDUMPERROR;
+    while (nelem--) {
+       p = (unsigned char *)tbuffer;
+       v = *array++;           /*this was register */
+
+       putint32(p, v);
+       code = iod_Write(iodp, tbuffer, 4);
+       if (code != 4)
+           return VOLSERDUMPERROR;
+    }
+    return 0;
+}
+
+static int
+DumpShort(register struct iod *iodp, char tag, unsigned int value)
+{
+    char tbuffer[3];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    *p++ = value >> 8;
+    *p = value;
+    return ((iod_Write(iodp, tbuffer, 3) == 3) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpBool(register struct iod *iodp, char tag, unsigned int value)
+{
+    char tbuffer[2];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    *p = value;
+    return ((iod_Write(iodp, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpString(register struct iod *iodp, char tag, register char *s)
+{
+    register n;
+    int code = 0;
+    code = iod_Write(iodp, &tag, 1);
+    if (code != 1)
+       return VOLSERDUMPERROR;
+    n = strlen(s) + 1;
+    code = iod_Write(iodp, s, n);
+    if (code != n)
+       return VOLSERDUMPERROR;
+    return 0;
+}
+
+static int
+DumpByteString(register struct iod *iodp, char tag, register byte * bs,
+              register int nbytes)
+{
+    int code = 0;
+
+    code = iod_Write(iodp, &tag, 1);
+    if (code != 1)
+       return VOLSERDUMPERROR;
+    code = iod_Write(iodp, (char *)bs, nbytes);
+    if (code != nbytes)
+       return VOLSERDUMPERROR;
+    return 0;
+}
+
+static int
+DumpFile(struct iod *iodp, int vnode, FdHandle_t * handleP)
+{
+    int code = 0, lcode = 0, error = 0;
+    afs_int32 pad = 0, offset;
+    afs_sfsize_t n, nbytes, howMany, howBig;
+    byte *p;
+#ifndef AFS_NT40_ENV
+    struct afs_stat status;
+#endif
+    afs_sfsize_t size;
+#ifdef AFS_AIX_ENV
+#include <sys/statfs.h>
+    struct statfs tstatfs;
+#endif
+
+#ifdef AFS_NT40_ENV
+    howBig = _filelength(handleP->fd_fd);
+    howMany = 4096;
+
+#else
+    afs_fstat(handleP->fd_fd, &status);
+    howBig = status.st_size;
+
+#ifdef AFS_AIX_ENV
+    /* Unfortunately in AIX valuable fields such as st_blksize are 
+     * gone from the stat structure.
+     */
+    fstatfs(handleP->fd_fd, &tstatfs);
+    howMany = tstatfs.f_bsize;
+#else
+    howMany = status.st_blksize;
+#endif /* AFS_AIX_ENV */
+#endif /* AFS_NT40_ENV */
+
+
+    size = FDH_SIZE(handleP);
+#ifdef AFS_LARGEFILE_ENV
+    {
+       afs_uint32 hi, lo;
+       SplitInt64(size, hi, lo);
+       if (hi == 0L) {
+           code = DumpInt32(iodp, 'f', lo);
+       } else {
+           code = DumpDouble(iodp, 'h', hi, lo);
+       }
+    }
+#else /* !AFS_LARGEFILE_ENV */
+    code = DumpInt32(iodp, 'f', size);
+#endif /* !AFS_LARGEFILE_ENV */
+    if (code) {
+       return VOLSERDUMPERROR;
+    }
+
+    p = (unsigned char *)malloc(howMany);
+    if (!p) {
+       Log("1 Volser: DumpFile: no memory");
+       return VOLSERDUMPERROR;
+    }
+
+    for (nbytes = size; (nbytes && !error); nbytes -= howMany) {
+       if (nbytes < howMany)
+           howMany = nbytes;
+
+       /* Read the data - unless we know we can't */
+       n = (lcode ? 0 : FDH_READ(handleP, p, howMany));
+
+       /* If read any good data and we null padded previously, log the
+        * amount that we had null padded.
+        */
+       if ((n > 0) && pad) {
+           Log("1 Volser: DumpFile: Null padding file %d bytes at offset %u\n", pad, offset);
+           pad = 0;
+       }
+
+       /* If didn't read enough data, null padd the rest of the buffer. This
+        * can happen if, for instance, the media has some bad spots. We don't
+        * want to quit the dump, so we start null padding.
+        */
+       if (n < howMany) {
+           /* Record the read error */
+           if (n < 0) {
+               n = 0;
+               Log("1 Volser: DumpFile: Error %d reading inode %s for vnode %d\n", errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
+           } else if (!pad) {
+               Log("1 Volser: DumpFile: Error reading inode %s for vnode %d\n", PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
+           }
+
+           /* Pad the rest of the buffer with zeros. Remember offset we started 
+            * padding. Keep total tally of padding.
+            */
+           memset(p + n, 0, howMany - n);
+           if (!pad)
+               offset = (howBig - nbytes) + n;
+           pad += (howMany - n);
+
+           /* Now seek over the data we could not get. An error here means we
+            * can't do the next read.
+            */
+           lcode = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET);
+           if (lcode != ((size - nbytes) + howMany)) {
+               if (lcode < 0) {
+                   Log("1 Volser: DumpFile: Error %d seeking in inode %s for vnode %d\n", errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
+               } else {
+                   Log("1 Volser: DumpFile: Error seeking in inode %s for vnode %d\n", PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
+                   lcode = -1;
+               }
+           } else {
+               lcode = 0;
+           }
+       }
+
+       /* Now write the data out */
+       if (iod_Write(iodp, (char *)p, howMany) != howMany)
+           error = VOLSERDUMPERROR;
+#ifndef AFS_PTHREAD_ENV
+       IOMGR_Poll();
+#endif
+    }
+
+    if (pad) {                 /* Any padding we hadn't reported yet */
+       Log("1 Volser: DumpFile: Null padding file: %d bytes at offset %u\n",
+           pad, offset);
+    }
+
+    free(p);
+    return error;
+}
+
+static int
+DumpVolumeHeader(register struct iod *iodp, register Volume * vp)
+{
+    int code = 0;
+    static char nullString[1] = "";    /*The ``contents'' of motd */
+
+    if (!code)
+       code = DumpTag(iodp, D_VOLUMEHEADER);
+    if (!code) {
+       code = DumpInt32(iodp, 'i', V_id(vp));
+    }
+    if (!code)
+       code = DumpInt32(iodp, 'v', V_stamp(vp).version);
+    if (!code)
+       code = DumpString(iodp, 'n', V_name(vp));
+    if (!code)
+       code = DumpBool(iodp, 's', V_inService(vp));
+    if (!code)
+       code = DumpBool(iodp, 'b', V_blessed(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'u', V_uniquifier(vp));
+    if (!code)
+       code = DumpByte(iodp, 't', (byte) V_type(vp));
+    if (!code) {
+       code = DumpInt32(iodp, 'p', V_parentId(vp));
+    }
+    if (!code)
+       code = DumpInt32(iodp, 'c', V_cloneId(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'q', V_maxquota(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'm', V_minquota(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'd', V_diskused(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'f', V_filecount(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'a', V_accountNumber(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'o', V_owner(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'C', V_creationDate(vp));        /* Rw volume creation date */
+    if (!code)
+       code = DumpInt32(iodp, 'A', V_accessDate(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'U', V_updateDate(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'E', V_expirationDate(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'B', V_backupDate(vp));  /* Rw volume backup clone date */
+    if (!code)
+       code = DumpString(iodp, 'O', V_offlineMessage(vp));
+    /*
+     * We do NOT dump the detailed volume statistics residing in the old
+     * motd field, since we cannot tell from the info in a dump whether
+     * statistics data has been put there.  Instead, we dump a null string,
+     * just as if that was what the motd contained.
+     */
+    if (!code)
+       code = DumpString(iodp, 'M', nullString);
+    if (!code)
+       code =
+           DumpArrayInt32(iodp, 'W', (afs_uint32 *) V_weekUse(vp),
+                          sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0]));
+    if (!code)
+       code = DumpInt32(iodp, 'D', V_dayUseDate(vp));
+    if (!code)
+       code = DumpInt32(iodp, 'Z', V_dayUse(vp));
+    return code;
+}
+
+static int
+DumpEnd(register struct iod *iodp)
+{
+    return (DumpInt32(iodp, D_DUMPEND, DUMPENDMAGIC));
+}
+
+/* Guts of the dump code */
+
+/* Dump a whole volume */
+int
+DumpVolume(register struct rx_call *call, register Volume * vp,
+          afs_int32 fromtime, int dumpAllDirs)
+{
+    struct iod iod;
+    int code = 0;
+    register struct iod *iodp = &iod;
+    iod_Init(iodp, call);
+
+    if (!code)
+       code = DumpDumpHeader(iodp, vp, fromtime);
+
+    if (!code)
+       code = DumpPartial(iodp, vp, fromtime, dumpAllDirs);
+
+/* hack follows.  Errors should be handled quite differently in this version of dump than they used to be.*/
+    if (rx_Error(iodp->call)) {
+       Log("1 Volser: DumpVolume: Rx call failed during dump, error %d\n",
+           rx_Error(iodp->call));
+       return VOLSERDUMPERROR;
+    }
+    if (!code)
+       code = DumpEnd(iodp);
+
+    return code;
+}
+
+/* Dump a volume to multiple places*/
+int
+DumpVolMulti(struct rx_call **calls, int ncalls, Volume * vp,
+            afs_int32 fromtime, int dumpAllDirs, int *codes)
+{
+    struct iod iod;
+    int code = 0;
+    iod_InitMulti(&iod, calls, ncalls, codes);
+
+    if (!code)
+       code = DumpDumpHeader(&iod, vp, fromtime);
+    if (!code)
+       code = DumpPartial(&iod, vp, fromtime, dumpAllDirs);
+    if (!code)
+       code = DumpEnd(&iod);
+    return code;
+}
+
+/* A partial dump (no dump header) */
+static int
+DumpPartial(register struct iod *iodp, register Volume * vp,
+           afs_int32 fromtime, int dumpAllDirs)
+{
+    int code = 0;
+    if (!code)
+       code = DumpVolumeHeader(iodp, vp);
+    if (!code)
+       code = DumpVnodeIndex(iodp, vp, vLarge, fromtime, dumpAllDirs);
+    if (!code)
+       code = DumpVnodeIndex(iodp, vp, vSmall, fromtime, 0);
+    return code;
+}
+
+static int
+DumpVnodeIndex(register struct iod *iodp, Volume * vp, VnodeClass class,
+              afs_int32 fromtime, int forcedump)
+{
+    register int code = 0;
+    register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
+    char buf[SIZEOF_LARGEDISKVNODE];
+    struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
+    StreamHandle_t *file;
+    FdHandle_t *fdP;
+    int size;
+    int flag;
+    register int vnodeIndex, nVnodes;
+
+    fdP = IH_OPEN(vp->vnodeIndex[class].handle);
+    assert(fdP != NULL);
+    file = FDH_FDOPEN(fdP, "r+");
+    assert(file != NULL);
+    size = OS_SIZE(fdP->fd_fd);
+    assert(size != -1);
+    nVnodes = (size / vcp->diskSize) - 1;
+    if (nVnodes > 0) {
+       assert((nVnodes + 1) * vcp->diskSize == size);
+       assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
+    } else
+       nVnodes = 0;
+    for (vnodeIndex = 0;
+        nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code;
+        nVnodes--, vnodeIndex++) {
+       flag = forcedump || (vnode->serverModifyTime >= fromtime);
+       /* Note:  the >= test is very important since some old volumes may not have
+        * a serverModifyTime.  For an epoch dump, this results in 0>=0 test, which
+        * does dump the file! */
+       if (!code)
+           code =
+               DumpVnode(iodp, vnode, V_id(vp),
+                         bitNumberToVnodeNumber(vnodeIndex, class), flag);
+#ifndef AFS_PTHREAD_ENV
+       if (!flag)
+           IOMGR_Poll();       /* if we dont' xfr data, but scan instead, could lose conn */
+#endif
+    }
+    STREAM_CLOSE(file);
+    FDH_CLOSE(fdP);
+    return code;
+}
+
+static int
+DumpDumpHeader(register struct iod *iodp, register Volume * vp,
+              afs_int32 fromtime)
+{
+    int code = 0;
+    int UseLatestReadOnlyClone = 1;
+    afs_int32 dumpTimes[2];
+    iodp->device = vp->device;
+    iodp->parentId = V_parentId(vp);
+    iodp->dumpPartition = vp->partition;
+    if (!code)
+       code = DumpDouble(iodp, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION);
+    if (!code)
+       code =
+           DumpInt32(iodp, 'v',
+                     UseLatestReadOnlyClone ? V_id(vp) : V_parentId(vp));
+    if (!code)
+       code = DumpString(iodp, 'n', V_name(vp));
+    dumpTimes[0] = fromtime;
+    dumpTimes[1] = V_backupDate(vp);   /* Until the time the clone was made */
+    if (!code)
+       code = DumpArrayInt32(iodp, 't', (afs_uint32 *) dumpTimes, 2);
+    return code;
+}
+
+static int
+DumpVnode(register struct iod *iodp, struct VnodeDiskObject *v, int volid,
+         int vnodeNumber, int dumpEverything)
+{
+    int code = 0;
+    IHandle_t *ihP;
+    FdHandle_t *fdP;
+
+    if (!v || v->type == vNull)
+       return code;
+    if (!code)
+       code = DumpDouble(iodp, D_VNODE, vnodeNumber, v->uniquifier);
+    if (!dumpEverything)
+       return code;
+    if (!code)
+       code = DumpByte(iodp, 't', (byte) v->type);
+    if (!code)
+       code = DumpShort(iodp, 'l', v->linkCount);      /* May not need this */
+    if (!code)
+       code = DumpInt32(iodp, 'v', v->dataVersion);
+    if (!code)
+       code = DumpInt32(iodp, 'm', v->unixModifyTime);
+    if (!code)
+       code = DumpInt32(iodp, 'a', v->author);
+    if (!code)
+       code = DumpInt32(iodp, 'o', v->owner);
+    if (!code && v->group)
+       code = DumpInt32(iodp, 'g', v->group);  /* default group is 0 */
+    if (!code)
+       code = DumpShort(iodp, 'b', v->modeBits);
+    if (!code)
+       code = DumpInt32(iodp, 'p', v->parent);
+    if (!code)
+       code = DumpInt32(iodp, 's', v->serverModifyTime);
+    if (v->type == vDirectory) {
+       acl_HtonACL(VVnodeDiskACL(v));
+       if (!code)
+           code =
+               DumpByteString(iodp, 'A', (byte *) VVnodeDiskACL(v),
+                              VAclDiskSize(v));
+    }
+    if (VNDISK_GET_INO(v)) {
+       IH_INIT(ihP, iodp->device, iodp->parentId, VNDISK_GET_INO(v));
+       fdP = IH_OPEN(ihP);
+       if (fdP == NULL) {
+           Log("1 Volser: DumpVnode: dump: Unable to open inode %llu for vnode %u (volume %i); not dumped, error %d\n", (afs_uintmax_t) VNDISK_GET_INO(v), vnodeNumber, volid, errno);
+           IH_RELEASE(ihP);
+           return VOLSERREAD_DUMPERROR;
+       }
+       code = DumpFile(iodp, vnodeNumber, fdP);
+       FDH_CLOSE(fdP);
+       IH_RELEASE(ihP);
+    }
+    return code;
+}
+
+
+int
+ProcessIndex(Volume * vp, VnodeClass class, afs_int32 ** Bufp, int *sizep,
+            int del)
+{
+    int i, nVnodes, offset, code, index = 0;
+    afs_int32 *Buf;
+    int cnt = 0;
+    int size;
+    StreamHandle_t *afile;
+    FdHandle_t *fdP;
+    struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
+    char buf[SIZEOF_LARGEDISKVNODE], zero[SIZEOF_LARGEDISKVNODE];
+    register struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
+
+    memset(zero, 0, sizeof(zero));     /* zero out our proto-vnode */
+    fdP = IH_OPEN(vp->vnodeIndex[class].handle);
+    if (fdP == NULL)
+       return -1;
+    afile = FDH_FDOPEN(fdP, "r+");
+    if (del) {
+       int cnt1 = 0;
+       Buf = *Bufp;
+       for (i = 0; i < *sizep; i++) {
+           if (Buf[i]) {
+               cnt++;
+               STREAM_SEEK(afile, Buf[i], 0);
+               code = STREAM_READ(vnode, vcp->diskSize, 1, afile);
+               if (code == 1) {
+                   if (vnode->type != vNull && VNDISK_GET_INO(vnode)) {
+                       cnt1++;
+                       if (DoLogging) {
+                           Log("RestoreVolume %u Cleanup: Removing old vnode=%u inode=%llu size=unknown\n", 
+                     V_id(vp), bitNumberToVnodeNumber(i, class), 
+                     (afs_uintmax_t) VNDISK_GET_INO(vnode));
+                       }
+                       IH_DEC(V_linkHandle(vp), VNDISK_GET_INO(vnode),
+                              V_parentId(vp));
+                       DOPOLL;
+                   }
+                   STREAM_SEEK(afile, Buf[i], 0);
+                   (void)STREAM_WRITE(zero, vcp->diskSize, 1, afile);  /* Zero it out */
+               }
+               Buf[i] = 0;
+           }
+       }
+       if (DoLogging) {
+           Log("RestoreVolume Cleanup: Removed %d inodes for volume %d\n",
+               cnt1, V_id(vp));
+       }
+       STREAM_FLUSH(afile);    /* ensure 0s are on the disk */
+       OS_SYNC(afile->str_fd);
+    } else {
+       size = OS_SIZE(fdP->fd_fd);
+       assert(size != -1);
+       nVnodes =
+           (size <=
+            vcp->diskSize ? 0 : size - vcp->diskSize) >> vcp->logSize;
+       if (nVnodes > 0) {
+           Buf = (afs_int32 *) malloc(nVnodes * sizeof(afs_int32));
+           if (Buf == NULL)
+               return 1;
+           memset((char *)Buf, 0, nVnodes * sizeof(afs_int32));
+           STREAM_SEEK(afile, offset = vcp->diskSize, 0);
+           while (1) {
+               code = STREAM_READ(vnode, vcp->diskSize, 1, afile);
+               if (code != 1) {
+                   break;
+               }
+               if (vnode->type != vNull && VNDISK_GET_INO(vnode)) {
+                   Buf[(offset >> vcp->logSize) - 1] = offset;
+                   cnt++;
+               }
+               offset += vcp->diskSize;
+           }
+           *Bufp = Buf;
+           *sizep = nVnodes;
+       }
+    }
+    STREAM_CLOSE(afile);
+    FDH_CLOSE(fdP);
+    return 0;
+}
+
+
+int
+RestoreVolume(register struct rx_call *call, Volume * avp, int incremental,
+             struct restoreCookie *cookie)
+{
+    VolumeDiskData vol;
+    struct DumpHeader header;
+    afs_uint32 endMagic;
+    Error error = 0, vupdate;
+    register Volume *vp;
+    struct iod iod;
+    register struct iod *iodp = &iod;
+    afs_int32 *b1 = 0, *b2 = 0;
+    int s1 = 0, s2 = 0, delo = 0, tdelo;
+    int tag;
+
+    iod_Init(iodp, call);
+
+    vp = avp;
+    if (!ReadDumpHeader(iodp, &header)) {
+       Log("1 Volser: RestoreVolume: Error reading header file for dump; aborted\n");
+       return VOLSERREAD_DUMPERROR;
+    }
+    if (iod_getc(iodp) != D_VOLUMEHEADER) {
+       Log("1 Volser: RestoreVolume: Volume header missing from dump; not restored\n");
+       return VOLSERREAD_DUMPERROR;
+    }
+    if (ReadVolumeHeader(iodp, &vol) == VOLSERREAD_DUMPERROR)
+       return VOLSERREAD_DUMPERROR;
+
+    delo = ProcessIndex(vp, vLarge, &b1, &s1, 0);
+    if (!delo)
+       delo = ProcessIndex(vp, vSmall, &b2, &s2, 0);
+    if (delo) {
+       if (b1)
+           free((char *)b1);
+       if (b2)
+           free((char *)b2);
+       b1 = b2 = 0;
+    }
+
+    strncpy(vol.name, cookie->name, VOLSER_OLDMAXVOLNAME);
+    vol.type = cookie->type;
+    vol.cloneId = cookie->clone;
+    vol.parentId = cookie->parent;
+
+
+    tdelo = delo;
+    while (1) {
+       if (ReadVnodes(iodp, vp, 0, b1, s1, b2, s2, tdelo)) {
+           error = VOLSERREAD_DUMPERROR;
+           goto clean;
+       }
+       tag = iod_getc(iodp);
+       if (tag != D_VOLUMEHEADER)
+           break;
+       if (ReadVolumeHeader(iodp, &vol) == VOLSERREAD_DUMPERROR) {
+           error = VOLSERREAD_DUMPERROR;
+           goto out;
+       }
+       tdelo = -1;
+    }
+    if (tag != D_DUMPEND || !ReadInt32(iodp, &endMagic)
+       || endMagic != DUMPENDMAGIC) {
+       Log("1 Volser: RestoreVolume: End of dump not found; restore aborted\n");
+       error = VOLSERREAD_DUMPERROR;
+       goto clean;
+    }
+
+
+    if (iod_getc(iodp) != EOF) {
+       Log("1 Volser: RestoreVolume: Unrecognized postamble in dump; restore aborted\n");
+       error = VOLSERREAD_DUMPERROR;
+       goto clean;
+    }
+
+    if (!delo) {
+       ProcessIndex(vp, vLarge, &b1, &s1, 1);
+       ProcessIndex(vp, vSmall, &b2, &s2, 1);
+    }
+
+  clean:
+    ClearVolumeStats(&vol);
+    CopyVolumeHeader(&vol, &V_disk(vp));
+    V_destroyMe(vp) = 0;
+    VUpdateVolume(&vupdate, vp);
+    if (vupdate) {
+       Log("1 Volser: RestoreVolume: Unable to rewrite volume header; restore aborted\n");
+       error = VOLSERREAD_DUMPERROR;
+       goto out;
+    }
+  out:
+    /* Free the malloced space above */
+    if (b1)
+       free((char *)b1);
+    if (b2)
+       free((char *)b2);
+    return error;
+}
+
+static int
+ReadVnodes(register struct iod *iodp, Volume * vp, int incremental,
+          afs_int32 * Lbuf, afs_int32 s1, afs_int32 * Sbuf, afs_int32 s2,
+          afs_int32 delo)
+{
+    afs_int32 vnodeNumber;
+    char buf[SIZEOF_LARGEDISKVNODE];
+    register tag;
+    struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
+    struct VnodeDiskObject oldvnode;
+    int idx;
+    VnodeClass class;
+    struct VnodeClassInfo *vcp;
+    IHandle_t *tmpH;
+    FdHandle_t *fdP;
+    Inode nearInode;
+
+    tag = iod_getc(iodp);
+    V_pref(vp, nearInode);
+    while (tag == D_VNODE) {
+       int haveStuff = 0;
+       memset(buf, 0, sizeof(buf));
+       if (!ReadInt32(iodp, (afs_uint32 *) & vnodeNumber))
+           break;
+
+       ReadInt32(iodp, &vnode->uniquifier);
+       while ((tag = iod_getc(iodp)) > D_MAX && tag != EOF) {
+           haveStuff = 1;
+           switch (tag) {
+           case 't':
+               vnode->type = (VnodeType) iod_getc(iodp);
+               break;
+           case 'l':
+               {
+                   unsigned short tlc;
+                   ReadShort(iodp, &tlc);
+                   vnode->linkCount = (signed int)tlc;
+               }
+               break;
+           case 'v':
+               ReadInt32(iodp, &vnode->dataVersion);
+               break;
+           case 'm':
+               ReadInt32(iodp, &vnode->unixModifyTime);
+               break;
+           case 's':
+               ReadInt32(iodp, &vnode->serverModifyTime);
+               break;
+           case 'a':
+               ReadInt32(iodp, &vnode->author);
+               break;
+           case 'o':
+               ReadInt32(iodp, &vnode->owner);
+               break;
+           case 'g':
+               ReadInt32(iodp, (afs_uint32 *) & vnode->group);
+               break;
+           case 'b':{
+                   unsigned short modeBits;
+                   ReadShort(iodp, &modeBits);
+                   vnode->modeBits = (unsigned int)modeBits;
+                   break;
+               }
+           case 'p':
+               ReadInt32(iodp, &vnode->parent);
+               break;
+           case 'A':
+               ReadByteString(iodp, (byte *) VVnodeDiskACL(vnode),
+                              VAclDiskSize(vnode));
+               acl_NtohACL(VVnodeDiskACL(vnode));
+               break;
+#ifdef AFS_LARGEFILE_ENV
+           case 'h':
+#endif
+           case 'f':{
+                   Inode ino;
+                   Error error;
+                   afs_fsize_t vnodeLength;
+
+                   ino =
+                       IH_CREATE(V_linkHandle(vp), V_device(vp),
+                                 VPartitionPath(V_partition(vp)), nearInode,
+                                 V_parentId(vp), vnodeNumber,
+                                 vnode->uniquifier, vnode->dataVersion);
+                   if (!VALID_INO(ino)) {
+                       perror("unable to allocate inode");
+                       Log("1 Volser: ReadVnodes: Restore aborted\n");
+                       return VOLSERREAD_DUMPERROR;
+                   }
+                   nearInode = ino;
+                   VNDISK_SET_INO(vnode, ino);
+                   IH_INIT(tmpH, vp->device, V_parentId(vp), ino);
+                   fdP = IH_OPEN(tmpH);
+                   if (fdP == NULL) {
+                       IH_RELEASE(tmpH);
+                       return VOLSERREAD_DUMPERROR;
+                   }
+                   vnodeLength =
+                       volser_WriteFile(vnodeNumber, iodp, fdP, tag, &error);
+                   VNDISK_SET_LEN(vnode, vnodeLength);
+                   FDH_REALLYCLOSE(fdP);
+                   IH_RELEASE(tmpH);
+                   if (error) {
+                       Log("1 Volser: ReadVnodes: IDEC inode %llu\n",
+                           (afs_uintmax_t) ino);
+                       IH_DEC(V_linkHandle(vp), ino, V_parentId(vp));
+                       return VOLSERREAD_DUMPERROR;
+                   }
+                   break;
+               }
+           }
+       }
+
+       class = vnodeIdToClass(vnodeNumber);
+       vcp = &VnodeClassInfo[class];
+
+       /* Mark this vnode as in this dump - so we don't delete it later */
+       if (!delo) {
+           idx = (vnodeIndexOffset(vcp, vnodeNumber) >> vcp->logSize) - 1;
+           if (class == vLarge) {
+               if (Lbuf && (idx < s1))
+                   Lbuf[idx] = 0;
+           } else {
+               if (Sbuf && (idx < s2))
+                   Sbuf[idx] = 0;
+           }
+       }
+
+       if (haveStuff) {
+           FdHandle_t *fdP = IH_OPEN(vp->vnodeIndex[class].handle);
+           if (fdP == NULL) {
+               Log("1 Volser: ReadVnodes: Error opening vnode index; restore aborted\n");
+               return VOLSERREAD_DUMPERROR;
+           }
+           if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET) <
+               0) {
+               Log("1 Volser: ReadVnodes: Error seeking into vnode index; restore aborted\n");
+               FDH_REALLYCLOSE(fdP);
+               return VOLSERREAD_DUMPERROR;
+           }
+           if (FDH_READ(fdP, &oldvnode, sizeof(oldvnode)) ==
+               sizeof(oldvnode)) {
+               if (oldvnode.type != vNull && VNDISK_GET_INO(&oldvnode)) {
+                   IH_DEC(V_linkHandle(vp), VNDISK_GET_INO(&oldvnode),
+                          V_parentId(vp));
+               }
+           }
+           vnode->vnodeMagic = vcp->magic;
+           if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET) <
+               0) {
+               Log("1 Volser: ReadVnodes: Error seeking into vnode index; restore aborted\n");
+               FDH_REALLYCLOSE(fdP);
+               return VOLSERREAD_DUMPERROR;
+           }
+           if (FDH_WRITE(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
+               Log("1 Volser: ReadVnodes: Error writing vnode index; restore aborted\n");
+               FDH_REALLYCLOSE(fdP);
+               return VOLSERREAD_DUMPERROR;
+           }
+           FDH_CLOSE(fdP);
+       }
+    }
+    iod_ungetc(iodp, tag);
+
+
+    return 0;
+}
+
+
+/* called with disk file only.  Note that we don't have to worry about rx_Read
+ * needing to read an ungetc'd character, since the ReadInt32 will have read
+ * it instead.
+ */
+static afs_fsize_t
+volser_WriteFile(int vn, struct iod *iodp, FdHandle_t * handleP, int tag,
+                Error * status)
+{
+    afs_int32 code;
+    afs_fsize_t filesize;
+    afs_fsize_t written = 0;
+    register afs_uint32 size = 8192;
+    register afs_fsize_t nbytes;
+    unsigned char *p;
+
+
+    *status = 0;
+#ifdef AFS_64BIT_ENV
+    {
+       afs_uint32 filesize_high = 0L, filesize_low = 0L;
+#ifdef AFS_LARGEFILE_ENV
+       if (tag == 'h') {
+           if (!ReadInt32(iodp, &filesize_high)) {
+               *status = 1;
+               return 0;
+           }
+       }
+#endif /* !AFS_LARGEFILE_ENV */
+       if (!ReadInt32(iodp, &filesize_low)) {
+           *status = 1;
+           return 0;
+       }
+       FillInt64(filesize, filesize_high, filesize_low);
+    }
+#else /* !AFS_64BIT_ENV */
+    if (!ReadInt32(iodp, &filesize)) {
+       *status = 1;
+       return (0);
+    }
+#endif /* !AFS_64BIT_ENV */
+    p = (unsigned char *)malloc(size);
+    if (p == NULL) {
+       *status = 2;
+       return (0);
+    }
+    for (nbytes = filesize; nbytes; nbytes -= size) {
+       if (nbytes < size)
+           size = nbytes;
+
+       if ((code = iod_Read(iodp, p, size)) != size) {
+           Log("1 Volser: WriteFile: Error reading dump file %d size=%llu nbytes=%u (%d of %u); restore aborted\n", vn, (afs_uintmax_t) filesize, nbytes, code, size);
+           *status = 3;
+           break;
+       }
+       code = FDH_WRITE(handleP, p, size);
+       if (code > 0)
+           written += code;
+       if (code != size) {
+           Log("1 Volser: WriteFile: Error creating file in volume; restore aborted\n");
+           *status = 4;
+           break;
+       }
+    }
+    free(p);
+    return (written);
+}
+
+static int
+ReadDumpHeader(register struct iod *iodp, struct DumpHeader *hp)
+{
+    register tag;
+    afs_uint32 beginMagic;
+    if (iod_getc(iodp) != D_DUMPHEADER || !ReadInt32(iodp, &beginMagic)
+       || !ReadInt32(iodp, (afs_uint32 *) & hp->version)
+       || beginMagic != DUMPBEGINMAGIC)
+       return 0;
+    hp->volumeId = 0;
+    hp->nDumpTimes = 0;
+    while ((tag = iod_getc(iodp)) > D_MAX) {
+       unsigned short arrayLength;
+       register int i;
+       switch (tag) {
+       case 'v':
+           if (!ReadInt32(iodp, &hp->volumeId))
+               return 0;
+           break;
+       case 'n':
+           ReadString(iodp, hp->volumeName, sizeof(hp->volumeName));
+           break;
+       case 't':
+           if (!ReadShort(iodp, &arrayLength))
+               return 0;
+           hp->nDumpTimes = (arrayLength >> 1);
+           for (i = 0; i < hp->nDumpTimes; i++)
+               if (!ReadInt32(iodp, (afs_uint32 *) & hp->dumpTimes[i].from)
+                   || !ReadInt32(iodp, (afs_uint32 *) & hp->dumpTimes[i].to))
+                   return 0;
+           break;
+       }
+    }
+    if (!hp->volumeId || !hp->nDumpTimes) {
+       return 0;
+    }
+    iod_ungetc(iodp, tag);
+    return 1;
+}
+
+
+/* ----- Below are the calls that calculate dump size ----- */
+
+static int
+SizeDumpVolumeHeader(register struct iod *iodp, register Volume * vp,
+                    register struct volintSize *v_size)
+{
+    int code = 0;
+    static char nullString[1] = "";    /*The ``contents'' of motd */
+    afs_uint64 addvar;
+
+/*     if (!code) code = DumpTag(iodp, D_VOLUMEHEADER); */
+    FillInt64(addvar,0, 1);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) {code = DumpInt32(iodp, 'i',V_id(vp));} */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'v',V_stamp(vp).version); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpString(iodp, 'n',V_name(vp)); */
+    FillInt64(addvar,0, (2 + strlen(V_name(vp))));
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpBool(iodp, 's',V_inService(vp)); */
+    FillInt64(addvar,0, 2);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpBool(iodp, 'b',V_blessed(vp)); */
+    FillInt64(addvar,0, 2);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'u',V_uniquifier(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpByte(iodp, 't',(byte)V_type(vp)); */
+    FillInt64(addvar,0, 2);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code){ code = DumpInt32(iodp, 'p',V_parentId(vp));} */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'c',V_cloneId(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'q',V_maxquota(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'm',V_minquota(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'd',V_diskused(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'f',V_filecount(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'a', V_accountNumber(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'o', V_owner(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'C',V_creationDate(vp));      /\* Rw volume creation date *\/ */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'A',V_accessDate(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'U',V_updateDate(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'E',V_expirationDate(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'B',V_backupDate(vp));                /\* Rw volume backup clone date *\/ */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpString(iodp, 'O',V_offlineMessage(vp)); */
+    FillInt64(addvar,0, (2 + strlen(V_offlineMessage(vp))));
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     /\* */
+/*      * We do NOT dump the detailed volume statistics residing in the old */
+/*      * motd field, since we cannot tell from the info in a dump whether */
+/*      * statistics data has been put there.  Instead, we dump a null string, */
+/*      * just as if that was what the motd contained. */
+/*      *\/ */
+/*     if (!code) code = DumpString(iodp, 'M', nullString); */
+    FillInt64(addvar,0, (2 + strlen(nullString)));
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpArrayInt32(iodp, 'W', (afs_uint32 *)V_weekUse(vp), sizeof(V_weekUse(vp))/sizeof(V_weekUse(vp)[0])); */
+    FillInt64(addvar,0, (3 + 4 * (sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0]))));
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'D', V_dayUseDate(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'Z', V_dayUse(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    return code;
+}
+
+static int
+SizeDumpEnd(register struct iod *iodp, register struct volintSize *v_size)
+{
+    int code = 0;
+    afs_uint64 addvar;
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    return code;
+}
+
+int
+SizeDumpVolume(register struct rx_call *call, register Volume * vp,
+              afs_int32 fromtime, int dumpAllDirs,
+              register struct volintSize *v_size)
+{
+    int code = 0;
+    register struct iod *iodp = (struct iod *)0;
+/*    iod_Init(iodp, call); */
+
+    if (!code)
+       code = SizeDumpDumpHeader(iodp, vp, fromtime, v_size);
+    if (!code)
+       code = SizeDumpPartial(iodp, vp, fromtime, dumpAllDirs, v_size);
+    if (!code)
+       code = SizeDumpEnd(iodp, v_size);
+
+    return code;
+}
+
+static int
+SizeDumpDumpHeader(register struct iod *iodp, register Volume * vp,
+                  afs_int32 fromtime, register struct volintSize *v_size)
+{
+    int code = 0;
+    int UseLatestReadOnlyClone = 1;
+/*    afs_int32 dumpTimes[2]; */
+    afs_uint64 addvar;
+/*    iodp->device = vp->device; */
+/*    iodp->parentId = V_parentId(vp); */
+/*    iodp->dumpPartition = vp->partition; */
+
+    ZeroInt64(v_size->dump_size);      /* initialize the size */
+/*     if (!code) code = DumpDouble(iodp, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION); */
+    FillInt64(addvar,0, 9);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'v', UseLatestReadOnlyClone? V_id(vp): V_parentId(vp)); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpString(iodp, 'n',V_name(vp)); */
+    FillInt64(addvar,0, (2 + strlen(V_name(vp))));
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     dumpTimes[0] = fromtime; */
+/*     dumpTimes[1] = V_backupDate(vp);        /\* Until the time the clone was made *\/ */
+/*     if (!code) code = DumpArrayInt32(iodp, 't', (afs_uint32 *)dumpTimes, 2); */
+    FillInt64(addvar,0, (3 + 4 * 2));
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    return code;
+}
+
+static int
+SizeDumpVnode(register struct iod *iodp, struct VnodeDiskObject *v, int volid,
+             int vnodeNumber, int dumpEverything,
+             register struct volintSize *v_size)
+{
+    int code = 0;
+    afs_uint64 addvar;
+
+    if (!v || v->type == vNull)
+       return code;
+/*     if (!code) code = DumpDouble(iodp, D_VNODE, vnodeNumber, v->uniquifier); */
+    FillInt64(addvar,0, 9);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    if (!dumpEverything)
+       return code;
+/*     if (!code)  code = DumpByte(iodp, 't',(byte)v->type); */
+    FillInt64(addvar,0, 2);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpShort(iodp, 'l', v->linkCount); /\* May not need this *\/ */
+    FillInt64(addvar,0, 3);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'v', v->dataVersion); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'm', v->unixModifyTime); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'a', v->author); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'o', v->owner); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code && v->group) code = DumpInt32(iodp, 'g', v->group);   /\* default group is 0 *\/ */
+    if (v->group) {
+       FillInt64(addvar,0, 5);
+       AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    }
+/*     if (!code) code = DumpShort(iodp, 'b', v->modeBits); */
+    FillInt64(addvar,0, 3);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 'p', v->parent); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+/*     if (!code) code = DumpInt32(iodp, 's', v->serverModifyTime); */
+    FillInt64(addvar,0, 5);
+    AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    if (v->type == vDirectory) {
+/*     acl_HtonACL(VVnodeDiskACL(v)); */
+/*     if (!code) code = DumpByteString(iodp, 'A', (byte *) VVnodeDiskACL(v), VAclDiskSize(v)); */
+       FillInt64(addvar,0, (1 + VAclDiskSize(v)));
+       AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    }
+
+    if (VNDISK_GET_INO(v)) {
+       FillInt64(addvar,0, (v->length + 5));
+       AddUInt64(v_size->dump_size, addvar, &v_size->dump_size);
+    }
+    return code;
+}
+
+/* A partial dump (no dump header) */
+static int
+SizeDumpPartial(register struct iod *iodp, register Volume * vp,
+               afs_int32 fromtime, int dumpAllDirs,
+               register struct volintSize *v_size)
+{
+    int code = 0;
+    if (!code)
+       code = SizeDumpVolumeHeader(iodp, vp, v_size);
+    if (!code)
+       code =
+           SizeDumpVnodeIndex(iodp, vp, vLarge, fromtime, dumpAllDirs,
+                              v_size);
+    if (!code)
+       code = SizeDumpVnodeIndex(iodp, vp, vSmall, fromtime, 0, v_size);
+    return code;
+}
+
+static int
+SizeDumpVnodeIndex(register struct iod *iodp, Volume * vp, VnodeClass class,
+                  afs_int32 fromtime, int forcedump,
+                  register struct volintSize *v_size)
+{
+    register int code = 0;
+    register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
+    char buf[SIZEOF_LARGEDISKVNODE];
+    struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
+    StreamHandle_t *file;
+    FdHandle_t *fdP;
+    int size;
+    int flag;
+    register int vnodeIndex, nVnodes;
+
+    fdP = IH_OPEN(vp->vnodeIndex[class].handle);
+    assert(fdP != NULL);
+    file = FDH_FDOPEN(fdP, "r+");
+    assert(file != NULL);
+    size = OS_SIZE(fdP->fd_fd);
+    assert(size != -1);
+    nVnodes = (size / vcp->diskSize) - 1;
+    if (nVnodes > 0) {
+       assert((nVnodes + 1) * vcp->diskSize == size);
+       assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
+    } else
+       nVnodes = 0;
+    for (vnodeIndex = 0;
+        nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code;
+        nVnodes--, vnodeIndex++) {
+       flag = forcedump || (vnode->serverModifyTime >= fromtime);
+       /* Note:  the >= test is very important since some old volumes may not have
+        * a serverModifyTime.  For an epoch dump, this results in 0>=0 test, which
+        * does dump the file! */
+       if (!code)
+           code =
+               SizeDumpVnode(iodp, vnode, V_id(vp),
+                             bitNumberToVnodeNumber(vnodeIndex, class), flag,
+                             v_size);
+    }
+    STREAM_CLOSE(file);
+    FDH_CLOSE(fdP);
+    return code;
+}
diff --git a/src/volser/lockdata.h b/src/volser/lockdata.h
new file mode 100644 (file)
index 0000000..7c8df8b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#ifndef _LOCKDATA_
+#define _LOCKDATA_ 1
+
+#define        ZERO 2147617029         /*to be replaced by 0L */
+#define N_SECURITY_OBJECTS 1
+
+
+struct aqueue {
+    char name[VOLSER_MAXVOLNAME];      /* related to max volname allowed in the vldb */
+    afs_int32 ids[3];
+    afs_int32 copyDate[3];
+    int isValid[3];
+    struct aqueue *next;
+};
+
+struct qHead {
+    int count;
+    struct aqueue *next;
+};
+
+#endif /* _LOCKDATA_ */
diff --git a/src/volser/lockprocs.c b/src/volser/lockprocs.c
new file mode 100644 (file)
index 0000000..9d175bd
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ *  Module:        lockprocs.c
+ *  System:        Volser
+ *  Instituition:   ITC, CMU
+ *  Date:          December, 88
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/lockprocs.c,v 1.8 2003/07/15 23:17:48 shadow Exp $");
+
+#include <sys/types.h>
+#ifdef AFS_NT40_ENV
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#include <afs/voldefs.h>
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <afs/vlserver.h>
+#include <afs/nfs.h>
+#include <afs/afsint.h>
+#include "volint.h"
+#include "volser.h"
+#include "lockdata.h"
+
+/* Finds an index in VLDB entry that matches the volume type, server, and partition.
+ * If type is zero, will match first index of ANY type (RW, BK, or RO).
+ * If server is zero, will match first index of ANY server and partition 
+ * Zero is a valid partition field.
+ */
+int
+FindIndex(entry, server, part, type)
+     struct nvldbentry *entry;
+     afs_int32 server, part, type;
+{
+    int e;
+    afs_int32 error = 0;
+
+    for (e = 0; (e < entry->nServers) && !error; e++) {
+       if (!type || (entry->serverFlags[e] & type)) {
+           if ((!server || (entry->serverPartition[e] == part))
+               && (!server
+                   || VLDB_IsSameAddrs(entry->serverNumber[e], server,
+                                       &error)))
+               break;
+           if (type == ITSRWVOL)
+               return -1;      /* quit when we are looking for RW entry (there's only 1) */
+       }
+    }
+
+    if (error) {
+       fprintf(STDERR,
+               "Failed to get info about server's %d address(es) from vlserver (err=%d)\n",
+               entry->serverNumber[e], error);
+       return -1;
+    }
+
+    if (e >= entry->nServers)
+       return -1;              /* Didn't find it */
+
+    return e;                  /* return the index */
+}
+
+/* Changes the rw site only */
+void
+SetAValue(entry, oserver, opart, nserver, npart, type)
+     struct nvldbentry *entry;
+     afs_int32 oserver, opart, nserver, npart, type;
+{
+    int e;
+    afs_int32 error = 0;
+
+    e = FindIndex(entry, oserver, opart, type);
+    if (e == -1)
+       return;                 /* If didn't find it, just return */
+
+    entry->serverNumber[e] = nserver;
+    entry->serverPartition[e] = npart;
+
+    /* Now move rest of entries up */
+    if ((nserver == 0L) && (npart == 0L)) {
+       for (e++; e < entry->nServers; e++) {
+           entry->serverNumber[e - 1] = entry->serverNumber[e];
+           entry->serverPartition[e - 1] = entry->serverPartition[e];
+           entry->serverFlags[e - 1] = entry->serverFlags[e];
+       }
+    }
+}
+
+/* Changes the RW site only */
+Lp_SetRWValue(entry, oserver, opart, nserver, npart)
+     struct nvldbentry *entry;
+     afs_int32 oserver, opart, nserver, npart;
+{
+    SetAValue(entry, oserver, opart, nserver, npart, ITSRWVOL);
+}
+
+/* Changes the RO site only */
+Lp_SetROValue(entry, oserver, opart, nserver, npart)
+     struct nvldbentry *entry;
+     afs_int32 oserver, opart, nserver, npart;
+{
+    SetAValue(entry, oserver, opart, nserver, npart, ITSROVOL);
+}
+
+/* Returns success if this server and partition matches the RW entry */
+Lp_Match(server, part, entry)
+     afs_int32 server, part;
+     struct nvldbentry *entry;
+{
+    if (FindIndex(entry, server, part, ITSRWVOL) == -1)
+       return 0;
+    return 1;
+}
+
+/* Return the index of the RO entry (plus 1) if it exists, else return 0 */
+Lp_ROMatch(server, part, entry)
+     afs_int32 server, part;
+     struct nvldbentry *entry;
+{
+    return (FindIndex(entry, server, part, ITSROVOL) + 1);
+}
+
+/* Return the index of the RW entry if it exists, else return -1 */
+Lp_GetRwIndex(entry)
+     struct nvldbentry *entry;
+{
+    return (FindIndex(entry, 0, 0, ITSRWVOL));
+}
+
+/*initialize queue pointed by <ahead>*/
+Lp_QInit(ahead)
+     struct qHead *ahead;
+{
+    ahead->count = 0;
+    ahead->next = NULL;
+}
+
+/*add <elem> in front of queue <ahead> */
+Lp_QAdd(ahead, elem)
+     struct qHead *ahead;
+     struct aqueue *elem;
+{
+    struct aqueue *temp;
+
+    if (ahead->count == 0) {
+       ahead->count += 1;
+       ahead->next = elem;
+       elem->next = NULL;
+    } else {
+       temp = ahead->next;
+       ahead->count += 1;
+       ahead->next = elem;
+       elem->next = temp;
+    }
+}
+
+Lp_QScan(ahead, id, success, elem)
+     struct qHead *ahead;
+     struct aqueue **elem;
+     afs_int32 id;
+     int *success;
+{
+    struct aqueue *cptr;
+
+    cptr = ahead->next;
+    while (cptr != NULL) {
+       if (cptr->ids[RWVOL] == id) {
+           *success = 1;
+           *elem = cptr;
+           return 0;
+       }
+       cptr = cptr->next;
+    }
+    *success = 0;
+    return 0;
+}
+
+/*return the element in the beginning of the queue <ahead>, free
+*the space used by that element . <success> indicates if enumeration was ok*/
+Lp_QEnumerate(ahead, success, elem)
+     struct qHead *ahead;
+     struct aqueue *elem;
+     int *success;
+{
+    int i;
+    struct aqueue *temp;
+
+    if (ahead->count > 0) {    /*more elements left */
+       ahead->count -= 1;
+       temp = ahead->next;
+       ahead->next = ahead->next->next;
+       strncpy(elem->name, temp->name, VOLSER_OLDMAXVOLNAME);
+       for (i = 0; i < 3; i++) {
+           elem->ids[i] = temp->ids[i];
+           elem->copyDate[i] = temp->copyDate[i];
+           elem->isValid[i] = temp->isValid[i];
+       }
+       elem->next = NULL;
+       *success = 1;
+       free(temp);
+    } else                     /*queue is empty */
+       *success = 0;
+}
+
+Lp_QTraverse(ahead)
+     struct qHead *ahead;
+{
+    int count;
+    struct aqueue *old, *new;
+
+    old = ahead->next;
+    new = old->next;
+    count = ahead->count;
+    printf
+       ("traversing the internal queue, which groups all the related volumes on a per partition basis\n");
+    while (count > 0) {
+       printf("---------------------------\n");
+       printf("%s RW-Id %lu", old->name, (unsigned long)old->ids[RWVOL]);
+       if (old->isValid[RWVOL])
+           printf(" valid ");
+       else
+           printf(" invalid ");
+       printf("RO-Id %lu", (unsigned long)old->ids[ROVOL]);
+       if (old->isValid[ROVOL])
+           printf(" valid ");
+       else
+           printf(" invalid ");
+       printf("BACKUP-Id %lu", (unsigned long)old->ids[BACKVOL]);
+       if (old->isValid[BACKVOL])
+           printf(" valid ");
+       else
+           printf(" invalid ");
+       printf("\n");
+       printf("---------------------------\n");
+       old = new;
+       if (count != 1)
+           new = new->next;
+       count--;
+    }
+}
diff --git a/src/volser/physio.c b/src/volser/physio.c
new file mode 100644 (file)
index 0000000..f8f5a91
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/physio.c,v 1.11 2003/12/09 23:07:57 shadow Exp $");
+
+#include <sys/types.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#ifdef AFS_SUN5_ENV
+#include <sys/fcntl.h>
+#endif
+#include <errno.h>
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <stdio.h>
+#include <afs/afsint.h>
+#include <afs/nfs.h>
+#include <afs/assert.h>
+#include <afs/dir.h>
+#include <afs/ihandle.h>
+#include "vol.h"
+
+/* returns 0 on success, errno on failure */
+int
+ReallyRead(DirHandle * file, int block, char *data)
+{
+    FdHandle_t *fdP;
+    int code;
+    errno = 0;
+    fdP = IH_OPEN(file->dirh_handle);
+    if (fdP == NULL) {
+       code = errno;
+       return code;
+    }
+    if (FDH_SEEK(fdP, block * AFS_PAGESIZE, SEEK_SET) < 0) {
+       code = errno;
+       FDH_REALLYCLOSE(fdP);
+       return code;
+    }
+    code = FDH_READ(fdP, data, AFS_PAGESIZE);
+    if (code != AFS_PAGESIZE) {
+       if (code < 0)
+           code = errno;
+       else
+           code = EIO;
+       FDH_REALLYCLOSE(fdP);
+       return code;
+    }
+    FDH_CLOSE(fdP);
+    return 0;
+}
+
+/* returns 0 on success, errno on failure */
+int
+ReallyWrite(DirHandle * file, int block, char *data)
+{
+    FdHandle_t *fdP;
+    extern int VolumeChanged;
+    int code;
+
+    errno = 0;
+
+    fdP = IH_OPEN(file->dirh_handle);
+    if (fdP == NULL) {
+       code = errno;
+       return code;
+    }
+    if (FDH_SEEK(fdP, block * AFS_PAGESIZE, SEEK_SET) < 0) {
+       code = errno;
+       FDH_REALLYCLOSE(fdP);
+       return code;
+    }
+    code = FDH_WRITE(fdP, data, AFS_PAGESIZE);
+    if (code != AFS_PAGESIZE) {
+       if (code < 0)
+           code = errno;
+       else
+           code = EIO;
+       FDH_REALLYCLOSE(fdP);
+       return code;
+    }
+    FDH_CLOSE(fdP);
+    VolumeChanged = 1;
+    return 0;
+}
+
+/* SetSalvageDirHandle:
+ * Create a handle to a directory entry and reference it (IH_INIT).
+ * The handle needs to be dereferenced with the FidZap() routine.
+ */
+void
+SetSalvageDirHandle(DirHandle * dir, afs_int32 volume, afs_int32 device,
+                    Inode inode)
+{
+    private SalvageCacheCheck = 1;
+    memset(dir, 0, sizeof(DirHandle));
+
+    dir->dirh_volume = volume;
+    dir->dirh_device = device;
+    dir->dirh_inode = inode;
+    IH_INIT(dir->dirh_handle, device, volume, inode);
+
+    /* Always re-read for a new dirhandle */
+    dir->dirh_cacheCheck = SalvageCacheCheck++;
+}
+
+void
+FidZap(DirHandle * file)
+{
+    IH_RELEASE(file->dirh_handle);
+    memset(file, 0, sizeof(DirHandle));
+}
+
+void
+FidZero(DirHandle * file)
+{
+    memset(file, 0, sizeof(DirHandle));
+}
+
+int
+FidEq(DirHandle * afile, DirHandle * bfile)
+{
+    if (afile->dirh_volume != bfile->dirh_volume)
+       return 0;
+    if (afile->dirh_device != bfile->dirh_device)
+       return 0;
+    if (afile->dirh_cacheCheck != bfile->dirh_cacheCheck)
+       return 0;
+    if (afile->dirh_inode != bfile->dirh_inode)
+       return 0;
+    return 1;
+}
+
+int
+FidVolEq(DirHandle * afile, afs_int32 vid)
+{
+    if (afile->dirh_volume != vid)
+       return 0;
+    return 1;
+}
+
+void
+FidCpy(DirHandle * tofile, DirHandle * fromfile)
+{
+    *tofile = *fromfile;
+    IH_COPY(tofile->dirh_handle, fromfile->dirh_handle);
+}
+
+void
+Die(char *msg)
+{
+    printf("%s\n", msg);
+    assert(1 == 2);
+}
diff --git a/src/volser/restorevol.c b/src/volser/restorevol.c
new file mode 100644 (file)
index 0000000..8e47822
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ * Read a vos dump and recreate the tree.
+ *
+ * restorevol [-file <dump file>]
+ *            [-dir <restore dir>]
+ *            [-extension <name extension>]
+ *            [-mountpoint <mount point root>]
+ *            [-umask <mode mask>]
+ *
+ * 1. The dump file will be restored within the current or that specified with -dir.
+ * 2. Within this dir, a subdir is created. It's name is the RW volume name
+ *    that was dumped. An extension can be appended to this directory name
+ *    with -extension.
+ * 3. All mountpoints will appear as symbolic links to the volume. The
+ *    pathname to the volume will be either that in -mountpoint, or -dir.
+ *    Symbolic links remain untouched.
+ * 4. You can change your umask during the restore with -umask. Otherwise, it
+ *    uses your current umask. Mode bits for directories are 0777 (then
+ *    AND'ed with the umask). Mode bits for files are the owner mode bits
+ *    duplicated accross group and user (then AND'ed with the umask).
+ * 5. For restores of full dumps, if a directory says it has a file and 
+ *    the file is not found, then a symbolic link "AFSFile-<#>" will 
+ *    appear in that restored tree. Restores of incremental dumps remove 
+ *    all these files at the end (expensive because it is a tree search).  
+ * 6. If a file or directory was found in the dump but found not to be 
+ *    connected to the hierarchical tree, then the file or directory 
+ *    will be connected at the root of the tree as "__ORPHANEDIR__.<#>" 
+ *    or "__ORPHANFILE__.<#>".  
+ * 7. ACLs are not restored.
+ *
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/restorevol.c,v 1.13 2003/12/05 08:36:06 shadow Exp $");
+
+#include <afs/afsint.h>
+#include <afs/nfs.h>
+#include <lock.h>
+#include <afs/ihandle.h>
+#include <afs/vnode.h>
+#include <afs/volume.h>
+#include "volint.h"
+#include "dump.h"
+#include <afs/cmd.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+
+char rootdir[MAXPATHLEN];
+char mntroot[MAXPATHLEN];
+#define ADIR  "AFSDir-"
+#define AFILE "AFSFile-"
+#define ODIR  "__ORPHANEDIR__."
+#define OFILE "__ORPHANFILE__."
+
+int inc_dump = 0;
+FILE *dumpfile;
+
+afs_int32
+readvalue(size)
+{
+    afs_int32 value, s;
+    int code;
+    char *ptr;
+
+    value = 0;
+    ptr = (char *)&value;
+
+    s = sizeof(value) - size;
+    if (size < 0) {
+       fprintf(stderr, "Too much data in afs_int32\n");
+       return 0;
+    }
+
+    code = fread(&ptr[s], 1, size, dumpfile);
+    if (code != size)
+       fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
+
+    return (value);
+}
+
+char
+readchar()
+{
+    char value;
+    int code;
+    char *ptr;
+
+    value = '\0';
+    code = fread(&value, 1, 1, dumpfile);
+    if (code != 1)
+       fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
+
+    return (value);
+}
+
+#define BUFSIZE 16384
+char buf[BUFSIZE];
+
+char
+readdata(buffer, size)
+     char *buffer;
+     afs_sfsize_t size;
+{
+    int code;
+    afs_int32 s;
+
+    if (!buffer) {
+       while (size > 0) {
+           s = (afs_int32) ((size > BUFSIZE) ? BUFSIZE : size);
+           code = fread(buf, 1, s, dumpfile);
+           if (code != s)
+               fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
+           size -= s;
+       }
+    } else {
+       code = fread(buffer, 1, size, dumpfile);
+       if (code != size) {
+           if (code < 0)
+               fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
+           else
+               fprintf(stderr, "Read %d bytes out of %lld\n", code, (afs_uintmax_t)size);
+       }
+       if ((code >= 0) && (code < BUFSIZE))
+           buffer[size] = 0;   /* Add null char at end */
+    }
+}
+
+afs_int32
+ReadDumpHeader(dh)
+     struct DumpHeader *dh;    /* Defined in dump.h */
+{
+    int code, i, done;
+    char tag, c;
+    afs_int32 magic;
+
+/*  memset(&dh, 0, sizeof(dh)); */
+
+    magic = ntohl(readvalue(4));
+    dh->version = ntohl(readvalue(4));
+
+    done = 0;
+    while (!done) {
+       tag = readchar();
+       switch (tag) {
+       case 'v':
+           dh->volumeId = ntohl(readvalue(4));
+           break;
+
+       case 'n':
+           for (i = 0, c = 'a'; c != '\0'; i++) {
+               dh->volumeName[i] = c = readchar();
+           }
+           dh->volumeName[i] = c;
+           break;
+
+       case 't':
+           dh->nDumpTimes = ntohl(readvalue(2)) >> 1;
+           for (i = 0; i < dh->nDumpTimes; i++) {
+               dh->dumpTimes[i].from = ntohl(readvalue(4));
+               dh->dumpTimes[i].to = ntohl(readvalue(4));
+           }
+           break;
+
+       default:
+           done = 1;
+           break;
+       }
+    }
+
+    return ((afs_int32) tag);
+}
+
+struct volumeHeader {
+    afs_int32 volumeId;
+    char volumeName[100];
+    afs_int32 volType;
+    afs_int32 uniquifier;
+    afs_int32 parentVol;
+    afs_int32 cloneId;
+    afs_int32 maxQuota;
+    afs_int32 minQuota;
+    afs_int32 diskUsed;
+    afs_int32 fileCount;
+    afs_int32 accountNumber;
+    afs_int32 owner;
+    afs_int32 creationDate;
+    afs_int32 accessDate;
+    afs_int32 updateDate;
+    afs_int32 expirationDate;
+    afs_int32 backupDate;
+    afs_int32 dayUseDate;
+    afs_int32 dayUse;
+    afs_int32 weekCount;
+    afs_int32 weekUse[100];    /* weekCount of these */
+    char motd[1024];
+    int inService;
+    int blessed;
+    char message[1024];
+};
+
+afs_int32
+ReadVolumeHeader(count)
+     afs_int32 count;
+{
+    struct volumeHeader vh;
+    int code, i, done, entries;
+    char tag, c;
+
+/*  memset(&vh, 0, sizeof(vh)); */
+
+    done = 0;
+    while (!done) {
+       tag = readchar();
+       switch (tag) {
+       case 'i':
+           vh.volumeId = ntohl(readvalue(4));
+           break;
+
+       case 'v':
+           ntohl(readvalue(4));        /* version stamp - ignore */
+           break;
+
+       case 'n':
+           for (i = 0, c = 'a'; c != '\0'; i++) {
+               vh.volumeName[i] = c = readchar();
+           }
+           vh.volumeName[i] = c;
+           break;
+
+       case 's':
+           vh.inService = ntohl(readvalue(1));
+           break;
+
+       case 'b':
+           vh.blessed = ntohl(readvalue(1));
+           break;
+
+       case 'u':
+           vh.uniquifier = ntohl(readvalue(4));
+           break;
+
+       case 't':
+           vh.volType = ntohl(readvalue(1));
+           break;
+
+       case 'p':
+           vh.parentVol = ntohl(readvalue(4));
+           break;
+
+       case 'c':
+           vh.cloneId = ntohl(readvalue(4));
+           break;
+
+       case 'q':
+           vh.maxQuota = ntohl(readvalue(4));
+           break;
+
+       case 'm':
+           vh.minQuota = ntohl(readvalue(4));
+           break;
+
+       case 'd':
+           vh.diskUsed = ntohl(readvalue(4));
+           break;
+
+       case 'f':
+           vh.fileCount = ntohl(readvalue(4));
+           break;
+
+       case 'a':
+           vh.accountNumber = ntohl(readvalue(4));
+           break;
+
+       case 'o':
+           vh.owner = ntohl(readvalue(4));
+           break;
+
+       case 'C':
+           vh.creationDate = ntohl(readvalue(4));
+           break;
+
+       case 'A':
+           vh.accessDate = ntohl(readvalue(4));
+           break;
+
+       case 'U':
+           vh.updateDate = ntohl(readvalue(4));
+           break;
+
+       case 'E':
+           vh.expirationDate = ntohl(readvalue(4));
+           break;
+
+       case 'B':
+           vh.backupDate = ntohl(readvalue(4));
+           break;
+
+       case 'O':
+           for (i = 0, c = 'a'; c != '\0'; i++) {
+               vh.message[i] = c = readchar();
+           }
+           vh.volumeName[i] = c;
+           break;
+
+       case 'W':
+           vh.weekCount = ntohl(readvalue(2));
+           for (i = 0; i < vh.weekCount; i++) {
+               vh.weekUse[i] = ntohl(readvalue(4));
+           }
+           break;
+
+       case 'M':
+           for (i = 0, c = 'a'; c != '\0'; i++) {
+               vh.motd[i] = c = readchar();
+           }
+           break;
+
+       case 'D':
+           vh.dayUseDate = ntohl(readvalue(4));
+           break;
+
+       case 'Z':
+           vh.dayUse = ntohl(readvalue(4));
+           break;
+
+       default:
+           done = 1;
+           break;
+       }
+    }
+
+    return ((afs_int32) tag);
+}
+
+struct vNode {
+    afs_int32 vnode;
+    afs_int32 uniquifier;
+    afs_int32 type;
+    afs_int32 linkCount;
+    afs_int32 dataVersion;
+    afs_int32 unixModTime;
+    afs_int32 servModTime;
+    afs_int32 author;
+    afs_int32 owner;
+    afs_int32 group;
+    afs_int32 modebits;
+    afs_int32 parent;
+    char acl[192];
+#ifdef notdef
+    struct acl_accessList {
+       int size;               /*size of this access list in bytes, including MySize itself */
+       int version;            /*to deal with upward compatibility ; <= ACL_ACLVERSION */
+       int total;
+       int positive;           /* number of positive entries */
+       int negative;           /* number of minus entries */
+       struct acl_accessEntry {
+           int id;             /*internally-used ID of user or group */
+           int rights;         /*mask */
+       } entries[100];
+    } acl;
+#endif
+    afs_sfsize_t dataSize;
+};
+
+#define MAXNAMELEN 256
+
+afs_int32
+ReadVNode(count)
+     afs_int32 count;
+{
+    struct vNode vn;
+    int code, i, done, entries;
+    char tag, c;
+    char dirname[MAXNAMELEN], linkname[MAXNAMELEN], lname[MAXNAMELEN];
+    char parentdir[MAXNAMELEN], vflink[MAXNAMELEN];
+    char filename[MAXNAMELEN], fname[MAXNAMELEN];
+    int len;
+    afs_int32 vnode;
+    afs_int32 mode = 0;
+
+/*  memset(&vn, 0, sizeof(vn)); */
+    vn.dataSize = 0;
+    vn.vnode = 0;
+    vn.parent = 0;
+    vn.type = 0;
+
+    vn.vnode = ntohl(readvalue(4));
+    vn.uniquifier = ntohl(readvalue(4));
+
+    done = 0;
+    while (!done) {
+       tag = readchar();
+       switch (tag) {
+       case 't':
+           vn.type = ntohl(readvalue(1));
+           break;
+
+       case 'l':
+           vn.linkCount = ntohl(readvalue(2));
+           break;
+
+       case 'v':
+           vn.dataVersion = ntohl(readvalue(4));
+           break;
+
+       case 'm':
+           vn.unixModTime = ntohl(readvalue(4));
+           break;
+
+       case 's':
+           vn.servModTime = ntohl(readvalue(4));
+           break;
+
+       case 'a':
+           vn.author = ntohl(readvalue(4));
+           break;
+
+       case 'o':
+           vn.owner = ntohl(readvalue(4));
+           break;
+
+       case 'g':
+           vn.group = ntohl(readvalue(4));
+           break;
+
+       case 'b':
+           vn.modebits = ntohl(readvalue(2));
+           break;
+
+       case 'p':
+           vn.parent = ntohl(readvalue(4));
+           break;
+
+       case 'A':
+           readdata(vn.acl, 192);      /* Skip ACL data */
+           break;
+
+#ifdef AFS_LARGEFILE_ENV
+       case 'h':
+           {
+               afs_uint32 hi, lo;
+               hi = ntohl(readvalue(4));
+               lo = ntohl(readvalue(4));
+               FillInt64(vn.dataSize, hi, lo);
+           }
+           goto common_vnode;
+#endif /* !AFS_LARGEFILE_ENV */
+
+       case 'f':
+           vn.dataSize = ntohl(readvalue(4));
+
+         common_vnode:
+           /* parentdir is the name of this dir's vnode-file-link
+            * or this file's parent vnode-file-link.
+            * "./AFSDir-<#>". It's a symbolic link to its real dir.
+            * The parent dir and symbolic link to it must exist.
+            */
+           vnode = ((vn.type == 2) ? vn.vnode : vn.parent);
+           if (vnode == 1)
+               strncpy(parentdir, rootdir, sizeof parentdir);
+           else {
+               afs_snprintf(parentdir, sizeof parentdir, "%s/%s%d", rootdir,
+                            ADIR, vnode);
+
+               len = readlink(parentdir, linkname, MAXNAMELEN);
+               if (len < 0) {
+                   /* parentdir does not exist. So create an orphan dir.
+                    * and then link the parentdir to the orphaned dir.
+                    */
+                   afs_snprintf(linkname, sizeof linkname, "%s/%s%d",
+                                rootdir, ODIR, vnode);
+                   code = mkdir(linkname, 0777);
+                   if ((code < 0) && (errno != EEXIST)) {
+                       fprintf(stderr,
+                               "Error creating directory %s  code=%d;%d\n",
+                               linkname, code, errno);
+                   }
+
+                   /* Link the parentdir to it - now parentdir exists */
+                   afs_snprintf(linkname, sizeof linkname, "%s%d/", ODIR,
+                                vnode);
+                   code = symlink(linkname, parentdir);
+                   if (code) {
+                       fprintf(stderr,
+                               "Error creating symlink %s -> %s  code=%d;%d\n",
+                               parentdir, linkname, code, errno);
+                   }
+               }
+           }
+
+           if (vn.type == 2) {
+                /*ITSADIR*/
+                   /* We read the directory entries. If the entry is a
+                    * directory, the subdir is created and the root dir
+                    * will contain a link to it. If its a file, we only
+                    * create a symlink in the dir to the file name.
+                    */
+               char *buffer;
+               unsigned short j;
+               afs_int32 this_vn;
+               char *this_name;
+
+               struct DirEntry {
+                   char flag;
+                   char length;
+                   unsigned short next;
+                   struct MKFid {
+                       afs_int32 vnode;
+                       afs_int32 vunique;
+                   } fid;
+                   char name[20];
+               };
+
+               struct Pageheader {
+                   unsigned short pgcount;
+                   unsigned short tag;
+                   char freecount;
+                   char freebitmap[8];
+                   char padding[19];
+               };
+
+               struct DirHeader {
+                   struct Pageheader header;
+                   char alloMap[128];
+                   unsigned short hashTable[128];
+               };
+
+               struct Page0 {
+                   struct DirHeader header;
+                   struct DirEntry entry[1];
+               } *page0;
+
+
+               buffer = NULL;
+               buffer = (char *)malloc(vn.dataSize);
+
+               readdata(buffer, vn.dataSize);
+               page0 = (struct Page0 *)buffer;
+
+               /* Step through each bucket in the hash table, i,
+                * and follow each element in the hash chain, j.
+                * This gives us each entry of the dir.
+                */
+               for (i = 0; i < 128; i++) {
+                   for (j = ntohs(page0->header.hashTable[i]); j;
+                        j = ntohs(page0->entry[j].next)) {
+                       j -= 13;
+                       this_vn = ntohl(page0->entry[j].fid.vnode);
+                       this_name = page0->entry[j].name;
+
+                       if ((strcmp(this_name, ".") == 0)
+                           || (strcmp(this_name, "..") == 0))
+                           continue;   /* Skip these */
+
+                       /* For a directory entry, create it. Then create the
+                        * link (from the rootdir) to this directory.
+                        */
+                       if (this_vn & 1) {
+                            /*ADIRENTRY*/
+                               /* dirname is the directory to create.
+                                * vflink is what will link to it. 
+                                */
+                               afs_snprintf(dirname, sizeof dirname, "%s/%s",
+                                            parentdir, this_name);
+                           afs_snprintf(vflink, sizeof vflink, "%s/%s%d",
+                                        rootdir, ADIR, this_vn);
+
+                           /* The link and directory may already exist */
+                           len = readlink(vflink, linkname, MAXNAMELEN);
+                           if (len < 0) {
+                               /* Doesn't already exist - so create the directory.
+                                * umask will pare the mode bits down.
+                                */
+                               code = mkdir(dirname, 0777);
+                               if ((code < 0) && (errno != EEXIST)) {
+                                   fprintf(stderr,
+                                           "Error creating directory %s  code=%d;%d\n",
+                                           dirname, code, errno);
+                               }
+                           } else {
+                               /* Does already exist - so move the directory.
+                                * It was created originally as orphaned.
+                                */
+                               linkname[len - 1] = '\0';       /* remove '/' at end */
+                               afs_snprintf(lname, sizeof lname, "%s/%s",
+                                            rootdir, linkname);
+                               code = rename(lname, dirname);
+                               if (code) {
+                                   fprintf(stderr,
+                                           "Error renaming %s to %s  code=%d;%d\n",
+                                           lname, dirname, code, errno);
+                               }
+                           }
+
+                           /* Now create/update the link to the new/moved directory */
+                           if (vn.vnode == 1)
+                               afs_snprintf(dirname, sizeof dirname, "%s/",
+                                            this_name);
+                           else
+                               afs_snprintf(dirname, sizeof dirname,
+                                            "%s%d/%s/", ADIR, vn.vnode,
+                                            this_name);
+                           unlink(vflink);
+                           code = symlink(dirname, vflink);
+                           if (code) {
+                               fprintf(stderr,
+                                       "Error creating symlink %s -> %s  code=%d;%d\n",
+                                       vflink, dirname, code, errno);
+                           }
+                       }
+                       /*ADIRENTRY*/
+                           /* For a file entry, we remember the name of the file
+                            * by creating a link within the directory. Restoring
+                            * the file will later remove the link.
+                            */
+                           else {
+                            /*AFILEENTRY*/ afs_snprintf(vflink,
+                                                        sizeof vflink,
+                                                        "%s/%s%d", parentdir,
+                                                        AFILE, this_vn);
+
+                           code = symlink(this_name, vflink);
+                           if ((code < 0) && (errno != EEXIST)) {
+                               fprintf(stderr,
+                                       "Error creating symlink %s -> %s  code=%d;%d\n",
+                                       vflink, page0->entry[j].name, code,
+                                       errno);
+                           }
+                       }
+                    /*AFILEENTRY*/}
+               }
+               free(buffer);
+           }
+           /*ITSADIR*/
+           else if (vn.type == 1) {
+                /*ITSAFILE*/
+                   /* A file vnode. So create it into the desired directory. A
+                    * link should exist in the directory naming the file.
+                    */
+               int fid;
+               int lfile;
+               afs_sfsize_t size, s;
+
+               /* Check if its vnode-file-link exists. If not,
+                * then the file will be an orphaned file.
+                */
+               lfile = 1;
+               afs_snprintf(filename, sizeof filename, "%s/%s%d", parentdir,
+                            AFILE, vn.vnode);
+               len = readlink(filename, fname, MAXNAMELEN);
+               if (len < 0) {
+                   afs_snprintf(filename, sizeof filename, "%s/%s%d",
+                                rootdir, OFILE, vn.vnode);
+                   lfile = 0;  /* no longer a linked file; a direct path */
+               }
+
+               /* Create a mode for the file. Use the owner bits and
+                * duplicate them across group and other. The umask
+                * will remove what we don't want.
+                */
+               mode = (vn.modebits >> 6) & 0x7;
+               mode |= (mode << 6) | (mode << 3);
+
+               /* Write the file out */
+               fid = open(filename, (O_CREAT | O_WRONLY | O_TRUNC), mode);
+               size = vn.dataSize;
+               while (size > 0) {
+                   s = (afs_int32) ((size > BUFSIZE) ? BUFSIZE : size);
+                   code = fread(buf, 1, s, dumpfile);
+                   if (code > 0) {
+                       (void)write(fid, buf, code);
+                       size -= code;
+                   }
+                   if (code != s) {
+                       if (code < 0)
+                           fprintf(stderr, "Code = %d; Errno = %d\n", code,
+                                   errno);
+                       else {
+                           char tmp[100];
+                           (void)afs_snprintf(tmp, sizeof tmp,
+                                              "Read %llu bytes out of %llu",
+                                              (afs_uintmax_t) (vn.dataSize -
+                                                               size),
+                                              (afs_uintmax_t) vn.dataSize);
+                           fprintf(stderr, "%s\n", tmp);
+                       }
+                       break;
+                   }
+               }
+               close(fid);
+               if (size != 0) {
+                   fprintf(stderr, "   File %s (%s) is incomplete\n",
+                           filename, fname);
+               }
+
+               /* Remove the link to the file */
+               if (lfile) {
+                   unlink(filename);
+               }
+           }
+           /*ITSAFILE*/
+           else if (vn.type == 3) {
+                /*ITSASYMLINK*/
+                   /* A symlink vnode. So read it into the desired directory. This could
+                    * also be a mount point. If the volume is being restored to AFS, this
+                    * will become a mountpoint. If not, it becomes a symlink to no-where.
+                    */
+               int fid;
+               afs_int32 size, s;
+
+               /* Check if its vnode-file-link exists and create pathname
+                * of the symbolic link. If it doesn't exist,
+                * then the link will be an orphaned link.
+                */
+               afs_snprintf(linkname, sizeof linkname, "%s/%s%d", parentdir,
+                            AFILE, vn.vnode);
+               len = readlink(linkname, fname, MAXNAMELEN);
+               if (len < 0) {
+                   afs_snprintf(filename, sizeof filename, "%s/%s%d",
+                                rootdir, OFILE, vn.vnode);
+               } else {
+                   fname[len] = '\0';
+                   afs_snprintf(filename, sizeof filename, "%s/%s",
+                                parentdir, fname);
+               }
+
+               /* Read the link in, delete it, and then create it */
+               readdata(buf, vn.dataSize);
+
+               /* If a mountpoint, change its link path to mountroot */
+               s = strlen(buf);
+               if (((buf[0] == '%') || (buf[0] == '#'))
+                   && (buf[s - 1] == '.')) {
+                   /* This is a symbolic link */
+                   buf[s - 1] = 0;     /* Remove prefix '.' */
+                   strcpy(lname, &buf[1]);     /* Remove postfix '#' or '%' */
+                   strcpy(buf, mntroot);
+                   strcat(buf, lname);
+               }
+
+               unlink(filename);
+               code = symlink(buf, filename);
+               if (code) {
+                   fprintf(stderr,
+                           "Error creating symlink %s -> %s  code=%d;%d\n",
+                           filename, buf, code, errno);
+               }
+
+               /* Remove the symbolic link */
+               unlink(linkname);
+           }
+           /*ITSASYMLINK*/
+           else {
+               fprintf(stderr, "Unknown Vnode block\n");
+           }
+           break;
+
+       default:
+           done = 1;
+           break;
+       }
+    }
+    if (vn.type == 0)
+       inc_dump = 1;
+
+    return ((afs_int32) tag);
+}
+
+WorkerBee(as, arock)
+     struct cmd_syndesc *as;
+     char *arock;
+{
+    int code = 0, c, len;
+    afs_int32 type, count, vcount;
+    DIR *dirP, *dirQ;
+    struct dirent *dirE, *dirF;
+    char fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN],
+       mname[MAXNAMELEN];
+    char thisdir[MAXPATHLEN], *t;
+    struct DumpHeader dh;      /* Defined in dump.h */
+#if 0/*ndef HAVE_GETCWD*/      /* XXX enable when autoconf happens */
+    extern char *getwd();
+#define getcwd(x,y) getwd(x)
+#endif
+
+    if (as->parms[0].items) {  /* -file <dumpfile> */
+       dumpfile = fopen(as->parms[0].items->data, "r");
+       if (!dumpfile) {
+           fprintf(stderr, "Cannot open '%s'. Code = %d\n",
+                   as->parms[0].items->data, errno);
+           goto cleanup;
+       }
+    } else {
+       dumpfile = (FILE *) stdin;      /* use stdin */
+    }
+
+    /* Read the dump header. From it we get the volume name */
+    type = ntohl(readvalue(1));
+    if (type != 1) {
+       fprintf(stderr, "Expected DumpHeader\n");
+       code = -1;
+       goto cleanup;
+    }
+    type = ReadDumpHeader(&dh);
+
+    /* Get the root directory we restore to */
+    if (as->parms[1].items) {  /* -dir <rootdir> */
+       strcpy(rootdir, as->parms[1].items->data);
+    } else {
+       strcpy(rootdir, ".");
+    }
+    strcat(rootdir, "/");
+
+    /* Append the RW volume name to the root directory */
+    strcat(rootdir, dh.volumeName);
+    len = strlen(rootdir);
+    if (strcmp(".backup", rootdir + len - 7) == 0) {
+       rootdir[len - 7] = 0;
+    } else if (strcmp(".readonly", rootdir + len - 9) == 0) {
+       rootdir[len - 9] = 0;
+    }
+
+    /* Append the extension we asked for */
+    if (as->parms[2].items) {
+       strcat(rootdir, as->parms[2].items->data);      /* -extension <ext> */
+    }
+
+    /* The mountpoint root is either specifid in -mountpoint
+     * or -dir or the current working dir.
+     */
+    if ((as->parms[3].items) || (as->parms[1].items)) {        /* -mountpoint  or -dir */
+       t = (char *)getcwd(thisdir, MAXPATHLEN);        /* remember current dir */
+       if (!t) {
+           fprintf(stderr,
+                   "Cannot get pathname of current working directory: %s\n",
+                   thisdir);
+           code = -1;
+           goto cleanup;
+       }
+       /* Change to the mount point dir */
+       code =
+           chdir((as->parms[3].items ? as->parms[3].items->data : as->
+                  parms[1].items->data));
+       if (code) {
+           fprintf(stderr, "Mount point directory not found: Error = %d\n",
+                   errno);
+           goto cleanup;
+       }
+       t = (char *)getcwd(mntroot, MAXPATHLEN);        /* get its full pathname */
+       if (!t) {
+           fprintf(stderr,
+                   "Cannot determine pathname of mount point root directory: %s\n",
+                   mntroot);
+           code = -1;
+           goto cleanup;
+       }
+       strcat(mntroot, "/");   /* append '/' to end of it */
+       code = chdir(thisdir);  /* return to original working dir */
+       if (code) {
+           fprintf(stderr, "Cannot find working directory: Error = %d\n",
+                   errno);
+           goto cleanup;
+       }
+    } else {                   /* use current directory */
+       t = (char *)getcwd(mntroot, MAXPATHLEN);        /* get full pathname of current dir */
+       if (!t) {
+           fprintf(stderr,
+                   "Cannot determine pathname of current working directory: %s\n",
+                   mntroot);
+           code = -1;
+           goto cleanup;
+       }
+    }
+    strcat(mntroot, "/");      /* append '/' to end of it */
+
+    /* Set the umask for the restore */
+    if (as->parms[4].items) {  /* -umask */
+       afs_int32 mask;
+       mask = strtol(as->parms[4].items->data, 0, 8);
+       fprintf(stderr, "Umask set to 0%03o\n", mask);
+       umask(mask);
+    }
+
+    fprintf(stderr, "Restoring volume dump of '%s' to directory '%s'.\n",
+           dh.volumeName, rootdir);
+    code = mkdir(rootdir, 0777);
+    if ((code < 0) && (errno != EEXIST)) {
+       fprintf(stderr, "Error creating directory %s  code=%d;%d\n", rootdir,
+               code, errno);
+    }
+
+    for (count = 1; type == 2; count++) {
+       type = ReadVolumeHeader(count);
+       for (vcount = 1; type == 3; vcount++)
+           type = ReadVNode(vcount);
+    }
+
+    if (type != 4) {
+       fprintf(stderr, "Expected End-of-Dump\n");
+       code = -1;
+       goto cleanup;
+    }
+
+  cleanup:
+    /* For incremental restores, Follow each directory link and
+     * remove an "AFSFile" links.
+     */
+    if (inc_dump) {
+       fprintf(stderr, "An incremental dump.\n");
+       dirP = opendir(rootdir);
+       while (dirP && (dirE = readdir(dirP))) {
+           if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
+               afs_snprintf(name, sizeof name, "%s/%s", rootdir,
+                            dirE->d_name);
+               dirQ = opendir(name);
+               while (dirQ && (dirF = readdir(dirQ))) {
+                   if (strncmp(dirF->d_name, AFILE, strlen(AFILE)) == 0) {
+                       afs_snprintf(name, sizeof name, "%s/%s/%s", rootdir,
+                                    dirE->d_name, dirF->d_name);
+                       unlink(name);
+                   }
+               }
+               closedir(dirQ);
+           } else if (strncmp(dirE->d_name, AFILE, strlen(AFILE)) == 0) {
+               afs_snprintf(name, sizeof name, "%s/%s", rootdir,
+                            dirE->d_name);
+               unlink(name);
+           }
+       }
+       closedir(dirP);
+    }
+
+    /* Now go through and remove all the directory links */
+    dirP = opendir(rootdir);
+    while (dirP && (dirE = readdir(dirP))) {
+       if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
+           afs_snprintf(name, sizeof name, "%s/%s", rootdir, dirE->d_name);
+           unlink(name);
+       }
+    }
+    closedir(dirP);
+
+    return (code);
+}
+
+main(argc, argv)
+     int argc;
+     char **argv;
+{
+    struct cmd_syndesc *ts;
+    struct cmd_item *ti;
+
+    setlinebuf(stdout);
+
+    ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "vldb check");
+    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
+    cmd_AddParm(ts, "-dir", CMD_SINGLE, CMD_OPTIONAL, "restore dir");
+    cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL, "name extension");
+    cmd_AddParm(ts, "-mountpoint", CMD_SINGLE, CMD_OPTIONAL,
+               "mount point root");
+    cmd_AddParm(ts, "-umask", CMD_SINGLE, CMD_OPTIONAL, "mode mask");
+
+    return cmd_Dispatch(argc, argv);
+}
diff --git a/src/volser/vol-dump.c b/src/volser/vol-dump.c
new file mode 100644 (file)
index 0000000..125ada6
--- /dev/null
@@ -0,0 +1,871 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+   System:             VICE-TWO
+   Module:             vol-dump.c
+   Institution:        The Information Technology Center, Carnegie-Mellon University
+   
+   */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/vol-dump.c,v 1.1 2003/08/28 03:16:34 shadow Exp $");
+
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <time.h>
+#include <io.h>
+#else
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#endif
+#include <afs/cmd.h>
+
+#include <rx/xdr.h>
+#include <afs/afsint.h>
+#include "nfs.h"
+#include <afs/errors.h>
+#include "lock.h"
+#include "lwp.h"
+#include <afs/afssyscalls.h>
+#include "ihandle.h"
+#include "vnode.h"
+#include "volume.h"
+#include "partition.h"
+#include "viceinode.h"
+#include <afs/afssyscalls.h>
+#include "acl.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef _AIX
+#include <time.h>
+#endif
+
+#include <dirent.h>
+
+#include "volser/volser.h"
+#include "volser/volint.h"
+#include "volser/dump.h"
+
+#define putint32(p, v)  *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v
+#define putshort(p, v) *p++ = v>>8, *p++ = v
+
+#ifdef O_LARGEFILE
+#define afs_stat       stat64
+#define afs_fstat      fstat64
+#define afs_open       open64
+#else /* !O_LARGEFILE */
+#define afs_stat       stat
+#define afs_fstat      fstat
+#define afs_open       open
+#endif /* !O_LARGEFILE */
+
+int VolumeChanged;             /* needed by physio - leave alone */
+int verbose = 0;
+
+/* Forward Declarations */
+void HandleVolume(struct DiskPartition *partP, char *name, char *filename);
+Volume *AttachVolume(struct DiskPartition *dp, char *volname,
+                    register struct VolumeHeader *header);
+static void DoMyVolDump(Volume * vp, struct DiskPartition *dp,
+                       char *dumpfile);
+
+#ifndef AFS_NT40_ENV
+#include "AFS_component_version_number.c"
+#endif
+
+char name[VMAXPATHLEN];
+
+
+int
+ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
+{
+    int bad = 0;
+    int code;
+
+    code = IH_IREAD(ih, 0, to, size);
+    if (code != size)
+       return -1;
+
+    return 0;
+}
+
+
+Volume *
+AttachVolume(struct DiskPartition * dp, char *volname,
+            register struct VolumeHeader * header)
+{
+    register Volume *vp;
+    afs_int32 ec = 0;
+
+    vp = (Volume *) calloc(1, sizeof(Volume));
+    vp->specialStatus = 0;
+    vp->device = dp->device;
+    vp->partition = dp;
+    IH_INIT(vp->vnodeIndex[vLarge].handle, dp->device, header->parent,
+           header->largeVnodeIndex);
+    IH_INIT(vp->vnodeIndex[vSmall].handle, dp->device, header->parent,
+           header->smallVnodeIndex);
+    IH_INIT(vp->diskDataHandle, dp->device, header->parent,
+           header->volumeInfo);
+    IH_INIT(V_linkHandle(vp), dp->device, header->parent, header->linkTable);
+    vp->cacheCheck = 0;                /* XXXX */
+    vp->shuttingDown = 0;
+    vp->goingOffline = 0;
+    vp->nUsers = 1;
+    vp->header = (struct volHeader *)calloc(1, sizeof(*vp->header));
+    ec = ReadHdr1(V_diskDataHandle(vp), (char *)&V_disk(vp),
+                 sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
+    if (!ec) {
+       struct IndexFileHeader iHead;
+       ec = ReadHdr1(vp->vnodeIndex[vSmall].handle, (char *)&iHead,
+                     sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION);
+    }
+    if (!ec) {
+       struct IndexFileHeader iHead;
+       ec = ReadHdr1(vp->vnodeIndex[vLarge].handle, (char *)&iHead,
+                     sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION);
+    }
+#ifdef AFS_NAMEI_ENV
+    if (!ec) {
+       struct versionStamp stamp;
+       ec = ReadHdr1(V_linkHandle(vp), (char *)&stamp, sizeof(stamp),
+                     LINKTABLEMAGIC, LINKTABLEVERSION);
+    }
+#endif
+    if (ec)
+       return (Volume *) 0;
+    return vp;
+}
+
+
+static int
+handleit(struct cmd_syndesc *as, char *arock)
+{
+    register struct cmd_item *ti;
+    int err = 0;
+    int volumeId = 0;
+    char *partName = 0;
+    char *fileName = NULL;
+    struct DiskPartition *partP = NULL;
+    char name1[128];
+    char tmpPartName[20];
+
+
+#ifndef AFS_NT40_ENV
+#if 0
+    if (geteuid() != 0) {
+       fprintf(stderr, "voldump must be run as root; sorry\n");
+       exit(1);
+    }
+#endif
+#endif
+
+    if ((ti = as->parms[0].items))
+       partName = ti->data;
+    if ((ti = as->parms[1].items))
+       volumeId = atoi(ti->data);
+    if ((ti = as->parms[2].items))
+       fileName = ti->data;
+    if ((ti = as->parms[3].items))
+       verbose = 1;
+
+    DInit(10);
+
+    err = VAttachPartitions();
+    if (err) {
+       fprintf(stderr, "%d partitions had errors during attach.\n", err);
+    }
+
+    if (partName) {
+       if (strlen(partName) == 1) {
+           if (partName[0] >= 'a' && partName[0] <= 'z') {
+               strcpy(tmpPartName, "/vicepa");
+               tmpPartName[6] = partName[0];
+               partP = VGetPartition(tmpPartName, 0);
+           }
+       } else {
+           partP = VGetPartition(partName, 0);
+       }
+       if (!partP) {
+           fprintf(stderr,
+                   "%s is not an AFS partition name on this server.\n",
+                   partName);
+           exit(1);
+       }
+    }
+
+    if (!volumeId) {
+       fprintf(stderr, "Must specify volume id!\n");
+       exit(1);
+    }
+
+    if (!partP) {
+       fprintf(stderr, "must specify vice partition.\n");
+       exit(1);
+    }
+
+    (void)afs_snprintf(name1, sizeof name1, VFORMAT, (unsigned long)volumeId);
+    HandleVolume(partP, name1, fileName);
+    return 0;
+}
+
+void
+HandleVolume(struct DiskPartition *dp, char *name, char *filename)
+{
+    struct VolumeHeader header;
+    struct VolumeDiskHeader diskHeader;
+    struct afs_stat status, stat;
+    register int fd;
+    Volume *vp;
+    IHandle_t *ih;
+    char headerName[1024];
+
+    afs_int32 n;
+
+    (void)afs_snprintf(headerName, sizeof headerName, "%s/%s",
+                      VPartitionPath(dp), name);
+    if ((fd = afs_open(headerName, O_RDONLY)) == -1
+       || afs_fstat(fd, &status) == -1) {
+       fprintf(stderr, "Cannot read volume header %s\n", name);
+       close(fd);
+       exit(1);
+    }
+    n = read(fd, &diskHeader, sizeof(diskHeader));
+
+    if (n != sizeof(diskHeader)
+       || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
+       fprintf(stderr, "Error reading volume header %s\n", name);
+       exit(1);
+    }
+    if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
+       fprintf(stderr,
+               "Volume %s, version number is incorrect; volume needs salvage\n",
+               name);
+       exit(1);
+    }
+    DiskToVolumeHeader(&header, &diskHeader);
+
+    close(fd);
+    vp = AttachVolume(dp, name, &header);
+    if (!vp) {
+       fprintf(stderr, "Error attaching volume header %s\n", name);
+       exit(1);
+    }
+
+    DoMyVolDump(vp, dp, filename);
+}
+
+
+int
+main(int argc, char **argv)
+{
+    register struct cmd_syndesc *ts;
+    afs_int32 code;
+
+    VInitVolumePackage(volumeUtility, 5, 5, DONT_CONNECT_FS, 0);
+
+    ts = cmd_CreateSyntax(NULL, handleit, 0,
+                         "Dump a volume to a 'vos dump' format file without using volserver");
+    cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL, "AFS partition name");
+    cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id");
+    cmd_AddParm(ts, "-file", CMD_LIST, CMD_OPTIONAL, "Dump filename");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL,
+               "Trace dump progress (very verbose)");
+    code = cmd_Dispatch(argc, argv);
+    return code;
+}
+
+
+
+
+static int
+DumpDouble(int dumpfd, char tag, register afs_uint32 value1,
+          register afs_uint32 value2)
+{
+    int res;
+    char tbuffer[9];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    putint32(p, value1);
+    putint32(p, value2);
+
+    res = write(dumpfd, tbuffer, 9);
+    return ((res == 9) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpInt32(int dumpfd, char tag, register afs_uint32 value)
+{
+    char tbuffer[5];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    putint32(p, value);
+    return ((write(dumpfd, tbuffer, 5) == 5) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpString(int dumpfd, char tag, register char *s)
+{
+    register int n;
+    int code = 0;
+    code = write(dumpfd, &tag, 1);
+    if (code != 1)
+       return VOLSERDUMPERROR;
+    n = strlen(s) + 1;
+    code = write(dumpfd, s, n);
+    if (code != n)
+       return VOLSERDUMPERROR;
+    return 0;
+}
+
+
+static int
+DumpArrayInt32(int dumpfd, char tag, register afs_uint32 * array,
+              register int nelem)
+{
+    char tbuffer[4];
+    register afs_uint32 v;
+    int code = 0;
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    putshort(p, nelem);
+    code = write(dumpfd, tbuffer, 3);
+    if (code != 3)
+       return VOLSERDUMPERROR;
+    while (nelem--) {
+       p = (unsigned char *)tbuffer;
+       v = *array++;           /*this was register */
+
+       putint32(p, v);
+       code = write(dumpfd, tbuffer, 4);
+       if (code != 4)
+           return VOLSERDUMPERROR;
+    }
+    return 0;
+}
+
+
+
+
+static int
+DumpDumpHeader(int dumpfd, register Volume * vp, afs_int32 fromtime)
+{
+    int code = 0;
+    afs_int32 dumpTimes[2];
+
+    if (verbose)
+       fprintf(stderr, "dumping dump header\n");
+
+    if (!code)
+       code = DumpDouble(dumpfd, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION);
+
+    if (!code)
+       code = DumpInt32(dumpfd, 'v', V_id(vp));
+
+    if (!code)
+       code = DumpString(dumpfd, 'n', V_name(vp));
+
+    dumpTimes[0] = fromtime;
+    dumpTimes[1] = V_backupDate(vp);   /* Until the time the clone was made */
+    if (!code)
+       code = DumpArrayInt32(dumpfd, 't', (afs_uint32 *) dumpTimes, 2);
+
+    return code;
+}
+
+
+static int
+DumpEnd(int dumpfd)
+{
+    return (DumpInt32(dumpfd, D_DUMPEND, DUMPENDMAGIC));
+}
+
+static int
+DumpByte(int dumpfd, char tag, byte value)
+{
+    char tbuffer[2];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    *p = value;
+    return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpTag(int dumpfd, register int tag)
+{
+    char p;
+
+    p = tag;
+    return ((write(dumpfd, &p, 1) == 1) ? 0 : VOLSERDUMPERROR);
+
+}
+
+static int
+DumpBool(int dumpfd, char tag, unsigned int value)
+{
+    char tbuffer[2];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    *p = value;
+    return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR);
+}
+
+
+
+static int
+DumpVolumeHeader(int dumpfd, register Volume * vp)
+{
+    int code = 0;
+
+    if (verbose)
+       fprintf(stderr, "dumping volume header\n");
+
+    if (!code)
+       code = DumpTag(dumpfd, D_VOLUMEHEADER);
+    if (!code)
+       code = DumpInt32(dumpfd, 'i', V_id(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'v', V_stamp(vp).version);
+    if (!code)
+       code = DumpString(dumpfd, 'n', V_name(vp));
+    if (!code)
+       code = DumpBool(dumpfd, 's', V_inService(vp));
+    if (!code)
+       code = DumpBool(dumpfd, 'b', V_blessed(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'u', V_uniquifier(vp));
+    if (!code)
+       code = DumpByte(dumpfd, 't', (byte) V_type(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'p', V_parentId(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'c', V_cloneId(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'q', V_maxquota(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'm', V_minquota(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'd', V_diskused(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'f', V_filecount(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'a', V_accountNumber(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'o', V_owner(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'C', V_creationDate(vp));      /* Rw volume creation date */
+    if (!code)
+       code = DumpInt32(dumpfd, 'A', V_accessDate(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'U', V_updateDate(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'E', V_expirationDate(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'B', V_backupDate(vp));        /* Rw volume backup clone date */
+    if (!code)
+       code = DumpString(dumpfd, 'O', V_offlineMessage(vp));
+
+    /*
+     * We do NOT dump the detailed volume statistics residing in the old
+     * motd field, since we cannot tell from the info in a dump whether
+     * statistics data has been put there.  Instead, we dump a null string,
+     * just as if that was what the motd contained.
+     */
+    if (!code)
+       code = DumpString(dumpfd, 'M', "");
+    if (!code)
+       code =
+           DumpArrayInt32(dumpfd, 'W', (afs_uint32 *) V_weekUse(vp),
+                          sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0]));
+    if (!code)
+       code = DumpInt32(dumpfd, 'D', V_dayUseDate(vp));
+    if (!code)
+       code = DumpInt32(dumpfd, 'Z', V_dayUse(vp));
+    return code;
+}
+
+static int
+DumpShort(int dumpfd, char tag, unsigned int value)
+{
+    char tbuffer[3];
+    register byte *p = (unsigned char *)tbuffer;
+    *p++ = tag;
+    *p++ = value >> 8;
+    *p = value;
+    return ((write(dumpfd, tbuffer, 3) == 3) ? 0 : VOLSERDUMPERROR);
+}
+
+static int
+DumpByteString(int dumpfd, char tag, register byte * bs, register int nbytes)
+{
+    int code = 0;
+
+    code = write(dumpfd, &tag, 1);
+    if (code != 1)
+       return VOLSERDUMPERROR;
+    code = write(dumpfd, (char *)bs, nbytes);
+    if (code != nbytes)
+       return VOLSERDUMPERROR;
+    return 0;
+}
+
+
+static int
+DumpFile(int dumpfd, int vnode, FdHandle_t * handleP,  struct VnodeDiskObject *v)
+{
+    int code = 0, failed_seek = 0, failed_write = 0;
+    afs_int32 pad = 0, offset;
+    afs_sfsize_t n, nbytes, howMany, howBig;
+    byte *p;
+#ifndef AFS_NT40_ENV
+    struct afs_stat status;
+#endif
+    afs_sfsize_t size, tmpsize;
+#ifdef AFS_AIX_ENV
+#include <sys/statfs.h>
+    struct statfs tstatfs;
+#endif
+
+    if (verbose)
+       fprintf(stderr, "dumping file for vnode %d\n", vnode);
+
+#ifdef AFS_NT40_ENV
+    howBig = _filelength(handleP->fd_fd);
+    howMany = 4096;
+
+#else
+    afs_fstat(handleP->fd_fd, &status);
+    howBig = status.st_size;
+
+#ifdef AFS_AIX_ENV
+    /* Unfortunately in AIX valuable fields such as st_blksize are 
+     * gone from the stat structure.
+     */
+    fstatfs(handleP->fd_fd, &tstatfs);
+    howMany = tstatfs.f_bsize;
+#else
+    howMany = status.st_blksize;
+#endif /* AFS_AIX_ENV */
+#endif /* AFS_NT40_ENV */
+
+
+    size = FDH_SIZE(handleP);
+
+    if (verbose)
+       fprintf(stderr, "  howBig = %u, howMany = %u, fdh size = %u\n",
+               howBig, howMany, size);
+
+#ifdef AFS_LARGEFILE_ENV
+    {
+       afs_uint32 hi, lo;
+       SplitInt64(size, hi, lo);
+       if (hi == 0L) {
+           code = DumpInt32(dumpfd, 'f', lo);
+       } else {
+           code = DumpDouble(dumpfd, 'h', hi, lo);
+       }
+    }
+#else /* !AFS_LARGEFILE_ENV */
+    code = DumpInt32(dumpfd, 'f', size);
+#endif /* !AFS_LARGEFILE_ENV */
+    if (code) {
+       return VOLSERDUMPERROR;
+    }
+
+    p = (unsigned char *)malloc(howMany);
+    if (!p) {
+       fprintf(stderr, "out of memory!\n");
+       return VOLSERDUMPERROR;
+    }
+
+    /* loop through whole file, while we still have bytes left, and no errors, in chunks of howMany bytes */
+    for (nbytes = size; (nbytes && !failed_write); nbytes -= howMany) {
+       if (nbytes < howMany)
+           howMany = nbytes;
+
+       /* Read the data - unless we know we can't */
+       n = (failed_seek ? 0 : FDH_READ(handleP, p, howMany));
+
+       /* If read any good data and we null padded previously, log the
+        * amount that we had null padded.
+        */
+       if ((n > 0) && pad) {
+           fprintf(stderr, "Null padding file %d bytes at offset %u\n", pad,
+                   offset);
+           pad = 0;
+       }
+
+       /* If didn't read enough data, null padd the rest of the buffer. This
+        * can happen if, for instance, the media has some bad spots. We don't
+        * want to quit the dump, so we start null padding.
+        */
+       if (n < howMany) {
+
+               if (verbose) fprintf(stderr, "  read %u instead of %u bytes.\n", n, howMany);
+
+           /* Record the read error */
+           if (n < 0) {
+               n = 0;
+               fprintf(stderr, "Error %d reading inode %s for vnode %d\n",
+                       errno, PrintInode(NULL, handleP->fd_ih->ih_ino),
+                       vnode);
+           } else if (!pad) {
+               fprintf(stderr, "Error reading inode %s for vnode %d\n",
+                       PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
+           }
+
+           /* Pad the rest of the buffer with zeros. Remember offset we started 
+            * padding. Keep total tally of padding.
+            */
+           memset(p + n, 0, howMany - n);
+           if (!pad)
+               offset = (howBig - nbytes) + n;
+           pad += (howMany - n);
+
+           /* Now seek over the data we could not get. An error here means we
+            * can't do the next read.
+            */
+           failed_seek = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET);
+           if (failed_seek != ((size - nbytes) + howMany)) {
+               if (failed_seek < 0) {
+                   fprintf(stderr,
+                           "Error %d seeking in inode %s for vnode %d\n",
+                           errno, PrintInode(NULL, handleP->fd_ih->ih_ino),
+                           vnode);
+               } else {
+                   fprintf(stderr,
+                           "Error seeking in inode %s for vnode %d\n",
+                           PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
+                   failed_seek = -1;
+               }
+           } else {
+               failed_seek = 0;
+           }
+       }
+
+       /* Now write the data out */
+       if (write(dumpfd, (char *)p, howMany) != howMany)
+           failed_write = VOLSERDUMPERROR;
+    }
+
+    if (pad) {                 /* Any padding we hadn't reported yet */
+       fprintf(stderr, "Null padding file: %d bytes at offset %u\n", pad,
+               offset);
+    }
+
+    free(p);
+    return failed_write;
+}
+
+
+static int
+DumpVnode(int dumpfd, struct VnodeDiskObject *v, int volid, int vnodeNumber,
+         int dumpEverything, struct Volume *vp)
+{
+    int code = 0;
+    IHandle_t *ihP;
+    FdHandle_t *fdP;
+
+    if (verbose)
+       fprintf(stderr, "dumping vnode %d\n", vnodeNumber);
+
+    if (!v || v->type == vNull)
+       return code;
+    if (!code)
+       code = DumpDouble(dumpfd, D_VNODE, vnodeNumber, v->uniquifier);
+    if (!dumpEverything)
+       return code;
+    if (!code)
+       code = DumpByte(dumpfd, 't', (byte) v->type);
+    if (!code)
+       code = DumpShort(dumpfd, 'l', v->linkCount);    /* May not need this */
+    if (!code)
+       code = DumpInt32(dumpfd, 'v', v->dataVersion);
+    if (!code)
+       code = DumpInt32(dumpfd, 'm', v->unixModifyTime);
+    if (!code)
+       code = DumpInt32(dumpfd, 'a', v->author);
+    if (!code)
+       code = DumpInt32(dumpfd, 'o', v->owner);
+    if (!code && v->group)
+       code = DumpInt32(dumpfd, 'g', v->group);        /* default group is 0 */
+    if (!code)
+       code = DumpShort(dumpfd, 'b', v->modeBits);
+    if (!code)
+       code = DumpInt32(dumpfd, 'p', v->parent);
+    if (!code)
+       code = DumpInt32(dumpfd, 's', v->serverModifyTime);
+    if (v->type == vDirectory) {
+       acl_HtonACL(VVnodeDiskACL(v));
+       if (!code)
+           code =
+               DumpByteString(dumpfd, 'A', (byte *) VVnodeDiskACL(v),
+                              VAclDiskSize(v));
+    }
+
+    if (VNDISK_GET_INO(v)) {
+       IH_INIT(ihP, V_device(vp), V_parentId(vp), VNDISK_GET_INO(v));
+       fdP = IH_OPEN(ihP);
+       if (fdP == NULL) {
+           fprintf(stderr,
+                   "Unable to open inode %s for vnode %u (volume %i); not dumped, error %d\n",
+                   PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber, volid,
+                   errno);
+       }
+       else
+       {
+               if (verbose)
+                   fprintf(stderr, "about to dump inode %s for vnode %u\n",
+                           PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber);
+               code = DumpFile(dumpfd, vnodeNumber, fdP, v);
+               FDH_CLOSE(fdP);
+       }
+       IH_RELEASE(ihP);
+    }
+
+    if (verbose)
+       fprintf(stderr, "done dumping vnode %d\n", vnodeNumber);
+    return code;
+}
+
+
+static int
+DumpVnodeIndex(int dumpfd, Volume * vp, VnodeClass class, afs_int32 fromtime,
+              int forcedump)
+{
+    register int code = 0;
+    register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
+    char buf[SIZEOF_LARGEDISKVNODE];
+    struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
+    StreamHandle_t *file;
+    FdHandle_t *fdP;
+    int size;
+    int flag;
+    int offset = 0;
+    register int vnodeIndex, nVnodes = 0;
+
+    fdP = IH_OPEN(vp->vnodeIndex[class].handle);
+    file = FDH_FDOPEN(fdP, "r+");
+    size = OS_SIZE(fdP->fd_fd);
+    nVnodes = (size / vcp->diskSize) - 1;
+
+    if (nVnodes > 0) {
+       STREAM_SEEK(file, vcp->diskSize, 0);
+    } else
+       nVnodes = 0;
+    for (vnodeIndex = 0;
+        nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code;
+        nVnodes--, vnodeIndex++, offset += vcp->diskSize) {
+       flag = forcedump || (vnode->serverModifyTime >= fromtime);
+       /* Note:  the >= test is very important since some old volumes may not have
+        * a serverModifyTime.  For an epoch dump, this results in 0>=0 test, which
+        * does dump the file! */
+       if (verbose)
+           fprintf(stderr, "about to dump %s vnode %u (vnode offset = %u)\n",
+                       class == vSmall ? "vSmall" : "vLarge",
+                   bitNumberToVnodeNumber(vnodeIndex, class), offset);
+       if (!code)
+           code =
+               DumpVnode(dumpfd, vnode, V_id(vp),
+                         bitNumberToVnodeNumber(vnodeIndex, class), flag,
+                         vp);
+    }
+    STREAM_CLOSE(file);
+    FDH_CLOSE(fdP);
+    return code;
+}
+
+
+
+/* A partial dump (no dump header) */
+static int
+DumpPartial(int dumpfd, register Volume * vp, afs_int32 fromtime,
+           int dumpAllDirs)
+{
+    int code = 0;
+
+    if (verbose)
+       fprintf(stderr, "about to dump the volume header\n");
+    if (!code)
+       code = DumpVolumeHeader(dumpfd, vp);
+
+    if (verbose)
+       fprintf(stderr, "about to dump the large vnode index\n");
+    if (!code)
+       code = DumpVnodeIndex(dumpfd, vp, vLarge, fromtime, dumpAllDirs);
+
+    if (verbose)
+       fprintf(stderr, "about to dump the small vnode index\n");
+    if (!code)
+       code = DumpVnodeIndex(dumpfd, vp, vSmall, fromtime, 0);
+    return code;
+}
+
+
+
+static void
+DoMyVolDump(Volume * vp, struct DiskPartition *dp, char *dumpfile)
+{
+    int code = 0;
+    int fromtime = 0;
+    int dumpAllDirs = 0;
+    int dumpfd = 0;
+
+    if (dumpfile) {
+       unlink(dumpfile);
+       dumpfd =
+           open(dumpfile, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
+       if (dumpfd < 0) {
+           fprintf(stderr, "Failed to open dump file! Exiting.\n");
+           exit(1);
+       }
+    } else {
+       dumpfd = 1;             /* stdout */
+    }
+
+    if (verbose)
+       fprintf(stderr, "about to dump the dump header\n");
+    if (!code)
+       code = DumpDumpHeader(dumpfd, vp, fromtime);
+
+    if (verbose)
+       fprintf(stderr, "about to dump volume contents\n");
+    if (!code)
+       code = DumpPartial(dumpfd, vp, fromtime, dumpAllDirs);
+
+    if (verbose)
+       fprintf(stderr, "about to dump the dump postamble\n");
+    if (!code)
+       code = DumpEnd(dumpfd);
+
+    if (verbose)
+       fprintf(stderr, "finished dump\n");
+    close(dumpfd);             /* might be closing stdout, no harm */
+}
diff --git a/src/volser/vol.h b/src/volser/vol.h
new file mode 100644 (file)
index 0000000..522fb97
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ *  Module:        Vol.h
+ *  System:        Volser
+ *  Instituition:   ITC, CMU
+ *  Date:          December, 88
+ */
+
+/* pick up the declaration of Inode */
+#include <afs/afssyscalls.h>
+
+typedef struct DirHandle {
+    int dirh_volume;
+    int dirh_device;
+    Inode dirh_inode;
+    afs_int32 dirh_cacheCheck;
+    IHandle_t *dirh_handle;
+} DirHandle;
diff --git a/src/volser/volerr.et b/src/volser/volerr.et
new file mode 100644 (file)
index 0000000..16db9f0
--- /dev/null
@@ -0,0 +1,32 @@
+# Copyright 2000, International Business Machines Corporation and others.
+# All Rights Reserved.
+# 
+# This software has been released under the terms of the IBM Public
+# License.  For details, see the LICENSE file in the top-level source
+# directory or online at http://www.openafs.org/dl/license10.html
+
+# */
+
+# NOTE: VOLSERBAD_ACCESS value is hardcoded in audit/audit.h, so if you
+# make changes here, make sure that audit/audit.h is kept up to date.
+
+error_table VOLS
+       ec VOLSERTRELE_ERROR, "internal error releasing transaction"
+       ec VOLSERNO_OP, "unknown internal error"
+       ec VOLSERREAD_DUMPERROR, "badly formatted dump"
+       ec VOLSERDUMPERROR, "badly formatted dump(2)"
+       ec VOLSERATTACH_ERROR, "could not attach volume"
+       ec VOLSERILLEGAL_PARTITION, "illegal partition"
+       ec VOLSERDETACH_ERROR, "could not detach volume"
+       ec VOLSERBAD_ACCESS, "insufficient privilege for volume operation"
+       ec VOLSERVLDB_ERROR, "error from volume location database"
+       ec VOLSERBADNAME, "bad volume name"
+       ec VOLSERVOLMOVED, "volume moved"
+       ec VOLSERBADOP, "illegal volume operation"
+       ec VOLSERBADRELEASE, "volume release failed"
+       ec VOLSERVOLBUSY, "volume still in use by volserver"
+       ec VOLSERNO_MEMORY, "out of virtual memory in volserver"
+       ec VOLSERNOVOL, "no such volume"
+       ec VOLSERMULTIRWVOL, "more than one read/write volume"
+       ec VOLSERFAILEDOP, "failed volume server operation"
+end
diff --git a/src/volser/volint.xg b/src/volser/volint.xg
new file mode 100644 (file)
index 0000000..cfa5155
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ *  Module:        Volint.xg
+ *  System:        Volser
+ *  Instituition:          ITC, CMU
+ *  Date:                  December, 88
+ */
+
+package AFSVol
+prefix S
+statindex 16
+
+#define            VOLCREATEVOLUME     100
+#define            VOLDELETEVOLUME     101
+#define            VOLRESTORE          102
+#define            VOLFORWARD          103
+#define            VOLENDTRANS         104
+#define            VOLCLONE            105
+#define            VOLSETFLAGS         106
+#define            VOLGETFLAGS         107
+#define            VOLTRANSCREATE      108
+#define            VOLDUMP             109
+#define            VOLGETNTHVOLUME     110
+#define            VOLSETFORWARDING    111  /* defunct */
+#define            VOLGETNAME          112
+#define            VOLGETSTATUS        113
+#define            VOLSIGRESTORE       114
+#define            VOLLISTPARTITIONS   115
+#define            VOLLISTVOLS         116
+#define            VOLSETIDSTYPES      117
+#define            VOLMONITOR          118
+#define            VOLDISKPART 119
+#define                VOLRECLONE      120
+#define            VOLLISTONEVOL       121
+#define                VOLNUKE         122
+#define        VOLSETDATE      123
+#define                VOLXLISTVOLS    124
+#define                VOLXLISTONEVOL  125
+#define            VOLSETINFO          126
+#define            VOLXLISTPARTITIONS  127
+#define            VOLFORWARDMULTIPLE  128
+#define     VOLCONVERTRO        65536
+#define     VOLGETSIZE          65537
+
+const SIZE = 1024;
+
+struct volser_status {
+       afs_int32 volID;                /* Volume id--unique over all systems */
+       afs_int32 nextUnique;           /* Next vnode uniquifier for this volume */
+       int type;                       /* readwrite, etc. */
+       afs_int32 parentID;             /* Id of parent, if type==readonly or backup */
+       afs_int32 cloneID;              /* Latest read-only clone, if type==readwrite */
+       afs_int32 backupID;             /* Latest backup copy of this read write volume */
+       afs_int32 restoredFromID;       /* The id in a dump this volume was restored from--used simply
+                                  to make sure that an incremental dump is not restored on top
+                                  of something inappropriate:  Note:  this field itself is NEVER
+                                  dumped!!! */
+       afs_int32 maxQuota;             /* Quota maximum, 1K blocks */
+       afs_int32 minQuota;             /* Quota minimum, 1K blocks */
+       afs_int32 owner;                /* The person responsible for this volume */
+       afs_int32 creationDate;         /* Creation date for a read/write
+                                  volume; cloning date for original copy of
+                                  a readonly volume (replicated volumes have
+                                  the same creation date) */
+       afs_int32 accessDate;           /* Last access time by a user, large granularity */
+       afs_int32 updateDate;           /* Last modification by user */
+       afs_int32 expirationDate;               /* 0 if it never expires */
+       afs_int32 backupDate;           /* last time a backup clone was taken */
+       afs_int32 copyDate;             /* Time that this copy of this volume was created */
+};
+
+struct destServer {
+    afs_int32 destHost;
+    afs_int32 destPort;
+    afs_int32 destSSID;
+};
+
+
+struct volintInfo {
+#define VNAMESIZE 32
+    char name[VNAMESIZE];
+    afs_int32 volid;           /* volume's id */
+    afs_int32 type;            /* read-only, read-write, backup */
+    afs_int32 backupID;
+    afs_int32 parentID;
+    afs_int32 cloneID;
+    afs_int32 status;
+    afs_int32 copyDate;
+    unsigned char inUse;
+    unsigned char needsSalvaged;
+    unsigned char destroyMe;
+    afs_int32 creationDate;
+    afs_int32 accessDate;
+    afs_int32 updateDate;
+    afs_int32 backupDate;
+    int dayUse;
+    int filecount;
+    int maxquota;
+    int size;
+    afs_int32 flags;           /* used by the backup system */
+    afs_int32 spare0;          /* Used to hold the minquota value */
+    afs_int32 spare1;          /* Used to hold the weekuse value */
+    afs_int32 spare2;
+    afs_int32 spare3;
+};
+
+/*
+ * Define some values needed for the detailed volume info structure.
+ */
+const VOLINT_STATS_NUM_RWINFO_FIELDS = 4;
+
+const VOLINT_STATS_SAME_NET      = 0;  /*Within same site (total)*/
+const VOLINT_STATS_SAME_NET_AUTH = 1;  /*Within same site (authenticated);
+                                         (must be 1 more than above)*/
+const VOLINT_STATS_DIFF_NET     = 2;   /*From external site (total)*/
+const VOLINT_STATS_DIFF_NET_AUTH = 3;  /*From external site (authenticated)
+                                         (must be 1 more than above)*/
+
+const VOLINT_STATS_NUM_TIME_RANGES = 6;
+
+const VOLINT_STATS_TIME_CAP_0  =     60;  /*60 seconds*/
+const VOLINT_STATS_TIME_CAP_1  =    600;  /*10 minutes, in seconds*/
+const VOLINT_STATS_TIME_CAP_2  =   3600;  /*1 hour, in seconds*/
+const VOLINT_STATS_TIME_CAP_3  =  86400;  /*1 day, in seconds*/
+const VOLINT_STATS_TIME_CAP_4  = 604800;  /*1 week, in seconds*/
+
+const VOLINT_STATS_NUM_TIME_FIELDS = 6;
+
+const VOLINT_STATS_TIME_IDX_0 = 0;     /*0 secs to 60 secs*/
+const VOLINT_STATS_TIME_IDX_1 = 1;     /*1 min to 10 mins*/
+const VOLINT_STATS_TIME_IDX_2 = 2;     /*10 mins to 60 mins*/
+const VOLINT_STATS_TIME_IDX_3 = 3;     /*1 hr to 24 hrs*/
+const VOLINT_STATS_TIME_IDX_4 = 4;     /*1 day to 7 days*/
+const VOLINT_STATS_TIME_IDX_5 = 5;     /*Greater than 1 week*/
+
+/*
+ * More detailed volume info
+ */
+struct volintXInfo {
+    char name[VNAMESIZE];
+    afs_int32 volid;                   /*Volume's ID*/
+    afs_int32 type;                    /*RWVOL, ROVOL, BACKVOL*/
+    afs_int32 backupID;                /*Backup volume ID*/
+    afs_int32 parentID;                /*Parent volume ID*/
+    afs_int32 cloneID;         /*Clone volume ID*/
+    afs_int32 status;          /*Volume status*/
+    afs_int32 copyDate;                /*Date when this volume INSTANCE created*/
+    unsigned char inUse;       /*In use at time of last crash?*/
+    afs_int32 creationDate;            /*Date when this volume was created*/
+    afs_int32 accessDate;              /*Date when this volume was last accessed*/
+    afs_int32 updateDate;              /*Date when this volume was last updated*/
+    afs_int32 backupDate;              /*Date when this volume was last backed up*/
+    int dayUse;                        /*Number of accesses since midnight*/
+    int filecount;             /*Number of files in the volume*/
+    int maxquota;              /*Max volume quota, in Kbytes*/
+    int size;                  /*Current size in Kbytes*/
+    /*
+     * Detailed statistics for reads/writes and authorship.
+     */
+    afs_int32 stat_reads[VOLINT_STATS_NUM_RWINFO_FIELDS];
+    afs_int32 stat_writes[VOLINT_STATS_NUM_RWINFO_FIELDS];
+    afs_int32 stat_fileSameAuthor[VOLINT_STATS_NUM_TIME_FIELDS];
+    afs_int32 stat_fileDiffAuthor[VOLINT_STATS_NUM_TIME_FIELDS];
+    afs_int32 stat_dirSameAuthor[VOLINT_STATS_NUM_TIME_FIELDS];
+    afs_int32 stat_dirDiffAuthor[VOLINT_STATS_NUM_TIME_FIELDS];
+};
+
+struct transDebugInfo {
+       afs_int32 tid;           /*transaction id */
+       afs_int32 time;  /* time transaction was last active (for timeouts) */
+       afs_int32 creationTime;    /* time the transaction started */
+       afs_int32 returnCode;       /* transaction error code */
+       afs_int32 volid;        /*sequence number of the next packet to be read*/ /* open volume's id */
+       afs_int32 partition;        /* open volume's partition */
+       short iflags;       /* initial attach mode flags (IT*) */
+       char vflags;        /* current volume status flags (VT*) */
+       char tflags;        /* transaction flags (TT*) */
+       char lastProcName[30];  /* name of the last procedure which used transaction */
+       int callValid;  /*flag which determines if following data is valid*/
+       afs_int32 readNext;     /*sequence number of the next packet to be read*/
+       afs_int32 transmitNext; /*sequence number of the next packet to be transmitted*/
+       int lastSendTime;
+       int lastReceiveTime;
+};
+
+struct pIDs {
+       afs_int32 partIds[26];
+};
+
+struct diskPartition {
+        char   name[32];       /* Mounted partition name */
+        char   devName[32];    
+        int    lock_fd;
+       int     totalUsable;
+       int     free;
+       int     minFree;
+
+};
+
+struct restoreCookie {
+       char name[32];
+       afs_int32 type;
+       afs_int32 clone;
+       afs_int32 parent;
+};
+
+struct replica {
+       afs_int32 trans;
+       struct destServer server;
+};
+
+/*  Various size parameters of the volume  */
+struct volintSize {
+    afs_uint64 dump_size;
+};
+
+typedef  replica manyDests<>;
+typedef  afs_int32 manyResults<>;
+typedef  transDebugInfo transDebugEntries<>;
+typedef  volintInfo volEntries<>;
+typedef  afs_int32 partEntries<>;
+typedef  volintXInfo volXEntries<>;
+
+proc CreateVolume(
+  IN afs_int32 partition,
+  string name<>,
+  IN afs_int32 type,
+  IN afs_int32 parent,
+  INOUT afs_int32 *volid,
+  OUT afs_int32 *trans
+) = VOLCREATEVOLUME;
+
+proc DeleteVolume(
+  IN afs_int32 trans
+) = VOLDELETEVOLUME;
+
+proc Restore(
+  IN afs_int32 toTrans,
+  IN afs_int32 flags,
+  IN struct restoreCookie *cookie
+) split = VOLRESTORE;
+
+proc Forward(
+  IN afs_int32 fromTrans,
+  IN afs_int32 fromDate,
+  IN struct destServer *destination,
+  IN afs_int32 destTrans,
+  IN struct restoreCookie *cookie
+) = VOLFORWARD;
+
+proc EndTrans(
+  IN afs_int32 trans,
+  OUT afs_int32 *rcode
+) = VOLENDTRANS;
+
+proc Clone(
+  IN afs_int32 trans,
+  IN afs_int32 purgeVol,
+  IN afs_int32 newType,
+  IN string newName<>,
+  INOUT afs_int32 *newVol
+) = VOLCLONE;
+
+proc SetFlags(
+  IN afs_int32 trans,
+  IN afs_int32 flags
+) = VOLSETFLAGS;
+
+proc GetFlags(
+  IN afs_int32 trans,
+  OUT afs_int32 *flags
+) = VOLGETFLAGS;
+
+proc TransCreate(
+  IN afs_int32 volume,
+  IN afs_int32 partition,
+  IN afs_int32 flags,
+  OUT afs_int32 *trans
+) = VOLTRANSCREATE;
+
+proc Dump(
+  IN afs_int32 fromTrans,
+  IN afs_int32 fromDate
+) split = VOLDUMP;
+
+proc GetNthVolume(
+  IN afs_int32 index,
+  OUT afs_int32 *volume,
+  OUT afs_int32 *partition
+) = VOLGETNTHVOLUME;
+
+proc SetForwarding(
+  IN afs_int32 tid,
+  IN afs_int32 newsite
+) = VOLSETFORWARDING;
+
+proc GetName(
+  IN afs_int32 tid,
+  OUT string tname<256>
+) = VOLGETNAME;
+
+proc GetStatus(
+  IN afs_int32 tid,
+  OUT struct volser_status *status
+) = VOLGETSTATUS;
+
+proc SignalRestore(
+  IN string name<>,
+  int type,
+  afs_int32 pid,
+  afs_int32 cloneid
+) = VOLSIGRESTORE;
+
+proc ListPartitions(
+  OUT struct pIDs *partIDs
+) = VOLLISTPARTITIONS;
+
+proc ListVolumes(
+  IN afs_int32 partID,
+  afs_int32 flags,
+  OUT volEntries *resultEntries
+) = VOLLISTVOLS;
+
+proc SetIdsTypes(
+  IN afs_int32 tId,
+  string name<>,
+  afs_int32 type,
+  afs_int32 pId,
+  afs_int32 cloneId,
+  afs_int32 backupId
+) = VOLSETIDSTYPES;
+
+proc Monitor(
+  OUT transDebugEntries *result
+) = VOLMONITOR;
+
+proc PartitionInfo(
+  IN string name<>,
+  OUT struct diskPartition *partition
+) = VOLDISKPART;
+
+proc ReClone(
+  IN afs_int32 tid,
+  afs_int32 cloneID
+) = VOLRECLONE;
+
+proc ListOneVolume(
+  IN afs_int32 partID,
+  afs_int32 volid,
+  OUT volEntries *resultEntries
+) = VOLLISTONEVOL;
+
+proc NukeVolume(
+  IN afs_int32 partID,
+  afs_int32 volID
+) = VOLNUKE;
+
+proc SetDate(
+  IN afs_int32 tid,
+  afs_int32 newDate
+) = VOLSETDATE;
+
+proc XListVolumes(
+  IN afs_int32 partID,
+  afs_int32 flags,
+  OUT volXEntries *resultXEntriesP
+) = VOLXLISTVOLS;
+
+proc XListOneVolume(
+  IN afs_int32 partID,
+  afs_int32 volid,
+  OUT volXEntries *resultXEntries
+) = VOLXLISTONEVOL;
+
+proc SetInfo(
+  IN afs_int32 tid,
+  struct volintInfo *status
+) = VOLSETINFO;
+
+proc XListPartitions(
+  OUT struct partEntries *partIDs
+) = VOLXLISTPARTITIONS;
+
+proc ForwardMultiple(
+  IN afs_int32 fromTrans,
+  IN afs_int32 fromDate,
+  IN manyDests *destinations,
+  IN afs_int32 spare,
+  IN struct restoreCookie *cookie,
+  OUT manyResults *results
+) = VOLFORWARDMULTIPLE;
+
+proc ConvertROtoRWvolume(
+  IN afs_int32 partid,
+  IN afs_int32 volid
+) = VOLCONVERTRO;
+
+proc GetSize(
+  IN afs_int32 fromTrans,
+  IN afs_int32 fromDate,
+  OUT struct volintSize *size
+) = VOLGETSIZE;
diff --git a/src/volser/volmain.c b/src/volser/volmain.c
new file mode 100644 (file)
index 0000000..237d32e
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/volmain.c,v 1.18 2003/12/07 22:49:44 jaltman Exp $");
+
+#include <sys/types.h>
+#ifdef AFS_NT40_ENV
+#include <time.h>
+#include <fcntl.h>
+#include <windows.h>
+#include <WINNT/afsevent.h>
+#else
+#include <sys/time.h>
+#include <sys/file.h>
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#include <rx/xdr.h>
+#include <afs/afsint.h>
+#include <stdio.h>
+#include <signal.h>
+#ifdef AFS_PTHREAD_ENV
+#include <assert.h>
+#else /* AFS_PTHREAD_ENV */
+#include <afs/assert.h>
+#endif /* AFS_PTHREAD_ENV */
+#include <afs/prs_fs.h>
+#include <afs/nfs.h>
+#include <lwp.h>
+#include <lock.h>
+#include <afs/afssyscalls.h>
+#include <afs/ihandle.h>
+#ifdef AFS_NT40_ENV
+#include <afs/ntops.h>
+#endif
+#include <afs/vnode.h>
+#include <afs/volume.h>
+#include <afs/partition.h>
+#include <rx/rx.h>
+#include <rx/rx_globals.h>
+#include <afs/auth.h>
+#include <rx/rxkad.h>
+#include <afs/cellconfig.h>
+#include <afs/keys.h>
+#include <ubik.h>
+
+#include "volser.h"
+#include <errno.h>
+#include <afs/audit.h>
+#include <afs/afsutil.h>
+
+/*@printflike@*/ extern void Log(const char *format, ...);
+/*@printflike@*/ extern void Abort(const char *format, ...);
+
+#define VolserVersion "2.0"
+#define N_SECURITY_OBJECTS 3
+
+extern struct volser_trans *TransList();
+#ifndef AFS_PTHREAD_ENV
+extern int (*vol_PollProc) ();
+extern int IOMGR_Poll();
+#endif
+char *GlobalNameHack = NULL;
+int hackIsIn = 0;
+afs_int32 GlobalVolCloneId, GlobalVolParentId;
+int GlobalVolType;
+int VolumeChanged;             /* XXXX */
+static char busyFlags[MAXHELPERS];
+struct volser_trans *QI_GlobalWriteTrans = 0;
+extern void AFSVolExecuteRequest();
+extern void RXSTATS_ExecuteRequest();
+struct afsconf_dir *tdir;
+static afs_int32 runningCalls = 0;
+int DoLogging = 0;
+#define MAXLWP 16
+int lwps = 9;
+int udpBufSize = 0;            /* UDP buffer size for receive */
+
+int Testing = 0;               /* for ListViceInodes */
+
+#define VS_EXIT(code)  {                                          \
+                          osi_audit(VS_ExitEvent, code, AUD_END); \
+                         exit(code);                             \
+                      }
+
+
+static
+MyBeforeProc(struct rx_call *acall)
+{
+    VTRANS_LOCK;
+    runningCalls++;
+    VTRANS_UNLOCK;
+    return 0;
+}
+
+static
+MyAfterProc(struct rx_call *acall, afs_int32 code)
+{
+    VTRANS_LOCK;
+    runningCalls--;
+    VTRANS_UNLOCK;
+    return 0;
+}
+
+/* Called every GCWAKEUP seconds to try to unlock all our partitions,
+ * if we're idle and there are no active transactions 
+ */
+static
+TryUnlock()
+{
+    /* if there are no running calls, and there are no active transactions, then
+     * it should be safe to release any partition locks we've accumulated */
+    VTRANS_LOCK;
+    if (runningCalls == 0 && TransList() == (struct volser_trans *)0) {
+        VTRANS_UNLOCK;
+       VPFullUnlock();         /* in volprocs.c */
+    } else
+        VTRANS_UNLOCK;
+}
+
+/* background daemon for timing out transactions */
+static
+BKGLoop()
+{
+    struct timeval tv;
+    int loop = 0;
+
+    while (1) {
+       tv.tv_sec = GCWAKEUP;
+       tv.tv_usec = 0;
+#ifdef AFS_PTHREAD_ENV
+        select(0, 0, 0, 0, &tv);
+#else
+       (void)IOMGR_Select(0, 0, 0, 0, &tv);
+#endif
+       GCTrans();
+       TryUnlock();
+       loop++;
+       if (loop == 10) {       /* reopen log every 5 minutes */
+           loop = 0;
+           ReOpenLog(AFSDIR_SERVER_VOLSERLOG_FILEPATH);
+       }
+    }
+}
+
+/* Background daemon for sleeping so the volserver does not become I/O bound */
+afs_int32 TTsleep, TTrun;
+static
+BKGSleep()
+{
+    struct volser_trans *tt;
+
+    if (TTsleep) {
+       while (1) {
+#ifdef AFS_PTHREAD_ENV
+           sleep(TTrun);
+#else /* AFS_PTHREAD_ENV */
+           IOMGR_Sleep(TTrun);
+#endif
+           VTRANS_LOCK;
+           for (tt = TransList(); tt; tt = tt->next) {
+               if ((strcmp(tt->lastProcName, "DeleteVolume") == 0)
+                   || (strcmp(tt->lastProcName, "Clone") == 0)
+                   || (strcmp(tt->lastProcName, "ReClone") == 0)
+                   || (strcmp(tt->lastProcName, "Forward") == 0)
+                   || (strcmp(tt->lastProcName, "Restore") == 0)
+                   || (strcmp(tt->lastProcName, "ForwardMulti") == 0))
+                   break;
+           }
+           if (tt) {
+               VTRANS_UNLOCK;
+               sleep(TTsleep);
+           } else
+               VTRANS_UNLOCK;
+       }
+    }
+}
+
+#ifndef AFS_NT40_ENV
+int
+volser_syscall(a3, a4, a5)
+     afs_uint32 a3, a4;
+     void *a5;
+{
+    afs_uint32 rcode;
+    void (*old) ();
+
+#ifndef AFS_LINUX20_ENV
+    old = signal(SIGSYS, SIG_IGN);
+#endif
+    rcode =
+       syscall(AFS_SYSCALL /* AFS_SYSCALL */ , 28 /* AFSCALL_CALL */ , a3,
+               a4, a5);
+#ifndef AFS_LINUX20_ENV
+    signal(SIGSYS, old);
+#endif
+
+    return rcode;
+}
+#endif
+
+
+/* check whether caller is authorized to manage RX statistics */
+int
+vol_rxstat_userok(call)
+     struct rx_call *call;
+{
+    return afsconf_SuperUser(tdir, call, NULL);
+}
+
+#include "AFS_component_version_number.c"
+main(argc, argv)
+     int argc;
+     char **argv;
+{
+    register afs_int32 code;
+    struct rx_securityClass *(securityObjects[3]);
+    struct rx_service *service;
+    struct ktc_encryptionKey tkey;
+    int rxpackets = 100;
+    char commandLine[150];
+    int i;
+    int rxJumbograms = 1;      /* default is to send and receive jumbograms. */
+    int bufSize = 0;           /* temp variable to read in udp socket buf size */
+
+#ifdef AFS_AIX32_ENV
+    /*
+     * The following signal action for AIX is necessary so that in case of a 
+     * crash (i.e. core is generated) we can include the user's data section 
+     * in the core dump. Unfortunately, by default, only a partial core is
+     * generated which, in many cases, isn't too useful.
+     */
+    struct sigaction nsa;
+
+    sigemptyset(&nsa.sa_mask);
+    nsa.sa_handler = SIG_DFL;
+    nsa.sa_flags = SA_FULLDUMP;
+    sigaction(SIGABRT, &nsa, NULL);
+    sigaction(SIGSEGV, &nsa, NULL);
+#endif
+    osi_audit(VS_StartEvent, 0, AUD_END);
+
+    /* Initialize dirpaths */
+    if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
+#ifdef AFS_NT40_ENV
+       ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
+#endif
+       fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
+               argv[0]);
+       exit(2);
+    }
+
+    for (commandLine[0] = '\0', i = 0; i < argc; i++) {
+       if (i > 0)
+           strcat(commandLine, " ");
+       strcat(commandLine, argv[i]);
+    }
+
+    TTsleep = TTrun = 0;
+
+    /* parse cmd line */
+    for (code = 1; code < argc; code++) {
+       if (strcmp(argv[code], "-log") == 0) {
+           /* set extra logging flag */
+           DoLogging = 1;
+       } else if (strcmp(argv[code], "-help") == 0) {
+           goto usage;
+       } else if (strcmp(argv[code], "-p") == 0) {
+           lwps = atoi(argv[++code]);
+           if (lwps > MAXLWP) {
+               printf("Warning: '-p %d' is too big; using %d instead\n",
+                      lwps, MAXLWP);
+               lwps = MAXLWP;
+           }
+       } else if (strcmp(argv[code], "-nojumbo") == 0) {
+           rxJumbograms = 0;
+       } else if (strcmp(argv[code], "-sleep") == 0) {
+           sscanf(argv[++code], "%d/%d", &TTsleep, &TTrun);
+           if ((TTsleep < 0) || (TTrun <= 0)) {
+               printf("Warning: '-sleep %d/%d' is incorrect; ignoring\n",
+                      TTsleep, TTrun);
+               TTsleep = TTrun = 0;
+           }
+       } else if (strcmp(argv[code], "-udpsize") == 0) {
+           if ((code + 1) >= argc) {
+               printf("You have to specify -udpsize <integer value>\n");
+               exit(1);
+           }
+           sscanf(argv[++code], "%d", &bufSize);
+           if (bufSize < rx_GetMinUdpBufSize())
+               printf
+                   ("Warning:udpsize %d is less than minimum %d; ignoring\n",
+                    bufSize, rx_GetMinUdpBufSize());
+           else
+               udpBufSize = bufSize;
+       } else if (strcmp(argv[code], "-enable_peer_stats") == 0) {
+           rx_enablePeerRPCStats();
+       } else if (strcmp(argv[code], "-enable_process_stats") == 0) {
+           rx_enableProcessRPCStats();
+       }
+#ifndef AFS_NT40_ENV
+       else if (strcmp(argv[code], "-syslog") == 0) {
+           /* set syslog logging flag */
+           serverLogSyslog = 1;
+       } else if (strncmp(argv[code], "-syslog=", 8) == 0) {
+           serverLogSyslog = 1;
+           serverLogSyslogFacility = atoi(argv[code] + 8);
+       }
+#endif
+       else {
+           printf("volserver: unrecognized flag '%s'\n", argv[code]);
+         usage:
+#ifndef AFS_NT40_ENV
+           printf("Usage: volserver [-log] [-p <number of processes>] "
+                  "[-udpsize <size of socket buffer in bytes>] "
+                  "[-syslog[=FACILITY]] "
+                  "[-enable_peer_stats] [-enable_process_stats] "
+                  "[-help]\n");
+#else
+           printf("Usage: volserver [-log] [-p <number of processes>] "
+                  "[-udpsize <size of socket buffer in bytes>] "
+                  "[-enable_peer_stats] [-enable_process_stats] "
+                  "[-help]\n");
+#endif
+           VS_EXIT(1);
+       }
+    }
+#ifdef AFS_SGI_VNODE_GLUE
+    if (afs_init_kernel_config(-1) < 0) {
+       printf
+           ("Can't determine NUMA configuration, not starting volserver.\n");
+       exit(1);
+    }
+#endif
+    InitErrTabs();
+
+#ifdef AFS_NT40_ENV
+    if (afs_winsockInit() < 0) {
+       ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0, argv[0], 0);
+       printf("Volume server unable to start winsock, exiting.\n");
+       exit(1);
+    }
+#endif
+    VInitVolumePackage(volumeUtility, 0, 0, CONNECT_FS, 0);
+    DInit(40);
+#ifndef AFS_PTHREAD_ENV
+    vol_PollProc = IOMGR_Poll; /* tell vol pkg to poll io system periodically */
+#endif
+#ifndef AFS_NT40_ENV
+    rxi_syscallp = volser_syscall;
+#endif
+    rx_nPackets = rxpackets;   /* set the max number of packets */
+    if (udpBufSize)
+       rx_SetUdpBufSize(udpBufSize);   /* set the UDP buffer size for receive */
+    code = rx_Init((int)htons(AFSCONF_VOLUMEPORT));
+    if (code) {
+       fprintf(stderr, "rx init failed on socket AFSCONF_VOLUMEPORT %u\n",
+               AFSCONF_VOLUMEPORT);
+       VS_EXIT(1);
+    }
+    if (!rxJumbograms) {
+       /* Don't allow 3.4 vos clients to send jumbograms and we don't send. */
+       rx_SetNoJumbo();
+    }
+    rx_GetIFInfo();
+    rx_SetRxDeadTime(420);
+    memset(busyFlags, 0, sizeof(busyFlags));
+
+    /* Open FileLog and map stdout, stderr into it */
+    OpenLog(AFSDIR_SERVER_VOLSERLOG_FILEPATH);
+    SetupLogSignals();
+
+    {
+#ifdef AFS_PTHREAD_ENV
+       pthread_t tid;
+       pthread_attr_t tattr;
+       assert(pthread_attr_init(&tattr) == 0);
+       assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0);
+
+       assert(pthread_create(&tid, &tattr, (void *)BKGLoop, NULL) == 0);
+#else
+       PROCESS pid;
+       LWP_CreateProcess(BKGLoop, 16*1024, 3, 0, "vol bkg daemon", &pid);
+       LWP_CreateProcess(BKGSleep,16*1024, 3, 0, "vol slp daemon", &pid);
+#endif
+    }
+
+    /* Create a single security object, in this case the null security object, for unauthenticated connections, which will be used to control security on connections made to this server */
+
+    tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
+    if (!tdir) {
+       Abort("volser: could not open conf files in %s\n",
+             AFSDIR_SERVER_ETC_DIRPATH);
+       VS_EXIT(1);
+    }
+    afsconf_GetKey(tdir, 999, &tkey);
+    securityObjects[0] = rxnull_NewServerSecurityObject();
+    securityObjects[1] = (struct rx_securityClass *)0; /* don't bother with rxvab */
+    securityObjects[2] =
+       rxkad_NewServerSecurityObject(0, tdir, afsconf_GetKey, NULL);
+    if (securityObjects[0] == (struct rx_securityClass *)0)
+       Abort("rxnull_NewServerSecurityObject");
+    service =
+       rx_NewService(0, VOLSERVICE_ID, "VOLSER", securityObjects, 3,
+                     AFSVolExecuteRequest);
+    if (service == (struct rx_service *)0)
+       Abort("rx_NewService");
+    rx_SetBeforeProc(service, MyBeforeProc);
+    rx_SetAfterProc(service, MyAfterProc);
+    rx_SetIdleDeadTime(service, 0);    /* never timeout */
+    if (lwps < 4)
+       lwps = 4;
+    rx_SetMaxProcs(service, lwps);
+#ifdef AFS_SGI_ENV
+    rx_SetStackSize(service, 49152);
+#else
+    rx_SetStackSize(service, 32768);
+#endif
+
+    service =
+       rx_NewService(0, RX_STATS_SERVICE_ID, "rpcstats", securityObjects, 3,
+                     RXSTATS_ExecuteRequest);
+    if (service == (struct rx_service *)0)
+       Abort("rx_NewService");
+    rx_SetMinProcs(service, 2);
+    rx_SetMaxProcs(service, 4);
+
+    Log("Starting AFS Volserver %s (%s)\n", VolserVersion, commandLine);
+    if (TTsleep) {
+       Log("Will sleep %d second%s every %d second%s\n", TTsleep,
+           (TTsleep > 1) ? "s" : "", TTrun + TTsleep,
+           (TTrun + TTsleep > 1) ? "s" : "");
+    }
+
+    /* allow super users to manage RX statistics */
+    rx_SetRxStatUserOk(vol_rxstat_userok);
+
+    rx_StartServer(1);         /* Donate this process to the server process pool */
+
+    osi_audit(VS_FinishEvent, (-1), AUD_END);
+    Abort("StartServer returned?");
+}
diff --git a/src/volser/volprocs.c b/src/volser/volprocs.c
new file mode 100644 (file)
index 0000000..433a7a6
--- /dev/null
@@ -0,0 +1,2908 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/volprocs.c,v 1.33 2004/01/01 06:22:31 shadow Exp $");
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <winsock2.h>
+#else
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <rx/rxkad.h>
+#include <afs/afsint.h>
+#include <signal.h>
+#ifdef AFS_PTHREAD_ENV
+#include <assert.h>
+#else /* AFS_PTHREAD_ENV */
+#include <afs/assert.h>
+#endif /* AFS_PTHREAD_ENV */
+#include <afs/prs_fs.h>
+#include <afs/nfs.h>
+#include <lwp.h>
+#include <lock.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/keys.h>
+#include <ubik.h>
+#include <afs/ihandle.h>
+#ifdef AFS_NT40_ENV
+#include <afs/ntops.h>
+#endif
+#include <afs/vnode.h>
+#include <afs/volume.h>
+#include <afs/partition.h>
+#include "vol.h"
+#include <afs/fssync.h>
+#include <afs/acl.h>
+#include "afs/audit.h"
+#include <afs/dir.h>
+
+#include "volser.h"
+#include "volint.h"
+
+#include "volser_prototypes.h"
+
+extern int DoLogging;
+extern struct volser_trans *FindTrans(), *NewTrans(), *TransList();
+extern struct afsconf_dir *tdir;
+
+/* Needed by Irix. Leave, or include a header */
+extern char *volutil_PartitionName();
+
+extern void LogError(afs_int32 errcode);
+
+/* Forward declarations */
+static int GetPartName(afs_int32 partid, char *pname);
+
+#define OneDay (24*60*60)
+
+#ifdef AFS_NT40_ENV
+#define ENOTCONN 134
+#endif
+
+afs_int32 localTid = 1;
+afs_int32 VolPartitionInfo(), VolNukeVolume(), VolCreateVolume(),
+VolDeleteVolume(), VolClone();
+afs_int32 VolReClone(), VolTransCreate(), VolGetNthVolume(), VolGetFlags(),
+VolForward(), VolDump();
+afs_int32 VolRestore(), VolEndTrans(), VolSetForwarding(), VolGetStatus(),
+VolSetInfo(), VolGetName();
+afs_int32 VolListPartitions(), VolListOneVolume(),
+VolXListOneVolume(), VolXListVolumes();
+afs_int32 VolListVolumes(), XVolListPartitions(), VolMonitor(),
+VolSetIdsTypes(), VolSetDate(), VolSetFlags();
+
+/* this call unlocks all of the partition locks we've set */
+int 
+VPFullUnlock()
+{
+    register struct DiskPartition *tp;
+    for (tp = DiskPartitionList; tp; tp = tp->next) {
+       if (tp->lock_fd != -1) {
+           close(tp->lock_fd); /* releases flock held on this partition */
+           tp->lock_fd = -1;
+       }
+    }
+    return 0;
+}
+
+/* get partition id from a name */
+afs_int32
+PartitionID(char *aname)
+{
+    register char tc;
+    register int code = 0;
+    char ascii[3];
+
+    tc = *aname;
+    if (tc == 0)
+       return -1;              /* unknown */
+
+    /* otherwise check for vicepa or /vicepa, or just plain "a" */
+    ascii[2] = 0;
+    if (!strncmp(aname, "/vicep", 6)) {
+       strncpy(ascii, aname + 6, 2);
+    } else
+       return -1;              /* bad partition name */
+    /* now partitions are named /vicepa ... /vicepz, /vicepaa, /vicepab, .../vicepzz, and are numbered
+     * from 0.  Do the appropriate conversion */
+    if (ascii[1] == 0) {
+       /* one char name, 0..25 */
+       if (ascii[0] < 'a' || ascii[0] > 'z')
+           return -1;          /* wrongo */
+       return ascii[0] - 'a';
+    } else {
+       /* two char name, 26 .. <whatever> */
+       if (ascii[0] < 'a' || ascii[0] > 'z')
+           return -1;          /* wrongo */
+       if (ascii[1] < 'a' || ascii[1] > 'z')
+           return -1;          /* just as bad */
+       code = (ascii[0] - 'a') * 26 + (ascii[1] - 'a') + 26;
+       if (code > VOLMAXPARTS)
+           return -1;
+       return code;
+    }
+}
+
+static int
+ConvertVolume(afs_int32 avol, char *aname, afs_int32 asize)
+{
+    if (asize < 18)
+       return -1;
+    /* It's better using the Generic VFORMAT since otherwise we have to make changes to too many places... The 14 char limitation in names hits us again in AIX; print in field of 9 digits (still 10 for the rest), right justified with 0 padding */
+    (void)afs_snprintf(aname, asize, VFORMAT, (unsigned long)avol);
+    return 0;
+}
+
+static int
+ConvertPartition(int apartno, char *aname, int asize)
+{
+    if (asize < 10)
+       return E2BIG;
+    if (apartno < 0)
+       return EINVAL;
+    strcpy(aname, "/vicep");
+    if (apartno < 26) {
+       aname[6] = 'a' + apartno;
+       aname[7] = 0;
+    } else {
+       apartno -= 26;
+       aname[6] = 'a' + (apartno / 26);
+       aname[7] = 'a' + (apartno % 26);
+       aname[8] = 0;
+    }
+    return 0;
+}
+
+/* the only attach function that takes a partition is "...ByName", so we use it */
+struct Volume *
+XAttachVolume(afs_int32 *error, afs_int32 avolid, afs_int32 apartid, int amode)
+{
+    char pbuf[30], vbuf[20];
+    register struct Volume *tv;
+
+    if (ConvertPartition(apartid, pbuf, sizeof(pbuf))) {
+       *error = EINVAL;
+       return NULL;
+    }
+    if (ConvertVolume(avolid, vbuf, sizeof(vbuf))) {
+       *error = EINVAL;
+       return NULL;
+    }
+    tv = VAttachVolumeByName(error, pbuf, vbuf, amode);
+    return tv;
+}
+
+/* Adapted from the file server; create a root directory for this volume */
+static int
+ViceCreateRoot(Volume *vp)
+{
+    DirHandle dir;
+    struct acl_accessList *ACL;
+    ViceFid did;
+    Inode inodeNumber, nearInode;
+    char buf[SIZEOF_LARGEDISKVNODE];
+    struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
+    struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
+    IHandle_t *h;
+    FdHandle_t *fdP;
+    int code;
+    afs_fsize_t length;
+
+    memset(vnode, 0, SIZEOF_LARGEDISKVNODE);
+
+    V_pref(vp, nearInode);
+    inodeNumber =
+       IH_CREATE(V_linkHandle(vp), V_device(vp),
+                 VPartitionPath(V_partition(vp)), nearInode, V_parentId(vp),
+                 1, 1, 0);
+    assert(VALID_INO(inodeNumber));
+
+    SetSalvageDirHandle(&dir, V_parentId(vp), vp->device, inodeNumber);
+    did.Volume = V_id(vp);
+    did.Vnode = (VnodeId) 1;
+    did.Unique = 1;
+
+    assert(!(MakeDir(&dir, &did, &did)));
+    DFlush();                  /* flush all modified dir buffers out */
+    DZap(&dir);                        /* Remove all buffers for this dir */
+    length = Length(&dir);     /* Remember size of this directory */
+
+    FidZap(&dir);              /* Done with the dir handle obtained via SetSalvageDirHandle() */
+
+    /* build a single entry ACL that gives all rights to system:administrators */
+    /* this section of code assumes that access list format is not going to
+     * change
+     */
+    ACL = VVnodeDiskACL(vnode);
+    ACL->size = sizeof(struct acl_accessList);
+    ACL->version = ACL_ACLVERSION;
+    ACL->total = 1;
+    ACL->positive = 1;
+    ACL->negative = 0;
+    ACL->entries[0].id = -204; /* this assumes System:administrators is group -204 */
+    ACL->entries[0].rights =
+       PRSFS_READ | PRSFS_WRITE | PRSFS_INSERT | PRSFS_LOOKUP | PRSFS_DELETE
+       | PRSFS_LOCK | PRSFS_ADMINISTER;
+
+    vnode->type = vDirectory;
+    vnode->cloned = 0;
+    vnode->modeBits = 0777;
+    vnode->linkCount = 2;
+    VNDISK_SET_LEN(vnode, length);
+    vnode->uniquifier = 1;
+    V_uniquifier(vp) = vnode->uniquifier + 1;
+    vnode->dataVersion = 1;
+    VNDISK_SET_INO(vnode, inodeNumber);
+    vnode->unixModifyTime = vnode->serverModifyTime = V_creationDate(vp);
+    vnode->author = 0;
+    vnode->owner = 0;
+    vnode->parent = 0;
+    vnode->vnodeMagic = vcp->magic;
+
+    IH_INIT(h, vp->device, V_parentId(vp),
+           vp->vnodeIndex[vLarge].handle->ih_ino);
+    fdP = IH_OPEN(h);
+    assert(fdP != NULL);
+    code = FDH_SEEK(fdP, vnodeIndexOffset(vcp, 1), SEEK_SET);
+    assert(code >= 0);
+    code = FDH_WRITE(fdP, vnode, SIZEOF_LARGEDISKVNODE);
+    assert(code == SIZEOF_LARGEDISKVNODE);
+    FDH_REALLYCLOSE(fdP);
+    IH_RELEASE(h);
+    VNDISK_GET_LEN(length, vnode);
+    V_diskused(vp) = nBlocks(length);
+
+    return 1;
+}
+
+afs_int32
+SAFSVolPartitionInfo(struct rx_call *acid, char *pname, struct diskPartition 
+                    *partition)
+{
+    afs_int32 code;
+
+    code = VolPartitionInfo(acid, pname, partition);
+    osi_auditU(acid, VS_ParInfEvent, code, AUD_STR, pname, AUD_END);
+    return code;
+}
+
+afs_int32
+VolPartitionInfo(struct rx_call *acid, char *pname, struct diskPartition 
+                *partition)
+{
+    register struct DiskPartition *dp;
+
+/*
+    if (!afsconf_SuperUser(tdir, acid, caller)) return VOLSERBAD_ACCESS;
+*/
+    VResetDiskUsage();
+    dp = VGetPartition(pname, 0);
+    if (dp) {
+       strncpy(partition->name, dp->name, 32);
+       strncpy(partition->devName, dp->devName, 32);
+       partition->lock_fd = dp->lock_fd;
+       partition->free = dp->free;
+       partition->minFree = dp->totalUsable;
+       return 0;
+    } else
+       return VOLSERILLEGAL_PARTITION;
+}
+
+/* obliterate a volume completely, and slowly. */
+afs_int32
+SAFSVolNukeVolume(struct rx_call *acid, afs_int32 apartID, afs_int32 avolID)
+{
+    afs_int32 code;
+
+    code = VolNukeVolume(acid, apartID, avolID);
+    osi_auditU(acid, VS_NukVolEvent, code, AUD_LONG, avolID, AUD_END);
+    return code;
+}
+
+afs_int32
+VolNukeVolume(struct rx_call *acid, afs_int32 apartID, afs_int32 avolID)
+{
+    register char *tp;
+    char partName[50];
+    afs_int32 error;
+    register afs_int32 code;
+    struct Volume *tvp;
+    char caller[MAXKTCNAMELEN];
+
+    /* check for access */
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;
+    if (DoLogging)
+       Log("%s is executing VolNukeVolume %u\n", caller, avolID);
+
+    tp = volutil_PartitionName(apartID);
+    if (!tp)
+       return VOLSERNOVOL;
+    strcpy(partName, tp);      /* remember it for later */
+    /* we first try to attach the volume in update mode, so that the file
+     * server doesn't try to use it (and abort) while (or after) we delete it.
+     * If we don't get the volume, that's fine, too.  We just won't put it back.
+     */
+    tvp = XAttachVolume(&error, avolID, apartID, V_VOLUPD);
+    code = nuke(partName, avolID);
+    if (tvp)
+       VDetachVolume(&error, tvp);
+    return code;
+}
+
+/* create a new volume, with name aname, on the specified partition (1..n)
+ * and of type atype (readwriteVolume, readonlyVolume, backupVolume).
+ * As input, if *avolid is 0, we allocate a new volume id, otherwise we use *avolid
+ * for the volume id (useful for things like volume restore).
+ * Return the new volume id in *avolid.
+ */
+afs_int32
+SAFSVolCreateVolume(struct rx_call *acid, afs_int32 apart, char *aname, 
+                   afs_int32 atype, afs_int32 aparent, afs_int32 *avolid, 
+                   afs_int32 *atrans)
+{
+    afs_int32 code;
+
+    code =
+       VolCreateVolume(acid, apart, aname, atype, aparent, avolid, atrans);
+    osi_auditU(acid, VS_CrVolEvent, code, AUD_LONG, *atrans, AUD_LONG,
+              *avolid, AUD_STR, aname, AUD_LONG, atype, AUD_LONG, aparent,
+              AUD_END);
+    return code;
+}
+
+afs_int32
+VolCreateVolume(struct rx_call *acid, afs_int32 apart, char *aname, 
+                   afs_int32 atype, afs_int32 aparent, afs_int32 *avolid, 
+                   afs_int32 *atrans)
+{
+    afs_int32 error;
+    register Volume *vp;
+    afs_int32 junk;            /* discardable error code */
+    register afs_int32 volumeID, doCreateRoot = 1;
+    register struct volser_trans *tt;
+    char ppath[30];
+    char caller[MAXKTCNAMELEN];
+
+    if (strlen(aname) > 31)
+       return VOLSERBADNAME;
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;
+    if (DoLogging)
+       Log("%s is executing CreateVolume '%s'\n", caller, aname);
+    if ((error = ConvertPartition(apart, ppath, sizeof(ppath))))
+       return error;           /*a standard unix error */
+    if (atype != readwriteVolume && atype != readonlyVolume
+       && atype != backupVolume)
+       return EINVAL;
+    if ((volumeID = *avolid) == 0) {
+
+       Log("1 Volser: CreateVolume: missing volume number; %s volume not created\n", aname);
+       return E2BIG;
+
+    }
+    if ((aparent == volumeID) && (atype == readwriteVolume)) {
+       doCreateRoot = 0;
+    }
+    if (aparent == 0)
+       aparent = volumeID;
+    tt = NewTrans(volumeID, apart);
+    if (!tt) {
+       Log("1 createvolume: failed to create trans\n");
+       return VOLSERVOLBUSY;   /* volume already busy! */
+    }
+    vp = VCreateVolume(&error, ppath, volumeID, aparent);
+    if (error) {
+       Log("1 Volser: CreateVolume: Unable to create the volume; aborted, error code %u\n", error);
+       LogError(error);
+       DeleteTrans(tt);
+       return EIO;
+    }
+    V_uniquifier(vp) = 1;
+    V_creationDate(vp) = V_copyDate(vp);
+    V_inService(vp) = V_blessed(vp) = 1;
+    V_type(vp) = atype;
+    AssignVolumeName(&V_disk(vp), aname, 0);
+    if (doCreateRoot)
+       ViceCreateRoot(vp);
+    V_destroyMe(vp) = DESTROY_ME;
+    V_inService(vp) = 0;
+    V_maxquota(vp) = 5000;     /* set a quota of 5000 at init time */
+    VUpdateVolume(&error, vp);
+    if (error) {
+       Log("1 Volser: create UpdateVolume failed, code %d\n", error);
+       LogError(error);
+       DeleteTrans(tt);
+       VDetachVolume(&junk, vp);       /* rather return the real error code */
+       return error;
+    }
+    tt->volume = vp;
+    *atrans = tt->tid;
+    strcpy(tt->lastProcName, "CreateVolume");
+    tt->rxCallPtr = acid;
+    Log("1 Volser: CreateVolume: volume %u (%s) created\n", volumeID, aname);
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+    return 0;
+}
+
+/* delete the volume associated with this transaction */
+afs_int32
+SAFSVolDeleteVolume(struct rx_call *acid, afs_int32 atrans)
+{
+    afs_int32 code;
+
+    code = VolDeleteVolume(acid, atrans);
+    osi_auditU(acid, VS_DelVolEvent, code, AUD_LONG, atrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolDeleteVolume(struct rx_call *acid, afs_int32 atrans)
+{
+    register struct volser_trans *tt;
+    afs_int32 error;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: Delete: volume %u already deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    if (DoLogging)
+       Log("%s is executing Delete Volume %u\n", caller, tt->volid);
+    strcpy(tt->lastProcName, "DeleteVolume");
+    tt->rxCallPtr = acid;
+    VPurgeVolume(&error, tt->volume);  /* don't check error code, it is not set! */
+    tt->vflags |= VTDeleted;   /* so we know not to do anything else to it */
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    Log("1 Volser: Delete: volume %u deleted \n", tt->volid);
+    return 0;                  /* vpurgevolume doesn't set an error code */
+}
+
+/* make a clone of the volume associated with atrans, possibly giving it a new
+ * number (allocate a new number if *newNumber==0, otherwise use *newNumber
+ * for the clone's id).  The new clone is given the name newName.  Finally, due to
+ * efficiency considerations, if purgeId is non-zero, we purge that volume when doing
+ * the clone operation.  This may be useful when making new backup volumes, for instance
+ * since the net result of a clone and a purge generally leaves many inode ref counts
+ * the same, while doing them separately would result in far more iincs and idecs being
+ * peformed (and they are slow operations).
+ */
+/* for efficiency reasons, sometimes faster to piggyback a purge here */
+afs_int32
+SAFSVolClone(struct rx_call *acid, afs_int32 atrans, afs_int32 purgeId, 
+            afs_int32 newType, char *newName, afs_int32 *newNumber)
+{
+    afs_int32 code;
+
+    code = VolClone(acid, atrans, purgeId, newType, newName, newNumber);
+    osi_auditU(acid, VS_CloneEvent, code, AUD_LONG, atrans, AUD_LONG, purgeId,
+              AUD_STR, newName, AUD_LONG, newType, AUD_LONG, *newNumber,
+              AUD_END);
+    return code;
+}
+
+afs_int32
+VolClone(struct rx_call *acid, afs_int32 atrans, afs_int32 purgeId, 
+            afs_int32 newType, char *newName, afs_int32 *newNumber)
+{
+    VolumeId newId;
+    register struct Volume *originalvp, *purgevp, *newvp;
+    Error error, code;
+    register struct volser_trans *tt, *ttc;
+    char caller[MAXKTCNAMELEN];
+
+    if (strlen(newName) > 31)
+       return VOLSERBADNAME;
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    if (DoLogging)
+       Log("%s is executing Clone Volume new name=%s\n", caller, newName);
+    error = 0;
+    originalvp = (Volume *) 0;
+    purgevp = (Volume *) 0;
+    newvp = (Volume *) 0;
+    tt = ttc = (struct volser_trans *)0;
+
+    if (!newNumber || !*newNumber) {
+       Log("1 Volser: Clone: missing volume number for the clone; aborted\n");
+       goto fail;
+    }
+    newId = *newNumber;
+
+    if (newType != readonlyVolume && newType != backupVolume)
+       return EINVAL;
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: Clone: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    ttc = NewTrans(newId, tt->partition);
+    if (!ttc) {                        /* someone is messing with the clone already */
+       TRELE(tt);
+       return VBUSY;
+    }
+    strcpy(tt->lastProcName, "Clone");
+    tt->rxCallPtr = acid;
+
+
+    if (purgeId) {
+       purgevp = VAttachVolume(&error, purgeId, V_VOLUPD);
+       if (error) {
+           Log("1 Volser: Clone: Could not attach 'purge' volume %u; clone aborted\n", purgeId);
+           goto fail;
+       }
+    } else {
+       purgevp = NULL;
+    }
+    originalvp = tt->volume;
+    if ((V_type(originalvp) == backupVolume)
+       || (V_type(originalvp) == readonlyVolume)) {
+       Log("1 Volser: Clone: The volume to be cloned must be a read/write; aborted\n");
+       error = EROFS;
+       goto fail;
+    }
+    if ((V_destroyMe(originalvp) == DESTROY_ME) || !V_inService(originalvp)) {
+       Log("1 Volser: Clone: Volume %d is offline and cannot be cloned\n",
+           V_id(originalvp));
+       error = VOFFLINE;
+       goto fail;
+    }
+    if (purgevp) {
+       if (originalvp->device != purgevp->device) {
+           Log("1 Volser: Clone: Volumes %u and %u are on different devices\n", tt->volid, purgeId);
+           error = EXDEV;
+           goto fail;
+       }
+       if (V_type(purgevp) != readonlyVolume) {
+           Log("1 Volser: Clone: The \"purge\" volume must be a read only volume; aborted\n");
+           error = EINVAL;
+           goto fail;
+       }
+       if (V_type(originalvp) == readonlyVolume
+           && V_parentId(originalvp) != V_parentId(purgevp)) {
+           Log("1 Volser: Clone: Volume %u and volume %u were not cloned from the same parent volume; aborted\n", tt->volid, purgeId);
+           error = EXDEV;
+           goto fail;
+       }
+       if (V_type(originalvp) == readwriteVolume
+           && tt->volid != V_parentId(purgevp)) {
+           Log("1 Volser: Clone: Volume %u was not originally cloned from volume %u; aborted\n", purgeId, tt->volid);
+           error = EXDEV;
+           goto fail;
+       }
+    }
+
+    error = 0;
+
+    newvp =
+       VCreateVolume(&error, originalvp->partition->name, newId,
+                     V_parentId(originalvp));
+    if (error) {
+       Log("1 Volser: Clone: Couldn't create new volume; clone aborted\n");
+       newvp = (Volume *) 0;
+       goto fail;
+    }
+    if (newType == readonlyVolume)
+       V_cloneId(originalvp) = newId;
+    Log("1 Volser: Clone: Cloning volume %u to new volume %u\n", tt->volid,
+       newId);
+    if (purgevp)
+       Log("1 Volser: Clone: Purging old read only volume %u\n", purgeId);
+    CloneVolume(&error, originalvp, newvp, purgevp);
+    purgevp = NULL;            /* clone releases it, maybe even if error */
+    if (error) {
+       Log("1 Volser: Clone: clone operation failed with code %u\n", error);
+       LogError(error);
+       goto fail;
+    }
+    if (newType == readonlyVolume) {
+       AssignVolumeName(&V_disk(newvp), V_name(originalvp), ".readonly");
+       V_type(newvp) = readonlyVolume;
+    } else if (newType == backupVolume) {
+       AssignVolumeName(&V_disk(newvp), V_name(originalvp), ".backup");
+       V_type(newvp) = backupVolume;
+       V_backupId(originalvp) = newId;
+    }
+    strcpy(newvp->header->diskstuff.name, newName);
+    V_creationDate(newvp) = V_copyDate(newvp);
+    ClearVolumeStats(&V_disk(newvp));
+    V_destroyMe(newvp) = DESTROY_ME;
+    V_inService(newvp) = 0;
+    if (newType == backupVolume) {
+       V_backupDate(originalvp) = V_copyDate(newvp);
+       V_backupDate(newvp) = V_copyDate(newvp);
+    }
+    V_inUse(newvp) = 0;
+    VUpdateVolume(&error, newvp);
+    if (error) {
+       Log("1 Volser: Clone: VUpdate failed code %u\n", error);
+       LogError(error);
+       goto fail;
+    }
+    VDetachVolume(&error, newvp);      /* allow file server to get it's hands on it */
+    newvp = NULL;
+    VUpdateVolume(&error, originalvp);
+    if (error) {
+       Log("1 Volser: Clone: original update %u\n", error);
+       LogError(error);
+       goto fail;
+    }
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt)) {
+       tt = (struct volser_trans *)0;
+       error = VOLSERTRELE_ERROR;
+       goto fail;
+    }
+    DeleteTrans(ttc);
+    return 0;
+
+  fail:
+    if (purgevp)
+       VDetachVolume(&code, purgevp);
+    if (newvp)
+       VDetachVolume(&code, newvp);
+    if (tt) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+    }
+    if (ttc)
+       DeleteTrans(ttc);
+    return error;
+}
+
+/* reclone this volume into the specified id */
+afs_int32
+SAFSVolReClone(struct rx_call *acid, afs_int32 atrans, afs_int32 cloneId)
+{
+    afs_int32 code;
+
+    code = VolReClone(acid, atrans, cloneId);
+    osi_auditU(acid, VS_ReCloneEvent, code, AUD_LONG, atrans, AUD_LONG,
+              cloneId, AUD_END);
+    return code;
+}
+
+afs_int32
+VolReClone(struct rx_call *acid, afs_int32 atrans, afs_int32 cloneId)
+{
+    register struct Volume *originalvp, *clonevp;
+    Error error, code;
+    afs_int32 newType;
+    register struct volser_trans *tt, *ttc;
+    char caller[MAXKTCNAMELEN];
+
+    /*not a super user */
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;
+    if (DoLogging)
+       Log("%s is executing Reclone Volume %u\n", caller, cloneId);
+    error = 0;
+    clonevp = originalvp = (Volume *) 0;
+    tt = (struct volser_trans *)0;
+
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolReClone: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    ttc = NewTrans(cloneId, tt->partition);
+    if (!ttc) {                        /* someone is messing with the clone already */
+       TRELE(tt);
+       return VBUSY;
+    }
+    strcpy(tt->lastProcName, "ReClone");
+    tt->rxCallPtr = acid;
+
+    originalvp = tt->volume;
+    if ((V_type(originalvp) == backupVolume)
+       || (V_type(originalvp) == readonlyVolume)) {
+       Log("1 Volser: Clone: The volume to be cloned must be a read/write; aborted\n");
+       error = EROFS;
+       goto fail;
+    }
+    if ((V_destroyMe(originalvp) == DESTROY_ME) || !V_inService(originalvp)) {
+       Log("1 Volser: Clone: Volume %d is offline and cannot be cloned\n",
+           V_id(originalvp));
+       error = VOFFLINE;
+       goto fail;
+    }
+
+    clonevp = VAttachVolume(&error, cloneId, V_VOLUPD);
+    if (error) {
+       Log("1 Volser: can't attach clone %d\n", cloneId);
+       goto fail;
+    }
+
+    newType = V_type(clonevp); /* type of the new volume */
+
+    if (originalvp->device != clonevp->device) {
+       Log("1 Volser: Clone: Volumes %u and %u are on different devices\n",
+           tt->volid, cloneId);
+       error = EXDEV;
+       goto fail;
+    }
+    if (V_type(clonevp) != readonlyVolume && V_type(clonevp) != backupVolume) {
+       Log("1 Volser: Clone: The \"recloned\" volume must be a read only volume; aborted\n");
+       error = EINVAL;
+       goto fail;
+    }
+    if (V_type(originalvp) == readonlyVolume
+       && V_parentId(originalvp) != V_parentId(clonevp)) {
+       Log("1 Volser: Clone: Volume %u and volume %u were not cloned from the same parent volume; aborted\n", tt->volid, cloneId);
+       error = EXDEV;
+       goto fail;
+    }
+    if (V_type(originalvp) == readwriteVolume
+       && tt->volid != V_parentId(clonevp)) {
+       Log("1 Volser: Clone: Volume %u was not originally cloned from volume %u; aborted\n", cloneId, tt->volid);
+       error = EXDEV;
+       goto fail;
+    }
+
+    error = 0;
+    Log("1 Volser: Clone: Recloning volume %u to volume %u\n", tt->volid,
+       cloneId);
+    CloneVolume(&error, originalvp, clonevp, clonevp);
+    if (error) {
+       Log("1 Volser: Clone: reclone operation failed with code %d\n",
+           error);
+       LogError(error);
+       goto fail;
+    }
+
+    /* fix up volume name and type, CloneVolume just propagated RW's */
+    if (newType == readonlyVolume) {
+       AssignVolumeName(&V_disk(clonevp), V_name(originalvp), ".readonly");
+       V_type(clonevp) = readonlyVolume;
+    } else if (newType == backupVolume) {
+       AssignVolumeName(&V_disk(clonevp), V_name(originalvp), ".backup");
+       V_type(clonevp) = backupVolume;
+       V_backupId(originalvp) = cloneId;
+    }
+    /* don't do strcpy onto diskstuff.name, it's still OK from 1st clone */
+
+    /* pretend recloned volume is a totally new instance */
+    V_copyDate(clonevp) = time(0);
+    V_creationDate(clonevp) = V_copyDate(clonevp);
+    ClearVolumeStats(&V_disk(clonevp));
+    V_destroyMe(clonevp) = 0;
+    V_inService(clonevp) = 0;
+    if (newType == backupVolume) {
+       V_backupDate(originalvp) = V_copyDate(clonevp);
+       V_backupDate(clonevp) = V_copyDate(clonevp);
+    }
+    V_inUse(clonevp) = 0;
+    VUpdateVolume(&error, clonevp);
+    if (error) {
+       Log("1 Volser: Clone: VUpdate failed code %u\n", error);
+       LogError(error);
+       goto fail;
+    }
+    VDetachVolume(&error, clonevp);    /* allow file server to get it's hands on it */
+    clonevp = NULL;
+    VUpdateVolume(&error, originalvp);
+    if (error) {
+       Log("1 Volser: Clone: original update %u\n", error);
+       LogError(error);
+       goto fail;
+    }
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt)) {
+       tt = (struct volser_trans *)0;
+       error = VOLSERTRELE_ERROR;
+       goto fail;
+    }
+
+    DeleteTrans(ttc);
+
+    {
+       struct DiskPartition *tpartp = originalvp->partition;
+       FSYNC_askfs(cloneId, tpartp->name, FSYNC_RESTOREVOLUME, 0);
+    }
+    return 0;
+
+  fail:
+    if (clonevp)
+       VDetachVolume(&code, clonevp);
+    if (tt) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+    }
+    if (ttc)
+       DeleteTrans(ttc);
+    return error;
+}
+
+/* create a new transaction, associated with volume and partition.  Type of
+ * volume transaction is spec'd by iflags.  New trans id is returned in ttid.
+ * See volser.h for definition of iflags (the constants are named IT*).
+ */
+afs_int32
+SAFSVolTransCreate(struct rx_call *acid, afs_int32 volume, afs_int32 partition,
+                  afs_int32 iflags, afs_int32 *ttid)
+{
+    afs_int32 code;
+
+    code = VolTransCreate(acid, volume, partition, iflags, ttid);
+    osi_auditU(acid, VS_TransCrEvent, code, AUD_LONG, *ttid, AUD_LONG, volume,
+              AUD_END);
+    return code;
+}
+
+afs_int32
+VolTransCreate(struct rx_call *acid, afs_int32 volume, afs_int32 partition,
+                  afs_int32 iflags, afs_int32 *ttid)
+{
+    register struct volser_trans *tt;
+    register Volume *tv;
+    afs_int32 error, code;
+    afs_int32 mode;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    if (iflags & ITCreate)
+       mode = V_SECRETLY;
+    else if (iflags & ITBusy)
+       mode = V_CLONE;
+    else if (iflags & ITReadOnly)
+       mode = V_READONLY;
+    else if (iflags & ITOffline)
+       mode = V_VOLUPD;
+    else {
+       Log("1 Volser: TransCreate: Could not create trans, error %u\n",
+           EINVAL);
+       LogError(EINVAL);
+       return EINVAL;
+    }
+    tt = NewTrans(volume, partition);
+    if (!tt) {
+       /* can't create a transaction? put the volume back */
+       Log("1 transcreate: can't create transaction\n");
+       return VOLSERVOLBUSY;
+    }
+    tv = XAttachVolume(&error, volume, partition, mode);
+    if (error) {
+       /* give up */
+       if (tv)
+           VDetachVolume(&code, tv);
+       DeleteTrans(tt);
+       return error;
+    }
+    tt->volume = tv;
+    *ttid = tt->tid;
+    tt->iflags = iflags;
+    tt->vflags = 0;
+    strcpy(tt->lastProcName, "TransCreate");
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+}
+
+/* using aindex as a 0-based index, return the aindex'th volume on this server
+ * Both the volume number and partition number (one-based) are returned.
+ */
+afs_int32
+SAFSVolGetNthVolume(struct rx_call *acid, afs_int32 aindex, afs_int32 *avolume,
+                   afs_int32 *apart)
+{
+    afs_int32 code;
+
+    code = VolGetNthVolume(acid, aindex, avolume, apart);
+    osi_auditU(acid, VS_GetNVolEvent, code, AUD_LONG, *avolume, AUD_END);
+    return code;
+}
+
+afs_int32
+VolGetNthVolume(struct rx_call *acid, afs_int32 aindex, afs_int32 *avolume,
+                   afs_int32 *apart)
+{
+    Log("1 Volser: GetNthVolume: Not yet implemented\n");
+    return VOLSERNO_OP;
+}
+
+/* return the volume flags (VT* constants in volser.h) associated with this
+ * transaction.
+ */
+afs_int32
+SAFSVolGetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 *aflags)
+{
+    afs_int32 code;
+
+    code = VolGetFlags(acid, atid, aflags);
+    osi_auditU(acid, VS_GetFlgsEvent, code, AUD_LONG, atid, AUD_END);
+    return code;
+}
+
+afs_int32
+VolGetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 *aflags)
+{
+    register struct volser_trans *tt;
+
+    tt = FindTrans(atid);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolGetFlags: volume %u has been deleted \n",
+           tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "GetFlags");
+    tt->rxCallPtr = acid;
+    *aflags = tt->vflags;
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+}
+
+/* Change the volume flags (VT* constants in volser.h) associated with this
+ * transaction.  Effects take place immediately on volume, although volume
+ * remains attached as usual by the transaction.
+ */
+afs_int32
+SAFSVolSetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 aflags)
+{
+    afs_int32 code;
+
+    code = VolSetFlags(acid, atid, aflags);
+    osi_auditU(acid, VS_SetFlgsEvent, code, AUD_LONG, atid, AUD_LONG, aflags,
+              AUD_END);
+    return code;
+}
+
+afs_int32
+VolSetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 aflags)
+{
+    register struct volser_trans *tt;
+    register struct Volume *vp;
+    afs_int32 error;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    /* find the trans */
+    tt = FindTrans(atid);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolSetFlags: volume %u has been deleted \n",
+           tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "SetFlags");
+    tt->rxCallPtr = acid;
+    vp = tt->volume;           /* pull volume out of transaction */
+
+    /* check if we're allowed to make any updates */
+    if (tt->iflags & ITReadOnly) {
+       TRELE(tt);
+       return EROFS;
+    }
+
+    /* handle delete-on-salvage flag */
+    if (aflags & VTDeleteOnSalvage) {
+       V_destroyMe(tt->volume) = DESTROY_ME;
+    } else {
+       V_destroyMe(tt->volume) = 0;
+    }
+
+    if (aflags & VTOutOfService) {
+       V_inService(vp) = 0;
+    } else {
+       V_inService(vp) = 1;
+    }
+    VUpdateVolume(&error, vp);
+    tt->vflags = aflags;
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt) && !error)
+       return VOLSERTRELE_ERROR;
+
+    return error;
+}
+
+/* dumpS the volume associated with a particular transaction from a particular
+ * date.  Send the dump to a different transaction (destTrans) on the server
+ * specified by the destServer structure.
+ */
+afs_int32
+SAFSVolForward(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate,
+              struct destServer *destination, afs_int32 destTrans, 
+              struct restoreCookie *cookie)
+{
+    afs_int32 code;
+
+    code =
+       VolForward(acid, fromTrans, fromDate, destination, destTrans, cookie);
+    osi_auditU(acid, VS_ForwardEvent, code, AUD_LONG, fromTrans, AUD_HOST,
+              destination->destHost, AUD_LONG, destTrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolForward(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate,
+              struct destServer *destination, afs_int32 destTrans, 
+              struct restoreCookie *cookie)
+{
+    register struct volser_trans *tt;
+    register afs_int32 code;
+    register struct rx_connection *tcon;
+    struct rx_call *tcall;
+    register struct Volume *vp;
+    struct rx_securityClass *securityObject;
+    afs_int32 securityIndex;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    /* initialize things */
+    tcon = (struct rx_connection *)0;
+    tt = (struct volser_trans *)0;
+
+    /* find the local transaction */
+    tt = FindTrans(fromTrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolForward: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    vp = tt->volume;
+    strcpy(tt->lastProcName, "Forward");
+
+    /* get auth info for the this connection (uses afs from ticket file) */
+    code = afsconf_ClientAuth(tdir, &securityObject, &securityIndex);
+    if (code) {
+       TRELE(tt);
+       return code;
+    }
+
+    /* make an rpc connection to the other server */
+    tcon =
+       rx_NewConnection(htonl(destination->destHost),
+                        htons(destination->destPort), VOLSERVICE_ID,
+                        securityObject, securityIndex);
+    if (!tcon) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+       return ENOTCONN;
+    }
+    tcall = rx_NewCall(tcon);
+    tt->rxCallPtr = tcall;
+    /* start restore going.  fromdate == 0 --> doing an incremental dump/restore */
+    code = StartAFSVolRestore(tcall, destTrans, (fromDate ? 1 : 0), cookie);
+    if (code) {
+       goto fail;
+    }
+
+    /* these next calls implictly call rx_Write when writing out data */
+    code = DumpVolume(tcall, vp, fromDate, 0); /* last field = don't dump all dirs */
+    if (code)
+       goto fail;
+    EndAFSVolRestore(tcall);   /* probably doesn't do much */
+    tt->rxCallPtr = (struct rx_call *)0;
+    code = rx_EndCall(tcall, 0);
+    rx_DestroyConnection(tcon);        /* done with the connection */
+    tcon = NULL;
+    if (code)
+       goto fail;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+
+  fail:
+    if (tcon) {
+       (void)rx_EndCall(tcall, 0);
+       rx_DestroyConnection(tcon);
+    }
+    if (tt) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+    }
+    return code;
+}
+
+/* Start a dump and send it to multiple places simultaneously.
+ * If this returns an error (eg, return ENOENT), it means that
+ * none of the releases worked.  If this returns 0, that means 
+ * that one or more of the releases worked, and the caller has
+ * to examine the results array to see which one(s).
+ * This will only do EITHER incremental or full, not both, so it's
+ * the caller's responsibility to be sure that all the destinations
+ * need just an incremental (and from the same time), if that's 
+ * what we're doing. 
+ */
+afs_int32
+SAFSVolForwardMultiple(struct rx_call *acid, afs_int32 fromTrans, afs_int32 
+                      fromDate, manyDests *destinations, afs_int32 spare,
+                      struct restoreCookie *cookie, manyResults *results)
+{
+    afs_int32 securityIndex;
+    struct rx_securityClass *securityObject;
+    char caller[MAXKTCNAMELEN];
+    struct volser_trans *tt;
+    afs_int32 ec, code, *codes;
+    struct rx_connection **tcons;
+    struct rx_call **tcalls;
+    struct Volume *vp;
+    int i, is_incremental;
+
+    if (results)
+       memset(results, 0, sizeof(manyResults));
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(fromTrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolForward: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    vp = tt->volume;
+    strcpy(tt->lastProcName, "ForwardMulti");
+
+    /* (fromDate == 0) ==> incremental dump */
+    is_incremental = (fromDate ? 1 : 0);
+
+    i = results->manyResults_len = destinations->manyDests_len;
+    results->manyResults_val = codes =
+       (afs_int32 *) malloc(i * sizeof(afs_int32));
+    tcons =
+       (struct rx_connection **)malloc(i * sizeof(struct rx_connection *));
+    tcalls = (struct rx_call **)malloc(i * sizeof(struct rx_call *));
+
+    /* get auth info for this connection (uses afs from ticket file) */
+    code = afsconf_ClientAuth(tdir, &securityObject, &securityIndex);
+    if (code) {
+       goto fail;              /* in order to audit each failure */
+    }
+
+    /* make connections to all the other servers */
+    for (i = 0; i < destinations->manyDests_len; i++) {
+       struct replica *dest = &(destinations->manyDests_val[i]);
+       tcons[i] =
+           rx_NewConnection(htonl(dest->server.destHost),
+                            htons(dest->server.destPort), VOLSERVICE_ID,
+                            securityObject, securityIndex);
+       if (!tcons[i]) {
+           codes[i] = ENOTCONN;
+       } else {
+           if (!(tcalls[i] = rx_NewCall(tcons[i])))
+               codes[i] = ENOTCONN;
+           else {
+               codes[i] =
+                   StartAFSVolRestore(tcalls[i], dest->trans, is_incremental,
+                                      cookie);
+               if (codes[i]) {
+                   (void)rx_EndCall(tcalls[i], 0);
+                   tcalls[i] = 0;
+                   rx_DestroyConnection(tcons[i]);
+                   tcons[i] = 0;
+               }
+           }
+       }
+    }
+
+    /* these next calls implictly call rx_Write when writing out data */
+    code = DumpVolMulti(tcalls, i, vp, fromDate, 0, codes);
+
+
+  fail:
+    for (i--; i >= 0; i--) {
+       struct replica *dest = &(destinations->manyDests_val[i]);
+
+       if (!code && tcalls[i] && !codes[i]) {
+           EndAFSVolRestore(tcalls[i]);
+       }
+       if (tcalls[i]) {
+           ec = rx_EndCall(tcalls[i], 0);
+           if (!codes[i])
+               codes[i] = ec;
+       }
+       if (tcons[i]) {
+           rx_DestroyConnection(tcons[i]);     /* done with the connection */
+       }
+
+       osi_auditU(acid, VS_ForwardEvent, (code ? code : codes[i]), AUD_LONG,
+                  fromTrans, AUD_HOST, dest->server.destHost, AUD_LONG,
+                  dest->trans, AUD_END);
+    }
+    free(tcons);
+    free(tcalls);
+
+    if (tt) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       if (TRELE(tt) && !code) /* return the first code if it's set */
+           return VOLSERTRELE_ERROR;
+    }
+
+    return code;
+}
+
+afs_int32
+SAFSVolDump(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate)
+{
+    afs_int32 code;
+
+    code = VolDump(acid, fromTrans, fromDate);
+    osi_auditU(acid, VS_DumpEvent, code, AUD_LONG, fromTrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolDump(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate)
+{
+    int code = 0;
+    register struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(fromTrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolDump: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "Dump");
+    tt->rxCallPtr = acid;
+    code = DumpVolume(acid, tt->volume, fromDate, 1);  /* squirt out the volume's data, too */
+    if (code) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+       return code;
+    }
+    tt->rxCallPtr = (struct rx_call *)0;
+
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+}
+
+/* 
+ * Ha!  No more helper process!
+ */
+afs_int32
+SAFSVolRestore(struct rx_call *acid, afs_int32 atrans, afs_int32 aflags, 
+              struct restoreCookie *cookie)
+{
+    afs_int32 code;
+
+    code = VolRestore(acid, atrans, aflags, cookie);
+    osi_auditU(acid, VS_RestoreEvent, code, AUD_LONG, atrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolRestore(struct rx_call *acid, afs_int32 atrans, afs_int32 aflags, 
+              struct restoreCookie *cookie)
+{
+    register struct volser_trans *tt;
+    register afs_int32 code, tcode;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolRestore: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "Restore");
+    tt->rxCallPtr = acid;
+
+    DFlushVolume(V_parentId(tt->volume)); /* Ensure dir buffers get dropped */
+
+    code = RestoreVolume(acid, tt->volume, (aflags & 1), cookie);      /* last is incrementalp */
+    FSYNC_askfs(tt->volid, NULL, FSYNC_RESTOREVOLUME, 0l);     /*break call backs on the
+                                                                * restored volume */
+    tt->rxCallPtr = (struct rx_call *)0;
+    tcode = TRELE(tt);
+
+    return (code ? code : tcode);
+}
+
+/* end a transaction, returning the transaction's final error code in rcode */
+afs_int32
+SAFSVolEndTrans(struct rx_call *acid, afs_int32 destTrans, afs_int32 *rcode)
+{
+    afs_int32 code;
+
+    code = VolEndTrans(acid, destTrans, rcode);
+    osi_auditU(acid, VS_EndTrnEvent, code, AUD_LONG, destTrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolEndTrans(struct rx_call *acid, afs_int32 destTrans, afs_int32 *rcode)
+{
+    register struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(destTrans);
+    if (!tt) {
+       return ENOENT;
+    }
+    *rcode = tt->returnCode;
+    DeleteTrans(tt);           /* this does an implicit TRELE */
+
+    return 0;
+}
+
+afs_int32
+SAFSVolSetForwarding(struct rx_call *acid, afs_int32 atid, afs_int32 anewsite)
+{
+    afs_int32 code;
+
+    code = VolSetForwarding(acid, atid, anewsite);
+    osi_auditU(acid, VS_SetForwEvent, code, AUD_LONG, atid, AUD_HOST,
+              anewsite, AUD_END);
+    return code;
+}
+
+afs_int32
+VolSetForwarding(struct rx_call *acid, afs_int32 atid, afs_int32 anewsite)
+{
+    register struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(atid);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolSetForwarding: volume %u has been deleted \n",
+           tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "SetForwarding");
+    tt->rxCallPtr = acid;
+    FSYNC_askfs(tt->volid, NULL, FSYNC_MOVEVOLUME, anewsite);
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+}
+
+afs_int32
+SAFSVolGetStatus(struct rx_call *acid, afs_int32 atrans, 
+                register struct volser_status *astatus)
+{
+    afs_int32 code;
+
+    code = VolGetStatus(acid, atrans, astatus);
+    osi_auditU(acid, VS_GetStatEvent, code, AUD_LONG, atrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolGetStatus(struct rx_call *acid, afs_int32 atrans, 
+                register struct volser_status *astatus)
+{
+    register struct Volume *tv;
+    register struct VolumeDiskData *td;
+    struct volser_trans *tt;
+
+
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolGetStatus: volume %u has been deleted \n",
+           tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "GetStatus");
+    tt->rxCallPtr = acid;
+    tv = tt->volume;
+    if (!tv) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+       return ENOENT;
+    }
+
+    td = &tv->header->diskstuff;
+    astatus->volID = td->id;
+    astatus->nextUnique = td->uniquifier;
+    astatus->type = td->type;
+    astatus->parentID = td->parentId;
+    astatus->cloneID = td->cloneId;
+    astatus->backupID = td->backupId;
+    astatus->restoredFromID = td->restoredFromId;
+    astatus->maxQuota = td->maxquota;
+    astatus->minQuota = td->minquota;
+    astatus->owner = td->owner;
+    astatus->creationDate = td->creationDate;
+    astatus->accessDate = td->accessDate;
+    astatus->updateDate = td->updateDate;
+    astatus->expirationDate = td->expirationDate;
+    astatus->backupDate = td->backupDate;
+    astatus->copyDate = td->copyDate;
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+}
+
+afs_int32
+SAFSVolSetInfo(struct rx_call *acid, afs_int32 atrans, 
+              register struct volintInfo *astatus)
+{
+    afs_int32 code;
+
+    code = VolSetInfo(acid, atrans, astatus);
+    osi_auditU(acid, VS_SetInfoEvent, code, AUD_LONG, atrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolSetInfo(struct rx_call *acid, afs_int32 atrans, 
+              register struct volintInfo *astatus)
+{
+    register struct Volume *tv;
+    register struct VolumeDiskData *td;
+    struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+    afs_int32 error;
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolSetInfo: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "SetStatus");
+    tt->rxCallPtr = acid;
+    tv = tt->volume;
+    if (!tv) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+       return ENOENT;
+    }
+
+    td = &tv->header->diskstuff;
+    /*
+     * Add more fields as necessary
+     */
+    if (astatus->maxquota != -1)
+       td->maxquota = astatus->maxquota;
+    if (astatus->dayUse != -1)
+       td->dayUse = astatus->dayUse;
+    VUpdateVolume(&error, tv);
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+    return 0;
+}
+
+
+afs_int32
+SAFSVolGetName(struct rx_call *acid, afs_int32 atrans, char **aname)
+{
+    afs_int32 code;
+
+    code = VolGetName(acid, atrans, aname);
+    osi_auditU(acid, VS_GetNameEvent, code, AUD_LONG, atrans, AUD_END);
+    return code;
+}
+
+afs_int32
+VolGetName(struct rx_call *acid, afs_int32 atrans, char **aname)
+{
+    register struct Volume *tv;
+    register struct VolumeDiskData *td;
+    struct volser_trans *tt;
+    register int len;
+
+    *aname = NULL;
+    tt = FindTrans(atrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolGetName: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "GetName");
+    tt->rxCallPtr = acid;
+    tv = tt->volume;
+    if (!tv) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+       return ENOENT;
+    }
+
+    td = &tv->header->diskstuff;
+    len = strlen(td->name) + 1;        /* don't forget the null */
+    if (len >= SIZE) {
+       tt->rxCallPtr = (struct rx_call *)0;
+       TRELE(tt);
+       return E2BIG;
+    }
+    *aname = (char *)malloc(len);
+    strcpy(*aname, td->name);
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+    return 0;
+}
+
+/*this is a handshake to indicate that the next call will be SAFSVolRestore
+ * - a noop now !*/
+afs_int32
+SAFSVolSignalRestore(struct rx_call *acid, char volname[], int volType, 
+                    afs_int32 parentId, afs_int32 cloneId)
+{
+    return 0;
+}
+
+
+/*return a list of all partitions on the server. The non mounted
+ *partitions are returned as -1 in the corresponding slot in partIds*/
+afs_int32
+SAFSVolListPartitions(struct rx_call *acid, struct pIDs *partIds)
+{
+    afs_int32 code;
+
+    code = VolListPartitions(acid, partIds);
+    osi_auditU(acid, VS_ListParEvent, code, AUD_END);
+    return code;
+}
+
+afs_int32
+VolListPartitions(struct rx_call *acid, struct pIDs *partIds)
+{
+    char namehead[9];
+    char i;
+
+    strcpy(namehead, "/vicep");        /*7 including null terminator */
+
+    /* Just return attached partitions. */
+    namehead[7] = '\0';
+    for (i = 0; i < 26; i++) {
+       namehead[6] = i + 'a';
+       if (VGetPartition(namehead, 0))
+           partIds->partIds[i] = VGetPartition(namehead, 0) ? i : -1;
+    }
+
+    return 0;
+}
+
+/*return a list of all partitions on the server. The non mounted
+ *partitions are returned as -1 in the corresponding slot in partIds*/
+afs_int32
+SAFSVolXListPartitions(struct rx_call *acid, struct partEntries *pEntries)
+{
+    afs_int32 code;
+
+    code = XVolListPartitions(acid, pEntries);
+    osi_auditU(acid, VS_ListParEvent, code, AUD_END);
+    return code;
+}
+
+afs_int32
+XVolListPartitions(struct rx_call *acid, struct partEntries *pEntries)
+{
+    struct stat rbuf, pbuf;
+    char namehead[9];
+    struct partList partList;
+    struct DiskPartition *dp;
+    int i, j = 0, k;
+
+    strcpy(namehead, "/vicep");        /*7 including null terminator */
+
+    /* Only report attached partitions */
+    for (i = 0; i < VOLMAXPARTS; i++) {
+       if (i < 26) {
+           namehead[6] = i + 'a';
+           namehead[7] = '\0';
+       } else {
+           k = i - 26;
+           namehead[6] = 'a' + (k / 26);
+           namehead[7] = 'a' + (k % 26);
+           namehead[8] = '\0';
+       }
+       dp = VGetPartition(namehead, 0);
+       if (dp)
+           partList.partId[j++] = i;
+    }
+    pEntries->partEntries_val = (afs_int32 *) malloc(j * sizeof(int));
+    memcpy((char *)pEntries->partEntries_val, (char *)&partList,
+          j * sizeof(int));
+    pEntries->partEntries_len = j;
+    return 0;
+
+}
+
+/*extract the volume id from string vname. Its of the form " V0*<id>.vol  "*/
+afs_int32
+ExtractVolId(char vname[])
+{
+    int i;
+    char name[VOLSER_MAXVOLNAME + 1];
+
+    strcpy(name, vname);
+    i = 0;
+    while (name[i] == 'V' || name[i] == '0')
+       i++;
+
+    name[11] = '\0';           /* smash the "." */
+    return (atol(&name[i]));
+}
+
+/*return the name of the next volume header in the directory associated with dirp and dp.
+*the volume id is  returned in volid, and volume header name is returned in volname*/
+int
+GetNextVol(DIR * dirp, char *volname, afs_int32 * volid)
+{
+    struct dirent *dp;
+
+    dp = readdir(dirp);                /*read next entry in the directory */
+    if (dp) {
+       if ((dp->d_name[0] == 'V') && !strcmp(&(dp->d_name[11]), VHDREXT)) {
+           *volid = ExtractVolId(dp->d_name);
+           strcpy(volname, dp->d_name);
+           return 0;           /*return the name of the file representing a volume */
+       } else {
+           strcpy(volname, "");
+           return 0;           /*volname doesnot represent a volume */
+       }
+    } else {
+       strcpy(volname, "EOD");
+       return 0;               /*end of directory */
+    }
+
+}
+
+/*return the header information about the <volid> */
+afs_int32
+SAFSVolListOneVolume(struct rx_call *acid, afs_int32 partid, afs_int32 
+                    volumeId, volEntries *volumeInfo)
+{
+    afs_int32 code;
+
+    code = VolListOneVolume(acid, partid, volumeId, volumeInfo);
+    osi_auditU(acid, VS_Lst1VolEvent, code, AUD_LONG, volumeId, AUD_END);
+    return code;
+}
+
+afs_int32
+VolListOneVolume(struct rx_call *acid, afs_int32 partid, afs_int32 
+                    volumeId, volEntries *volumeInfo)
+{
+    volintInfo *pntr;
+    register struct Volume *tv;
+    struct DiskPartition *partP;
+    struct volser_trans *ttc;
+    char pname[9], volname[20];
+    afs_int32 error = 0;
+    DIR *dirp;
+    afs_int32 volid;
+    int found = 0;
+    unsigned int now;
+
+    volumeInfo->volEntries_val = (volintInfo *) malloc(sizeof(volintInfo));
+    pntr = volumeInfo->volEntries_val;
+    volumeInfo->volEntries_len = 1;
+    if (GetPartName(partid, pname))
+       return VOLSERILLEGAL_PARTITION;
+    if (!(partP = VGetPartition(pname, 0)))
+       return VOLSERILLEGAL_PARTITION;
+    dirp = opendir(VPartitionPath(partP));
+    if (dirp == NULL)
+       return VOLSERILLEGAL_PARTITION;
+    strcpy(volname, "");
+    ttc = (struct volser_trans *)0;
+    tv = (Volume *) 0;         /* volume not attached */
+
+    while (strcmp(volname, "EOD") && !found) { /*while there are more volumes in the partition */
+
+       if (!strcmp(volname, "")) {     /* its not a volume, fetch next file */
+           GetNextVol(dirp, volname, &volid);
+           continue;           /*back to while loop */
+       }
+
+       if (volid == volumeId) {        /*copy other things too */
+           found = 1;
+#ifndef AFS_PTHREAD_ENV
+           IOMGR_Poll();       /*make sure that the client doesnot time out */
+#endif
+           ttc = NewTrans(volid, partid);
+           if (!ttc) {
+               pntr->status = VBUSY;
+               pntr->volid = volid;
+               goto drop;
+           }
+           tv = VAttachVolumeByName(&error, pname, volname, V_READONLY);
+           if (error) {
+               pntr->status = 0;       /*things are messed up */
+               strcpy(pntr->name, volname);
+               pntr->volid = volid;
+               Log("1 Volser: ListVolumes: Could not attach volume %u (%s:%s), error=%d\n", volid, pname, volname, error);
+               goto drop;
+           }
+           if (tv->header->diskstuff.destroyMe == DESTROY_ME) {
+               /*this volume will be salvaged */
+               pntr->status = 0;
+               strcpy(pntr->name, volname);
+               pntr->volid = volid;
+               Log("1 Volser: ListVolumes: Volume %u (%s) will be destroyed on next salvage\n", volid, volname);
+               goto drop;
+           }
+
+           if (tv->header->diskstuff.needsSalvaged) {
+               /*this volume will be salvaged */
+               pntr->status = 0;
+               strcpy(pntr->name, volname);
+               pntr->volid = volid;
+               Log("1 Volser: ListVolumes: Volume %u (%s) needs to be salvaged\n", volid, volname);
+               goto drop;
+           }
+
+           /*read in the relevant info */
+           pntr->status = VOK; /*its ok */
+           pntr->volid = tv->header->diskstuff.id;
+           strcpy(pntr->name, tv->header->diskstuff.name);
+           pntr->type = tv->header->diskstuff.type;    /*if ro volume */
+           pntr->cloneID = tv->header->diskstuff.cloneId;      /*if rw volume */
+           pntr->backupID = tv->header->diskstuff.backupId;
+           pntr->parentID = tv->header->diskstuff.parentId;
+           pntr->copyDate = tv->header->diskstuff.copyDate;
+           pntr->inUse = tv->header->diskstuff.inUse;
+           pntr->size = tv->header->diskstuff.diskused;
+           pntr->needsSalvaged = tv->header->diskstuff.needsSalvaged;
+           pntr->destroyMe = tv->header->diskstuff.destroyMe;
+           pntr->maxquota = tv->header->diskstuff.maxquota;
+           pntr->filecount = tv->header->diskstuff.filecount;
+           now = FT_ApproxTime();
+           if (now - tv->header->diskstuff.dayUseDate > OneDay)
+               pntr->dayUse = 0;
+           else
+               pntr->dayUse = tv->header->diskstuff.dayUse;
+           pntr->creationDate = tv->header->diskstuff.creationDate;
+           pntr->accessDate = tv->header->diskstuff.accessDate;
+           pntr->updateDate = tv->header->diskstuff.updateDate;
+           pntr->backupDate = tv->header->diskstuff.backupDate;
+           pntr->spare0 = tv->header->diskstuff.minquota;
+           pntr->spare1 =
+               (long)tv->header->diskstuff.weekUse[0] +
+               (long)tv->header->diskstuff.weekUse[1] +
+               (long)tv->header->diskstuff.weekUse[2] +
+               (long)tv->header->diskstuff.weekUse[3] +
+               (long)tv->header->diskstuff.weekUse[4] +
+               (long)tv->header->diskstuff.weekUse[5] +
+               (long)tv->header->diskstuff.weekUse[6];
+           pntr->flags = pntr->spare2 = pntr->spare3 = (long)0;
+           VDetachVolume(&error, tv);  /*free the volume */
+           tv = (Volume *) 0;
+           if (error) {
+               pntr->status = 0;       /*things are messed up */
+               strcpy(pntr->name, volname);
+               Log("1 Volser: ListVolumes: Could not detach volume %s\n",
+                   volname);
+               goto drop;
+           }
+       }
+       GetNextVol(dirp, volname, &volid);
+    }
+  drop:
+    if (tv) {
+       VDetachVolume(&error, tv);
+       tv = (Volume *) 0;
+    }
+    if (ttc) {
+       DeleteTrans(ttc);
+       ttc = (struct volser_trans *)0;
+    }
+
+    closedir(dirp);
+    if (found)
+       return 0;
+    else
+       return ENODEV;
+}
+
+/*------------------------------------------------------------------------
+ * EXPORTED SAFSVolXListOneVolume
+ *
+ * Description:
+ *     Returns extended info on volume a_volID on partition a_partID.
+ *
+ * Arguments:
+ *     a_rxCidP       : Pointer to the Rx call we're performing.
+ *     a_partID       : Partition for which we want the extended list.
+ *     a_volID        : Volume ID we wish to know about.
+ *     a_volumeXInfoP : Ptr to the extended info blob.
+ *
+ * Returns:
+ *     0                       Successful operation
+ *     VOLSERILLEGAL_PARTITION if we got a bogus partition ID
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+afs_int32
+SAFSVolXListOneVolume(struct rx_call *a_rxCidP, afs_int32 a_partID, 
+                     afs_int32 a_volID, volXEntries *a_volumeXInfoP)
+{
+    afs_int32 code;
+
+    code = VolXListOneVolume(a_rxCidP, a_partID, a_volID, a_volumeXInfoP);
+    osi_auditU(a_rxCidP, VS_XLst1VlEvent, code, AUD_LONG, a_volID, AUD_END);
+    return code;
+}
+
+afs_int32
+VolXListOneVolume(struct rx_call *a_rxCidP, afs_int32 a_partID, 
+                     afs_int32 a_volID, volXEntries *a_volumeXInfoP)
+{                              /*SAFSVolXListOneVolume */
+
+    volintXInfo *xInfoP;       /*Ptr to the extended vol info */
+    register struct Volume *tv;        /*Volume ptr */
+    struct volser_trans *ttc;  /*Volume transaction ptr */
+    struct DiskPartition *partP;       /*Ptr to partition */
+    char pname[9], volname[20];        /*Partition, volume names */
+    afs_int32 error;           /*Error code */
+    afs_int32 code;            /*Return code */
+    DIR *dirp;                 /*Partition directory ptr */
+    afs_int32 currVolID;       /*Current volume ID */
+    int found = 0;             /*Did we find the volume we need? */
+    struct VolumeDiskData *volDiskDataP;       /*Ptr to on-disk volume data */
+    int numStatBytes;          /*Num stat bytes to copy per volume */
+    unsigned int now;
+
+    /*
+     * Set up our pointers for action, marking our structure to hold exactly
+     * one entry.  Also, assume we'll fail in our quest.
+     */
+    a_volumeXInfoP->volXEntries_val =
+       (volintXInfo *) malloc(sizeof(volintXInfo));
+    xInfoP = a_volumeXInfoP->volXEntries_val;
+    a_volumeXInfoP->volXEntries_len = 1;
+    code = ENODEV;
+
+    /*
+     * If the partition name we've been given is bad, bogue out.
+     */
+    if (GetPartName(a_partID, pname))
+       return (VOLSERILLEGAL_PARTITION);
+
+    /*
+     * Open the directory representing the given AFS parttion.  If we can't
+     * do that, we lose.
+     */
+    if (!(partP = VGetPartition(pname, 0)))
+       return VOLSERILLEGAL_PARTITION;
+    dirp = opendir(VPartitionPath(partP));
+    if (dirp == NULL)
+       return (VOLSERILLEGAL_PARTITION);
+
+    /*
+     * Sweep through the partition directory, looking for the desired entry.
+     * First, of course, figure out how many stat bytes to copy out of each
+     * volume.
+     */
+    numStatBytes =
+       4 * ((2 * VOLINT_STATS_NUM_RWINFO_FIELDS) +
+            (4 * VOLINT_STATS_NUM_TIME_FIELDS));
+    strcpy(volname, "");
+    ttc = (struct volser_trans *)0;    /*No transaction yet */
+    tv = (Volume *) 0;         /*Volume not yet attached */
+
+    while (strcmp(volname, "EOD") && !found) {
+       /*
+        * If this is not a volume, move on to the next entry in the
+        * partition's directory.
+        */
+       if (!strcmp(volname, "")) {
+           GetNextVol(dirp, volname, &currVolID);
+           continue;
+       }
+
+       if (currVolID == a_volID) {
+           /*
+            * We found the volume entry we're interested.  Pull out the
+            * extended information, remembering to poll (so that the client
+            * doesn't time out) and to set up a transaction on the volume.
+            */
+           found = 1;
+#ifndef AFS_PTHREAD_ENV
+           IOMGR_Poll();
+#endif
+           ttc = NewTrans(currVolID, a_partID);
+           if (!ttc) {
+               /*
+                * Couldn't get a transaction on this volume; let our caller
+                * know it's busy.
+                */
+               xInfoP->status = VBUSY;
+               xInfoP->volid = currVolID;
+               goto drop;
+           }
+
+           /*
+            * Attach the volume, give up on the volume if we can't.
+            */
+           tv = VAttachVolumeByName(&error, pname, volname, V_READONLY);
+           if (error) {
+               xInfoP->status = 0;     /*things are messed up */
+               strcpy(xInfoP->name, volname);
+               xInfoP->volid = currVolID;
+               Log("1 Volser: XListOneVolume: Could not attach volume %u\n",
+                   currVolID);
+               goto drop;
+           }
+
+           /*
+            * Also bag out on this volume if it's been marked as needing a
+            * salvage or to-be-destroyed.
+            */
+           volDiskDataP = &(tv->header->diskstuff);
+           if (volDiskDataP->destroyMe == DESTROY_ME) {
+               xInfoP->status = 0;
+               strcpy(xInfoP->name, volname);
+               xInfoP->volid = currVolID;
+               Log("1 Volser: XListOneVolume: Volume %u will be destroyed on next salvage\n", currVolID);
+               goto drop;
+           }
+
+           if (volDiskDataP->needsSalvaged) {
+               xInfoP->status = 0;
+               strcpy(xInfoP->name, volname);
+               xInfoP->volid = currVolID;
+               Log("1 Volser: XListOneVolume: Volume %u needs to be salvaged\n", currVolID);
+               goto drop;
+           }
+
+           /*
+            * Pull out the desired info and stuff it into the area we'll be
+            * returning to our caller.
+            */
+           strcpy(xInfoP->name, volDiskDataP->name);
+           xInfoP->volid = volDiskDataP->id;
+           xInfoP->type = volDiskDataP->type;
+           xInfoP->backupID = volDiskDataP->backupId;
+           xInfoP->parentID = volDiskDataP->parentId;
+           xInfoP->cloneID = volDiskDataP->cloneId;
+           xInfoP->status = VOK;
+           xInfoP->copyDate = volDiskDataP->copyDate;
+           xInfoP->inUse = volDiskDataP->inUse;
+           xInfoP->creationDate = volDiskDataP->creationDate;
+           xInfoP->accessDate = volDiskDataP->accessDate;
+           xInfoP->updateDate = volDiskDataP->updateDate;
+           xInfoP->backupDate = volDiskDataP->backupDate;
+           now = FT_ApproxTime();
+           if (now - volDiskDataP->dayUseDate > OneDay)
+               xInfoP->dayUse = 0;
+           else
+               xInfoP->dayUse = volDiskDataP->dayUse;
+           xInfoP->filecount = volDiskDataP->filecount;
+           xInfoP->maxquota = volDiskDataP->maxquota;
+           xInfoP->size = volDiskDataP->diskused;
+
+           /*
+            * Copy out the stat fields in a single operation.
+            */
+           memcpy((char *)&(xInfoP->stat_reads[0]),
+                  (char *)&(volDiskDataP->stat_reads[0]), numStatBytes);
+
+           /*
+            * We're done copying.  Detach the volume and iterate (at this
+            * point, since we found our volume, we'll then drop out of the
+            * loop).
+            */
+           VDetachVolume(&error, tv);
+           tv = (Volume *) 0;
+           if (error) {
+               xInfoP->status = 0;
+               strcpy(xInfoP->name, volname);
+               Log("1 Volser: XListOneVolumes Couldn't detach volume %s\n",
+                   volname);
+               goto drop;
+           }
+
+           /*
+            * At this point, we're golden.
+            */
+           code = 0;
+       }                       /*Found desired volume */
+       GetNextVol(dirp, volname, &currVolID);
+    }
+
+    /*
+     * Drop the transaction we have for this volume.
+     */
+  drop:
+    if (tv) {
+       VDetachVolume(&error, tv);
+       tv = (Volume *) 0;
+    }
+    if (ttc) {
+       DeleteTrans(ttc);
+       ttc = (struct volser_trans *)0;
+    }
+
+    /*
+     * Clean up before going to dinner: close the partition directory,
+     * return the proper value.
+     */
+    closedir(dirp);
+    return (code);
+
+}                              /*SAFSVolXListOneVolume */
+
+/*returns all the volumes on partition partid. If flags = 1 then all the 
+* relevant info about the volumes  is also returned */
+afs_int32
+SAFSVolListVolumes(struct rx_call *acid, afs_int32 partid, afs_int32 flags, 
+                  volEntries *volumeInfo)
+{
+    afs_int32 code;
+
+    code = VolListVolumes(acid, partid, flags, volumeInfo);
+    osi_auditU(acid, VS_ListVolEvent, code, AUD_END);
+    return code;
+}
+
+afs_int32
+VolListVolumes(struct rx_call *acid, afs_int32 partid, afs_int32 flags, 
+                  volEntries *volumeInfo)
+{
+    volintInfo *pntr;
+    register struct Volume *tv;
+    struct DiskPartition *partP;
+    struct volser_trans *ttc;
+    afs_int32 allocSize = 1000;        /*to be changed to a larger figure */
+    char pname[9], volname[20];
+    afs_int32 error = 0;
+    DIR *dirp;
+    afs_int32 volid;
+    unsigned int now;
+
+    volumeInfo->volEntries_val =
+       (volintInfo *) malloc(allocSize * sizeof(volintInfo));
+    pntr = volumeInfo->volEntries_val;
+    volumeInfo->volEntries_len = 0;
+    if (GetPartName(partid, pname))
+       return VOLSERILLEGAL_PARTITION;
+    if (!(partP = VGetPartition(pname, 0)))
+       return VOLSERILLEGAL_PARTITION;
+    dirp = opendir(VPartitionPath(partP));
+    if (dirp == NULL)
+       return VOLSERILLEGAL_PARTITION;
+    strcpy(volname, "");
+    while (strcmp(volname, "EOD")) {   /*while there are more partitions in the partition */
+       ttc = (struct volser_trans *)0; /* new one for each pass */
+       tv = (Volume *) 0;      /* volume not attached */
+
+       if (!strcmp(volname, "")) {     /* its not a volume, fetch next file */
+           GetNextVol(dirp, volname, &volid);
+           continue;           /*back to while loop */
+       }
+
+       if (flags) {            /*copy other things too */
+#ifndef AFS_PTHREAD_ENV
+           IOMGR_Poll();       /*make sure that the client doesnot time out */
+#endif
+           ttc = NewTrans(volid, partid);
+           if (!ttc) {
+               pntr->status = VBUSY;
+               pntr->volid = volid;
+               goto drop;
+           }
+           tv = VAttachVolumeByName(&error, pname, volname, V_READONLY);
+           if (error) {
+               pntr->status = 0;       /*things are messed up */
+               strcpy(pntr->name, volname);
+               pntr->volid = volid;
+               Log("1 Volser: ListVolumes: Could not attach volume %u (%s) error=%d\n", volid, volname, error);
+               goto drop;
+           }
+           if (tv->header->diskstuff.needsSalvaged) {
+               /*this volume will be salvaged */
+               pntr->status = 0;
+               strcpy(pntr->name, volname);
+               pntr->volid = volid;
+               Log("1 Volser: ListVolumes: Volume %u (%s) needs to be salvaged\n", volid, volname);
+               goto drop;
+           }
+
+           if (tv->header->diskstuff.destroyMe == DESTROY_ME) {
+               /*this volume will be salvaged */
+               goto drop2;
+           }
+           /*read in the relevant info */
+           pntr->status = VOK; /*its ok */
+           pntr->volid = tv->header->diskstuff.id;
+           strcpy(pntr->name, tv->header->diskstuff.name);
+           pntr->type = tv->header->diskstuff.type;    /*if ro volume */
+           pntr->cloneID = tv->header->diskstuff.cloneId;      /*if rw volume */
+           pntr->backupID = tv->header->diskstuff.backupId;
+           pntr->parentID = tv->header->diskstuff.parentId;
+           pntr->copyDate = tv->header->diskstuff.copyDate;
+           pntr->inUse = tv->header->diskstuff.inUse;
+           pntr->size = tv->header->diskstuff.diskused;
+           pntr->needsSalvaged = tv->header->diskstuff.needsSalvaged;
+           pntr->maxquota = tv->header->diskstuff.maxquota;
+           pntr->filecount = tv->header->diskstuff.filecount;
+           now = FT_ApproxTime();
+           if (now - tv->header->diskstuff.dayUseDate > OneDay)
+               pntr->dayUse = 0;
+           else
+               pntr->dayUse = tv->header->diskstuff.dayUse;
+           pntr->creationDate = tv->header->diskstuff.creationDate;
+           pntr->accessDate = tv->header->diskstuff.accessDate;
+           pntr->updateDate = tv->header->diskstuff.updateDate;
+           pntr->backupDate = tv->header->diskstuff.backupDate;
+           pntr->spare0 = tv->header->diskstuff.minquota;
+           pntr->spare1 =
+               (long)tv->header->diskstuff.weekUse[0] +
+               (long)tv->header->diskstuff.weekUse[1] +
+               (long)tv->header->diskstuff.weekUse[2] +
+               (long)tv->header->diskstuff.weekUse[3] +
+               (long)tv->header->diskstuff.weekUse[4] +
+               (long)tv->header->diskstuff.weekUse[5] +
+               (long)tv->header->diskstuff.weekUse[6];
+           pntr->flags = pntr->spare2 = pntr->spare3 = (long)0;
+           VDetachVolume(&error, tv);  /*free the volume */
+           tv = (Volume *) 0;
+           if (error) {
+               pntr->status = 0;       /*things are messed up */
+               strcpy(pntr->name, volname);
+               Log("1 Volser: ListVolumes: Could not detach volume %s\n",
+                   volname);
+               goto drop;
+           }
+       } else {
+           pntr->volid = volid;
+           /*just volids are needed */
+       }
+
+      drop:
+       if (ttc) {
+           DeleteTrans(ttc);
+           ttc = (struct volser_trans *)0;
+       }
+       pntr++;
+       volumeInfo->volEntries_len += 1;
+       if ((allocSize - volumeInfo->volEntries_len) < 5) {
+           /*running out of space, allocate more space */
+           allocSize = (allocSize * 3) / 2;
+           pntr =
+               (volintInfo *) realloc((char *)volumeInfo->volEntries_val,
+                                      allocSize * sizeof(volintInfo));
+           if (pntr == NULL) {
+               if (tv) {
+                   VDetachVolume(&error, tv);
+                   tv = (Volume *) 0;
+               }
+               if (ttc) {
+                   DeleteTrans(ttc);
+                   ttc = (struct volser_trans *)0;
+               }
+               closedir(dirp);
+               return VOLSERNO_MEMORY;
+           }
+           volumeInfo->volEntries_val = pntr;  /* point to new block */
+           /* set pntr to the right position */
+           pntr = volumeInfo->volEntries_val + volumeInfo->volEntries_len;
+
+       }
+
+      drop2:
+       if (tv) {
+           VDetachVolume(&error, tv);
+           tv = (Volume *) 0;
+       }
+       if (ttc) {
+           DeleteTrans(ttc);
+           ttc = (struct volser_trans *)0;
+       }
+       GetNextVol(dirp, volname, &volid);
+
+    }
+    closedir(dirp);
+    if (ttc)
+       DeleteTrans(ttc);
+
+    return 0;
+}
+
+/*------------------------------------------------------------------------
+ * EXPORTED SAFSVolXListVolumes
+ *
+ * Description:
+ *     Returns all the volumes on partition a_partID.  If a_flags
+ *     is set to 1, then all the relevant extended volume information
+ *     is also returned.
+ *
+ * Arguments:
+ *     a_rxCidP       : Pointer to the Rx call we're performing.
+ *     a_partID       : Partition for which we want the extended list.
+ *     a_flags        : Various flags.
+ *     a_volumeXInfoP : Ptr to the extended info blob.
+ *
+ * Returns:
+ *     0                       Successful operation
+ *     VOLSERILLEGAL_PARTITION if we got a bogus partition ID
+ *     VOLSERNO_MEMORY         if we ran out of memory allocating
+ *                             our return blob
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+afs_int32
+SAFSVolXListVolumes(struct rx_call *a_rxCidP, afs_int32 a_partID,
+                   afs_int32 a_flags, volXEntries *a_volumeXInfoP)
+{
+    afs_int32 code;
+
+    code = VolXListVolumes(a_rxCidP, a_partID, a_flags, a_volumeXInfoP);
+    osi_auditU(a_rxCidP, VS_XLstVolEvent, code, AUD_END);
+    return code;
+}
+
+afs_int32
+VolXListVolumes(struct rx_call *a_rxCidP, afs_int32 a_partID,
+                   afs_int32 a_flags, volXEntries *a_volumeXInfoP)
+{                              /*SAFSVolXListVolumes */
+
+    volintXInfo *xInfoP;       /*Ptr to the extended vol info */
+    register struct Volume *tv;        /*Volume ptr */
+    struct DiskPartition *partP;       /*Ptr to partition */
+    struct volser_trans *ttc;  /*Volume transaction ptr */
+    afs_int32 allocSize = 1000;        /*To be changed to a larger figure */
+    char pname[9], volname[20];        /*Partition, volume names */
+    afs_int32 error = 0;       /*Return code */
+    DIR *dirp;                 /*Partition directory ptr */
+    afs_int32 volid;           /*Current volume ID */
+    struct VolumeDiskData *volDiskDataP;       /*Ptr to on-disk volume data */
+    int numStatBytes;          /*Num stat bytes to copy per volume */
+    unsigned int now;
+
+    /*
+     * Allocate a large array of extended volume info structures, then
+     * set it up for action.
+     */
+    a_volumeXInfoP->volXEntries_val =
+       (volintXInfo *) malloc(allocSize * sizeof(volintXInfo));
+    xInfoP = a_volumeXInfoP->volXEntries_val;
+    a_volumeXInfoP->volXEntries_len = 0;
+
+    /*
+     * If the partition name we've been given is bad, bogue out.
+     */
+    if (GetPartName(a_partID, pname))
+       return (VOLSERILLEGAL_PARTITION);
+
+    /*
+     * Open the directory representing the given AFS parttion.  If we can't
+     * do that, we lose.
+     */
+    if (!(partP = VGetPartition(pname, 0)))
+       return VOLSERILLEGAL_PARTITION;
+    dirp = opendir(VPartitionPath(partP));
+    if (dirp == NULL)
+       return (VOLSERILLEGAL_PARTITION);
+
+    /*
+     * Sweep through the partition directory, acting on each entry.  First,
+     * of course, figure out how many stat bytes to copy out of each volume.
+     */
+    numStatBytes =
+       4 * ((2 * VOLINT_STATS_NUM_RWINFO_FIELDS) +
+            (4 * VOLINT_STATS_NUM_TIME_FIELDS));
+    strcpy(volname, "");
+    while (strcmp(volname, "EOD")) {
+       ttc = (struct volser_trans *)0; /*New one for each pass */
+       tv = (Volume *) 0;      /*Volume not yet attached */
+
+       /*
+        * If this is not a volume, move on to the next entry in the
+        * partition's directory.
+        */
+       if (!strcmp(volname, "")) {
+           GetNextVol(dirp, volname, &volid);
+           continue;
+       }
+
+       if (a_flags) {
+           /*
+            * Full info about the volume desired.  Poll to make sure the
+            * client doesn't time out, then start up a new transaction.
+            */
+#ifndef AFS_PTHREAD_ENV
+           IOMGR_Poll();
+#endif
+           ttc = NewTrans(volid, a_partID);
+           if (!ttc) {
+               /*
+                * Couldn't get a transaction on this volume; let our caller
+                * know it's busy.
+                */
+               xInfoP->status = VBUSY;
+               xInfoP->volid = volid;
+               goto drop;
+           }
+
+           /*
+            * Attach the volume, give up on this volume if we can't.
+            */
+           tv = VAttachVolumeByName(&error, pname, volname, V_READONLY);
+           if (error) {
+               xInfoP->status = 0;     /*things are messed up */
+               strcpy(xInfoP->name, volname);
+               xInfoP->volid = volid;
+               Log("1 Volser: XListVolumes: Could not attach volume %u\n",
+                   volid);
+               goto drop;
+           }
+
+           /*
+            * Also bag out on this volume if it's been marked as needing a
+            * salvage or to-be-destroyed.
+            */
+           volDiskDataP = &(tv->header->diskstuff);
+           if (volDiskDataP->needsSalvaged) {
+               xInfoP->status = 0;
+               strcpy(xInfoP->name, volname);
+               xInfoP->volid = volid;
+               Log("1 Volser: XListVolumes: Volume %u needs to be salvaged\n", volid);
+               goto drop;
+           }
+
+           if (volDiskDataP->destroyMe == DESTROY_ME)
+               goto drop2;
+
+           /*
+            * Pull out the desired info and stuff it into the area we'll be
+            * returning to our caller.
+            */
+           strcpy(xInfoP->name, volDiskDataP->name);
+           xInfoP->volid = volDiskDataP->id;
+           xInfoP->type = volDiskDataP->type;
+           xInfoP->backupID = volDiskDataP->backupId;
+           xInfoP->parentID = volDiskDataP->parentId;
+           xInfoP->cloneID = volDiskDataP->cloneId;
+           xInfoP->status = VOK;
+           xInfoP->copyDate = volDiskDataP->copyDate;
+           xInfoP->inUse = volDiskDataP->inUse;
+           xInfoP->creationDate = volDiskDataP->creationDate;
+           xInfoP->accessDate = volDiskDataP->accessDate;
+           xInfoP->updateDate = volDiskDataP->updateDate;
+           xInfoP->backupDate = volDiskDataP->backupDate;
+           now = FT_ApproxTime();
+           if (now - volDiskDataP->dayUseDate > OneDay)
+               xInfoP->dayUse = 0;
+           else
+               xInfoP->dayUse = volDiskDataP->dayUse;
+           xInfoP->filecount = volDiskDataP->filecount;
+           xInfoP->maxquota = volDiskDataP->maxquota;
+           xInfoP->size = volDiskDataP->diskused;
+
+           /*
+            * Copy out the stat fields in a single operation.
+            */
+           memcpy((char *)&(xInfoP->stat_reads[0]),
+                  (char *)&(volDiskDataP->stat_reads[0]), numStatBytes);
+
+           /*
+            * We're done copying.  Detach the volume and iterate.
+            */
+           VDetachVolume(&error, tv);
+           tv = (Volume *) 0;
+           if (error) {
+               xInfoP->status = 0;
+               strcpy(xInfoP->name, volname);
+               Log("1 Volser: XListVolumes: Could not detach volume %s\n",
+                   volname);
+               goto drop;
+           }
+       } /*Full contents desired */
+       else
+           /*
+            * Just volume IDs are needed.
+            */
+           xInfoP->volid = volid;
+
+      drop:
+       /*
+        * Drop the transaction we have for this volume.
+        */
+       if (ttc) {
+           DeleteTrans(ttc);
+           ttc = (struct volser_trans *)0;
+       }
+
+       /*
+        * Bump the pointer in the data area we're building, along with
+        * the count of the number of entries it contains.
+        */
+       xInfoP++;
+       (a_volumeXInfoP->volXEntries_len)++;
+       if ((allocSize - a_volumeXInfoP->volXEntries_len) < 5) {
+           /*
+            * We're running out of space in the area we've built.  Grow it.
+            */
+           allocSize = (allocSize * 3) / 2;
+           xInfoP = (volintXInfo *)
+               realloc((char *)a_volumeXInfoP->volXEntries_val,
+                       (allocSize * sizeof(volintXInfo)));
+           if (xInfoP == NULL) {
+               /*
+                * Bummer, no memory. Bag it, tell our caller what went wrong.
+                */
+               if (tv) {
+                   VDetachVolume(&error, tv);
+                   tv = (Volume *) 0;
+               }
+               if (ttc) {
+                   DeleteTrans(ttc);
+                   ttc = (struct volser_trans *)0;
+               }
+               closedir(dirp);
+               return (VOLSERNO_MEMORY);
+           }
+
+           /*
+            * Memory reallocation worked.  Correct our pointers so they
+            * now point to the new block and the current open position within
+            * the new block.
+            */
+           a_volumeXInfoP->volXEntries_val = xInfoP;
+           xInfoP =
+               a_volumeXInfoP->volXEntries_val +
+               a_volumeXInfoP->volXEntries_len;
+       }
+       /*Need more space */
+      drop2:
+       /*
+        * Detach our current volume and the transaction on it, then move on
+        * to the next volume in the partition directory.
+        */
+       if (tv) {
+           VDetachVolume(&error, tv);
+           tv = (Volume *) 0;
+       }
+       if (ttc) {
+           DeleteTrans(ttc);
+           ttc = (struct volser_trans *)0;
+       }
+       GetNextVol(dirp, volname, &volid);
+    }                          /*Sweep through the partition directory */
+
+    /*
+     * We've examined all entries in the partition directory.  Close it,
+     * delete our transaction (if any), and go home happy.
+     */
+    closedir(dirp);
+    if (ttc)
+       DeleteTrans(ttc);
+    return (0);
+
+}                              /*SAFSVolXListVolumes */
+
+/*this call is used to monitor the status of volser for debugging purposes.
+ *information about all the active transactions is returned in transInfo*/
+afs_int32
+SAFSVolMonitor(struct rx_call *acid, transDebugEntries *transInfo)
+{
+    afs_int32 code;
+
+    code = VolMonitor(acid, transInfo);
+    osi_auditU(acid, VS_MonitorEvent, code, AUD_END);
+    return code;
+}
+
+afs_int32
+VolMonitor(struct rx_call *acid, transDebugEntries *transInfo)
+{
+    transDebugInfo *pntr;
+    afs_int32 allocSize = 50;
+    struct volser_trans *tt, *allTrans;
+
+    transInfo->transDebugEntries_val =
+       (transDebugInfo *) malloc(allocSize * sizeof(transDebugInfo));
+    pntr = transInfo->transDebugEntries_val;
+    transInfo->transDebugEntries_len = 0;
+    allTrans = TransList();
+    if (allTrans == (struct volser_trans *)0)
+       return 0;               /*no active transactions */
+    for (tt = allTrans; tt; tt = tt->next) {   /*copy relevant info into pntr */
+       pntr->tid = tt->tid;
+       pntr->time = tt->time;
+       pntr->creationTime = tt->creationTime;
+       pntr->returnCode = tt->returnCode;
+       pntr->volid = tt->volid;
+       pntr->partition = tt->partition;
+       pntr->iflags = tt->iflags;
+       pntr->vflags = tt->vflags;
+       pntr->tflags = tt->tflags;
+       strcpy(pntr->lastProcName, tt->lastProcName);
+       pntr->callValid = 0;
+       if (tt->rxCallPtr) {    /*record call related info */
+           pntr->callValid = 1;
+           pntr->readNext = tt->rxCallPtr->rnext;
+           pntr->transmitNext = tt->rxCallPtr->tnext;
+           pntr->lastSendTime = tt->rxCallPtr->lastSendTime;
+           pntr->lastReceiveTime = tt->rxCallPtr->lastReceiveTime;
+       }
+       pntr++;
+       transInfo->transDebugEntries_len += 1;
+       if ((allocSize - transInfo->transDebugEntries_len) < 5) {       /*alloc some more space */
+           allocSize = (allocSize * 3) / 2;
+           pntr =
+               (transDebugInfo *) realloc((char *)transInfo->
+                                          transDebugEntries_val,
+                                          allocSize *
+                                          sizeof(transDebugInfo));
+           transInfo->transDebugEntries_val = pntr;
+           pntr =
+               transInfo->transDebugEntries_val +
+               transInfo->transDebugEntries_len;
+           /*set pntr to right position */
+       }
+
+    }
+
+    return 0;
+}
+
+afs_int32
+SAFSVolSetIdsTypes(struct rx_call *acid, afs_int32 atid, char name[], afs_int32 type, afs_int32 pId, afs_int32 cloneId, afs_int32 backupId)
+{
+    afs_int32 code;
+
+    code = VolSetIdsTypes(acid, atid, name, type, pId, cloneId, backupId);
+    osi_auditU(acid, VS_SetIdTyEvent, code, AUD_LONG, atid, AUD_STR, name,
+              AUD_STR, type, AUD_LONG, pId, AUD_LONG, cloneId, AUD_LONG,
+              backupId, AUD_END);
+    return code;
+}
+
+afs_int32
+VolSetIdsTypes(struct rx_call *acid, afs_int32 atid, char name[], afs_int32 type, afs_int32 pId, afs_int32 cloneId, afs_int32 backupId)
+{
+    struct Volume *tv;
+    afs_int32 error = 0;
+    register struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+
+    if (strlen(name) > 31)
+       return VOLSERBADNAME;
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    /* find the trans */
+    tt = FindTrans(atid);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolSetIds: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "SetIdsTypes");
+    tt->rxCallPtr = acid;
+    tv = tt->volume;
+
+    V_type(tv) = type;
+    V_backupId(tv) = backupId;
+    V_cloneId(tv) = cloneId;
+    V_parentId(tv) = pId;
+    strcpy((&V_disk(tv))->name, name);
+    VUpdateVolume(&error, tv);
+    if (error) {
+       Log("1 Volser: SetIdsTypes: VUpdate failed code %d\n", error);
+       LogError(error);
+       goto fail;
+    }
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt) && !error)
+       return VOLSERTRELE_ERROR;
+
+    return error;
+  fail:
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt) && !error)
+       return VOLSERTRELE_ERROR;
+    return error;
+}
+
+afs_int32
+SAFSVolSetDate(struct rx_call *acid, afs_int32 atid, afs_int32 cdate)
+{
+    afs_int32 code;
+
+    code = VolSetDate(acid, atid, cdate);
+    osi_auditU(acid, VS_SetDateEvent, code, AUD_LONG, atid, AUD_LONG, cdate,
+              AUD_END);
+    return code;
+}
+
+afs_int32
+VolSetDate(struct rx_call *acid, afs_int32 atid, afs_int32 cdate)
+{
+    struct Volume *tv;
+    afs_int32 error = 0;
+    register struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    /* find the trans */
+    tt = FindTrans(atid);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       Log("1 Volser: VolSetDate: volume %u has been deleted \n", tt->volid);
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "SetDate");
+    tt->rxCallPtr = acid;
+    tv = tt->volume;
+
+    V_creationDate(tv) = cdate;
+    VUpdateVolume(&error, tv);
+    if (error) {
+       Log("1 Volser: SetDate: VUpdate failed code %d\n", error);
+       LogError(error);
+       goto fail;
+    }
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt) && !error)
+       return VOLSERTRELE_ERROR;
+
+    return error;
+  fail:
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt) && !error)
+       return VOLSERTRELE_ERROR;
+    return error;
+}
+
+#ifdef AFS_NAMEI_ENV
+/* 
+ * Inode number format  (from namei_ops.c): 
+ * low 26 bits - vnode number - all 1's if volume special file.
+ * next 3 bits - tag
+ * next 3 bits spare (0's)
+ * high 32 bits - uniquifier (regular) or type if spare
+ */
+#define NAMEI_VNODEMASK    0x003ffffff
+#define NAMEI_TAGMASK      0x7
+#define NAMEI_TAGSHIFT     26
+#define NAMEI_UNIQMASK     0xffffffff
+#define NAMEI_UNIQSHIFT    32
+#define NAMEI_INODESPECIAL ((Inode)NAMEI_VNODEMASK)
+#define NAMEI_VNODESPECIAL NAMEI_VNODEMASK
+#endif /* AFS_NAMEI_ENV */
+
+afs_int32
+SAFSVolConvertROtoRWvolume(struct rx_call *acid, afs_int32 partId,
+                          afs_int32 volumeId)
+{
+#if defined(AFS_NAMEI_ENV) && !defined(AFS_NT40_ENV)
+    DIR *dirp;
+    char pname[16];
+    char volname[20];
+    afs_int32 error = 0;
+    afs_int32 volid;
+    int found = 0;
+    char caller[MAXKTCNAMELEN];
+    char headername[16];
+    char opath[256];
+    char npath[256];
+    struct VolumeDiskHeader h;
+    int fd;
+    IHandle_t *ih;
+    Inode ino;
+    struct DiskPartition *dp;
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    if (GetPartName(partId, pname))
+       return VOLSERILLEGAL_PARTITION;
+    dirp = opendir(pname);
+    if (dirp == NULL)
+       return VOLSERILLEGAL_PARTITION;
+    strcpy(volname, "");
+
+    while (strcmp(volname, "EOD") && !found) { /*while there are more volumes in the partition */
+       GetNextVol(dirp, volname, &volid);
+       if (strcmp(volname, "")) {      /* its a volume */
+           if (volid == volumeId)
+               found = 1;
+       }
+    }
+    if (!found)
+       return ENOENT;
+    (void)afs_snprintf(headername, sizeof headername, VFORMAT, volumeId);
+    (void)afs_snprintf(opath, sizeof opath, "%s/%s", pname, headername);
+    fd = open(opath, O_RDONLY);
+    if (fd < 0) {
+       Log("1 SAFS_VolConvertROtoRWvolume: Couldn't open header for RO-volume %lu.\n", volumeId);
+       return ENOENT;
+    }
+    if (read(fd, &h, sizeof(h)) != sizeof(h)) {
+       Log("1 SAFS_VolConvertROtoRWvolume: Couldn't read header for RO-volume %lu.\n", volumeId);
+       close(fd);
+       return EIO;
+    }
+    close(fd);
+    FSYNC_askfs(volumeId, pname, FSYNC_RESTOREVOLUME, 0);
+
+    for (dp = DiskPartitionList; dp && strcmp(dp->name, pname);
+        dp = dp->next);
+    if (!dp) {
+       Log("1 SAFS_VolConvertROtoRWvolume: Couldn't find DiskPartition for %s\n", pname);
+       return EIO;
+    }
+    ino = namei_MakeSpecIno(h.parent, VI_LINKTABLE);
+    IH_INIT(ih, dp->device, h.parent, ino);
+
+    error = namei_ConvertROtoRWvolume(ih, volumeId);
+    if (error)
+       return error;
+    h.id = h.parent;
+    h.volumeInfo_hi = h.id;
+    h.smallVnodeIndex_hi = h.id;
+    h.largeVnodeIndex_hi = h.id;
+    h.linkTable_hi = h.id;
+    (void)afs_snprintf(headername, sizeof headername, VFORMAT, h.id);
+    (void)afs_snprintf(npath, sizeof npath, "%s/%s", pname, headername);
+    fd = open(npath, O_CREAT | O_EXCL | O_RDWR, 0644);
+    if (fd < 0) {
+       Log("1 SAFS_VolConvertROtoRWvolume: Couldn't create header for RW-volume %lu.\n", h.id);
+       return EIO;
+    }
+    if (write(fd, &h, sizeof(h)) != sizeof(h)) {
+       Log("1 SAFS_VolConvertROtoRWvolume: Couldn't write header for RW-volume %lu.\n", h.id);
+       close(fd);
+       return EIO;
+    }
+    close(fd);
+    if (unlink(opath) < 0) {
+       Log("1 SAFS_VolConvertROtoRWvolume: Couldn't unlink RO header, error = %d\n", error);
+    }
+    FSYNC_askfs(volumeId, pname, FSYNC_DONE, 0);
+    FSYNC_askfs(h.id, pname, FSYNC_ON, 0);
+    return 0;
+#else /* AFS_NAMEI_ENV */
+    return EINVAL;
+#endif /* AFS_NAMEI_ENV */
+}
+
+afs_int32
+SAFSVolGetSize(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate,
+              register struct volintSize *size)
+{
+    int code = 0;
+    register struct volser_trans *tt;
+    char caller[MAXKTCNAMELEN];
+
+    if (!afsconf_SuperUser(tdir, acid, caller))
+       return VOLSERBAD_ACCESS;        /*not a super user */
+    tt = FindTrans(fromTrans);
+    if (!tt)
+       return ENOENT;
+    if (tt->vflags & VTDeleted) {
+       TRELE(tt);
+       return ENOENT;
+    }
+    strcpy(tt->lastProcName, "GetSize");
+    tt->rxCallPtr = acid;
+    code = SizeDumpVolume(acid, tt->volume, fromDate, 1, size);        /* measure volume's data */
+    tt->rxCallPtr = (struct rx_call *)0;
+    if (TRELE(tt))
+       return VOLSERTRELE_ERROR;
+
+/*    osi_auditU(acid, VS_DumpEvent, code, AUD_LONG, fromTrans, AUD_END);  */
+    return code;
+}
+
+/* GetPartName - map partid (a decimal number) into pname (a string)
+ * Since for NT we actually want to return the drive name, we map through the
+ * partition struct.
+ */
+static int
+GetPartName(afs_int32 partid, char *pname)
+{
+    if (partid < 0)
+       return -1;
+    if (partid < 26) {
+       strcpy(pname, "/vicep");
+       pname[6] = 'a' + partid;
+       pname[7] = '\0';
+       return 0;
+    } else if (partid < VOLMAXPARTS) {
+       strcpy(pname, "/vicep");
+       partid -= 26;
+       pname[6] = 'a' + (partid / 26);
+       pname[7] = 'a' + (partid % 26);
+       pname[8] = '\0';
+       return 0;
+    } else
+       return -1;
+}
diff --git a/src/volser/volser.p.h b/src/volser/volser.p.h
new file mode 100644 (file)
index 0000000..091b523
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#ifndef _VOLSER_
+#define _VOLSER_ 1
+
+#ifdef AFS_PTHREAD_ENV
+#include <assert.h>
+#include <pthread.h>
+#endif
+
+/* vflags, representing state of the volume */
+#define        VTDeleteOnSalvage       1       /* delete on next salvage */
+#define        VTOutOfService          2       /* never put this volume online */
+#define        VTDeleted               4       /* deleted, don't do anything else */
+
+/* iflags, representing "attach mode" for this volume at the start of this transaction */
+#define        ITOffline       1       /* volume offline on server (returns VOFFLINE) */
+#define        ITBusy          2       /* volume busy on server (returns VBUSY) */
+#define        ITReadOnly      8       /* volume readonly on client, readwrite on server -DO NOT USE */
+#define        ITCreate        0x10    /* volume does not exist correctly yet */
+#define        ITCreateVolID   0x1000  /* create volid */
+
+/* tflags, representing transaction state */
+#define        TTDeleted       1       /* delete transaction not yet freed due  to high refCount */
+
+/* other names for volumes in voldefs.h */
+#define volser_RW      0
+#define volser_RO      1
+#define        volser_BACK     2
+
+#define        THOLD(tt)       ((tt)->refCount++)
+
+struct volser_trans {
+    struct volser_trans *next; /* next ptr in active trans list */
+    afs_int32 tid;             /* transaction id */
+    afs_int32 time;            /* time transaction was last active (for timeouts) */
+    afs_int32 creationTime;    /* time the transaction started */
+    afs_int32 returnCode;      /* transaction error code */
+    struct Volume *volume;     /* pointer to open volume */
+    afs_int32 volid;           /* open volume's id */
+    afs_int32 partition;       /* open volume's partition */
+    afs_int32 dumpTransId;     /* other side's trans id during a dump */
+    afs_int32 dumpSeq;         /* next sequence number to use during a dump */
+    short refCount;            /* reference count on this structure */
+    short iflags;              /* initial attach mode flags (IT*) */
+    char vflags;               /* current volume status flags (VT*) */
+    char tflags;               /* transaction flags (TT*) */
+    char incremental;          /* do an incremental restore */
+    /* the fields below are useful for debugging */
+    char lastProcName[30];     /* name of the last procedure which used transaction */
+    struct rx_call *rxCallPtr; /* pointer to latest associated rx_call */
+
+};
+
+/* This is how often the garbage collection thread wakes up and 
+ * checks for transactions that have timed out: BKGLoop()
+ */
+#define GCWAKEUP            30
+
+struct volser_dest {
+    afs_int32 destHost;
+    afs_int32 destPort;
+    afs_int32 destSSID;
+};
+
+#define        MAXHELPERS          10
+/* flags for vol helper busyFlags array.  First, VHIdle goes on when a server
+ * becomes idle.  Next, idle flag is cleared and VHRequest goes on when
+ * trans is queued.  Finally, VHRequest goes off (but VHIdle stays off) when
+ * helper is done.  VHIdle goes on again when an lwp waits for work.
+ */
+#define        VHIdle              1   /* vol helper is waiting for a request here */
+#define        VHRequest           2   /* a request has been queued here */
+extern struct volser_trans *QI_GlobalWriteTrans;
+
+/* the stuff below is from errors.h in vol directory */
+#define VICE_SPECIAL_ERRORS    101     /* Lowest special error code */
+
+#define VSALVAGE       101     /* Volume needs salvage */
+#define VNOVNODE       102     /* Bad vnode number quoted */
+#define VNOVOL         103     /* Volume not attached, doesn't exist, 
+                                * not created or not online */
+#define VVOLEXISTS     104     /* Volume already exists */
+#define VNOSERVICE     105     /* Volume is not in service (i.e. it's
+                                * is out of funds, is obsolete, or somesuch) */
+#define VOFFLINE       106     /* Volume is off line, for the reason
+                                * given in the offline message */
+#define VONLINE                107     /* Volume is already on line */
+#define VDISKFULL      108     /* Partition is "full", i.e. rougly within
+                                * n% of full */
+#define VOVERQUOTA     109     /* Volume max quota exceeded */
+#define VBUSY          110     /* Volume temporarily unavailable; try again.
+                                * The volume should be available again shortly; if
+                                * it isn't something is wrong.  Not normally to be
+                                * propagated to the application level */
+#define VMOVED         111     /* Volume has moved to another server; do a VGetVolumeInfo
+                                * to THIS server to find out where */
+
+#define MyPort 5003
+#define NameLen 80
+#define VLDB_MAXSERVERS 10
+#define VOLSERVICE_ID 4
+#define INVALID_BID 0
+#define VOLSER_MAXVOLNAME 65
+#define VOLSER_OLDMAXVOLNAME 32
+#define        VOLMAXPARTS     255
+
+/*flags used for interfacing with the  backup system */
+struct volDescription {                /*used for interfacing with the backup system */
+    char volName[VOLSER_MAXVOLNAME];   /* should be VNAMESIZE as defined in volume.h */
+    afs_int32 volId;
+    int volSize;
+    afs_int32 volFlags;
+    afs_int32 volCloneId;
+};
+
+struct partList {              /*used by the backup system */
+    afs_int32 partId[VOLMAXPARTS];
+    afs_int32 partFlags[VOLMAXPARTS];
+};
+
+#define        STDERR  stderr
+#define        STDOUT  stdout
+
+#define ISNAMEVALID(name) (strlen(name) < (VOLSER_OLDMAXVOLNAME - 9))
+
+/* values for flags in struct nvldbEntry */
+#define RW_EXISTS 0x1000
+#define RO_EXISTS 0x2000
+#define BACK_EXISTS 0x4000
+
+/* values for serverFlags in struct nvldbEntry */
+#define NEW_REPSITE 0x01
+#define ITSROVOL    0x02
+#define ITSRWVOL    0x04
+#define ITSBACKVOL  0x08
+#define RO_DONTUSE  0x20
+
+#define VLOP_RESTORE 0x100     /*this is bogus, clashes with VLOP_DUMP */
+#define VLOP_ADDSITE 0x80      /*this is bogus, clashes with VLOP_DELETE */
+#define PARTVALID 0x01
+#define CLONEVALID 0x02
+#define CLONEZAPPED 0x04
+#define IDVALID 0x08
+#define NAMEVALID 0x10
+#define SIZEVALID 0x20
+#define ENTRYVALID 0x40
+#define REUSECLONEID 0x80
+#define        VOK 0x02
+
+/* Values for the UV_RestoreVolume flags parameter */
+/* Also used for UV_CopyVolume and UV_CloneVolume */
+#define RV_FULLRST 0x1
+#define RV_OFFLINE 0x2
+#define RV_RDONLY  0x10000
+#define RV_CPINCR  0x20000
+#define RV_NOVLDB  0x40000
+#define RV_NOCLONE 0x80000
+
+extern afs_uint32 vsu_GetVolumeID(char *astring, struct ubik_client *acstruct, afs_int32 *errp);
+extern int vsu_ExtractName(char rname[], char name[]);
+extern afs_int32 vsu_ClientInit(int noAuthFlag, char *confDir, char *cellName, afs_int32 sauth, struct ubik_client **uclientp, int (*secproc)());
+extern void vsu_SetCrypt(int cryptflag);
+
+#endif /* _VOLSER_ */
diff --git a/src/volser/volser_prototypes.h b/src/volser/volser_prototypes.h
new file mode 100644 (file)
index 0000000..6d2de88
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#ifndef        _VOLSER_PROTOTYPES_H
+#define _VOLSER_PROTOTYPES_H
+
+/* common.c */
+
+/* vsprocs.c */
+struct nvldbentry;
+extern void MapPartIdIntoName(afs_int32 partId, char *partName);
+extern int yesprompt(char *str);
+extern int PrintError(char *msg, afs_int32 errcode);
+extern int UV_SetSecurity(register struct rx_securityClass *as,
+                         afs_int32 aindex);
+extern struct rx_connection *UV_Bind(afs_int32 aserver, afs_int32 port);
+extern void SubEnumerateEntry(struct nvldbentry *entry);
+extern void EnumerateEntry(struct nvldbentry *entry);
+extern int UV_NukeVolume(afs_int32 server, afs_int32 partid, afs_int32 volid);
+extern int UV_PartitionInfo(afs_int32 server, char *pname,
+                           struct diskPartition *partition);
+extern int UV_CreateVolume(afs_int32 aserver, afs_int32 apart, char *aname,
+                          afs_int32 * anewid);
+extern int UV_CreateVolume2(afs_int32 aserver, afs_int32 apart, char *aname,
+                           afs_int32 aquota, afs_int32 aspare1,
+                           afs_int32 aspare2, afs_int32 aspare3,
+                           afs_int32 aspare4, afs_int32 * anewid);
+extern int UV_AddVLDBEntry(afs_int32 aserver, afs_int32 apart, char *aname,
+                          afs_int32 aid);
+extern int UV_DeleteVolume(afs_int32 aserver, afs_int32 apart,
+                          afs_int32 avolid);
+extern void sigint_handler(int x);
+extern int UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver,
+                        afs_int32 afrompart, afs_int32 atoserver,
+                        afs_int32 atopart);
+extern int UV_BackupVolume(afs_int32 aserver, afs_int32 apart,
+                          afs_int32 avolid);
+extern int UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver,
+                           afs_int32 afrompart, int forceflag);
+extern void dump_sig_handler(int x);
+extern int UV_DumpVolume(afs_int32 afromvol, afs_int32 afromserver,
+                        afs_int32 afrompart, afs_int32 fromdate,
+                        afs_int32(*DumpFunction) (), char *rock);
+extern int UV_RestoreVolume(afs_int32 toserver, afs_int32 topart,
+                           afs_int32 tovolid, char tovolname[], int flags,
+                           afs_int32(*WriteData) (), char *rock);
+extern int UV_LockRelease(afs_int32 volid);
+extern int UV_AddSite(afs_int32 server, afs_int32 part, afs_int32 volid);
+extern int UV_RemoveSite(afs_int32 server, afs_int32 part, afs_int32 volid);
+extern int UV_ChangeLocation(afs_int32 server, afs_int32 part,
+                            afs_int32 volid);
+extern int UV_ListPartitions(afs_int32 aserver, struct partList *ptrPartList,
+                            afs_int32 * cntp);
+extern int UV_ZapVolumeClones(afs_int32 aserver, afs_int32 apart,
+                             struct volDescription *volPtr,
+                             afs_int32 arraySize);
+extern int UV_GenerateVolumeClones(afs_int32 aserver, afs_int32 apart,
+                                  struct volDescription *volPtr,
+                                  afs_int32 arraySize);
+extern int UV_ListVolumes(afs_int32 aserver, afs_int32 apart, int all,
+                         struct volintInfo **resultPtr, afs_int32 * size);
+extern int UV_XListVolumes(afs_int32 a_serverID, afs_int32 a_partID,
+                          int a_all, struct volintXInfo **a_resultPP,
+                          afs_int32 * a_numEntsInResultP);
+extern int UV_ListOneVolume(afs_int32 aserver, afs_int32 apart,
+                           afs_int32 volid, struct volintInfo **resultPtr);
+extern int UV_XListOneVolume(afs_int32 a_serverID, afs_int32 a_partID,
+                            afs_int32 a_volID,
+                            struct volintXInfo **a_resultPP);
+extern int sortVolumes(const void *a, const void *b);
+extern int UV_SyncVolume(afs_int32 aserver, afs_int32 apart, char *avolname,
+                        int flags);
+extern int UV_SyncVldb(afs_int32 aserver, afs_int32 apart, int flags,
+                      int force);
+extern afs_int32 VolumeExists(afs_int32 server, afs_int32 partition,
+                             afs_int32 volumeid);
+extern afs_int32 CheckVldbRWBK(struct nvldbentry *entry,
+                              afs_int32 * modified);
+extern int CheckVldbRO(struct nvldbentry *entry, afs_int32 * modified);
+extern afs_int32 CheckVldb(struct nvldbentry *entry, afs_int32 * modified);
+extern int UV_SyncServer(afs_int32 aserver, afs_int32 apart, int flags,
+                        int force);
+extern int UV_RenameVolume(struct nvldbentry *entry, char oldname[],
+                          char newname[]);
+extern int UV_VolserStatus(afs_int32 server, transDebugInfo ** rpntr,
+                          afs_int32 * rcount);
+extern int UV_VolumeZap(afs_int32 server, afs_int32 part, afs_int32 volid);
+extern int UV_SetVolume(afs_int32 server, afs_int32 partition,
+                       afs_int32 volid, afs_int32 transflag,
+                       afs_int32 setflag, int sleeptime);
+extern int UV_SetVolumeInfo(afs_int32 server, afs_int32 partition,
+                           afs_int32 volid, volintInfo * infop);
+extern void MapNetworkToHost(struct nvldbentry *old, struct nvldbentry *new);
+extern void MapHostToNetwork(struct nvldbentry *entry);
+
+#endif
diff --git a/src/volser/volserver.rc b/src/volser/volserver.rc
new file mode 100644 (file)
index 0000000..074216f
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/* Define VERSIONINFO resource */
+
+#define  AFS_VERINFO_FILE_DESCRIPTION "AFS Volume Server"
+#define  AFS_VERINFO_NAME "volserver"
+#define  AFS_VERINFO_FILENAME "volserver.exe"
+
+#include "AFS_component_version_number.h"
+#include "..\config\NTVersioninfo.rc"
diff --git a/src/volser/voltrans.c b/src/volser/voltrans.c
new file mode 100644 (file)
index 0000000..4c28fbe
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/*
+ *  Module:        Voltrans.c
+ *  System:        Volser
+ *  Instituition:          ITC, CMU
+ *  Date:                  December, 88
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/voltrans.c,v 1.10 2003/11/22 02:57:04 shadow Exp $");
+
+#ifdef AFS_NT40_ENV
+#include <afs/afsutil.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <winsock2.h>
+#else
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+#include <dirent.h>
+#include <sys/stat.h>
+#include <afs/afsint.h>
+#include <signal.h>
+#ifdef AFS_PTHREAD_ENV
+#include <assert.h>
+#else /* AFS_PTHREAD_ENV */
+#include <afs/assert.h>
+#endif /* AFS_PTHREAD_ENV */
+#include <afs/prs_fs.h>
+#include <afs/nfs.h>
+#include <lwp.h>
+#include <lock.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/keys.h>
+#include <rx/rx.h>
+#include <ubik.h>
+#include <afs/ihandle.h>
+#ifdef AFS_NT40_ENV
+#include <afs/ntops.h>
+#endif
+#include <afs/vnode.h>
+#include <afs/volume.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#include "volser.h"
+
+/*@printflike@*/ extern void Log(const char *format, ...);
+
+static struct volser_trans *allTrans = 0;
+static afs_int32 transCounter = 1;
+
+/* create a new transaction, returning ptr to same with high ref count */
+struct volser_trans *
+NewTrans(avol, apart)
+     afs_int32 avol;
+     afs_int32 apart;
+{
+    /* set volid, next, partition */
+    register struct volser_trans *tt;
+    struct timeval tp;
+    struct timezone tzp;
+
+    VTRANS_LOCK;
+    /* don't allow the same volume to be attached twice */
+    for (tt = allTrans; tt; tt = tt->next) {
+       if ((tt->volid == avol) && (tt->partition == apart)) {
+           VTRANS_UNLOCK;
+           return (struct volser_trans *)0;    /* volume busy */
+       }
+    }
+    VTRANS_UNLOCK;
+    tt = (struct volser_trans *)malloc(sizeof(struct volser_trans));
+    memset(tt, 0, sizeof(struct volser_trans));
+    tt->volid = avol;
+    tt->partition = apart;
+    tt->refCount = 1;
+    tt->rxCallPtr = (struct rx_call *)0;
+    strcpy(tt->lastProcName, "");
+    gettimeofday(&tp, &tzp);
+    tt->creationTime = tp.tv_sec;
+    tt->time = FT_ApproxTime();
+    VTRANS_LOCK;
+    tt->tid = transCounter++;
+    tt->next = allTrans;
+    allTrans = tt;
+    VTRANS_UNLOCK;
+    return tt;
+}
+
+/* find a trans, again returning with high ref count */
+struct volser_trans *
+FindTrans(atrans)
+     register afs_int32 atrans;
+{
+    register struct volser_trans *tt;
+    VTRANS_LOCK;
+    for (tt = allTrans; tt; tt = tt->next) {
+       if (tt->tid == atrans) {
+           tt->time = FT_ApproxTime();
+           tt->refCount++;
+           VTRANS_UNLOCK;
+           return tt;
+       }
+    }
+    VTRANS_UNLOCK;
+    return (struct volser_trans *)0;
+}
+
+/* delete transaction if refcount == 1, otherwise queue delete for later.  Does implicit TRELE */
+DeleteTrans(atrans)
+     register struct volser_trans *atrans;
+{
+    register struct volser_trans *tt, **lt;
+    afs_int32 error;
+
+    if (atrans->refCount > 1) {
+       /* someone else is using it now */
+       atrans->refCount--;
+       atrans->tflags |= TTDeleted;
+       return 0;
+    }
+
+    /* otherwise we zap it ourselves */
+    VTRANS_LOCK;
+    lt = &allTrans;
+    for (tt = *lt; tt; lt = &tt->next, tt = *lt) {
+       if (tt == atrans) {
+           if (tt->volume)
+               VDetachVolume(&error, tt->volume);
+           tt->volume = NULL;
+           *lt = tt->next;
+           free(tt);
+           VTRANS_UNLOCK;
+           return 0;
+       }
+    }
+    VTRANS_UNLOCK;
+    return -1;                 /* failed to find the transaction in the generic list */
+}
+
+/* THOLD is a macro defined in volser.h */
+
+/* put a transaction back */
+TRELE(at)
+     register struct volser_trans *at;
+{
+    if (at->refCount == 0) {
+       Log("TRELE: bad refcount\n");
+       return VOLSERTRELE_ERROR;
+    }
+
+    at->time = FT_ApproxTime();        /* we're still using it */
+    if (at->refCount == 1 && (at->tflags & TTDeleted)) {
+       DeleteTrans(at);
+       return 0;
+    }
+    /* otherwise simply drop refcount */
+    at->refCount--;
+    return 0;
+}
+
+/* look for old transactions and delete them */
+#define        OLDTRANSTIME        600 /* seconds */
+#define        OLDTRANSWARN        300 /* seconds */
+static int GCDeletes = 0;
+GCTrans()
+{
+    register struct volser_trans *tt, *nt;
+    afs_int32 now;
+
+    now = FT_ApproxTime();
+
+    VTRANS_LOCK;
+    for (tt = allTrans; tt; tt = nt) {
+       nt = tt->next;          /* remember in case we zap it */
+       if (tt->time + OLDTRANSWARN < now) {
+           Log("trans %u on volume %u %s than %d seconds\n", tt->tid,
+               tt->volid,
+               ((tt->refCount > 0) ? "is older" : "has been idle for more"),
+               (((now - tt->time) / GCWAKEUP) * GCWAKEUP));
+       }
+       if (tt->refCount > 0)
+           continue;
+       if (tt->time + OLDTRANSTIME < now) {
+           Log("trans %u on volume %u has timed out\n", tt->tid, tt->volid);
+           tt->refCount++;     /* we're using it now */
+           DeleteTrans(tt);    /* drops refCount or deletes it */
+           GCDeletes++;
+       }
+    }
+    VTRANS_UNLOCK;
+    return 0;
+}
+
+/*return the head of the transaction list */
+struct volser_trans *
+TransList()
+{
+    return (allTrans);
+}
diff --git a/src/volser/vos.c b/src/volser/vos.c
new file mode 100644 (file)
index 0000000..23689f0
--- /dev/null
@@ -0,0 +1,5593 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/vos.c,v 1.38 2004/04/08 22:20:39 jaltman Exp $");
+
+#include <sys/types.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <io.h>
+#include <winsock2.h>
+#else
+#include <sys/time.h>
+#include <sys/file.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/stat.h>
+#ifdef AFS_AIX_ENV
+#include <sys/statfs.h>
+#endif
+#include <errno.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#include <lock.h>
+#include <afs/stds.h>
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <rx/rx_globals.h>
+#include <afs/nfs.h>
+#include <afs/vlserver.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/keys.h>
+#include <afs/afsutil.h>
+#include <ubik.h>
+#include <afs/afsint.h>
+#include <afs/cmd.h>
+#include <afs/usd.h>
+#include <rx/rxkad.h>
+#include "volser.h"
+#include "volint.h"
+#include "lockdata.h"
+#ifdef AFS_AIX32_ENV
+#include <signal.h>
+#endif
+#include "volser_prototypes.h"
+
+#ifdef HAVE_POSIX_REGEX
+#include <regex.h>
+#endif
+
+struct tqElem {
+    afs_int32 volid;
+    struct tqElem *next;
+};
+
+struct tqHead {
+    afs_int32 count;
+    struct tqElem *next;
+};
+
+#define COMMONPARMS     cmd_Seek(ts, 12);\
+cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");\
+cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL, "don't authenticate");\
+cmd_AddParm(ts, "-localauth",CMD_FLAG,CMD_OPTIONAL,"use server tickets");\
+cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");\
+cmd_AddParm(ts, "-encrypt", CMD_FLAG, CMD_OPTIONAL, "encrypt commands");\
+
+#define ERROR_EXIT(code) {error=(code); goto error_exit;}
+
+extern int verbose;
+int rxInitDone = 0;
+struct rx_connection *tconn;
+afs_int32 tserver;
+extern struct ubik_client *cstruct;
+const char *confdir;
+
+static struct tqHead busyHead, notokHead;
+
+static void
+qInit(struct tqHead *ahead)
+{
+    memset((char *)ahead, 0, sizeof(struct tqHead));
+    return;
+}
+
+
+static void
+qPut(struct tqHead *ahead, afs_int32 volid)
+{
+    struct tqElem *elem;
+
+    elem = (struct tqElem *)malloc(sizeof(struct tqElem));
+    elem->next = ahead->next;
+    elem->volid = volid;
+    ahead->next = elem;
+    ahead->count++;
+    return;
+}
+
+static void
+qGet(struct tqHead *ahead, afs_int32 *volid)
+{
+    struct tqElem *tmp;
+
+    if (ahead->count <= 0)
+       return;
+    *volid = ahead->next->volid;
+    tmp = ahead->next;
+    ahead->next = tmp->next;
+    ahead->count--;
+    free(tmp);
+    return;
+}
+
+/* returns 1 if <filename> exists else 0 */
+static int
+FileExists(char *filename)
+{
+    usd_handle_t ufd;
+    int code;
+    afs_hyper_t size;
+
+    code = usd_Open(filename, USD_OPEN_RDONLY, 0, &ufd);
+    if (code) {
+       return 0;
+    }
+    code = USD_IOCTL(ufd, USD_IOCTL_GETSIZE, &size);
+    USD_CLOSE(ufd);
+    if (code) {
+       return 0;
+    }
+    return 1;
+}
+
+/* returns 1 if <name> doesnot end in .readonly or .backup, else 0 */
+static int
+VolNameOK(char *name)
+{
+    int total;
+
+
+    total = strlen(name);
+    if (!strcmp(&name[total - 9], ".readonly")) {
+       return 0;
+    } else if (!strcmp(&name[total - 7], ".backup")) {
+       return 0;
+    } else {
+       return 1;
+    }
+}
+
+/* return 1 if name is a number else 0 */
+static int
+IsNumeric(char *name)
+{
+    int result, len, i;
+    char *ptr;
+
+    result = 1;
+    ptr = name;
+    len = strlen(name);
+    for (i = 0; i < len; i++) {
+       if (*ptr < '0' || *ptr > '9') {
+           result = 0;
+           break;
+       }
+       ptr++;
+
+    }
+    return result;
+}
+
+
+/*
+ * Parse a server name/address and return the address in HOST BYTE order
+ */
+afs_int32
+GetServer(char *aname)
+{
+    register struct hostent *th;
+    afs_int32 addr;
+    int b1, b2, b3, b4;
+    register afs_int32 code;
+    char hostname[MAXHOSTCHARS];
+
+    code = sscanf(aname, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);
+    if (code == 4) {
+       addr = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
+       addr = ntohl(addr);     /* convert to host order */
+    } else {
+       th = gethostbyname(aname);
+       if (!th)
+           return 0;
+       memcpy(&addr, th->h_addr, sizeof(addr));
+    }
+
+    if (addr == htonl(0x7f000001)) {   /* local host */
+       code = gethostname(hostname, MAXHOSTCHARS);
+       if (code)
+           return 0;
+       th = gethostbyname(hostname);   /* returns host byte order */
+       if (!th)
+           return 0;
+       memcpy(&addr, th->h_addr, sizeof(addr));
+    }
+
+    return (addr);
+}
+
+afs_int32
+GetVolumeType(char *aname)
+{
+
+    if (!strcmp(aname, "ro"))
+       return (ROVOL);
+    else if (!strcmp(aname, "rw"))
+       return (RWVOL);
+    else if (!strcmp(aname, "bk"))
+       return (BACKVOL);
+    else
+       return (-1);
+}
+
+int
+IsPartValid(afs_int32 partId, afs_int32 server, afs_int32 *code)
+{
+    struct partList dummyPartList;
+    int i, success, cnt;
+
+    success = 0;
+    *code = 0;
+
+    *code = UV_ListPartitions(server, &dummyPartList, &cnt);
+    if (*code)
+       return success;
+    for (i = 0; i < cnt; i++) {
+       if (dummyPartList.partFlags[i] & PARTVALID)
+           if (dummyPartList.partId[i] == partId)
+               success = 1;
+    }
+    return success;
+}
+
+
+
+ /*sends the contents of file associated with <fd> and <blksize>  to Rx Stream 
+  * associated  with <call> */
+int 
+SendFile(usd_handle_t ufd, register struct rx_call *call, long blksize)
+{
+    char *buffer = (char *)0;
+    afs_int32 error = 0;
+    int done = 0;
+    afs_uint32 nbytes;
+
+    buffer = (char *)malloc(blksize);
+    if (!buffer) {
+       fprintf(STDERR, "malloc failed\n");
+       return -1;
+    }
+
+    while (!error && !done) {
+#ifndef AFS_NT40_ENV           /* NT csn't select on non-socket fd's */
+       fd_set in;
+       FD_ZERO(&in);
+       FD_SET((int)(ufd->handle), &in);
+       /* don't timeout if read blocks */
+       IOMGR_Select(((int)(ufd->handle)) + 1, &in, 0, 0, 0);
+#endif
+       error = USD_READ(ufd, buffer, blksize, &nbytes);
+       if (error) {
+           fprintf(STDERR, "File system read failed\n");
+           break;
+       }
+       if (nbytes == 0) {
+           done = 1;
+           break;
+       }
+       if (rx_Write(call, buffer, nbytes) != nbytes) {
+           error = -1;
+           break;
+       }
+    }
+    if (buffer)
+       free(buffer);
+    return error;
+}
+
+/* function invoked by UV_RestoreVolume, reads the data from rx_trx_stream and
+ * writes it out to the volume. */
+afs_int32
+WriteData(struct rx_call *call, char *rock)
+{
+    char *filename;
+    usd_handle_t ufd;
+    long blksize;
+    afs_int32 error, code;
+    int ufdIsOpen = 0;
+
+    error = 0;
+
+    filename = rock;
+    if (!filename || !*filename) {
+       usd_StandardInput(&ufd);
+       blksize = 4096;
+       ufdIsOpen = 1;
+    } else {
+       code = usd_Open(filename, USD_OPEN_RDONLY, 0, &ufd);
+       if (code == 0) {
+           ufdIsOpen = 1;
+           code = USD_IOCTL(ufd, USD_IOCTL_GETBLKSIZE, &blksize);
+       }
+       if (code) {
+           fprintf(STDERR, "Could not access file '%s'\n", filename);
+           error = VOLSERBADOP;
+           goto wfail;
+       }
+    }
+    code = SendFile(ufd, call, blksize);
+    if (code) {
+       error = code;
+       goto wfail;
+    }
+  wfail:
+    if (ufdIsOpen) {
+       code = USD_CLOSE(ufd);
+       if (code) {
+           fprintf(STDERR, "Could not close dump file %s\n",
+                   (filename && *filename) ? filename : "STDOUT");
+           if (!error)
+               error = code;
+       }
+    }
+    return error;
+}
+
+/* Receive data from <call> stream into file associated
+ * with <fd> <blksize>
+ */
+int
+ReceiveFile(usd_handle_t ufd, struct rx_call *call, long blksize)
+{
+    char *buffer = NULL;
+    afs_int32 bytesread;
+    afs_uint32 bytesleft, w;
+    afs_int32 error = 0;
+
+    buffer = (char *)malloc(blksize);
+    if (!buffer) {
+       fprintf(STDERR, "memory allocation failed\n");
+       ERROR_EXIT(-1);
+    }
+
+    while ((bytesread = rx_Read(call, buffer, blksize)) > 0) {
+       for (bytesleft = bytesread; bytesleft; bytesleft -= w) {
+#ifndef AFS_NT40_ENV           /* NT csn't select on non-socket fd's */
+           fd_set out;
+           FD_ZERO(&out);
+           FD_SET((int)(ufd->handle), &out);
+           /* don't timeout if write blocks */
+           IOMGR_Select(((int)(ufd->handle)) + 1, 0, &out, 0, 0);
+#endif
+           error =
+               USD_WRITE(ufd, &buffer[bytesread - bytesleft], bytesleft, &w);
+           if (error) {
+               fprintf(STDERR, "File system write failed\n");
+               ERROR_EXIT(-1);
+           }
+       }
+    }
+
+  error_exit:
+    if (buffer)
+       free(buffer);
+    return (error);
+}
+
+afs_int32
+DumpFunction(struct rx_call *call, char *filename)
+{
+    usd_handle_t ufd;          /* default is to stdout */
+    afs_int32 error = 0, code;
+    afs_hyper_t size;
+    long blksize;
+    int ufdIsOpen = 0;
+
+    /* Open the output file */
+    if (!filename || !*filename) {
+       usd_StandardOutput(&ufd);
+       blksize = 4096;
+       ufdIsOpen = 1;
+    } else {
+       code =
+           usd_Open(filename, USD_OPEN_CREATE | USD_OPEN_RDWR, 0666, &ufd);
+       if (code == 0) {
+           ufdIsOpen = 1;
+           hzero(size);
+           code = USD_IOCTL(ufd, USD_IOCTL_SETSIZE, &size);
+       }
+       if (code == 0) {
+           code = USD_IOCTL(ufd, USD_IOCTL_GETBLKSIZE, &blksize);
+       }
+       if (code) {
+           fprintf(STDERR, "Could not create file '%s'\n", filename);
+           ERROR_EXIT(VOLSERBADOP);
+       }
+    }
+
+    code = ReceiveFile(ufd, call, blksize);
+    if (code)
+       ERROR_EXIT(code);
+
+  error_exit:
+    /* Close the output file */
+    if (ufdIsOpen) {
+       code = USD_CLOSE(ufd);
+       if (code) {
+           fprintf(STDERR, "Could not close dump file %s\n",
+                   (filename && *filename) ? filename : "STDIN");
+           if (!error)
+               error = code;
+       }
+    }
+
+    return (error);
+}
+
+static void
+DisplayFormat(pntr, server, part, totalOK, totalNotOK, totalBusy, fast,
+             longlist, disp)
+     volintInfo *pntr;
+     afs_int32 server, part;
+     int *totalOK, *totalNotOK, *totalBusy;
+     int fast, longlist, disp;
+{
+    char pname[10];
+
+    if (fast) {
+       fprintf(STDOUT, "%-10lu\n", (unsigned long)pntr->volid);
+    } else if (longlist) {
+       if (pntr->status == VOK) {
+           fprintf(STDOUT, "%-32s ", pntr->name);
+           fprintf(STDOUT, "%10lu ", (unsigned long)pntr->volid);
+           if (pntr->type == 0)
+               fprintf(STDOUT, "RW ");
+           if (pntr->type == 1)
+               fprintf(STDOUT, "RO ");
+           if (pntr->type == 2)
+               fprintf(STDOUT, "BK ");
+           fprintf(STDOUT, "%10d K  ", pntr->size);
+           if (pntr->inUse == 1) {
+               fprintf(STDOUT, "On-line");
+               *totalOK += 1;
+           } else {
+               fprintf(STDOUT, "Off-line");
+               *totalNotOK++;
+           }
+           if (pntr->needsSalvaged == 1)
+               fprintf(STDOUT, "**needs salvage**");
+           fprintf(STDOUT, "\n");
+           MapPartIdIntoName(part, pname);
+           fprintf(STDOUT, "    %s %s \n", hostutil_GetNameByINet(server),
+                   pname);
+           fprintf(STDOUT, "    RWrite %10lu ROnly %10lu Backup %10lu \n",
+                   (unsigned long)pntr->parentID,
+                   (unsigned long)pntr->cloneID,
+                   (unsigned long)pntr->backupID);
+           fprintf(STDOUT, "    MaxQuota %10d K \n", pntr->maxquota);
+           fprintf(STDOUT, "    Creation    %s",
+                   ctime((time_t *) & pntr->creationDate));
+#ifdef FULL_LISTVOL_SWITCH
+           fprintf(STDOUT, "    Copy        %s",
+                   ctime((time_t *) & pntr->copyDate));
+           if (!pntr->backupDate)
+               fprintf(STDOUT, "    Backup      Never\n");
+           else
+               fprintf(STDOUT, "    Backup      %s",
+                       ctime((time_t *) & pntr->backupDate));
+           if (pntr->accessDate)
+               fprintf(STDOUT, "    Last Access %s",
+                       ctime((time_t *) & pntr->accessDate));
+#endif
+           if (pntr->updateDate < pntr->creationDate)
+               fprintf(STDOUT, "    Last Update %s",
+                       ctime((time_t *) & pntr->creationDate));
+           else
+               fprintf(STDOUT, "    Last Update %s",
+                       ctime((time_t *) & pntr->updateDate));
+           fprintf(STDOUT,
+                   "    %d accesses in the past day (i.e., vnode references)\n",
+                   pntr->dayUse);
+       } else if (pntr->status == VBUSY) {
+           *totalBusy += 1;
+           qPut(&busyHead, pntr->volid);
+           if (disp)
+               fprintf(STDOUT, "**** Volume %lu is busy ****\n",
+                       (unsigned long)pntr->volid);
+       } else {
+           *totalNotOK += 1;
+           qPut(&notokHead, pntr->volid);
+           if (disp)
+               fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
+                       (unsigned long)pntr->volid);
+       }
+       fprintf(STDOUT, "\n");
+    } else {                   /* default listing */
+       if (pntr->status == VOK) {
+           fprintf(STDOUT, "%-32s ", pntr->name);
+           fprintf(STDOUT, "%10lu ", (unsigned long)pntr->volid);
+           if (pntr->type == 0)
+               fprintf(STDOUT, "RW ");
+           if (pntr->type == 1)
+               fprintf(STDOUT, "RO ");
+           if (pntr->type == 2)
+               fprintf(STDOUT, "BK ");
+           fprintf(STDOUT, "%10d K ", pntr->size);
+           if (pntr->inUse == 1) {
+               fprintf(STDOUT, "On-line");
+               *totalOK += 1;
+           } else {
+               fprintf(STDOUT, "Off-line");
+               *totalNotOK += 1;
+           }
+           if (pntr->needsSalvaged == 1)
+               fprintf(STDOUT, "**needs salvage**");
+           fprintf(STDOUT, "\n");
+       } else if (pntr->status == VBUSY) {
+           *totalBusy += 1;
+           qPut(&busyHead, pntr->volid);
+           if (disp)
+               fprintf(STDOUT, "**** Volume %lu is busy ****\n",
+                       (unsigned long)pntr->volid);
+       } else {
+           *totalNotOK += 1;
+           qPut(&notokHead, pntr->volid);
+           if (disp)
+               fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
+                       (unsigned long)pntr->volid);
+       }
+    }
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE XDisplayFormat
+ *
+ * Description:
+ *     Display the contents of one extended volume info structure.
+ *
+ * Arguments:
+ *     a_xInfoP        : Ptr to extended volume info struct to print.
+ *     a_servID        : Server ID to print.
+ *     a_partID        : Partition ID to print.
+ *     a_totalOKP      : Ptr to total-OK counter.
+ *     a_totalNotOKP   : Ptr to total-screwed counter.
+ *     a_totalBusyP    : Ptr to total-busy counter.
+ *     a_fast          : Fast listing?
+ *     a_int32         : Int32 listing?
+ *     a_showProblems  : Show volume problems?
+ *
+ * Returns:
+ *     Nothing.
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+static void
+XDisplayFormat(a_xInfoP, a_servID, a_partID, a_totalOKP, a_totalNotOKP,
+              a_totalBusyP, a_fast, a_int32, a_showProblems)
+     volintXInfo *a_xInfoP;
+     afs_int32 a_servID;
+     afs_int32 a_partID;
+     int *a_totalOKP;
+     int *a_totalNotOKP;
+     int *a_totalBusyP;
+     int a_fast;
+     int a_int32;
+     int a_showProblems;
+
+{                              /*XDisplayFormat */
+
+    char pname[10];
+
+    if (a_fast) {
+       /*
+        * Short & sweet.
+        */
+       fprintf(STDOUT, "%-10lu\n", (unsigned long)a_xInfoP->volid);
+    } else if (a_int32) {
+       /*
+        * Fully-detailed listing.
+        */
+       if (a_xInfoP->status == VOK) {
+           /*
+            * Volume's status is OK - all the fields are valid.
+            */
+           fprintf(STDOUT, "%-32s ", a_xInfoP->name);
+           fprintf(STDOUT, "%10lu ", (unsigned long)a_xInfoP->volid);
+           if (a_xInfoP->type == 0)
+               fprintf(STDOUT, "RW ");
+           if (a_xInfoP->type == 1)
+               fprintf(STDOUT, "RO ");
+           if (a_xInfoP->type == 2)
+               fprintf(STDOUT, "BK ");
+           fprintf(STDOUT, "%10d K used ", a_xInfoP->size);
+           fprintf(STDOUT, "%d files ", a_xInfoP->filecount);
+           if (a_xInfoP->inUse == 1) {
+               fprintf(STDOUT, "On-line");
+               (*a_totalOKP)++;
+           } else {
+               fprintf(STDOUT, "Off-line");
+               (*a_totalNotOKP)++;
+           }
+           fprintf(STDOUT, "\n");
+           MapPartIdIntoName(a_partID, pname);
+           fprintf(STDOUT, "    %s %s \n", hostutil_GetNameByINet(a_servID),
+                   pname);
+           fprintf(STDOUT, "    RWrite %10lu ROnly %10lu Backup %10lu \n",
+                   (unsigned long)a_xInfoP->parentID,
+                   (unsigned long)a_xInfoP->cloneID,
+                   (unsigned long)a_xInfoP->backupID);
+           fprintf(STDOUT, "    MaxQuota %10d K \n", a_xInfoP->maxquota);
+           fprintf(STDOUT, "    Creation    %s",
+                   ctime((time_t *) & a_xInfoP->creationDate));
+#ifdef FULL_LISTVOL_SWITCH
+           fprintf(STDOUT, "    Copy        %s",
+                   ctime((time_t *) & a_xInfoP->copyDate));
+           if (!a_xInfoP->backupDate)
+               fprintf(STDOUT, "    Backup      Never\n");
+           else
+               fprintf(STDOUT, "    Backup      %s",
+                       ctime((time_t *) & a_xInfoP->backupDate));
+           if (a_xInfoP->accessDate)
+               fprintf(STDOUT, "    Last Access %s",
+                       ctime((time_t *) & a_xInfoP->accessDate));
+#endif
+           if (a_xInfoP->updateDate < a_xInfoP->creationDate)
+               fprintf(STDOUT, "    Last Update %s",
+                       ctime((time_t *) & a_xInfoP->creationDate));
+           else
+               fprintf(STDOUT, "    Last Update %s",
+                       ctime((time_t *) & a_xInfoP->updateDate));
+           fprintf(STDOUT,
+                   "    %d accesses in the past day (i.e., vnode references)\n",
+                   a_xInfoP->dayUse);
+
+           /*
+            * Print all the read/write and authorship stats.
+            */
+           fprintf(STDOUT, "\n                      Raw Read/Write Stats\n");
+           fprintf(STDOUT,
+                   "          |-------------------------------------------|\n");
+           fprintf(STDOUT,
+                   "          |    Same Network     |    Diff Network     |\n");
+           fprintf(STDOUT,
+                   "          |----------|----------|----------|----------|\n");
+           fprintf(STDOUT,
+                   "          |  Total   |   Auth   |   Total  |   Auth   |\n");
+           fprintf(STDOUT,
+                   "          |----------|----------|----------|----------|\n");
+           fprintf(STDOUT, "Reads     | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET],
+                   a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET_AUTH],
+                   a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET],
+                   a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET_AUTH]);
+           fprintf(STDOUT, "Writes    | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET],
+                   a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET_AUTH],
+                   a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET],
+                   a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET_AUTH]);
+           fprintf(STDOUT,
+                   "          |-------------------------------------------|\n\n");
+
+           fprintf(STDOUT,
+                   "                   Writes Affecting Authorship\n");
+           fprintf(STDOUT,
+                   "          |-------------------------------------------|\n");
+           fprintf(STDOUT,
+                   "          |   File Authorship   | Directory Authorship|\n");
+           fprintf(STDOUT,
+                   "          |----------|----------|----------|----------|\n");
+           fprintf(STDOUT,
+                   "          |   Same   |   Diff   |    Same  |   Diff   |\n");
+           fprintf(STDOUT,
+                   "          |----------|----------|----------|----------|\n");
+           fprintf(STDOUT, "0-60 sec  | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_0],
+                   a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_0],
+                   a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_0],
+                   a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_0]);
+           fprintf(STDOUT, "1-10 min  | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_1],
+                   a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_1],
+                   a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_1],
+                   a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_1]);
+           fprintf(STDOUT, "10min-1hr | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_2],
+                   a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_2],
+                   a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_2],
+                   a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_2]);
+           fprintf(STDOUT, "1hr-1day  | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_3],
+                   a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_3],
+                   a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_3],
+                   a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_3]);
+           fprintf(STDOUT, "1day-1wk  | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_4],
+                   a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_4],
+                   a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_4],
+                   a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_4]);
+           fprintf(STDOUT, "> 1wk     | %8d | %8d | %8d | %8d |\n",
+                   a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_5],
+                   a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_5],
+                   a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_5],
+                   a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_5]);
+           fprintf(STDOUT,
+                   "          |-------------------------------------------|\n");
+       } /*Volume status OK */
+       else if (a_xInfoP->status == VBUSY) {
+           (*a_totalBusyP)++;
+           qPut(&busyHead, a_xInfoP->volid);
+           if (a_showProblems)
+               fprintf(STDOUT, "**** Volume %lu is busy ****\n",
+                       (unsigned long)a_xInfoP->volid);
+       } /*Busy volume */
+       else {
+           (*a_totalNotOKP)++;
+           qPut(&notokHead, a_xInfoP->volid);
+           if (a_showProblems)
+               fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
+                       (unsigned long)a_xInfoP->volid);
+       }                       /*Screwed volume */
+       fprintf(STDOUT, "\n");
+    } /*Long listing */
+    else {
+       /*
+        * Default listing.
+        */
+       if (a_xInfoP->status == VOK) {
+           fprintf(STDOUT, "%-32s ", a_xInfoP->name);
+           fprintf(STDOUT, "%10lu ", (unsigned long)a_xInfoP->volid);
+           if (a_xInfoP->type == 0)
+               fprintf(STDOUT, "RW ");
+           if (a_xInfoP->type == 1)
+               fprintf(STDOUT, "RO ");
+           if (a_xInfoP->type == 2)
+               fprintf(STDOUT, "BK ");
+           fprintf(STDOUT, "%10d K ", a_xInfoP->size);
+           if (a_xInfoP->inUse == 1) {
+               fprintf(STDOUT, "On-line");
+               (*a_totalOKP)++;
+           } else {
+               fprintf(STDOUT, "Off-line");
+               (*a_totalNotOKP)++;
+           }
+           fprintf(STDOUT, "\n");
+       } /*Volume OK */
+       else if (a_xInfoP->status == VBUSY) {
+           (*a_totalBusyP)++;
+           qPut(&busyHead, a_xInfoP->volid);
+           if (a_showProblems)
+               fprintf(STDOUT, "**** Volume %lu is busy ****\n",
+                       (unsigned long)a_xInfoP->volid);
+       } /*Busy volume */
+       else {
+           (*a_totalNotOKP)++;
+           qPut(&notokHead, a_xInfoP->volid);
+           if (a_showProblems)
+               fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
+                       (unsigned long)a_xInfoP->volid);
+       }                       /*Screwed volume */
+    }                          /*Default listing */
+}                              /*XDisplayFormat */
+
+#ifdef FULL_LISTVOL_SWITCH
+static void
+DisplayFormat2(server, partition, pntr)
+     long server, partition;
+     volintInfo *pntr;
+{
+    static long server_cache = -1, partition_cache = -1;
+    static char hostname[256], address[32], pname[16];
+
+    if (server != server_cache) {
+       struct in_addr s;
+
+       s.s_addr = server;
+       strcpy(hostname, hostutil_GetNameByINet(server));
+       strcpy(address, inet_ntoa(s));
+       server_cache = server;
+    }
+    if (partition != partition_cache) {
+       MapPartIdIntoName(partition, pname);
+       partition_cache = partition;
+    } else {
+        pname[0] = '\0';
+    }
+    fprintf(STDOUT, "name\t\t%s\n", pntr->name);
+    fprintf(STDOUT, "id\t\t%lu\n", pntr->volid);
+    fprintf(STDOUT, "serv\t\t%s\t%s\n", address, hostname);
+    fprintf(STDOUT, "part\t\t%s\n", pname);
+    switch (pntr->status) {
+    case VOK:
+       fprintf(STDOUT, "status\t\tOK\n");
+       break;
+    case VBUSY:
+       fprintf(STDOUT, "status\t\tBUSY\n");
+       return;
+    default:
+       fprintf(STDOUT, "status\t\tUNATTACHABLE\n");
+       return;
+    }
+    fprintf(STDOUT, "backupID\t%lu\n", pntr->backupID);
+    fprintf(STDOUT, "parentID\t%lu\n", pntr->parentID);
+    fprintf(STDOUT, "cloneID\t\t%lu\n", pntr->cloneID);
+    fprintf(STDOUT, "inUse\t\t%s\n", pntr->inUse ? "Y" : "N");
+    fprintf(STDOUT, "needsSalvaged\t%s\n", pntr->needsSalvaged ? "Y" : "N");
+    /* 0xD3 is from afs/volume.h since I had trouble including the file */
+    fprintf(STDOUT, "destroyMe\t%s\n", pntr->destroyMe == 0xD3 ? "Y" : "N");
+    switch (pntr->type) {
+    case 0:
+       fprintf(STDOUT, "type\t\tRW\n");
+       break;
+    case 1:
+       fprintf(STDOUT, "type\t\tRO\n");
+       break;
+    case 2:
+       fprintf(STDOUT, "type\t\tBK\n");
+       break;
+    default:
+       fprintf(STDOUT, "type\t\t?\n");
+       break;
+    }
+    fprintf(STDOUT, "creationDate\t%-9lu\t%s", pntr->creationDate,
+           ctime(&pntr->creationDate));
+    fprintf(STDOUT, "accessDate\t%-9lu\t%s", pntr->accessDate,
+           ctime(&pntr->accessDate));
+    fprintf(STDOUT, "updateDate\t%-9lu\t%s", pntr->updateDate,
+           ctime(&pntr->updateDate));
+    fprintf(STDOUT, "backupDate\t%-9lu\t%s", pntr->backupDate,
+           ctime(&pntr->backupDate));
+    fprintf(STDOUT, "copyDate\t%-9lu\t%s", pntr->copyDate,
+           ctime(&pntr->copyDate));
+    fprintf(STDOUT, "flags\t\t%#lx\t(Optional)\n", pntr->flags);
+    fprintf(STDOUT, "diskused\t%u\n", pntr->size);
+    fprintf(STDOUT, "maxquota\t%u\n", pntr->maxquota);
+    fprintf(STDOUT, "minquota\t%lu\t(Optional)\n", pntr->spare0);
+    fprintf(STDOUT, "filecount\t%u\n", pntr->filecount);
+    fprintf(STDOUT, "dayUse\t\t%u\n", pntr->dayUse);
+    fprintf(STDOUT, "weekUse\t\t%lu\t(Optional)\n", pntr->spare1);
+    fprintf(STDOUT, "spare2\t\t%lu\t(Optional)\n", pntr->spare2);
+    fprintf(STDOUT, "spare3\t\t%lu\t(Optional)\n", pntr->spare3);
+    return;
+}
+
+static void
+DisplayVolumes2(server, partition, pntr, count)
+     volintInfo *pntr;
+     long server, partition, count;
+{
+    long i;
+
+    for (i = 0; i < count; i++) {
+       fprintf(STDOUT, "BEGIN_OF_ENTRY\n");
+       DisplayFormat2(server, partition, pntr);
+       fprintf(STDOUT, "END_OF_ENTRY\n\n");
+       pntr++;
+    }
+    return;
+}
+#endif /* FULL_LISTVOL_SWITCH */
+
+static void
+DisplayVolumes(server, part, pntr, count, longlist, fast, quiet)
+     afs_int32 server, part;
+     volintInfo *pntr;
+     afs_int32 count, longlist, fast;
+     int quiet;
+{
+    int totalOK, totalNotOK, totalBusy, i;
+    afs_int32 volid;
+
+    totalOK = 0;
+    totalNotOK = 0;
+    totalBusy = 0;
+    qInit(&busyHead);
+    qInit(&notokHead);
+    for (i = 0; i < count; i++) {
+       DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy,
+                     fast, longlist, 0);
+       pntr++;
+    }
+    if (totalBusy) {
+       while (busyHead.count) {
+           qGet(&busyHead, &volid);
+           fprintf(STDOUT, "**** Volume %lu is busy ****\n",
+                   (unsigned long)volid);
+       }
+    }
+    if (totalNotOK) {
+       while (notokHead.count) {
+           qGet(&notokHead, &volid);
+           fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
+                   (unsigned long)volid);
+       }
+    }
+    if (!quiet) {
+       fprintf(STDOUT, "\n");
+       if (!fast) {
+           fprintf(STDOUT,
+                   "Total volumes onLine %d ; Total volumes offLine %d ; Total busy %d\n\n",
+                   totalOK, totalNotOK, totalBusy);
+       }
+    }
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE XDisplayVolumes
+ *
+ * Description:
+ *     Display extended volume information.
+ *
+ * Arguments:
+ *     a_servID : Pointer to the Rx call we're performing.
+ *     a_partID : Partition for which we want the extended list.
+ *     a_xInfoP : Ptr to extended volume info.
+ *     a_count  : Number of volume records contained above.
+ *     a_int32   : Int32 listing generated?
+ *     a_fast   : Fast listing generated?
+ *     a_quiet  : Quiet listing generated?
+ *
+ * Returns:
+ *     Nothing.
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+static void
+XDisplayVolumes(a_servID, a_partID, a_xInfoP, a_count, a_int32, a_fast,
+               a_quiet)
+     afs_int32 a_servID;
+     afs_int32 a_partID;
+     volintXInfo *a_xInfoP;
+     afs_int32 a_count;
+     afs_int32 a_int32;
+     afs_int32 a_fast;
+     int a_quiet;
+
+{                              /*XDisplayVolumes */
+
+    int totalOK;               /*Total OK volumes */
+    int totalNotOK;            /*Total screwed volumes */
+    int totalBusy;             /*Total busy volumes */
+    int i;                     /*Loop variable */
+    afs_int32 volid;           /*Current volume ID */
+
+    /*
+     * Initialize counters and (global!!) queues.
+     */
+    totalOK = 0;
+    totalNotOK = 0;
+    totalBusy = 0;
+    qInit(&busyHead);
+    qInit(&notokHead);
+
+    /*
+     * Display each volume in the list.
+     */
+    for (i = 0; i < a_count; i++) {
+       XDisplayFormat(a_xInfoP, a_servID, a_partID, &totalOK, &totalNotOK,
+                      &totalBusy, a_fast, a_int32, 0);
+       a_xInfoP++;
+    }
+
+    /*
+     * If any volumes were found to be busy or screwed, display them.
+     */
+    if (totalBusy) {
+       while (busyHead.count) {
+           qGet(&busyHead, &volid);
+           fprintf(STDOUT, "**** Volume %lu is busy ****\n",
+                   (unsigned long)volid);
+       }
+    }
+    if (totalNotOK) {
+       while (notokHead.count) {
+           qGet(&notokHead, &volid);
+           fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
+                   (unsigned long)volid);
+       }
+    }
+
+    if (!a_quiet) {
+       fprintf(STDOUT, "\n");
+       if (!a_fast) {
+           fprintf(STDOUT,
+                   "Total volumes: %d on-line, %d off-line, %d  busyd\n\n",
+                   totalOK, totalNotOK, totalBusy);
+       }
+    }
+
+}                              /*XDisplayVolumes */
+
+/* set <server> and <part> to the correct values depending on 
+ * <voltype> and <entry> */
+static void
+GetServerAndPart(entry, voltype, server, part, previdx)
+     struct nvldbentry *entry;
+     afs_int32 *server, *part;
+     int voltype;
+     int *previdx;
+{
+    int i, istart, vtype;
+
+    *server = -1;
+    *part = -1;
+
+    /* Doesn't check for non-existance of backup volume */
+    if ((voltype == RWVOL) || (voltype == BACKVOL)) {
+       vtype = ITSRWVOL;
+       istart = 0;             /* seach the entire entry */
+    } else {
+       vtype = ITSROVOL;
+       /* Seach from beginning of entry or pick up where we left off */
+       istart = ((*previdx < 0) ? 0 : *previdx + 1);
+    }
+
+    for (i = istart; i < entry->nServers; i++) {
+       if (entry->serverFlags[i] & vtype) {
+           *server = entry->serverNumber[i];
+           *part = entry->serverPartition[i];
+           *previdx = i;
+           return;
+       }
+    }
+
+    /* Didn't find any, return -1 */
+    *previdx = -1;
+    return;
+}
+
+static void
+PostVolumeStats(entry)
+     struct nvldbentry *entry;
+{
+    SubEnumerateEntry(entry);
+    /* Check for VLOP_ALLOPERS */
+    if (entry->flags & VLOP_ALLOPERS)
+       fprintf(STDOUT, "    Volume is currently LOCKED  \n");
+    return;
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE XVolumeStats
+ *
+ * Description:
+ *     Display extended volume information.
+ *
+ * Arguments:
+ *     a_xInfoP  : Ptr to extended volume info.
+ *     a_entryP  : Ptr to the volume's VLDB entry.
+ *     a_srvID   : Server ID.
+ *     a_partID  : Partition ID.
+ *     a_volType : Type of volume to print.
+ *
+ * Returns:
+ *     Nothing.
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+static void
+XVolumeStats(a_xInfoP, a_entryP, a_srvID, a_partID, a_volType)
+     volintXInfo *a_xInfoP;
+     struct nvldbentry *a_entryP;
+     afs_int32 a_srvID;
+     afs_int32 a_partID;
+     int a_volType;
+
+{                              /*XVolumeStats */
+
+    int totalOK, totalNotOK, totalBusy;        /*Dummies - we don't really count here */
+
+    XDisplayFormat(a_xInfoP,   /*Ptr to extended volume info */
+                  a_srvID,     /*Server ID to print */
+                  a_partID,    /*Partition ID to print */
+                  &totalOK,    /*Ptr to total-OK counter */
+                  &totalNotOK, /*Ptr to total-screwed counter */
+                  &totalBusy,  /*Ptr to total-busy counter */
+                  0,           /*Don't do a fast listing */
+                  1,           /*Do a long listing */
+                  1);          /*Show volume problems */
+    return;
+
+}                              /*XVolumeStats */
+
+static void
+VolumeStats(pntr, entry, server, part, voltype)
+     volintInfo *pntr;
+     struct nvldbentry *entry;
+     int voltype;
+     afs_int32 server, part;
+{
+    int totalOK, totalNotOK, totalBusy;
+
+    DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy, 0, 1,
+                 1);
+    return;
+}
+
+/* command to forcibly remove a volume */
+static
+NukeVolume(as)
+     register struct cmd_syndesc *as;
+{
+    register afs_int32 code;
+    afs_int32 volID, err;
+    afs_int32 partID;
+    afs_int32 server;
+    register char *tp;
+
+    server = GetServer(tp = as->parms[0].items->data);
+    if (!server) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n", tp);
+       return 1;
+    }
+
+    partID = volutil_GetPartitionID(tp = as->parms[1].items->data);
+    if (partID == -1) {
+       fprintf(STDERR, "vos: could not parse '%s' as a partition name", tp);
+       return 1;
+    }
+
+    volID = vsu_GetVolumeID(tp = as->parms[2].items->data, cstruct, &err);
+    if (volID == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR,
+                   "vos: could not parse '%s' as a numeric volume ID", tp);
+       return 1;
+    }
+
+    fprintf(STDOUT,
+           "vos: forcibly removing all traces of volume %d, please wait...",
+           volID);
+    fflush(STDOUT);
+    code = UV_NukeVolume(server, partID, volID);
+    if (code == 0)
+       fprintf(STDOUT, "done.\n");
+    else
+       fprintf(STDOUT, "failed with code %d.\n", code);
+    return code;
+}
+
+
+/*------------------------------------------------------------------------
+ * PRIVATE ExamineVolume
+ *
+ * Description:
+ *     Routine used to examine a single volume, contacting the VLDB as
+ *     well as the Volume Server.
+ *
+ * Arguments:
+ *     as : Ptr to parsed command line arguments.
+ *
+ * Returns:
+ *     0 for a successful operation,
+ *     Otherwise, one of the ubik or VolServer error values.
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------
+ */
+static
+ExamineVolume(as)
+     register struct cmd_syndesc *as;
+{
+    struct nvldbentry entry;
+    afs_int32 vcode = 0;
+    volintInfo *pntr = (volintInfo *) 0;
+    volintXInfo *xInfoP = (volintXInfo *) 0;
+    afs_int32 volid;
+    afs_int32 code, err, error = 0;
+    int voltype, foundserv = 0, foundentry = 0;
+    afs_int32 aserver, apart;
+    int previdx = -1;
+    int wantExtendedInfo;      /*Do we want extended vol info? */
+
+    wantExtendedInfo = (as->parms[1].items ? 1 : 0);   /* -extended */
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);  /* -id */
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "Unknown volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return -1;
+    }
+
+    if (verbose) {
+       fprintf(STDOUT, "Fetching VLDB entry for %lu .. ",
+               (unsigned long)volid);
+       fflush(STDOUT);
+    }
+    vcode = VLDB_GetEntryByID(volid, -1, &entry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not fetch the entry for volume number %lu from VLDB \n",
+               (unsigned long)volid);
+       return (vcode);
+    }
+    if (verbose)
+       fprintf(STDOUT, "done\n");
+    MapHostToNetwork(&entry);
+
+    if (entry.volumeId[RWVOL] == volid)
+       voltype = RWVOL;
+    else if (entry.volumeId[BACKVOL] == volid)
+       voltype = BACKVOL;
+    else                       /* (entry.volumeId[ROVOL] == volid) */
+       voltype = ROVOL;
+
+    do {                       /* do {...} while (voltype == ROVOL) */
+       /* Get the entry for the volume. If its a RW vol, get the RW entry.
+        * It its a BK vol, get the RW entry (even if VLDB may say the BK doen't exist).
+        * If its a RO vol, get the next RO entry.
+        */
+       GetServerAndPart(&entry, ((voltype == ROVOL) ? ROVOL : RWVOL),
+                        &aserver, &apart, &previdx);
+       if (previdx == -1) {    /* searched all entries */
+           if (!foundentry) {
+               fprintf(STDERR, "Volume %s does not exist in VLDB\n\n",
+                       as->parms[0].items->data);
+               error = ENOENT;
+           }
+           break;
+       }
+       foundentry = 1;
+
+       /* Get information about the volume from the server */
+       if (verbose) {
+           fprintf(STDOUT, "Getting volume listing from the server %s .. ",
+                   hostutil_GetNameByINet(aserver));
+           fflush(STDOUT);
+       }
+       if (wantExtendedInfo)
+           code = UV_XListOneVolume(aserver, apart, volid, &xInfoP);
+       else
+           code = UV_ListOneVolume(aserver, apart, volid, &pntr);
+       if (verbose)
+           fprintf(STDOUT, "done\n");
+
+       if (code) {
+           error = code;
+           if (code == ENODEV) {
+               if ((voltype == BACKVOL) && !(entry.flags & BACK_EXISTS)) {
+                   /* The VLDB says there is no backup volume and its not on disk */
+                   fprintf(STDERR, "Volume %s does not exist\n",
+                           as->parms[0].items->data);
+                   error = ENOENT;
+               } else {
+                   fprintf(STDERR,
+                           "Volume does not exist on server %s as indicated by the VLDB\n",
+                           hostutil_GetNameByINet(aserver));
+               }
+           } else {
+               PrintDiagnostics("examine", code);
+           }
+           fprintf(STDOUT, "\n");
+       } else {
+           foundserv = 1;
+           if (wantExtendedInfo)
+               XVolumeStats(xInfoP, &entry, aserver, apart, voltype);
+           else
+#ifdef FULL_LISTVOL_SWITCH
+           if (as->parms[2].items) {
+               DisplayFormat2(aserver, apart, pntr);
+               EnumerateEntry(&entry);
+           } else
+#endif /* FULL_LISTVOL_SWITCH */
+               VolumeStats(pntr, &entry, aserver, apart, voltype);
+
+           if ((voltype == BACKVOL) && !(entry.flags & BACK_EXISTS)) {
+               /* The VLDB says there is no backup volume yet we found one on disk */
+               fprintf(STDERR, "Volume %s does not exist in VLDB\n",
+                       as->parms[0].items->data);
+               error = ENOENT;
+           }
+       }
+
+       if (pntr)
+           free(pntr);
+       if (xInfoP)
+           free(xInfoP);
+    } while (voltype == ROVOL);
+
+    if (!foundserv) {
+       fprintf(STDERR, "Dump only information from VLDB\n\n");
+       fprintf(STDOUT, "%s \n", entry.name);   /* PostVolumeStats doesn't print name */
+    }
+    PostVolumeStats(&entry);
+
+    return (error);
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE SetFields
+ *
+ * Description:
+ *     Routine used to change the status of a single volume.
+ *
+ * Arguments:
+ *     as : Ptr to parsed command line arguments.
+ *
+ * Returns:
+ *     0 for a successful operation,
+ *     Otherwise, one of the ubik or VolServer error values.
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------
+ */
+static
+SetFields(as)
+     register struct cmd_syndesc *as;
+{
+    struct nvldbentry entry;
+    afs_int32 vcode = 0;
+    volintInfo info;
+    afs_int32 volid;
+    afs_int32 code, err;
+    afs_int32 aserver, apart;
+    int previdx = -1;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);  /* -id */
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "Unknown volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return -1;
+    }
+
+    code = VLDB_GetEntryByID(volid, RWVOL, &entry);
+    if (code) {
+       fprintf(STDERR,
+               "Could not fetch the entry for volume number %lu from VLDB \n",
+               (unsigned long)volid);
+       return (code);
+    }
+    MapHostToNetwork(&entry);
+
+    GetServerAndPart(&entry, RWVOL, &aserver, &apart, &previdx);
+    if (previdx == -1) {
+       fprintf(STDERR, "Volume %s does not exist in VLDB\n\n",
+               as->parms[0].items->data);
+       return (ENOENT);
+    }
+
+    memset(&info, 0, sizeof(info));
+    info.volid = volid;
+    info.type = RWVOL;
+    info.dayUse = -1;
+    info.maxquota = -1;
+    info.flags = -1;
+    info.spare0 = -1;
+    info.spare1 = -1;
+    info.spare2 = -1;
+    info.spare3 = -1;
+
+    if (as->parms[1].items) {
+       /* -max <quota> */
+       code = util_GetInt32(as->parms[1].items->data, &info.maxquota);
+       if (code) {
+           fprintf(STDERR, "invalid quota value\n");
+           return code;
+       }
+    }
+    if (as->parms[2].items) {
+       /* -clearuse */
+       info.dayUse = 0;
+    }
+    code = UV_SetVolumeInfo(aserver, apart, volid, &info);
+    if (code)
+       fprintf(STDERR,
+               "Could not update volume info fields for volume number %lu\n",
+               (unsigned long)volid);
+    return (code);
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE volOnline
+ *
+ * Description:
+ *      Brings a volume online.
+ *
+ * Arguments:
+ *     as : Ptr to parsed command line arguments.
+ *
+ * Returns:
+ *     0 for a successful operation,
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------
+ */
+static
+volOnline(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 server, partition, volid;
+    afs_int32 code, err = 0;
+
+    server = GetServer(as->parms[0].items->data);
+    if (server == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       return -1;
+    }
+
+    partition = volutil_GetPartitionID(as->parms[1].items->data);
+    if (partition < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+
+    volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);  /* -id */
+    if (!volid) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "Unknown volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return -1;
+    }
+
+    code = UV_SetVolume(server, partition, volid, ITOffline, 0 /*online */ ,
+                       0 /*sleep */ );
+    if (code) {
+       fprintf(STDERR, "Failed to set volume. Code = %d\n", code);
+       return -1;
+    }
+
+    return 0;
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE volOffline
+ *
+ * Description:
+ *      Brings a volume offline.
+ *
+ * Arguments:
+ *     as : Ptr to parsed command line arguments.
+ *
+ * Returns:
+ *     0 for a successful operation,
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------
+ */
+static int
+volOffline(register struct cmd_syndesc *as)
+{
+    afs_int32 server, partition, volid;
+    afs_int32 code, err = 0;
+    afs_int32 transflag, sleeptime, transdone;
+
+    server = GetServer(as->parms[0].items->data);
+    if (server == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       return -1;
+    }
+
+    partition = volutil_GetPartitionID(as->parms[1].items->data);
+    if (partition < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+
+    volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);  /* -id */
+    if (!volid) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "Unknown volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return -1;
+    }
+
+    transflag = (as->parms[4].items ? ITBusy : ITOffline);
+    sleeptime = (as->parms[3].items ? atol(as->parms[3].items->data) : 0);
+    transdone = (sleeptime ? 0 /*online */ : VTOutOfService);
+    if (as->parms[4].items && !as->parms[3].items) {
+       fprintf(STDERR, "-sleep option must be used with -busy flag\n");
+       return -1;
+    }
+
+    code =
+       UV_SetVolume(server, partition, volid, transflag, transdone,
+                    sleeptime);
+    if (code) {
+       fprintf(STDERR, "Failed to set volume. Code = %d\n", code);
+       return -1;
+    }
+
+    return 0;
+}
+
+static int
+CreateVolume(register struct cmd_syndesc *as)
+{
+    afs_int32 pnum;
+    char part[10];
+    afs_int32 volid, code;
+    struct nvldbentry entry;
+    afs_int32 vcode;
+    afs_int32 quota;
+
+    quota = 5000;
+    tserver = GetServer(as->parms[0].items->data);
+    if (!tserver) {
+       fprintf(STDERR, "vos: host '%s' not found in host table\n",
+               as->parms[0].items->data);
+       return ENOENT;
+    }
+    pnum = volutil_GetPartitionID(as->parms[1].items->data);
+    if (pnum < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+    if (!IsPartValid(pnum, tserver, &code)) {  /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[1].items->data);
+       return ENOENT;
+    }
+    if (!ISNAMEVALID(as->parms[2].items->data)) {
+       fprintf(STDERR,
+               "vos: the name of the root volume %s exceeds the size limit of %d\n",
+               as->parms[2].items->data, VOLSER_OLDMAXVOLNAME - 10);
+       return E2BIG;
+    }
+    if (!VolNameOK(as->parms[2].items->data)) {
+       fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               as->parms[2].items->data);
+       return EINVAL;
+    }
+    if (IsNumeric(as->parms[2].items->data)) {
+       fprintf(STDERR, "Illegal volume name %s, should not be a number\n",
+               as->parms[2].items->data);
+       return EINVAL;
+    }
+    vcode = VLDB_GetEntryByName(as->parms[2].items->data, &entry);
+    if (!vcode) {
+       fprintf(STDERR, "Volume %s already exists\n",
+               as->parms[2].items->data);
+       PrintDiagnostics("create", code);
+       return EEXIST;
+    }
+
+    if (as->parms[3].items) {
+       if (!IsNumeric(as->parms[3].items->data)) {
+           fprintf(STDERR, "Initial quota %s should be numeric.\n",
+                   as->parms[3].items->data);
+           return EINVAL;
+       }
+
+       code = util_GetInt32(as->parms[3].items->data, &quota);
+       if (code) {
+           fprintf(STDERR, "vos: bad integer specified for quota.\n");
+           return code;
+       }
+    }
+
+    code =
+       UV_CreateVolume2(tserver, pnum, as->parms[2].items->data, quota, 0,
+                        0, 0, 0, &volid);
+    if (code) {
+       PrintDiagnostics("create", code);
+       return code;
+    }
+    MapPartIdIntoName(pnum, part);
+    fprintf(STDOUT, "Volume %lu created on partition %s of %s\n",
+           (unsigned long)volid, part, as->parms[0].items->data);
+
+    return 0;
+}
+
+static afs_int32
+DeleteAll(entry)
+     struct nvldbentry *entry;
+{
+    int i;
+    afs_int32 error, code, curserver, curpart, volid;
+
+    MapHostToNetwork(entry);
+    error = 0;
+    for (i = 0; i < entry->nServers; i++) {
+       curserver = entry->serverNumber[i];
+       curpart = entry->serverPartition[i];
+       if (entry->serverFlags[i] & ITSROVOL) {
+           volid = entry->volumeId[ROVOL];
+       } else {
+           volid = entry->volumeId[RWVOL];
+       }
+       code = UV_DeleteVolume(curserver, curpart, volid);
+       if (code && !error)
+           error = code;
+    }
+    return error;
+}
+
+static
+DeleteVolume(as)
+     struct cmd_syndesc *as;
+{
+    afs_int32 err, code = 0;
+    afs_int32 server = 0, partition = -1, volid;
+    char pname[10];
+    afs_int32 idx, j;
+
+    if (as->parms[0].items) {
+       server = GetServer(as->parms[0].items->data);
+       if (!server) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[0].items->data);
+           return ENOENT;
+       }
+    }
+
+    if (as->parms[1].items) {
+       partition = volutil_GetPartitionID(as->parms[1].items->data);
+       if (partition < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[1].items->data);
+           return EINVAL;
+       }
+
+       /* Check for validity of the partition */
+       if (!IsPartValid(partition, server, &code)) {
+           if (code) {
+               PrintError("", code);
+           } else {
+               fprintf(STDERR,
+                       "vos : partition %s does not exist on the server\n",
+                       as->parms[1].items->data);
+           }
+           return ENOENT;
+       }
+    }
+
+    volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);
+    if (volid == 0) {
+       fprintf(STDERR, "Can't find volume name '%s' in VLDB\n",
+               as->parms[2].items->data);
+       if (err)
+           PrintError("", err);
+       return ENOENT;
+    }
+
+    /* If the server or partition option are not complete, try to fill
+     * them in from the VLDB entry.
+     */
+    if ((partition == -1) || !server) {
+       struct nvldbentry entry;
+
+       code = VLDB_GetEntryByID(volid, -1, &entry);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not fetch the entry for volume %lu from VLDB\n",
+                   (unsigned long)volid);
+           PrintError("", code);
+           return (code);
+       }
+
+       if (((volid == entry.volumeId[RWVOL]) && (entry.flags & RW_EXISTS))
+           || ((volid == entry.volumeId[BACKVOL])
+               && (entry.flags & BACK_EXISTS))) {
+           idx = Lp_GetRwIndex(&entry);
+           if ((idx == -1) || (server && (server != entry.serverNumber[idx]))
+               || ((partition != -1)
+                   && (partition != entry.serverPartition[idx]))) {
+               fprintf(STDERR, "VLDB: Volume '%s' no match\n",
+                       as->parms[2].items->data);
+               return ENOENT;
+           }
+       } else if ((volid == entry.volumeId[ROVOL])
+                  && (entry.flags & RO_EXISTS)) {
+           for (idx = -1, j = 0; j < entry.nServers; j++) {
+               if (entry.serverFlags[j] != ITSROVOL)
+                   continue;
+
+               if (((server == 0) || (server == entry.serverNumber[j]))
+                   && ((partition == -1)
+                       || (partition == entry.serverPartition[j]))) {
+                   if (idx != -1) {
+                       fprintf(STDERR,
+                               "VLDB: Volume '%s' matches more than one RO\n",
+                               as->parms[2].items->data);
+                       return ENOENT;
+                   }
+                   idx = j;
+               }
+           }
+           if (idx == -1) {
+               fprintf(STDERR, "VLDB: Volume '%s' no match\n",
+                       as->parms[2].items->data);
+               return ENOENT;
+           }
+       } else {
+           fprintf(STDERR, "VLDB: Volume '%s' no match\n",
+                   as->parms[2].items->data);
+           return ENOENT;
+       }
+
+       server = htonl(entry.serverNumber[idx]);
+       partition = entry.serverPartition[idx];
+    }
+
+
+    code = UV_DeleteVolume(server, partition, volid);
+    if (code) {
+       PrintDiagnostics("remove", code);
+       return code;
+    }
+
+    MapPartIdIntoName(partition, pname);
+    fprintf(STDOUT, "Volume %lu on partition %s server %s deleted\n",
+           (unsigned long)volid, pname, hostutil_GetNameByINet(server));
+    return 0;
+}
+
+#define TESTM  0               /* set for move space tests, clear for production */
+static
+MoveVolume(as)
+     register struct cmd_syndesc *as;
+{
+
+    afs_int32 volid, fromserver, toserver, frompart, topart;
+    afs_int32 flags, code, err;
+    char fromPartName[10], toPartName[10];
+
+    struct diskPartition partition;    /* for space check */
+    volintInfo *p;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+    fromserver = GetServer(as->parms[1].items->data);
+    if (fromserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+    toserver = GetServer(as->parms[3].items->data);
+    if (toserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[3].items->data);
+       return ENOENT;
+    }
+    frompart = volutil_GetPartitionID(as->parms[2].items->data);
+    if (frompart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[2].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(frompart, fromserver, &code)) {   /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[2].items->data);
+       return ENOENT;
+    }
+    topart = volutil_GetPartitionID(as->parms[4].items->data);
+    if (topart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[4].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(topart, toserver, &code)) {       /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[4].items->data);
+       return ENOENT;
+    }
+
+    flags = 0;
+    if (as->parms[5].items) flags |= RV_NOCLONE;
+
+    /*
+     * check source partition for space to clone volume
+     */
+
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+
+    /*
+     * check target partition for space to move volume
+     */
+
+    code = UV_PartitionInfo(toserver, toPartName, &partition);
+    if (code) {
+       fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
+       exit(1);
+    }
+    if (TESTM)
+       fprintf(STDOUT, "target partition %s free space %d\n", toPartName,
+               partition.free);
+
+    p = (volintInfo *) 0;
+    code = UV_ListOneVolume(fromserver, frompart, volid, &p);
+    if (code) {
+       fprintf(STDERR, "vos:cannot access volume %lu\n",
+               (unsigned long)volid);
+       free(p);
+       exit(1);
+    }
+    if (TESTM)
+       fprintf(STDOUT, "volume %lu size %d\n", (unsigned long)volid,
+               p->size);
+    if (partition.free <= p->size) {
+       fprintf(STDERR,
+               "vos: no space on target partition %s to move volume %lu\n",
+               toPartName, (unsigned long)volid);
+       free(p);
+       exit(1);
+    }
+    free(p);
+
+    if (TESTM) {
+       fprintf(STDOUT, "size test - don't do move\n");
+       exit(0);
+    }
+
+    /* successful move still not guaranteed but shoot for it */
+
+    code =
+       UV_MoveVolume2(volid, fromserver, frompart, toserver, topart, flags);
+    if (code) {
+       PrintDiagnostics("move", code);
+       return code;
+    }
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+    fprintf(STDOUT, "Volume %lu moved from %s %s to %s %s \n",
+           (unsigned long)volid, as->parms[1].items->data, fromPartName,
+           as->parms[3].items->data, toPartName);
+
+    return 0;
+}
+
+static
+CopyVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 volid, fromserver, toserver, frompart, topart, code, err, flags;
+    char fromPartName[10], toPartName[10], *tovolume;
+    struct nvldbentry entry;
+    struct diskPartition partition;    /* for space check */
+    volintInfo *p;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+    fromserver = GetServer(as->parms[1].items->data);
+    if (fromserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+
+    toserver = GetServer(as->parms[4].items->data);
+    if (toserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[4].items->data);
+       return ENOENT;
+    }
+
+    tovolume = as->parms[3].items->data;
+    if (!ISNAMEVALID(tovolume)) {
+       fprintf(STDERR,
+               "vos: the name of the root volume %s exceeds the size limit of %d\n",
+               tovolume, VOLSER_OLDMAXVOLNAME - 10);
+       return E2BIG;
+    }
+    if (!VolNameOK(tovolume)) {
+       fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               tovolume);
+       return EINVAL;
+    }
+    if (IsNumeric(tovolume)) {
+       fprintf(STDERR, "Illegal volume name %s, should not be a number\n",
+               tovolume);
+       return EINVAL;
+    }
+    code = VLDB_GetEntryByName(tovolume, &entry);
+    if (!code) {
+       fprintf(STDERR, "Volume %s already exists\n", tovolume);
+       PrintDiagnostics("copy", code);
+       return EEXIST;
+    }
+
+    frompart = volutil_GetPartitionID(as->parms[2].items->data);
+    if (frompart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[2].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(frompart, fromserver, &code)) {   /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[2].items->data);
+       return ENOENT;
+    }
+
+    topart = volutil_GetPartitionID(as->parms[5].items->data);
+    if (topart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[5].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(topart, toserver, &code)) {       /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[5].items->data);
+       return ENOENT;
+    }
+
+    flags = 0;
+    if (as->parms[6].items) flags |= RV_OFFLINE;
+    if (as->parms[7].items) flags |= RV_RDONLY;
+    if (as->parms[8].items) flags |= RV_NOCLONE;
+
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+
+    /*
+     * check target partition for space to move volume
+     */
+
+    code = UV_PartitionInfo(toserver, toPartName, &partition);
+    if (code) {
+       fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
+       exit(1);
+    }
+    if (TESTM)
+       fprintf(STDOUT, "target partition %s free space %d\n", toPartName,
+               partition.free);
+
+    p = (volintInfo *) 0;
+    code = UV_ListOneVolume(fromserver, frompart, volid, &p);
+    if (code) {
+       fprintf(STDERR, "vos:cannot access volume %lu\n",
+               (unsigned long)volid);
+       free(p);
+       exit(1);
+    }
+
+    if (partition.free <= p->size) {
+       fprintf(STDERR,
+               "vos: no space on target partition %s to copy volume %lu\n",
+               toPartName, (unsigned long)volid);
+       free(p);
+       exit(1);
+    }
+    free(p);
+
+    /* successful copy still not guaranteed but shoot for it */
+
+    code =
+       UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver,
+                      topart, 0, flags);
+    if (code) {
+       PrintDiagnostics("copy", code);
+       return code;
+    }
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+    fprintf(STDOUT, "Volume %lu copied from %s %s to %s on %s %s \n",
+           (unsigned long)volid, as->parms[1].items->data, fromPartName,
+           tovolume, as->parms[4].items->data, toPartName);
+
+    return 0;
+}
+
+
+static
+ShadowVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 volid, fromserver, toserver, frompart, topart, tovolid;
+    afs_int32 code, err, flags;
+    char fromPartName[10], toPartName[10], toVolName[32], *tovolume;
+    struct nvldbentry entry;
+    struct diskPartition partition;    /* for space check */
+    volintInfo *p, *q;
+
+    p = (volintInfo *) 0;
+    q = (volintInfo *) 0;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+    fromserver = GetServer(as->parms[1].items->data);
+    if (fromserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+
+    toserver = GetServer(as->parms[3].items->data);
+    if (toserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[3].items->data);
+       return ENOENT;
+    }
+
+    frompart = volutil_GetPartitionID(as->parms[2].items->data);
+    if (frompart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[2].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(frompart, fromserver, &code)) {   /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[2].items->data);
+       return ENOENT;
+    }
+
+    topart = volutil_GetPartitionID(as->parms[4].items->data);
+    if (topart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[4].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(topart, toserver, &code)) {       /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[4].items->data);
+       return ENOENT;
+    }
+
+    if (as->parms[5].items) {
+       tovolume = as->parms[5].items->data;
+       if (!ISNAMEVALID(tovolume)) {
+           fprintf(STDERR,
+               "vos: the name of the root volume %s exceeds the size limit of %d\n",
+               tovolume, VOLSER_OLDMAXVOLNAME - 10);
+           return E2BIG;
+       }
+       if (!VolNameOK(tovolume)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               tovolume);
+           return EINVAL;
+       }
+       if (IsNumeric(tovolume)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not be a number\n",
+               tovolume);
+           return EINVAL;
+       }
+    } else {
+       /* use actual name of source volume */
+       code = UV_ListOneVolume(fromserver, frompart, volid, &p);
+       if (code) {
+           fprintf(STDERR, "vos:cannot access volume %lu\n",
+               (unsigned long)volid);
+           exit(1);
+       }
+       strcpy(toVolName, p->name);
+       tovolume = toVolName;
+       /* save p for size checks later */
+    }
+
+    if (as->parms[6].items) {
+       tovolid = vsu_GetVolumeID(as->parms[6].items->data, cstruct, &err);
+       if (tovolid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                       as->parms[6].items->data);
+           if (p)
+               free(p);
+           return ENOENT;
+       }
+    } else {
+       tovolid = vsu_GetVolumeID(tovolume, cstruct, &err);
+       if (tovolid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                       tovolume);
+           if (p)
+               free(p);
+           return ENOENT;
+       }
+    }
+
+    flags = RV_NOVLDB;
+    if (as->parms[7].items) flags |= RV_OFFLINE;
+    if (as->parms[8].items) flags |= RV_RDONLY;
+    if (as->parms[9].items) flags |= RV_NOCLONE;
+    if (as->parms[10].items) flags |= RV_CPINCR;
+
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+
+    /*
+     * check target partition for space to move volume
+     */
+
+    code = UV_PartitionInfo(toserver, toPartName, &partition);
+    if (code) {
+       fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
+       exit(1);
+    }
+    if (TESTM)
+       fprintf(STDOUT, "target partition %s free space %d\n", toPartName,
+               partition.free);
+
+    /* Don't do this again if we did it above */
+    if (!p) {
+       code = UV_ListOneVolume(fromserver, frompart, volid, &p);
+       if (code) {
+           fprintf(STDERR, "vos:cannot access volume %lu\n",
+               (unsigned long)volid);
+           exit(1);
+       }
+    }
+
+    /* OK if this fails */
+    code = UV_ListOneVolume(toserver, topart, tovolid, &q);
+
+    /* Treat existing volume size as "free" */
+    if (q)
+       p->size = (q->size < p->size) ? p->size - q->size : 0;
+
+    if (partition.free <= p->size) {
+       fprintf(STDERR,
+               "vos: no space on target partition %s to copy volume %lu\n",
+               toPartName, (unsigned long)volid);
+       free(p);
+       if (q) free(q);
+       exit(1);
+    }
+    free(p);
+    if (q) free(q);
+
+    /* successful copy still not guaranteed but shoot for it */
+
+    code =
+       UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver,
+                      topart, tovolid, flags);
+    if (code) {
+       PrintDiagnostics("shadow", code);
+       return code;
+    }
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+    fprintf(STDOUT, "Volume %lu shadowed from %s %s to %s %s \n",
+           (unsigned long)volid, as->parms[1].items->data, fromPartName,
+           as->parms[4].items->data, toPartName);
+
+    return 0;
+}
+
+
+static
+CloneVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 server, part, volid, cloneid, voltype;
+    char partName[10], *volname;
+    afs_int32 code, err, flags;
+    struct nvldbentry entry;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+
+    if (as->parms[1].items || as->parms[2].items) {
+       if (!as->parms[1].items || !as->parms[2].items) {
+           fprintf(STDERR,
+                   "Must specify both -server and -partition options\n");
+           return -1;
+       }
+       server = GetServer(as->parms[1].items->data);
+       if (server == 0) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[1].items->data);
+           return ENOENT;
+       }
+       part = volutil_GetPartitionID(as->parms[2].items->data);
+       if (part < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[2].items->data);
+           return EINVAL;
+       }
+       if (!IsPartValid(part, server, &code)) {        /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[2].items->data);
+           return ENOENT;
+        }
+    } else {
+       code = GetVolumeInfo(volid, &server, &part, &voltype, &entry);
+       if (code)
+           return code;
+    }
+
+    volname = 0;
+    if (as->parms[3].items) {
+       volname = as->parms[3].items->data;
+       if (strlen(volname) > VOLSER_OLDMAXVOLNAME - 1) {
+           fprintf(STDERR,
+               "vos: the name of the root volume %s exceeds the size limit of %d\n",
+               volname, VOLSER_OLDMAXVOLNAME - 1);
+           return E2BIG;
+       }
+       if (!VolNameOK(volname)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               volname);
+           return EINVAL;
+       }
+       if (IsNumeric(volname)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not be a number\n",
+               volname);
+           return EINVAL;
+       }
+    }
+
+    cloneid = 0;
+    if (as->parms[4].items) {
+       cloneid = vsu_GetVolumeID(as->parms[4].items->data, cstruct, &err);
+       if (cloneid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                       as->parms[4].items->data);
+           return ENOENT;
+       }
+    }
+
+    flags = 0;
+    if (as->parms[5].items) flags |= RV_OFFLINE;
+    if (as->parms[6].items) flags |= RV_RDONLY;
+
+
+    code = 
+       UV_CloneVolume(server, part, volid, cloneid, volname, flags);
+
+    if (code) {
+       PrintDiagnostics("clone", code);
+       return code;
+    }
+    MapPartIdIntoName(part, partName);
+    fprintf(STDOUT, "Created clone for volume %lu\n",
+           as->parms[0].items->data);
+
+    return 0;
+}
+
+
+static
+BackupVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 avolid, aserver, apart, vtype, code, err;
+    struct nvldbentry entry;
+
+    afs_int32 buvolid, buserver, bupart, butype;
+    struct nvldbentry buentry;
+
+    avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+    code = GetVolumeInfo(avolid, &aserver, &apart, &vtype, &entry);
+    if (code)
+       exit(1);
+
+    /* verify this is a readwrite volume */
+
+    if (vtype != RWVOL) {
+       fprintf(STDERR, "%s not RW volume\n", as->parms[0].items->data);
+       exit(1);
+    }
+
+    /* is there a backup volume already? */
+
+    if (entry.flags & BACK_EXISTS) {
+       /* yep, where is it? */
+
+       buvolid = entry.volumeId[BACKVOL];
+       code = GetVolumeInfo(buvolid, &buserver, &bupart, &butype, &buentry);
+       if (code)
+           exit(1);
+
+       /* is it local? */
+       code = VLDB_IsSameAddrs(buserver, aserver, &err);
+       if (err) {
+           fprintf(STDERR,
+                   "Failed to get info about server's %d address(es) from vlserver; aborting call!\n",
+                   buserver);
+           exit(1);
+       }
+       if (!code) {
+           fprintf(STDERR,
+                   "FATAL ERROR: backup volume %lu exists on server %lu\n",
+                   (unsigned long)buvolid, (unsigned long)buserver);
+           exit(1);
+       }
+    }
+
+    /* nope, carry on */
+
+    code = UV_BackupVolume(aserver, apart, avolid);
+
+    if (code) {
+       PrintDiagnostics("backup", code);
+       return code;
+    }
+    fprintf(STDOUT, "Created backup volume for %s \n",
+           as->parms[0].items->data);
+    return 0;
+}
+
+static
+ReleaseVolume(as)
+     register struct cmd_syndesc *as;
+{
+
+    struct nvldbentry entry;
+    afs_int32 avolid, aserver, apart, vtype, code, err;
+    int force = 0;
+
+    if (as->parms[1].items)
+       force = 1;
+    avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+    code = GetVolumeInfo(avolid, &aserver, &apart, &vtype, &entry);
+    if (code)
+       return code;
+
+    if (vtype != RWVOL) {
+       fprintf(STDERR, "%s not a RW volume\n", as->parms[0].items->data);
+       return (ENOENT);
+    }
+
+    if (!ISNAMEVALID(entry.name)) {
+       fprintf(STDERR,
+               "Volume name %s is too long, rename before releasing\n",
+               entry.name);
+       return E2BIG;
+    }
+
+    code = UV_ReleaseVolume(avolid, aserver, apart, force);
+    if (code) {
+       PrintDiagnostics("release", code);
+       return code;
+    }
+    fprintf(STDOUT, "Released volume %s successfully\n",
+           as->parms[0].items->data);
+    return 0;
+}
+
+static
+DumpVolume(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 avolid, aserver, apart, voltype, fromdate = 0, code, err, i;
+    char filename[NameLen];
+    struct nvldbentry entry;
+
+    rx_SetRxDeadTime(60 * 10);
+    for (i = 0; i < MAXSERVERS; i++) {
+       struct rx_connection *rxConn = ubik_GetRPCConn(cstruct, i);
+       if (rxConn == 0)
+           break;
+       rx_SetConnDeadTime(rxConn, rx_connDeadTime);
+       if (rxConn->service)
+           rxConn->service->connDeadTime = rx_connDeadTime;
+    }
+
+    avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+
+    if (as->parms[3].items || as->parms[4].items) {
+       if (!as->parms[3].items || !as->parms[4].items) {
+           fprintf(STDERR,
+                   "Must specify both -server and -partition options\n");
+           return -1;
+       }
+       aserver = GetServer(as->parms[3].items->data);
+       if (aserver == 0) {
+           fprintf(STDERR, "Invalid server name\n");
+           return -1;
+       }
+       apart = volutil_GetPartitionID(as->parms[4].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "Invalid partition name\n");
+           return -1;
+       }
+    } else {
+       code = GetVolumeInfo(avolid, &aserver, &apart, &voltype, &entry);
+       if (code)
+           return code;
+    }
+
+    if (as->parms[1].items && strcmp(as->parms[1].items->data, "0")) {
+       code = ktime_DateToInt32(as->parms[1].items->data, &fromdate);
+       if (code) {
+           fprintf(STDERR, "vos: failed to parse date '%s' (error=%d))\n",
+                   as->parms[1].items->data, code);
+           return code;
+       }
+    }
+    if (as->parms[2].items) {
+       strcpy(filename, as->parms[2].items->data);
+    } else {
+       strcpy(filename, "");
+    }
+
+    if (as->parms[5].items) {
+       code =
+           UV_DumpClonedVolume(avolid, aserver, apart, fromdate,
+                               DumpFunction, filename);
+    } else {
+       code =
+           UV_DumpVolume(avolid, aserver, apart, fromdate, DumpFunction,
+                         filename);
+    }
+    if (code) {
+       PrintDiagnostics("dump", code);
+       return code;
+    }
+    if (strcmp(filename, ""))
+       fprintf(STDERR, "Dumped volume %s in file %s\n",
+               as->parms[0].items->data, filename);
+    else
+       fprintf(STDERR, "Dumped volume %s in stdout \n",
+               as->parms[0].items->data);
+    return 0;
+}
+
+#define ASK   0
+#define ABORT 1
+#define FULL  2
+#define INC   3
+
+static
+RestoreVolume(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 avolid, aserver, apart, code, vcode, err;
+    afs_int32 aoverwrite = ASK;
+    int restoreflags, readonly = 0, offline = 0, voltype = RWVOL;
+    char prompt;
+    char afilename[NameLen], avolname[VOLSER_MAXVOLNAME + 1], apartName[10];
+    char volname[VOLSER_MAXVOLNAME + 1];
+    struct nvldbentry entry;
+
+    prompt = 'n';
+
+    if (as->parms[4].items) {
+       avolid = vsu_GetVolumeID(as->parms[4].items->data, cstruct, &err);
+       if (avolid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume '%s'\n",
+                       as->parms[4].items->data);
+           exit(1);
+       }
+    } else
+       avolid = 0;
+
+    if (as->parms[5].items) {
+       if ((strcmp(as->parms[5].items->data, "a") == 0)
+           || (strcmp(as->parms[5].items->data, "abort") == 0)) {
+           aoverwrite = ABORT;
+       } else if ((strcmp(as->parms[5].items->data, "f") == 0)
+                  || (strcmp(as->parms[5].items->data, "full") == 0)) {
+           aoverwrite = FULL;
+       } else if ((strcmp(as->parms[5].items->data, "i") == 0)
+                  || (strcmp(as->parms[5].items->data, "inc") == 0)
+                  || (strcmp(as->parms[5].items->data, "increment") == 0)
+                  || (strcmp(as->parms[5].items->data, "incremental") == 0)) {
+           aoverwrite = INC;
+       } else {
+           fprintf(STDERR, "vos: %s is not a valid argument to -overwrite\n",
+                   as->parms[5].items->data);
+           exit(1);
+       }
+    }
+    if (as->parms[6].items)
+       offline = 1;
+    if (as->parms[7].items) {
+       readonly = 1;
+       voltype = ROVOL;
+    }
+
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    apart = volutil_GetPartitionID(as->parms[1].items->data);
+    if (apart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+    if (!IsPartValid(apart, aserver, &code)) { /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[1].items->data);
+       exit(1);
+    }
+    strcpy(avolname, as->parms[2].items->data);
+    if (!ISNAMEVALID(avolname)) {
+       fprintf(STDERR,
+               "vos: the name of the volume %s exceeds the size limit\n",
+               avolname);
+       exit(1);
+    }
+    if (!VolNameOK(avolname)) {
+       fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               avolname);
+       exit(1);
+    }
+    if (as->parms[3].items) {
+       strcpy(afilename, as->parms[3].items->data);
+       if (!FileExists(afilename)) {
+           fprintf(STDERR, "Can't access file %s\n", afilename);
+           exit(1);
+       }
+    } else {
+       strcpy(afilename, "");
+    }
+
+    /* Check if volume exists or not */
+
+    vsu_ExtractName(volname, avolname);
+    vcode = VLDB_GetEntryByName(volname, &entry);
+    if (vcode) {               /* no volume - do a full restore */
+       restoreflags = RV_FULLRST;
+       if ((aoverwrite == INC) || (aoverwrite == ABORT))
+           fprintf(STDERR,
+                   "Volume does not exist; Will perform a full restore\n");
+    }
+
+    else if ((!readonly && Lp_GetRwIndex(&entry) == -1)        /* RW volume does not exist - do a full */
+            ||(readonly && !Lp_ROMatch(0, 0, &entry))) {       /* RO volume does not exist - do a full */
+       restoreflags = RV_FULLRST;
+       if ((aoverwrite == INC) || (aoverwrite == ABORT))
+           fprintf(STDERR,
+                   "%s Volume does not exist; Will perform a full restore\n",
+                   readonly ? "RO" : "RW");
+
+       if (avolid == 0) {
+           avolid = entry.volumeId[voltype];
+       } else if (entry.volumeId[voltype] != 0
+                  && entry.volumeId[voltype] != avolid) {
+           avolid = entry.volumeId[voltype];
+       }
+    }
+
+    else {                     /* volume exists - do we do a full incremental or abort */
+       int Oserver, Opart, Otype, vol_elsewhere = 0;
+       struct nvldbentry Oentry;
+       int c, dc;
+
+       if (avolid == 0) {
+           avolid = entry.volumeId[voltype];
+       } else if (entry.volumeId[voltype] != 0
+                  && entry.volumeId[voltype] != avolid) {
+           avolid = entry.volumeId[voltype];
+       }
+
+       /* A file name was specified  - check if volume is on another partition */
+       vcode = GetVolumeInfo(avolid, &Oserver, &Opart, &Otype, &Oentry);
+       if (vcode)
+           exit(1);
+
+       vcode = VLDB_IsSameAddrs(Oserver, aserver, &err);
+       if (err) {
+           fprintf(STDERR,
+                   "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n",
+                   Oserver, err);
+           exit(1);
+       }
+       if (!vcode || (Opart != apart))
+           vol_elsewhere = 1;
+
+       if (aoverwrite == ASK) {
+           if (strcmp(afilename, "") == 0) {   /* The file is from standard in */
+               fprintf(STDERR,
+                       "Volume exists and no -overwrite option specified; Aborting restore command\n");
+               exit(1);
+           }
+
+           /* Ask what to do */
+           if (vol_elsewhere) {
+               fprintf(STDERR,
+                       "The volume %s %u already exists on a different server/part\n",
+                       volname, entry.volumeId[voltype]);
+               fprintf(STDERR,
+                       "Do you want to do a full restore or abort? [fa](a): ");
+           } else {
+               fprintf(STDERR,
+                       "The volume %s %u already exists in the VLDB\n",
+                       volname, entry.volumeId[voltype]);
+               fprintf(STDERR,
+                       "Do you want to do a full/incremental restore or abort? [fia](a): ");
+           }
+           dc = c = getchar();
+           while (!(dc == EOF || dc == '\n'))
+               dc = getchar(); /* goto end of line */
+           if ((c == 'f') || (c == 'F'))
+               aoverwrite = FULL;
+           else if ((c == 'i') || (c == 'I'))
+               aoverwrite = INC;
+           else
+               aoverwrite = ABORT;
+       }
+
+       if (aoverwrite == ABORT) {
+           fprintf(STDERR, "Volume exists; Aborting restore command\n");
+           exit(1);
+       } else if (aoverwrite == FULL) {
+           restoreflags = RV_FULLRST;
+           fprintf(STDERR,
+                   "Volume exists; Will delete and perform full restore\n");
+       } else if (aoverwrite == INC) {
+           restoreflags = 0;
+           if (vol_elsewhere) {
+               fprintf(STDERR,
+                       "%s volume %lu already exists on a different server/part; not allowed\n",
+                       readonly ? "RO" : "RW", (unsigned long)avolid);
+               exit(1);
+           }
+       }
+    }
+    if (offline)
+       restoreflags |= RV_OFFLINE;
+    if (readonly)
+       restoreflags |= RV_RDONLY;
+    code =
+       UV_RestoreVolume(aserver, apart, avolid, avolname, restoreflags,
+                        WriteData, afilename);
+    if (code) {
+       PrintDiagnostics("restore", code);
+       exit(1);
+    }
+    MapPartIdIntoName(apart, apartName);
+
+    /*
+     * patch typo here - originally "parms[1]", should be "parms[0]"
+     */
+
+    fprintf(STDOUT, "Restored volume %s on %s %s\n", avolname,
+           as->parms[0].items->data, apartName);
+    return 0;
+}
+
+static
+LockReleaseCmd(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 avolid, code, err;
+
+    avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[0].items->data);
+       exit(1);
+    }
+
+    code = UV_LockRelease(avolid);
+    if (code) {
+       PrintDiagnostics("unlock", code);
+       exit(1);
+    }
+    fprintf(STDOUT, "Released lock on vldb entry for volume %s\n",
+           as->parms[0].items->data);
+    return 0;
+}
+
+static
+AddSite(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 avolid, aserver, apart, code, err;
+    char apartName[10], avolname[VOLSER_MAXVOLNAME + 1];
+
+    vsu_ExtractName(avolname, as->parms[2].items->data);;
+    avolid = vsu_GetVolumeID(avolname, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[2].items->data);
+       exit(1);
+    }
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    apart = volutil_GetPartitionID(as->parms[1].items->data);
+    if (apart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+    if (!IsPartValid(apart, aserver, &code)) { /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[1].items->data);
+       exit(1);
+    }
+    code = UV_AddSite(aserver, apart, avolid);
+    if (code) {
+       PrintDiagnostics("addsite", code);
+       exit(1);
+    }
+    MapPartIdIntoName(apart, apartName);
+    fprintf(STDOUT, "Added replication site %s %s for volume %s\n",
+           as->parms[0].items->data, apartName, as->parms[2].items->data);
+    return 0;
+}
+
+static
+RemoveSite(as)
+     register struct cmd_syndesc *as;
+{
+
+    afs_int32 avolid, aserver, apart, code, err;
+    char apartName[10], avolname[VOLSER_MAXVOLNAME + 1];
+
+    vsu_ExtractName(avolname, as->parms[2].items->data);
+    avolid = vsu_GetVolumeID(avolname, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[2].items->data);
+       exit(1);
+    }
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    apart = volutil_GetPartitionID(as->parms[1].items->data);
+    if (apart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+/*
+ *skip the partition validity check, since it is possible that the partition
+ *has since been decomissioned.
+ */
+/*
+       if (!IsPartValid(apart,aserver,&code)){
+           if(code) PrintError("",code);
+           else fprintf(STDERR,"vos : partition %s does not exist on the server\n",as->parms[1].items->data);
+           exit(1);
+       }
+*/
+    code = UV_RemoveSite(aserver, apart, avolid);
+    if (code) {
+       PrintDiagnostics("remsite", code);
+       exit(1);
+    }
+    MapPartIdIntoName(apart, apartName);
+    fprintf(STDOUT, "Removed replication site %s %s for volume %s\n",
+           as->parms[0].items->data, apartName, as->parms[2].items->data);
+    return 0;
+}
+
+static
+ChangeLocation(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 avolid, aserver, apart, code, err;
+    char apartName[10];
+
+    avolid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[2].items->data);
+       exit(1);
+    }
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    apart = volutil_GetPartitionID(as->parms[1].items->data);
+    if (apart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+    if (!IsPartValid(apart, aserver, &code)) { /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[1].items->data);
+       exit(1);
+    }
+    code = UV_ChangeLocation(aserver, apart, avolid);
+    if (code) {
+       PrintDiagnostics("addsite", code);
+       exit(1);
+    }
+    MapPartIdIntoName(apart, apartName);
+    fprintf(STDOUT, "Changed location to %s %s for volume %s\n",
+           as->parms[0].items->data, apartName, as->parms[2].items->data);
+    return 0;
+}
+
+static
+ListPartitions(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 aserver, code;
+    struct partList dummyPartList;
+    int i;
+    char pname[10];
+    int total, cnt;
+
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+
+
+    code = UV_ListPartitions(aserver, &dummyPartList, &cnt);
+    if (code) {
+       PrintDiagnostics("listpart", code);
+       exit(1);
+    }
+    total = 0;
+    fprintf(STDOUT, "The partitions on the server are:\n");
+    for (i = 0; i < cnt; i++) {
+       if (dummyPartList.partFlags[i] & PARTVALID) {
+           memset(pname, 0, sizeof(pname));
+           MapPartIdIntoName(dummyPartList.partId[i], pname);
+           fprintf(STDOUT, " %10s ", pname);
+           total++;
+           if ((i % 5) == 0 && (i != 0))
+               fprintf(STDOUT, "\n");
+       }
+    }
+    fprintf(STDOUT, "\n");
+    fprintf(STDOUT, "Total: %d\n", total);
+    return 0;
+
+}
+
+static int
+CompareVolName(p1, p2)
+     char *p1, *p2;
+{
+    volintInfo *arg1, *arg2;
+
+    arg1 = (volintInfo *) p1;
+    arg2 = (volintInfo *) p2;
+    return (strcmp(arg1->name, arg2->name));
+
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE XCompareVolName
+ *
+ * Description:
+ *     Comparison routine for volume names coming from an extended
+ *     volume listing.
+ *
+ * Arguments:
+ *     a_obj1P : Char ptr to first extended vol info object
+ *     a_obj1P : Char ptr to second extended vol info object
+ *
+ * Returns:
+ *     The value of strcmp() on the volume names within the passed
+ *     objects (i,e., -1, 0, or 1).
+ *
+ * Environment:
+ *     Passed to qsort() as the designated comparison routine.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+static int
+XCompareVolName(a_obj1P, a_obj2P)
+     char *a_obj1P, *a_obj2P;
+
+{                              /*XCompareVolName */
+
+    return (strcmp
+           (((struct volintXInfo *)(a_obj1P))->name,
+            ((struct volintXInfo *)(a_obj2P))->name));
+
+}                              /*XCompareVolName */
+
+static int
+CompareVolID(p1, p2)
+     char *p1, *p2;
+{
+    volintInfo *arg1, *arg2;
+
+    arg1 = (volintInfo *) p1;
+    arg2 = (volintInfo *) p2;
+    if (arg1->volid == arg2->volid)
+       return 0;
+    if (arg1->volid > arg2->volid)
+       return 1;
+    else
+       return -1;
+
+}
+
+/*------------------------------------------------------------------------
+ * PRIVATE XCompareVolID
+ *
+ * Description:
+ *     Comparison routine for volume IDs coming from an extended
+ *     volume listing.
+ *
+ * Arguments:
+ *     a_obj1P : Char ptr to first extended vol info object
+ *     a_obj1P : Char ptr to second extended vol info object
+ *
+ * Returns:
+ *     The value of strcmp() on the volume names within the passed
+ *     objects (i,e., -1, 0, or 1).
+ *
+ * Environment:
+ *     Passed to qsort() as the designated comparison routine.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+static int
+XCompareVolID(a_obj1P, a_obj2P)
+     char *a_obj1P, *a_obj2P;
+
+{                              /*XCompareVolID */
+
+    afs_int32 id1, id2;                /*Volume IDs we're comparing */
+
+    id1 = ((struct volintXInfo *)(a_obj1P))->volid;
+    id2 = ((struct volintXInfo *)(a_obj2P))->volid;
+    if (id1 == id2)
+       return (0);
+    else if (id1 > id2)
+       return (1);
+    else
+       return (-1);
+
+}                              /*XCompareVolID */
+
+/*------------------------------------------------------------------------
+ * PRIVATE ListVolumes
+ *
+ * Description:
+ *     Routine used to list volumes, contacting the Volume Server
+ *     directly, bypassing the VLDB.
+ *
+ * Arguments:
+ *     as : Ptr to parsed command line arguments.
+ *
+ * Returns:
+ *     0                       Successful operation
+ *
+ * Environment:
+ *     Nothing interesting.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+static
+ListVolumes(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 apart, int32list, fast;
+    afs_int32 aserver, code;
+    volintInfo *pntr, *oldpntr;
+    afs_int32 count;
+    int i;
+    char *base;
+    volintXInfo *xInfoP, *origxInfoP;  /*Ptr to current/orig extended vol info */
+    int wantExtendedInfo;      /*Do we want extended vol info? */
+
+    char pname[10];
+    struct partList dummyPartList;
+    int all;
+    int quiet, cnt;
+
+    apart = -1;
+    fast = 0;
+    int32list = 0;
+
+    if (as->parms[3].items)
+       int32list = 1;
+    if (as->parms[4].items)
+       quiet = 1;
+    else
+       quiet = 0;
+    if (as->parms[2].items)
+       fast = 1;
+    if (fast)
+       all = 0;
+    else
+       all = 1;
+    if (as->parms[5].items) {
+       /*
+        * We can't coexist with the fast flag.
+        */
+       if (fast) {
+           fprintf(STDERR,
+                   "vos: Can't use the -fast and -extended flags together\n");
+           exit(1);
+       }
+
+       /*
+        * We need to turn on ``long'' listings to get the full effect.
+        */
+       wantExtendedInfo = 1;
+       int32list = 1;
+    } else
+       wantExtendedInfo = 0;
+    if (as->parms[1].items) {
+       apart = volutil_GetPartitionID(as->parms[1].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       dummyPartList.partId[0] = apart;
+       dummyPartList.partFlags[0] = PARTVALID;
+       cnt = 1;
+    }
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+
+    if (apart != -1) {
+       if (!IsPartValid(apart, aserver, &code)) {      /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                       "vos : partition %s does not exist on the server\n",
+                       as->parms[1].items->data);
+           exit(1);
+       }
+    } else {
+       code = UV_ListPartitions(aserver, &dummyPartList, &cnt);
+       if (code) {
+           PrintDiagnostics("listvol", code);
+           exit(1);
+       }
+    }
+    for (i = 0; i < cnt; i++) {
+       if (dummyPartList.partFlags[i] & PARTVALID) {
+           if (wantExtendedInfo)
+               code =
+                   UV_XListVolumes(aserver, dummyPartList.partId[i], all,
+                                   &xInfoP, &count);
+           else
+               code =
+                   UV_ListVolumes(aserver, dummyPartList.partId[i], all,
+                                  &pntr, &count);
+           if (code) {
+               PrintDiagnostics("listvol", code);
+               if (pntr)
+                   free(pntr);
+               exit(1);
+           }
+           if (wantExtendedInfo) {
+               origxInfoP = xInfoP;
+               base = (char *)xInfoP;
+           } else {
+               oldpntr = pntr;
+               base = (char *)pntr;
+           }
+
+           if (!fast) {
+               if (wantExtendedInfo)
+                   qsort(base, count, sizeof(volintXInfo), XCompareVolName);
+               else
+                   qsort(base, count, sizeof(volintInfo), CompareVolName);
+           } else {
+               if (wantExtendedInfo)
+                   qsort(base, count, sizeof(volintXInfo), XCompareVolID);
+               else
+                   qsort(base, count, sizeof(volintInfo), CompareVolID);
+           }
+           MapPartIdIntoName(dummyPartList.partId[i], pname);
+           if (!quiet)
+               fprintf(STDOUT,
+                       "Total number of volumes on server %s partition %s: %lu \n",
+                       as->parms[0].items->data, pname,
+                       (unsigned long)count);
+           if (wantExtendedInfo) {
+               XDisplayVolumes(aserver, dummyPartList.partId[i], origxInfoP,
+                               count, int32list, fast, quiet);
+               if (xInfoP)
+                   free(xInfoP);
+               xInfoP = (volintXInfo *) 0;
+           } else {
+#ifdef FULL_LISTVOL_SWITCH
+               if (as->parms[6].items)
+                   DisplayVolumes2(aserver, dummyPartList.partId[i], oldpntr,
+                                   count);
+               else
+#endif /* FULL_LISTVOL_SWITCH */
+                   DisplayVolumes(aserver, dummyPartList.partId[i], oldpntr,
+                                  count, int32list, fast, quiet);
+               if (pntr)
+                   free(pntr);
+               pntr = (volintInfo *) 0;
+           }
+       }
+    }
+    return 0;
+}
+
+static
+SyncVldb(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 pnum, code;      /* part name */
+    char part[10];
+    int flags = 0;
+    char *volname = 0;
+
+    tserver = 0;
+    if (as->parms[0].items) {
+       tserver = GetServer(as->parms[0].items->data);
+       if (!tserver) {
+           fprintf(STDERR, "vos: host '%s' not found in host table\n",
+                   as->parms[0].items->data);
+           exit(1);
+       }
+    }
+
+    if (as->parms[1].items) {
+       pnum = volutil_GetPartitionID(as->parms[1].items->data);
+       if (pnum < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       if (!IsPartValid(pnum, tserver, &code)) {       /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                       "vos: partition %s does not exist on the server\n",
+                       as->parms[1].items->data);
+           exit(1);
+       }
+       flags = 1;
+
+       if (!tserver) {
+           fprintf(STDERR,
+                   "The -partition option requires a -server option\n");
+           exit(1);
+       }
+    }
+
+    if (as->parms[2].items) {
+       /* Synchronize an individual volume */
+       volname = as->parms[2].items->data;
+       code = UV_SyncVolume(tserver, pnum, volname, flags);
+    } else {
+       if (!tserver) {
+           fprintf(STDERR,
+                   "Without a -volume option, the -server option is required\n");
+           exit(1);
+       }
+       code = UV_SyncVldb(tserver, pnum, flags, 0 /*unused */ );
+    }
+
+    if (code) {
+       PrintDiagnostics("syncvldb", code);
+       exit(1);
+    }
+
+    /* Print a summary of what we did */
+    if (volname)
+       fprintf(STDOUT, "VLDB volume %s synchronized", volname);
+    else
+       fprintf(STDOUT, "VLDB synchronized");
+    if (tserver) {
+       fprintf(STDOUT, " with state of server %s", as->parms[0].items->data);
+    }
+    if (flags) {
+       MapPartIdIntoName(pnum, part);
+       fprintf(STDOUT, " partition %s\n", part);
+    }
+    fprintf(STDOUT, "\n");
+
+    return 0;
+}
+
+static
+SyncServer(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 pnum, code;      /* part name */
+    char part[10];
+
+    int flags = 0;
+
+    tserver = GetServer(as->parms[0].items->data);
+    if (!tserver) {
+       fprintf(STDERR, "vos: host '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    if (as->parms[1].items) {
+       pnum = volutil_GetPartitionID(as->parms[1].items->data);
+       if (pnum < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       if (!IsPartValid(pnum, tserver, &code)) {       /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                       "vos : partition %s does not exist on the server\n",
+                       as->parms[1].items->data);
+           exit(1);
+       }
+       flags = 1;
+    } else {
+        pnum = -1;
+    }
+
+    code = UV_SyncServer(tserver, pnum, flags, 0 /*unused */ );
+    if (code) {
+       PrintDiagnostics("syncserv", code);
+       exit(1);
+    }
+    if (flags) {
+       MapPartIdIntoName(pnum, part);
+       fprintf(STDOUT, "Server %s partition %s synchronized with VLDB\n",
+               as->parms[0].items->data, part);
+    } else
+       fprintf(STDOUT, "Server %s synchronized with VLDB\n",
+               as->parms[0].items->data);
+    return 0;
+
+}
+
+static
+VolumeInfoCmd(name)
+     char *name;
+{
+    struct nvldbentry entry;
+    afs_int32 vcode;
+
+    /* The vlserver will handle names with the .readonly
+     * and .backup extension as well as volume ids.
+     */
+    vcode = VLDB_GetEntryByName(name, &entry);
+    if (vcode) {
+       PrintError("", vcode);
+       exit(1);
+    }
+    MapHostToNetwork(&entry);
+    EnumerateEntry(&entry);
+
+    /* Defect #3027: grubby check to handle locked volume.
+     * If VLOP_ALLOPERS is set, the entry is locked.
+     * Leave this routine as is, but put in correct check.
+     */
+    if (entry.flags & VLOP_ALLOPERS)
+       fprintf(STDOUT, "    Volume is currently LOCKED  \n");
+
+    return 0;
+}
+
+static
+VolumeZap(as)
+     register struct cmd_syndesc *as;
+
+{
+    struct nvldbentry entry;
+    afs_int32 volid, code, server, part, zapbackupid = 0, backupid = 0, err;
+
+    if (as->parms[3].items) {
+       /* force flag is on, use the other version */
+       return NukeVolume(as);
+    }
+
+    if (as->parms[4].items) {
+       zapbackupid = 1;
+    }
+
+    volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[2].items->data);
+       exit(1);
+    }
+    part = volutil_GetPartitionID(as->parms[1].items->data);
+    if (part < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+    server = GetServer(as->parms[0].items->data);
+    if (!server) {
+       fprintf(STDERR, "vos: host '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    if (!IsPartValid(part, server, &code)) {   /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[1].items->data);
+       exit(1);
+    }
+    code = VLDB_GetEntryByID(volid, -1, &entry);
+    if (!code) {
+       if (volid == entry.volumeId[RWVOL])
+           backupid = entry.volumeId[BACKVOL];
+       fprintf(STDERR,
+               "Warning: Entry for volume number %lu exists in VLDB (but we're zapping it anyway!)\n",
+               (unsigned long)volid);
+    }
+    if (zapbackupid) {
+       volintInfo *pntr = (volintInfo *) 0;
+
+       if (!backupid) {
+           code = UV_ListOneVolume(server, part, volid, &pntr);
+           if (!code) {
+               if (volid == pntr->parentID)
+                   backupid = pntr->backupID;
+               if (pntr)
+                   free(pntr);
+           }
+       }
+       if (backupid) {
+           code = UV_VolumeZap(server, part, backupid);
+           if (code) {
+               PrintDiagnostics("zap", code);
+               exit(1);
+           }
+           fprintf(STDOUT, "Backup Volume %lu deleted\n",
+                   (unsigned long)backupid);
+       }
+    }
+    code = UV_VolumeZap(server, part, volid);
+    if (code) {
+       PrintDiagnostics("zap", code);
+       exit(1);
+    }
+    fprintf(STDOUT, "Volume %lu deleted\n", (unsigned long)volid);
+
+    return 0;
+}
+
+static
+VolserStatus(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 server, code;
+    transDebugInfo *pntr, *oldpntr;
+    afs_int32 count;
+    int i;
+    char pname[10];
+
+    server = GetServer(as->parms[0].items->data);
+    if (!server) {
+       fprintf(STDERR, "vos: host '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    code = UV_VolserStatus(server, &pntr, &count);
+    if (code) {
+       PrintDiagnostics("status", code);
+       exit(1);
+    }
+    oldpntr = pntr;
+    if (count == 0)
+       fprintf(STDOUT, "No active transactions on %s\n",
+               as->parms[0].items->data);
+    else {
+       fprintf(STDOUT, "Total transactions: %d\n", count);
+    }
+    for (i = 0; i < count; i++) {
+       /*print out the relevant info */
+       fprintf(STDOUT, "--------------------------------------\n");
+       fprintf(STDOUT, "transaction: %lu  created: %s",
+               (unsigned long)pntr->tid, ctime((time_t *) & pntr->time));
+       if (pntr->returnCode) {
+           fprintf(STDOUT, "returnCode: %lu\n",
+                   (unsigned long)pntr->returnCode);
+       }
+       if (pntr->iflags) {
+           fprintf(STDOUT, "attachFlags:  ");
+           switch (pntr->iflags) {
+           case ITOffline:
+               fprintf(STDOUT, "offline ");
+               break;
+           case ITBusy:
+               fprintf(STDOUT, "busy ");
+               break;
+           case ITReadOnly:
+               fprintf(STDOUT, "readonly ");
+               break;
+           case ITCreate:
+               fprintf(STDOUT, "create ");
+               break;
+           case ITCreateVolID:
+               fprintf(STDOUT, "create volid ");
+               break;
+           }
+           fprintf(STDOUT, "\n");
+       }
+       if (pntr->vflags) {
+           fprintf(STDOUT, "volumeStatus: ");
+           switch (pntr->vflags) {
+           case VTDeleteOnSalvage:
+               fprintf(STDOUT, "deleteOnSalvage ");
+           case VTOutOfService:
+               fprintf(STDOUT, "outOfService ");
+           case VTDeleted:
+               fprintf(STDOUT, "deleted ");
+           }
+           fprintf(STDOUT, "\n");
+       }
+       if (pntr->tflags) {
+           fprintf(STDOUT, "transactionFlags: ");
+           fprintf(STDOUT, "delete\n");
+       }
+       MapPartIdIntoName(pntr->partition, pname);
+       fprintf(STDOUT, "volume: %lu  partition: %s  procedure: %s\n",
+               (unsigned long)pntr->volid, pname, pntr->lastProcName);
+       if (pntr->callValid) {
+           fprintf(STDOUT,
+                   "packetRead: %lu  lastReceiveTime: %d  packetSend: %lu  lastSendTime: %d\n",
+                   (unsigned long)pntr->readNext, pntr->lastReceiveTime,
+                   (unsigned long)pntr->transmitNext, pntr->lastSendTime);
+       }
+       pntr++;
+       fprintf(STDOUT, "--------------------------------------\n");
+       fprintf(STDOUT, "\n");
+    }
+    if (oldpntr)
+       free(oldpntr);
+    return 0;
+}
+
+static
+RenameVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 code1, code2, code;
+    struct nvldbentry entry;
+
+    code1 = VLDB_GetEntryByName(as->parms[0].items->data, &entry);
+    if (code1) {
+       fprintf(STDERR, "vos: Could not find entry for volume %s\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    code2 = VLDB_GetEntryByName(as->parms[1].items->data, &entry);
+    if ((!code1) && (!code2)) {        /*the newname already exists */
+       fprintf(STDERR, "vos: volume %s already exists\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+
+    if (code1 && code2) {
+       fprintf(STDERR, "vos: Could not find entry for volume %s or %s\n",
+               as->parms[0].items->data, as->parms[1].items->data);
+       exit(1);
+    }
+    if (!VolNameOK(as->parms[0].items->data)) {
+       fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    if (!ISNAMEVALID(as->parms[1].items->data)) {
+       fprintf(STDERR,
+               "vos: the new volume name %s exceeds the size limit of %d\n",
+               as->parms[1].items->data, VOLSER_OLDMAXVOLNAME - 10);
+       exit(1);
+    }
+    if (!VolNameOK(as->parms[1].items->data)) {
+       fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+    if (IsNumeric(as->parms[1].items->data)) {
+       fprintf(STDERR, "Illegal volume name %s, should not be a number\n",
+               as->parms[1].items->data);
+       exit(1);
+    }
+    MapHostToNetwork(&entry);
+    code =
+       UV_RenameVolume(&entry, as->parms[0].items->data,
+                       as->parms[1].items->data);
+    if (code) {
+       PrintDiagnostics("rename", code);
+       exit(1);
+    }
+    fprintf(STDOUT, "Renamed volume %s to %s\n", as->parms[0].items->data,
+           as->parms[1].items->data);
+    return 0;
+}
+
+GetVolumeInfo(volid, server, part, voltype, rentry)
+     afs_int32 *server, volid, *part, *voltype;
+     register struct nvldbentry *rentry;
+{
+    afs_int32 vcode;
+    int i, index = -1;
+
+    vcode = VLDB_GetEntryByID(volid, -1, rentry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not fetch the entry for volume %lu from VLDB \n",
+               (unsigned long)volid);
+       PrintError("", vcode);
+       return (vcode);
+    }
+    MapHostToNetwork(rentry);
+    if (volid == rentry->volumeId[ROVOL]) {
+       *voltype = ROVOL;
+       for (i = 0; i < rentry->nServers; i++) {
+           if ((index == -1) && (rentry->serverFlags[i] & ITSROVOL)
+               && !(rentry->serverFlags[i] & RO_DONTUSE))
+               index = i;
+       }
+       if (index == -1) {
+           fprintf(STDERR,
+                   "RO volume is not found in VLDB entry for volume %lu\n",
+                   (unsigned long)volid);
+           return -1;
+       }
+
+       *server = rentry->serverNumber[index];
+       *part = rentry->serverPartition[index];
+       return 0;
+    }
+
+    index = Lp_GetRwIndex(rentry);
+    if (index == -1) {
+       fprintf(STDERR,
+               "RW Volume is not found in VLDB entry for volume %lu\n",
+               (unsigned long)volid);
+       return -1;
+    }
+    if (volid == rentry->volumeId[RWVOL]) {
+       *voltype = RWVOL;
+       *server = rentry->serverNumber[index];
+       *part = rentry->serverPartition[index];
+       return 0;
+    }
+    if (volid == rentry->volumeId[BACKVOL]) {
+       *voltype = BACKVOL;
+       *server = rentry->serverNumber[index];
+       *part = rentry->serverPartition[index];
+       return 0;
+    }
+    fprintf(STDERR,
+            "unexpected volume type for volume %lu\n",
+            (unsigned long)volid);
+    return -1;
+}
+
+static
+DeleteEntry(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 apart;
+    afs_int32 avolid;
+    afs_int32 vcode;
+    struct VldbListByAttributes attributes;
+    nbulkentries arrayEntries;
+    register struct nvldbentry *vllist;
+    struct cmd_item *itp;
+    afs_int32 nentries;
+    int j;
+    char prefix[VOLSER_MAXVOLNAME + 1];
+    int seenprefix = 0;
+    afs_int32 totalBack = 0, totalFail = 0, err;
+
+    if (as->parms[0].items) {  /* -id */
+       if (as->parms[1].items || as->parms[2].items || as->parms[3].items) {
+           fprintf(STDERR,
+                   "You cannot use -server, -partition, or -prefix with the -id argument\n");
+           exit(-2);
+       }
+       for (itp = as->parms[0].items; itp; itp = itp->next) {
+           avolid = vsu_GetVolumeID(itp->data, cstruct, &err);
+           if (avolid == 0) {
+               if (err)
+                   PrintError("", err);
+               else
+                   fprintf(STDERR, "vos: can't find volume '%s'\n",
+                           itp->data);
+               continue;
+           }
+           if (as->parms[4].items) {   /* -noexecute */
+               fprintf(STDOUT, "Would have deleted VLDB entry for %s \n",
+                       itp->data);
+               fflush(STDOUT);
+               continue;
+           }
+           vcode = ubik_Call(VL_DeleteEntry, cstruct, 0, avolid, RWVOL);
+           if (vcode) {
+               fprintf(STDERR, "Could not delete entry for volume %s\n",
+                       itp->data);
+               fprintf(STDERR,
+                       "You must specify a RW volume name or ID "
+                       "(the entire VLDB entry will be deleted)\n");
+               PrintError("", vcode);
+               totalFail++;
+               continue;
+           }
+           totalBack++;
+       }
+       fprintf(STDOUT, "Deleted %d VLDB entries\n", totalBack);
+       return (totalFail);
+    }
+
+    if (!as->parms[1].items && !as->parms[2].items && !as->parms[3].items) {
+       fprintf(STDERR, "You must specify an option\n");
+       exit(-2);
+    }
+
+    /* Zero out search attributes */
+    memset(&attributes, 0, sizeof(struct VldbListByAttributes));
+
+    if (as->parms[1].items) {  /* -prefix */
+       strncpy(prefix, as->parms[1].items->data, VOLSER_MAXVOLNAME);
+       seenprefix = 1;
+       if (!as->parms[2].items && !as->parms[3].items) {       /* a single entry only */
+           fprintf(STDERR,
+                   "You must provide -server with the -prefix argument\n");
+           exit(-2);
+       }
+    }
+
+    if (as->parms[2].items) {  /* -server */
+       afs_int32 aserver;
+       aserver = GetServer(as->parms[2].items->data);
+       if (aserver == 0) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[2].items->data);
+           exit(-1);
+       }
+       attributes.server = ntohl(aserver);
+       attributes.Mask |= VLLIST_SERVER;
+    }
+
+    if (as->parms[3].items) {  /* -partition */
+       if (!as->parms[2].items) {
+           fprintf(STDERR,
+                   "You must provide -server with the -partition argument\n");
+           exit(-2);
+       }
+       apart = volutil_GetPartitionID(as->parms[3].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[3].items->data);
+           exit(-1);
+       }
+       attributes.partition = apart;
+       attributes.Mask |= VLLIST_PARTITION;
+    }
+
+    /* Print status line of what we are doing */
+    fprintf(STDOUT, "Deleting VLDB entries for ");
+    if (as->parms[2].items) {
+       fprintf(STDOUT, "server %s ", as->parms[2].items->data);
+    }
+    if (as->parms[3].items) {
+       char pname[10];
+       MapPartIdIntoName(apart, pname);
+       fprintf(STDOUT, "partition %s ", pname);
+    }
+    if (seenprefix) {
+       fprintf(STDOUT, "which are prefixed with %s ", prefix);
+    }
+    fprintf(STDOUT, "\n");
+    fflush(STDOUT);
+
+    /* Get all the VLDB entries on a server and/or partition */
+    memset(&arrayEntries, 0, sizeof(arrayEntries));
+    vcode = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries);
+    if (vcode) {
+       fprintf(STDERR, "Could not access the VLDB for attributes\n");
+       PrintError("", vcode);
+       exit(-1);
+    }
+
+    /* Process each entry */
+    for (j = 0; j < nentries; j++) {
+       vllist = &arrayEntries.nbulkentries_val[j];
+       if (seenprefix) {
+           /* It only deletes the RW volumes */
+           if (strncmp(vllist->name, prefix, strlen(prefix))) {
+               if (verbose) {
+                   fprintf(STDOUT,
+                           "Omitting to delete %s due to prefix %s mismatch\n",
+                           vllist->name, prefix);
+               }
+               fflush(STDOUT);
+               continue;
+           }
+       }
+
+       if (as->parms[4].items) {       /* -noexecute */
+           fprintf(STDOUT, "Would have deleted VLDB entry for %s \n",
+                   vllist->name);
+           fflush(STDOUT);
+           continue;
+       }
+
+       /* Only matches the RW volume name */
+       avolid = vllist->volumeId[RWVOL];
+       vcode = ubik_Call(VL_DeleteEntry, cstruct, 0, avolid, RWVOL);
+       if (vcode) {
+           fprintf(STDOUT, "Could not delete VDLB entry for  %s\n",
+                   vllist->name);
+           totalFail++;
+           PrintError("", vcode);
+           continue;
+       } else {
+           totalBack++;
+           if (verbose)
+               fprintf(STDOUT, "Deleted VLDB entry for %s \n", vllist->name);
+       }
+       fflush(STDOUT);
+    }                          /*for */
+
+    fprintf(STDOUT, "----------------------\n");
+    fprintf(STDOUT,
+           "Total VLDB entries deleted: %lu; failed to delete: %lu\n",
+           (unsigned long)totalBack, (unsigned long)totalFail);
+    if (arrayEntries.nbulkentries_val)
+       free(arrayEntries.nbulkentries_val);
+    return 0;
+}
+
+
+static int
+CompareVldbEntryByName(p1, p2)
+     char *p1, *p2;
+{
+    struct nvldbentry *arg1, *arg2;
+
+    arg1 = (struct nvldbentry *)p1;
+    arg2 = (struct nvldbentry *)p2;
+    return (strcmp(arg1->name, arg2->name));
+}
+
+/*
+static int CompareVldbEntry(p1,p2)
+char *p1,*p2;
+{
+    struct nvldbentry *arg1,*arg2;
+    int i;
+    int pos1, pos2;
+    char comp1[100],comp2[100];
+    char temp1[20],temp2[20];
+
+    arg1 = (struct nvldbentry *)p1;
+    arg2 = (struct nvldbentry *)p2;
+    pos1 = -1;
+    pos2 = -1;
+
+    for(i = 0; i < arg1->nServers; i++)
+       if(arg1->serverFlags[i] & ITSRWVOL) pos1 = i;
+    for(i = 0; i < arg2->nServers; i++)
+       if(arg2->serverFlags[i] & ITSRWVOL) pos2 = i;
+    if(pos1 == -1 || pos2 == -1){
+       pos1 = 0;
+       pos2 = 0;
+    }
+    sprintf(comp1,"%10u",arg1->serverNumber[pos1]);
+    sprintf(comp2,"%10u",arg2->serverNumber[pos2]);
+    sprintf(temp1,"%10u",arg1->serverPartition[pos1]);
+    sprintf(temp2,"%10u",arg2->serverPartition[pos2]);
+    strcat(comp1,temp1);
+    strcat(comp2,temp2);
+    strcat(comp1,arg1->name);
+    strcat(comp1,arg2->name);
+    return(strcmp(comp1,comp2));
+
+}
+
+*/
+static
+ListVLDB(as)
+     struct cmd_syndesc *as;
+{
+    afs_int32 apart;
+    afs_int32 aserver, code;
+    afs_int32 vcode;
+    struct VldbListByAttributes attributes;
+    nbulkentries arrayEntries;
+    struct nvldbentry *vllist, *tarray = 0, *ttarray;
+    afs_int32 centries, nentries = 0, tarraysize, parraysize;
+    int j;
+    char pname[10];
+    int quiet, sort, lock;
+    afs_int32 thisindex, nextindex;
+
+    aserver = 0;
+    apart = 0;
+
+    attributes.Mask = 0;
+    lock = (as->parms[3].items ? 1 : 0);       /* -lock   flag */
+    quiet = (as->parms[4].items ? 1 : 0);      /* -quit   flag */
+    sort = (as->parms[5].items ? 0 : 1);       /* -nosort flag */
+
+    /* If the volume name is given, Use VolumeInfoCmd to look it up
+     * and not ListAttributes.
+     */
+    if (as->parms[0].items) {
+       if (lock) {
+           fprintf(STDERR,
+                   "vos: illegal use of '-locked' switch, need to specify server and/or partition\n");
+           exit(1);
+       }
+       code = VolumeInfoCmd(as->parms[0].items->data);
+       if (code) {
+           PrintError("", code);
+           exit(1);
+       }
+       return 0;
+    }
+
+    /* Server specified */
+    if (as->parms[1].items) {
+       aserver = GetServer(as->parms[1].items->data);
+       if (aserver == 0) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       attributes.server = ntohl(aserver);
+       attributes.Mask |= VLLIST_SERVER;
+    }
+
+    /* Partition specified */
+    if (as->parms[2].items) {
+       apart = volutil_GetPartitionID(as->parms[2].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[2].items->data);
+           exit(1);
+       }
+       attributes.partition = apart;
+       attributes.Mask |= VLLIST_PARTITION;
+    }
+
+    if (lock) {
+       attributes.Mask |= VLLIST_FLAG;
+       attributes.flag = VLOP_ALLOPERS;
+    }
+
+    /* Print header information */
+    if (!quiet) {
+       MapPartIdIntoName(apart, pname);
+       fprintf(STDOUT, "VLDB entries for %s %s%s%s %s\n",
+               (as->parms[1].items ? "server" : "all"),
+               (as->parms[1].items ? as->parms[1].items->data : "servers"),
+               (as->parms[2].items ? " partition " : ""),
+               (as->parms[2].items ? pname : ""),
+               (lock ? "which are locked:" : ""));
+    }
+
+    for (thisindex = 0; (thisindex != -1); thisindex = nextindex) {
+       memset(&arrayEntries, 0, sizeof(arrayEntries));
+       centries = 0;
+       nextindex = -1;
+
+       vcode =
+           VLDB_ListAttributesN2(&attributes, 0, thisindex, &centries,
+                                 &arrayEntries, &nextindex);
+       if (vcode == RXGEN_OPCODE) {
+           /* Vlserver not running with ListAttributesN2. Fall back */
+           vcode =
+               VLDB_ListAttributes(&attributes, &centries, &arrayEntries);
+           nextindex = -1;
+       }
+       if (vcode) {
+           fprintf(STDERR, "Could not access the VLDB for attributes\n");
+           PrintError("", vcode);
+           exit(1);
+       }
+       nentries += centries;
+
+       /* We don't sort, so just print the entries now */
+       if (!sort) {
+           for (j = 0; j < centries; j++) {    /* process each entry */
+               vllist = &arrayEntries.nbulkentries_val[j];
+               MapHostToNetwork(vllist);
+               EnumerateEntry(vllist);
+
+               if (vllist->flags & VLOP_ALLOPERS)
+                   fprintf(STDOUT, "    Volume is currently LOCKED  \n");
+           }
+       }
+
+       /* So we sort. First we must collect all the entries and keep
+        * them in memory.
+        */
+       else if (centries > 0) {
+           if (!tarray) {
+               /* steal away the first bulk entries array */
+               tarray = (struct nvldbentry *)arrayEntries.nbulkentries_val;
+               tarraysize = centries * sizeof(struct nvldbentry);
+               arrayEntries.nbulkentries_val = 0;
+           } else {
+               /* Grow the tarray to keep the extra entries */
+               parraysize = (centries * sizeof(struct nvldbentry));
+               ttarray =
+                   (struct nvldbentry *)realloc(tarray,
+                                                tarraysize + parraysize);
+               if (!ttarray) {
+                   fprintf(STDERR,
+                           "Could not allocate enough space for  the VLDB entries\n");
+                   goto bypass;
+               }
+               tarray = ttarray;
+
+               /* Copy them in */
+               memcpy(((char *)tarray) + tarraysize,
+                      (char *)arrayEntries.nbulkentries_val, parraysize);
+               tarraysize += parraysize;
+           }
+       }
+
+       /* Free the bulk array */
+       if (arrayEntries.nbulkentries_val) {
+           free(arrayEntries.nbulkentries_val);
+           arrayEntries.nbulkentries_val = 0;
+       }
+    }
+
+    /* Here is where we now sort all the entries and print them */
+    if (sort && (nentries > 0)) {
+       qsort((char *)tarray, nentries, sizeof(struct nvldbentry),
+             CompareVldbEntryByName);
+       for (vllist = tarray, j = 0; j < nentries; j++, vllist++) {
+           MapHostToNetwork(vllist);
+           EnumerateEntry(vllist);
+
+           if (vllist->flags & VLOP_ALLOPERS)
+               fprintf(STDOUT, "    Volume is currently LOCKED  \n");
+       }
+    }
+
+  bypass:
+    if (!quiet)
+       fprintf(STDOUT, "\nTotal entries: %lu\n", (unsigned long)nentries);
+    if (tarray)
+       free(tarray);
+    return 0;
+}
+
+static
+BackSys(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 apart = 0, avolid;
+    afs_int32 aserver = 0, code, aserver1, apart1;
+    afs_int32 vcode;
+    struct VldbListByAttributes attributes;
+    nbulkentries arrayEntries;
+    register struct nvldbentry *vllist;
+    afs_int32 nentries;
+    int j;
+    char pname[10];
+    int seenprefix, seenxprefix, exclude, ex, exp, noaction;
+    afs_int32 totalBack = 0;
+    afs_int32 totalFail = 0;
+    int previdx = -1, error, same;
+    int comp = 0;
+    struct cmd_item *ti;
+    char *ccode;
+    int match;
+
+    memset(&attributes, 0, sizeof(struct VldbListByAttributes));
+    attributes.Mask = 0;
+
+    seenprefix = (as->parms[0].items ? 1 : 0);
+    exclude = (as->parms[3].items ? 1 : 0);
+    seenxprefix = (as->parms[4].items ? 1 : 0);
+    noaction = (as->parms[5].items ? 1 : 0);
+
+    if (as->parms[1].items) {  /* -server */
+       aserver = GetServer(as->parms[1].items->data);
+       if (aserver == 0) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       attributes.server = ntohl(aserver);
+       attributes.Mask |= VLLIST_SERVER;
+    }
+
+    if (as->parms[2].items) {  /* -partition */
+       apart = volutil_GetPartitionID(as->parms[2].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[2].items->data);
+           exit(1);
+       }
+       attributes.partition = apart;
+       attributes.Mask |= VLLIST_PARTITION;
+    }
+
+    /* Check to make sure the prefix and xprefix expressions compile ok */
+    if (seenprefix) {
+       for (ti = as->parms[0].items; ti; ti = ti->next) {
+           if (strncmp(ti->data, "^", 1) == 0) {
+#ifdef HAVE_POSIX_REGEX
+               regex_t re;
+               char errbuf[256];
+
+               code = regcomp(&re, ti->data, REG_NOSUB);
+               if (code != 0) {
+                   regerror(code, &re, errbuf, sizeof errbuf);
+                   fprintf(STDERR,
+                           "Unrecognizable -prefix regular expression: '%s': %s\n",
+                           ti->data, errbuf);
+                   exit(1);
+               }
+               regfree(&re);
+#else
+               ccode = (char *)re_comp(ti->data);
+               if (ccode) {
+                   fprintf(STDERR,
+                           "Unrecognizable -prefix regular expression: '%s': %s\n",
+                           ti->data, ccode);
+                   exit(1);
+               }
+#endif
+           }
+       }
+    }
+    if (seenxprefix) {
+       for (ti = as->parms[4].items; ti; ti = ti->next) {
+           if (strncmp(ti->data, "^", 1) == 0) {
+#ifdef HAVE_POSIX_REGEX
+               regex_t re;
+               char errbuf[256];
+
+               code = regcomp(&re, ti->data, REG_NOSUB);
+               if (code != 0) {
+                   regerror(code, &re, errbuf, sizeof errbuf);
+                   fprintf(STDERR,
+                           "Unrecognizable -xprefix regular expression: '%s': %s\n",
+                           ti->data, errbuf);
+                   exit(1);
+               }
+               regfree(&re);
+#else
+               ccode = (char *)re_comp(ti->data);
+               if (ccode) {
+                   fprintf(STDERR,
+                           "Unrecognizable -xprefix regular expression: '%s': %s\n",
+                           ti->data, ccode);
+                   exit(1);
+               }
+#endif
+           }
+       }
+    }
+
+    memset(&arrayEntries, 0, sizeof(arrayEntries));    /* initialize to hint the stub to alloc space */
+    vcode = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries);
+    if (vcode) {
+       fprintf(STDERR, "Could not access the VLDB for attributes\n");
+       PrintError("", vcode);
+       exit(1);
+    }
+
+    if (as->parms[1].items || as->parms[2].items || verbose) {
+       fprintf(STDOUT, "%s up volumes",
+               (noaction ? "Would have backed" : "Backing"));
+
+       if (as->parms[1].items) {
+           fprintf(STDOUT, " on server %s", as->parms[1].items->data);
+       } else if (as->parms[2].items) {
+           fprintf(STDOUT, " for all servers");
+       }
+
+       if (as->parms[2].items) {
+           MapPartIdIntoName(apart, pname);
+           fprintf(STDOUT, " partition %s", pname);
+       }
+
+       if (seenprefix || (!seenprefix && seenxprefix)) {
+           ti = (seenprefix ? as->parms[0].items : as->parms[4].items);
+           ex = (seenprefix ? exclude : !exclude);
+           exp = (strncmp(ti->data, "^", 1) == 0);
+           fprintf(STDOUT, " which %smatch %s '%s'", (ex ? "do not " : ""),
+                   (exp ? "expression" : "prefix"), ti->data);
+           for (ti = ti->next; ti; ti = ti->next) {
+               exp = (strncmp(ti->data, "^", 1) == 0);
+               printf(" %sor %s '%s'", (ex ? "n" : ""),
+                      (exp ? "expression" : "prefix"), ti->data);
+           }
+       }
+
+       if (seenprefix && seenxprefix) {
+           ti = as->parms[4].items;
+           exp = (strncmp(ti->data, "^", 1) == 0);
+           fprintf(STDOUT, " %swhich match %s '%s'",
+                   (exclude ? "adding those " : "removing those "),
+                   (exp ? "expression" : "prefix"), ti->data);
+           for (ti = ti->next; ti; ti = ti->next) {
+               exp = (strncmp(ti->data, "^", 1) == 0);
+               printf(" or %s '%s'", (exp ? "expression" : "prefix"),
+                      ti->data);
+           }
+       }
+       fprintf(STDOUT, " .. ");
+       if (verbose)
+           fprintf(STDOUT, "\n");
+       fflush(STDOUT);
+    }
+
+    for (j = 0; j < nentries; j++) {   /* process each vldb entry */
+       vllist = &arrayEntries.nbulkentries_val[j];
+
+       if (seenprefix) {
+           for (ti = as->parms[0].items; ti; ti = ti->next) {
+               if (strncmp(ti->data, "^", 1) == 0) {
+#ifdef HAVE_POSIX_REGEX
+                   regex_t re;
+                   char errbuf[256];
+
+                   /* XXX -- should just do the compile once! */
+                   code = regcomp(&re, ti->data, REG_NOSUB);
+                   if (code != 0) {
+                       regerror(code, &re, errbuf, sizeof errbuf);
+                       fprintf(STDERR,
+                               "Error in -prefix regular expression: '%s': %s\n",
+                               ti->data, errbuf);
+                       exit(1);
+                   }
+                   match = (regexec(&re, vllist->name, 0, NULL, 0) == 0);
+                   regfree(&re);
+#else
+                   ccode = (char *)re_comp(ti->data);
+                   if (ccode) {
+                       fprintf(STDERR,
+                               "Error in -prefix regular expression: '%s': %s\n",
+                               ti->data, ccode);
+                       exit(1);
+                   }
+                   match = (re_exec(vllist->name) == 1);
+#endif
+               } else {
+                   match =
+                       (strncmp(vllist->name, ti->data, strlen(ti->data)) ==
+                        0);
+               }
+               if (match)
+                   break;
+           }
+       } else {
+           match = 1;
+       }
+
+       /* Without the -exclude flag: If it matches the prefix, then
+        *    check if we want to exclude any from xprefix.
+        * With the -exclude flag: If it matches the prefix, then
+        *    check if we want to add any from xprefix.
+        */
+       if (match && seenxprefix) {
+           for (ti = as->parms[4].items; ti; ti = ti->next) {
+               if (strncmp(ti->data, "^", 1) == 0) {
+#ifdef HAVE_POSIX_REGEX
+                   regex_t re;
+                   char errbuf[256];
+
+                   /* XXX -- should just do the compile once! */
+                   code = regcomp(&re, ti->data, REG_NOSUB);
+                   if (code != 0) {
+                       regerror(code, &re, errbuf, sizeof errbuf);
+                       fprintf(STDERR,
+                               "Error in -xprefix regular expression: '%s': %s\n",
+                               ti->data, errbuf);
+                       exit(1);
+                   }
+                   if (regexec(&re, vllist->name, 0, NULL, 0) == 0)
+                           match = 0;
+                   regfree(&re);
+#else
+                   ccode = (char *)re_comp(ti->data);
+                   if (ccode) {
+                       fprintf(STDERR,
+                               "Error in -xprefix regular expression: '%s': %s\n",
+                               ti->data, ccode);
+                       exit(1);
+                   }
+                   if (re_exec(vllist->name) == 1) {
+                       match = 0;
+                       break;
+                   }
+#endif
+               } else {
+                   if (strncmp(vllist->name, ti->data, strlen(ti->data)) ==
+                       0) {
+                       match = 0;
+                       break;
+                   }
+               }
+           }
+       }
+
+       if (exclude)
+           match = !match;     /* -exclude will reverse the match */
+       if (!match)
+           continue;           /* Skip if no match */
+
+       /* Print list of volumes to backup */
+       if (noaction) {
+           fprintf(STDOUT, "     %s\n", vllist->name);
+           continue;
+       }
+
+       if (!(vllist->flags & RW_EXISTS)) {
+           if (verbose) {
+               fprintf(STDOUT,
+                       "Omitting to backup %s since RW volume does not exist \n",
+                       vllist->name);
+               fprintf(STDOUT, "\n");
+           }
+           fflush(STDOUT);
+           continue;
+       }
+
+       avolid = vllist->volumeId[RWVOL];
+       MapHostToNetwork(vllist);
+       GetServerAndPart(vllist, RWVOL, &aserver1, &apart1, &previdx);
+       if (aserver1 == -1 || apart1 == -1) {
+           fprintf(STDOUT, "could not backup %s, invalid VLDB entry\n",
+                   vllist->name);
+           totalFail++;
+           continue;
+       }
+       if (aserver) {
+           same = VLDB_IsSameAddrs(aserver, aserver1, &error);
+           if (error) {
+               fprintf(STDERR,
+                       "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n",
+                       aserver, error);
+               totalFail++;
+               continue;
+           }
+       }
+       if ((aserver && !same) || (apart && (apart != apart1))) {
+           if (verbose) {
+               fprintf(STDOUT,
+                       "Omitting to backup %s since the RW is in a different location\n",
+                       vllist->name);
+           }
+           continue;
+       }
+       if (verbose) {
+           time_t now = time(0);
+           fprintf(STDOUT, "Creating backup volume for %s on %s",
+                   vllist->name, ctime(&now));
+           fflush(STDOUT);
+       }
+
+       code = UV_BackupVolume(aserver1, apart1, avolid);
+       if (code) {
+           fprintf(STDOUT, "Could not backup %s\n", vllist->name);
+           totalFail++;
+       } else {
+           totalBack++;
+       }
+       if (verbose)
+           fprintf(STDOUT, "\n");
+       fflush(STDOUT);
+    }                          /* process each vldb entry */
+    fprintf(STDOUT, "done\n");
+    fprintf(STDOUT, "Total volumes backed up: %lu; failed to backup: %lu\n",
+           (unsigned long)totalBack, (unsigned long)totalFail);
+    fflush(STDOUT);
+    if (arrayEntries.nbulkentries_val)
+       free(arrayEntries.nbulkentries_val);
+    return 0;
+}
+
+static
+UnlockVLDB(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 apart;
+    afs_int32 aserver, code;
+    afs_int32 vcode;
+    struct VldbListByAttributes attributes;
+    nbulkentries arrayEntries;
+    register struct nvldbentry *vllist;
+    afs_int32 nentries;
+    int j;
+    afs_int32 volid;
+    afs_int32 totalE;
+    char pname[10];
+
+    apart = -1;
+    totalE = 0;
+    attributes.Mask = 0;
+
+    if (as->parms[0].items) {  /* server specified */
+       aserver = GetServer(as->parms[0].items->data);
+       if (aserver == 0) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[0].items->data);
+           exit(1);
+       }
+       attributes.server = ntohl(aserver);
+       attributes.Mask |= VLLIST_SERVER;
+    }
+    if (as->parms[1].items) {  /* partition specified */
+       apart = volutil_GetPartitionID(as->parms[1].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       if (!IsPartValid(apart, aserver, &code)) {      /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                       "vos : partition %s does not exist on the server\n",
+                       as->parms[1].items->data);
+           exit(1);
+       }
+       attributes.partition = apart;
+       attributes.Mask |= VLLIST_PARTITION;
+    }
+    attributes.flag = VLOP_ALLOPERS;
+    attributes.Mask |= VLLIST_FLAG;
+    memset(&arrayEntries, 0, sizeof(arrayEntries));    /*initialize to hint the stub  to alloc space */
+    vcode = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries);
+    if (vcode) {
+       fprintf(STDERR, "Could not access the VLDB for attributes\n");
+       PrintError("", vcode);
+       exit(1);
+    }
+    for (j = 0; j < nentries; j++) {   /* process each entry */
+       vllist = &arrayEntries.nbulkentries_val[j];
+       volid = vllist->volumeId[RWVOL];
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, -1,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR, "Could not unlock entry for volume %s\n",
+                   vllist->name);
+           PrintError("", vcode);
+           totalE++;
+       }
+
+    }
+    MapPartIdIntoName(apart, pname);
+    if (totalE)
+       fprintf(STDOUT,
+               "Could not lock %lu VLDB entries of %lu locked entries\n",
+               (unsigned long)totalE, (unsigned long)nentries);
+    else {
+       if (as->parms[0].items) {
+           fprintf(STDOUT,
+                   "Unlocked all the VLDB entries for volumes on server %s ",
+                   as->parms[0].items->data);
+           if (as->parms[1].items) {
+               MapPartIdIntoName(apart, pname);
+               fprintf(STDOUT, "partition %s\n", pname);
+           } else
+               fprintf(STDOUT, "\n");
+
+       } else if (as->parms[1].items) {
+           MapPartIdIntoName(apart, pname);
+           fprintf(STDOUT,
+                   "Unlocked all the VLDB entries for volumes on partition %s on all servers\n",
+                   pname);
+       }
+    }
+
+    if (arrayEntries.nbulkentries_val)
+       free(arrayEntries.nbulkentries_val);
+    return 0;
+}
+
+static
+PartitionInfo(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 apart;
+    afs_int32 aserver, code;
+    char pname[10];
+    struct diskPartition partition;
+    struct partList dummyPartList;
+    int i, cnt;
+
+    apart = -1;
+    aserver = GetServer(as->parms[0].items->data);
+    if (aserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[0].items->data);
+       exit(1);
+    }
+    if (as->parms[1].items) {
+       apart = volutil_GetPartitionID(as->parms[1].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[1].items->data);
+           exit(1);
+       }
+       dummyPartList.partId[0] = apart;
+       dummyPartList.partFlags[0] = PARTVALID;
+       cnt = 1;
+    }
+    if (apart != -1) {
+       if (!IsPartValid(apart, aserver, &code)) {      /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                       "vos : partition %s does not exist on the server\n",
+                       as->parms[1].items->data);
+           exit(1);
+       }
+    } else {
+       code = UV_ListPartitions(aserver, &dummyPartList, &cnt);
+       if (code) {
+           PrintDiagnostics("listpart", code);
+           exit(1);
+       }
+    }
+    for (i = 0; i < cnt; i++) {
+       if (dummyPartList.partFlags[i] & PARTVALID) {
+           MapPartIdIntoName(dummyPartList.partId[i], pname);
+           code = UV_PartitionInfo(aserver, pname, &partition);
+           if (code) {
+               fprintf(STDERR, "Could not get information on partition %s\n",
+                       pname);
+               PrintError("", code);
+               exit(1);
+           }
+           fprintf(STDOUT,
+                   "Free space on partition %s: %d K blocks out of total %d\n",
+                   pname, partition.free, partition.minFree);
+       }
+    }
+    return 0;
+}
+
+static
+ChangeAddr(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 ip1, ip2, vcode;
+    int remove = 0;
+
+    ip1 = GetServer(as->parms[0].items->data);
+    if (!ip1) {
+       fprintf(STDERR, "vos: invalid host address\n");
+       return (EINVAL);
+    }
+
+    if ((as->parms[1].items && as->parms[2].items)
+       || (!as->parms[1].items && !as->parms[2].items)) {
+       fprintf(STDERR,
+               "vos: Must specify either '-newaddr <addr>' or '-remove' flag\n");
+       return (EINVAL);
+    }
+
+    if (as->parms[1].items) {
+       ip2 = GetServer(as->parms[1].items->data);
+       if (!ip2) {
+           fprintf(STDERR, "vos: invalid host address\n");
+           return (EINVAL);
+       }
+    } else {
+       /* Play a trick here. If we are removing an address, ip1 will be -1
+        * and ip2 will be the original address. This switch prevents an 
+        * older revision vlserver from removing the IP address.
+        */
+       remove = 1;
+       ip2 = ip1;
+       ip1 = 0xffffffff;
+    }
+
+    vcode = ubik_Call_New(VL_ChangeAddr, cstruct, 0, ntohl(ip1), ntohl(ip2));
+    if (vcode) {
+       if (remove) {
+           fprintf(STDERR, "Could not remove server %s from the VLDB\n",
+                   as->parms[0].items->data);
+           if (vcode == VL_NOENT) {
+               fprintf(STDERR,
+                       "vlserver does not support the remove flag or ");
+           }
+       } else {
+           fprintf(STDERR, "Could not change server %s to server %s\n",
+                   as->parms[0].items->data, as->parms[1].items->data);
+       }
+       PrintError("", vcode);
+       return (vcode);
+    }
+
+    if (remove) {
+       fprintf(STDOUT, "Removed server %s from the VLDB\n",
+               as->parms[0].items->data);
+    } else {
+       fprintf(STDOUT, "Changed server %s to server %s\n",
+               as->parms[0].items->data, as->parms[1].items->data);
+    }
+    return 0;
+}
+
+static void
+print_addrs(const bulkaddrs * addrs, const afsUUID * m_uuid, int nentries,
+           int print, int noresolve)
+{
+    afs_int32 vcode;
+    afs_int32 i, j;
+    struct VLCallBack vlcb;
+    afs_int32 *addrp;
+    bulkaddrs m_addrs;
+    ListAddrByAttributes m_attrs;
+    afs_int32 m_nentries, *m_addrp;
+    afs_int32 base, index;
+    char buf[1024];
+
+    if (print) {
+       afsUUID_to_string(m_uuid, buf, sizeof(buf));
+       printf("UUID: %s\n", buf);
+    }
+
+    /* print out the list of all the server */
+    addrp = (afs_int32 *) addrs->bulkaddrs_val;
+    for (i = 0; i < nentries; i++, addrp++) {
+       /* If it is a multihomed address, then we will need to 
+        * get the addresses for this multihomed server from
+        * the vlserver and print them.
+        */
+       if (((*addrp & 0xff000000) == 0xff000000) && ((*addrp) & 0xffff)) {
+           /* Get the list of multihomed fileservers */
+           base = (*addrp >> 16) & 0xff;
+           index = (*addrp) & 0xffff;
+
+           if ((base >= 0) && (base <= VL_MAX_ADDREXTBLKS) && (index >= 1)
+               && (index <= VL_MHSRV_PERBLK)) {
+               m_attrs.Mask = VLADDR_INDEX;
+               m_attrs.index = (base * VL_MHSRV_PERBLK) + index;
+               m_nentries = 0;
+               m_addrs.bulkaddrs_val = 0;
+               m_addrs.bulkaddrs_len = 0;
+               vcode =
+                   ubik_Call(VL_GetAddrsU, cstruct, 0, &m_attrs, &m_uuid,
+                             &vlcb, &m_nentries, &m_addrs);
+               if (vcode) {
+                   fprintf(STDERR,
+                           "vos: could not list the multi-homed server addresses\n");
+                   PrintError("", vcode);
+               }
+
+               /* Print the list */
+               m_addrp = (afs_int32 *) m_addrs.bulkaddrs_val;
+               for (j = 0; j < m_nentries; j++, m_addrp++) {
+                   *m_addrp = htonl(*m_addrp);
+                   if (noresolve) {
+                       char hoststr[16];
+                       printf("%s ", afs_inet_ntoa_r(*m_addrp, hoststr));
+                   } else {
+                       printf("%s ", hostutil_GetNameByINet(*m_addrp));
+                   }
+               }
+               if (j == 0) {
+                   printf("<unknown>\n");
+               } else {
+                   printf("\n");
+               }
+
+               continue;
+           }
+       }
+
+       /* Otherwise, it is a non-multihomed entry and contains
+        * the IP address of the server - print it.
+        */
+       *addrp = htonl(*addrp);
+       if (noresolve) {
+           char hoststr[16];
+           printf("%s\n", afs_inet_ntoa_r(*addrp, hoststr));
+       } else {
+           printf("%s\n", hostutil_GetNameByINet(*addrp));
+       }
+    }
+
+    if (print) {
+       printf("\n");
+    }
+    return;
+}
+
+static
+ListAddrs(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 vcode;
+    afs_int32 i, noresolve = 0, printuuid = 0;
+    struct VLCallBack vlcb;
+    afs_int32 nentries;
+    bulkaddrs m_addrs;
+    ListAddrByAttributes m_attrs;
+    afsUUID m_uuid, askuuid;
+    afs_int32 m_nentries;
+
+    memset(&m_attrs, 0, sizeof(struct ListAddrByAttributes));
+    m_attrs.Mask = VLADDR_INDEX;
+
+    memset(&m_addrs, 0, sizeof(bulkaddrs));
+    memset(&askuuid, 0, sizeof(afsUUID));
+    if (as->parms[0].items) {
+       /* -uuid */
+       afsUUID_from_string(as->parms[0].items->data, &askuuid);
+       m_attrs.Mask = VLADDR_UUID;
+       m_attrs.uuid = askuuid;
+    }
+    if (as->parms[1].items) {
+       /* -host */
+       struct hostent *he;
+       afs_int32 saddr;
+       he = hostutil_GetHostByName((char *)as->parms[1].items->data);
+       if (he == NULL) {
+           fprintf(stderr, "Can't get host info for '%s'\n",
+                   as->parms[1].items->data);
+           exit(-1);
+       }
+       memcpy(&saddr, he->h_addr, 4);
+       m_attrs.Mask = VLADDR_IPADDR;
+       m_attrs.ipaddr = ntohl(saddr);
+    }
+    if (as->parms[2].items) {
+       noresolve = 1;
+    }
+    if (as->parms[3].items) {
+       printuuid = 1;
+    }
+
+    m_addrs.bulkaddrs_val = 0;
+    m_addrs.bulkaddrs_len = 0;
+
+    vcode =
+       ubik_Call_New(VL_GetAddrs, cstruct, 0, 0, 0, &vlcb, &nentries,
+                     &m_addrs);
+    if (vcode) {
+       fprintf(STDERR, "vos: could not list the server addresses\n");
+       PrintError("", vcode);
+       return (vcode);
+    }
+
+    m_nentries = 0;
+    m_addrs.bulkaddrs_val = 0;
+    m_addrs.bulkaddrs_len = 0;
+    i = 1;
+    while (1) {
+       m_attrs.index = i;
+
+       vcode =
+           ubik_Call_New(VL_GetAddrsU, cstruct, 0, &m_attrs, &m_uuid,
+                         &vlcb, &m_nentries, &m_addrs);
+       if (vcode == VL_NOENT) {
+           i++;
+           nentries++;
+           continue;
+       }
+
+       if (vcode == VL_INDEXERANGE) {
+           break;
+       }
+
+       if (vcode) {
+           fprintf(STDERR, "vos: could not list the server addresses\n");
+           PrintError("", vcode);
+           return (vcode);
+       }
+
+       print_addrs(&m_addrs, &m_uuid, m_nentries, printuuid, noresolve);
+       i++;
+
+       if ((as->parms[1].items) || (as->parms[0].items) || (i > nentries))
+           break;
+    }
+
+    return 0;
+}
+
+static
+LockEntry(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 avolid, vcode, err;
+
+    avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[0].items->data);
+       exit(1);
+    }
+    vcode = ubik_Call(VL_SetLock, cstruct, 0, avolid, -1, VLOP_DELETE);
+    if (vcode) {
+       fprintf(STDERR, "Could not lock VLDB entry for volume %s\n",
+               as->parms[0].items->data);
+       PrintError("", vcode);
+       exit(1);
+    }
+    fprintf(STDOUT, "Locked VLDB entry for volume %s\n",
+           as->parms[0].items->data);
+    return 0;
+}
+
+static
+ConvertRO(as)
+     register struct cmd_syndesc *as;
+
+{
+    afs_int32 partition = -1;
+    afs_int32 server, volid, code, i, same;
+    struct nvldbentry entry, storeEntry;
+    afs_int32 vcode;
+    afs_int32 rwindex;
+    afs_int32 rwserver = 0;
+    afs_int32 rwpartition;
+    afs_int32 roindex;
+    afs_int32 roserver = 0;
+    afs_int32 ropartition;
+    int force = 0;
+    struct rx_connection *aconn;
+    char c, dc;
+
+    server = GetServer(as->parms[0].items->data);
+    if (!server) {
+       fprintf(STDERR, "vos: host '%s' not found in host table\n",
+               as->parms[0].items->data);
+       return ENOENT;
+    }
+    partition = volutil_GetPartitionID(as->parms[1].items->data);
+    if (partition < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+    if (!IsPartValid(partition, server, &code)) {
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[1].items->data);
+       return ENOENT;
+    }
+    volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &code);
+    if (volid == 0) {
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR, "Unknown volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return -1;
+    }
+    if (as->parms[3].items)
+       force = 1;
+
+    vcode = VLDB_GetEntryByID(volid, -1, &entry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not fetch the entry for volume %lu from VLDB\n",
+               (unsigned long)volid);
+       PrintError("convertROtoRW", code);
+       return vcode;
+    }
+
+    /* use RO volid even if user specified RW or BK volid */
+
+    if (volid != entry.volumeId[ROVOL])
+       volid = entry.volumeId[ROVOL];
+
+    MapHostToNetwork(&entry);
+    for (i = 0; i < entry.nServers; i++) {
+       if (entry.serverFlags[i] & ITSRWVOL) {
+           rwindex = i;
+           rwserver = entry.serverNumber[i];
+           rwpartition = entry.serverPartition[i];
+       }
+       if (entry.serverFlags[i] & ITSROVOL) {
+           same = VLDB_IsSameAddrs(server, entry.serverNumber[i], &code);
+           if (code) {
+               fprintf(STDERR,
+                       "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n",
+                       server, code);
+               return ENOENT;
+           }
+           if (same) {
+               roindex = i;
+               roserver = entry.serverNumber[i];
+               ropartition = entry.serverPartition[i];
+               break;
+           }
+       }
+    }
+    if (!roserver) {
+       fprintf(STDERR, "Warning: RO volume didn't exist in vldb!\n");
+    }
+    if (ropartition != partition) {
+       fprintf(STDERR,
+               "Warning: RO volume should be in partition %d instead of %d (vldb)\n",
+               ropartition, partition);
+    }
+
+    if (rwserver) {
+       fprintf(STDERR,
+               "VLDB indicates that a RW volume exists already on %s in partition %s.\n",
+               hostutil_GetNameByINet(rwserver),
+               volutil_PartitionName(rwpartition));
+       if (!force) {
+           fprintf(STDERR, "Overwrite this VLDB entry? [y|n] (n)\n");
+           dc = c = getchar();
+           while (!(dc == EOF || dc == '\n'))
+               dc = getchar(); /* goto end of line */
+           if ((c != 'y') && (c != 'Y')) {
+               fprintf(STDERR, "aborted.\n");
+               return -1;
+           }
+       }
+    }
+
+    vcode =
+       ubik_Call(VL_SetLock, cstruct, 0, entry.volumeId[RWVOL], RWVOL,
+                 VLOP_MOVE);
+    aconn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    code = AFSVolConvertROtoRWvolume(aconn, partition, volid);
+    if (code) {
+       fprintf(STDERR,
+               "Converting RO volume %lu to RW volume failed with code %d\n",
+               (unsigned long)volid, code);
+       PrintError("convertROtoRW ", code);
+       return -1;
+    }
+    entry.serverFlags[roindex] = ITSRWVOL;
+    entry.flags |= RW_EXISTS;
+    entry.flags &= ~BACK_EXISTS;
+    if (rwserver) {
+       (entry.nServers)--;
+       if (rwindex != entry.nServers) {
+           entry.serverNumber[rwindex] = entry.serverNumber[entry.nServers];
+           entry.serverPartition[rwindex] =
+               entry.serverPartition[entry.nServers];
+           entry.serverFlags[rwindex] = entry.serverFlags[entry.nServers];
+           entry.serverNumber[entry.nServers] = 0;
+           entry.serverPartition[entry.nServers] = 0;
+           entry.serverFlags[entry.nServers] = 0;
+       }
+    }
+    entry.flags &= ~RO_EXISTS;
+    for (i = 0; i < entry.nServers; i++) {
+       if (entry.serverFlags[i] & ITSROVOL) {
+           if (!(entry.serverFlags[i] & (RO_DONTUSE | NEW_REPSITE)))
+               entry.flags |= RO_EXISTS;
+       }
+    }
+    MapNetworkToHost(&entry, &storeEntry);
+    code =
+       VLDB_ReplaceEntry(entry.volumeId[RWVOL], RWVOL, &storeEntry,
+                         (LOCKREL_OPCODE | LOCKREL_AFSID |
+                          LOCKREL_TIMESTAMP));
+    if (code) {
+       fprintf(STDERR,
+               "Warning: volume converted, but vldb update failed with code %d!\n",
+               code);
+    }
+    vcode = UV_LockRelease(entry.volumeId[RWVOL]);
+    if (vcode) {
+       PrintDiagnostics("unlock", vcode);
+    }
+    return code;
+}
+
+static
+Sizes(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 avolid, aserver, apart, voltype, fromdate = 0, code, err, i;
+    struct nvldbentry entry;
+    volintSize vol_size;
+
+    rx_SetRxDeadTime(60 * 10);
+    for (i = 0; i < MAXSERVERS; i++) {
+       struct rx_connection *rxConn = ubik_GetRPCConn(cstruct, i);
+       if (rxConn == 0)
+           break;
+       rx_SetConnDeadTime(rxConn, rx_connDeadTime);
+       if (rxConn->service)
+           rxConn->service->connDeadTime = rx_connDeadTime;
+    }
+
+    avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (avolid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+
+    if (as->parms[1].items || as->parms[2].items) {
+       if (!as->parms[1].items || !as->parms[2].items) {
+           fprintf(STDERR,
+                   "Must specify both -server and -partition options\n");
+           return -1;
+       }
+       aserver = GetServer(as->parms[2].items->data);
+       if (aserver == 0) {
+           fprintf(STDERR, "Invalid server name\n");
+           return -1;
+       }
+       apart = volutil_GetPartitionID(as->parms[1].items->data);
+       if (apart < 0) {
+           fprintf(STDERR, "Invalid partition name\n");
+           return -1;
+       }
+    } else {
+       code = GetVolumeInfo(avolid, &aserver, &apart, &voltype, &entry);
+       if (code)
+           return code;
+    }
+
+    fromdate = 0;
+
+    if (as->parms[4].items && strcmp(as->parms[4].items->data, "0")) {
+       code = ktime_DateToInt32(as->parms[4].items->data, &fromdate);
+       if (code) {
+           fprintf(STDERR, "vos: failed to parse date '%s' (error=%d))\n",
+                   as->parms[1].items->data, code);
+           return code;
+       }
+    }
+
+    fprintf(STDOUT, "Volume: %s\n", as->parms[0].items->data);
+
+    if (as->parms[3].items) {  /* do the dump estimate */
+#ifdef AFS_64BIT_ENV
+       vol_size.dump_size = 0;
+#else
+   FillInt64(vol_size.dump_size,0, 1);
+#endif
+       code = UV_GetSize(avolid, aserver, apart, fromdate, &vol_size);
+       if (code) {
+           PrintDiagnostics("size", code);
+           return code;
+       }
+       /* presumably the size info is now gathered in pntr */
+       /* now we display it */
+
+       fprintf(STDOUT, "dump_size: %llu\n", vol_size.dump_size);
+    }
+
+    /* Display info */
+
+    return 0;
+}
+
+PrintDiagnostics(astring, acode)
+     char *astring;
+     afs_int32 acode;
+{
+    if (acode == EACCES) {
+       fprintf(STDERR,
+               "You are not authorized to perform the 'vos %s' command (%d)\n",
+               astring, acode);
+    } else {
+       fprintf(STDERR, "Error in vos %s command.\n", astring);
+       PrintError("", acode);
+    }
+    return 0;
+}
+
+
+static
+MyBeforeProc(as, arock)
+     struct cmd_syndesc *as;
+     char *arock;
+{
+    register char *tcell;
+    register afs_int32 code;
+    register afs_int32 sauth;
+
+    /* Initialize the ubik_client connection */
+    rx_SetRxDeadTime(90);
+    cstruct = (struct ubik_client *)0;
+
+    sauth = 0;
+    tcell = NULL;
+    if (as->parms[12].items)   /* if -cell specified */
+       tcell = as->parms[12].items->data;
+    if (as->parms[14].items)   /* -serverauth specified */
+       sauth = 1;
+    if (as->parms[16].items)   /* -crypt specified */
+       vsu_SetCrypt(1);
+    if ((code =
+        vsu_ClientInit((as->parms[13].items != 0), confdir, tcell, sauth,
+                       &cstruct, UV_SetSecurity))) {
+       fprintf(STDERR, "could not initialize VLDB library (code=%lu) \n",
+               (unsigned long)code);
+       exit(1);
+    }
+    rxInitDone = 1;
+    if (as->parms[15].items)   /* -verbose flag set */
+       verbose = 1;
+    else
+       verbose = 0;
+    return 0;
+}
+
+int
+osi_audit()
+{
+/* this sucks but it works for now.
+*/
+    return 0;
+}
+
+#include "AFS_component_version_number.c"
+
+main(argc, argv)
+     int argc;
+     char **argv;
+{
+    register afs_int32 code;
+
+    register struct cmd_syndesc *ts;
+
+#ifdef AFS_AIX32_ENV
+    /*
+     * The following signal action for AIX is necessary so that in case of a 
+     * crash (i.e. core is generated) we can include the user's data section 
+     * in the core dump. Unfortunately, by default, only a partial core is
+     * generated which, in many cases, isn't too useful.
+     */
+    struct sigaction nsa;
+
+    sigemptyset(&nsa.sa_mask);
+    nsa.sa_handler = SIG_DFL;
+    nsa.sa_flags = SA_FULLDUMP;
+    sigaction(SIGSEGV, &nsa, NULL);
+#endif
+
+    confdir = AFSDIR_CLIENT_ETC_DIRPATH;
+
+    cmd_SetBeforeProc(MyBeforeProc, NULL);
+
+    ts = cmd_CreateSyntax("create", CreateVolume, 0, "create a new volume");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "volume name");
+    cmd_AddParm(ts, "-maxquota", CMD_SINGLE, CMD_OPTIONAL,
+               "initial quota (KB)");
+#ifdef notdef
+    cmd_AddParm(ts, "-minquota", CMD_SINGLE, CMD_OPTIONAL, "");
+#endif
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("remove", DeleteVolume, 0, "delete a volume");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("move", MoveVolume, 0, "move a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-fromserver", CMD_SINGLE, 0, "machine name on source");
+    cmd_AddParm(ts, "-frompartition", CMD_SINGLE, 0,
+               "partition name on source");
+    cmd_AddParm(ts, "-toserver", CMD_SINGLE, 0,
+               "machine name on destination");
+    cmd_AddParm(ts, "-topartition", CMD_SINGLE, 0,
+               "partition name on destination");
+    cmd_AddParm(ts, "-live", CMD_FLAG, CMD_OPTIONAL,
+               "copy live volume without cloning");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("copy", CopyVolume, 0, "copy a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID on source");
+    cmd_AddParm(ts, "-fromserver", CMD_SINGLE, 0, "machine name on source");
+    cmd_AddParm(ts, "-frompartition", CMD_SINGLE, 0,
+               "partition name on source");
+    cmd_AddParm(ts, "-toname", CMD_SINGLE, 0, "volume name on destination");
+    cmd_AddParm(ts, "-toserver", CMD_SINGLE, 0,
+               "machine name on destination");
+    cmd_AddParm(ts, "-topartition", CMD_SINGLE, 0,
+               "partition name on destination");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave new volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make new volume read-only");
+    cmd_AddParm(ts, "-live", CMD_FLAG, CMD_OPTIONAL,
+               "copy live volume without cloning");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("shadow", ShadowVolume, 0,
+                         "make or update a shadow volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID on source");
+    cmd_AddParm(ts, "-fromserver", CMD_SINGLE, 0, "machine name on source");
+    cmd_AddParm(ts, "-frompartition", CMD_SINGLE, 0,
+               "partition name on source");
+    cmd_AddParm(ts, "-toserver", CMD_SINGLE, 0,
+               "machine name on destination");
+    cmd_AddParm(ts, "-topartition", CMD_SINGLE, 0,
+               "partition name on destination");
+    cmd_AddParm(ts, "-toname", CMD_SINGLE, CMD_OPTIONAL,
+               "volume name on destination");
+    cmd_AddParm(ts, "-toid", CMD_SINGLE, CMD_OPTIONAL,
+               "volume ID on destination");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave shadow volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make shadow volume read-only");
+    cmd_AddParm(ts, "-live", CMD_FLAG, CMD_OPTIONAL,
+               "copy live volume without cloning");
+    cmd_AddParm(ts, "-incremental", CMD_FLAG, CMD_OPTIONAL,
+               "do incremental update if target exists");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("backup", BackupVolume, 0,
+                         "make backup of a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("clone", CloneVolume, 0,
+                         "make clone of a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "server");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition");
+    cmd_AddParm(ts, "-toname", CMD_SINGLE, CMD_OPTIONAL,
+               "volume name on destination");
+    cmd_AddParm(ts, "-toid", CMD_SINGLE, CMD_OPTIONAL,
+               "volume ID on destination");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave clone volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make clone volume read-only, not readwrite");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("release", ReleaseVolume, 0, "release a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
+               "force a complete release");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("dump", DumpVolume, 0, "dump a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-time", CMD_SINGLE, CMD_OPTIONAL, "dump from time");
+    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "server");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition");
+    cmd_AddParm(ts, "-clone", CMD_FLAG, CMD_OPTIONAL,
+               "dump a clone of the volume");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("restore", RestoreVolume, 0, "restore a volume");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of volume to be restored");
+    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_OPTIONAL, "volume ID");
+    cmd_AddParm(ts, "-overwrite", CMD_SINGLE, CMD_OPTIONAL,
+               "abort | full | incremental");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave restored volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make restored volume read-only");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("unlock", LockReleaseCmd, 0,
+                         "release lock on VLDB entry for a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("changeloc", ChangeLocation, 0,
+                         "change an RW volume's location in the VLDB");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0,
+               "machine name for new location");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0,
+               "partition name for new location");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("addsite", AddSite, 0, "add a replication site");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name for new site");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0,
+               "partition name for new site");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("remsite", RemoveSite, 0,
+                         "remove a replication site");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("listpart", ListPartitions, 0, "list partitions");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("listvol", ListVolumes, 0,
+                         "list volumes on server (bypass VLDB)");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL, "minimal listing");
+    cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
+               "list all normal volume fields");
+    cmd_AddParm(ts, "-quiet", CMD_FLAG, CMD_OPTIONAL,
+               "generate minimal information");
+    cmd_AddParm(ts, "-extended", CMD_FLAG, CMD_OPTIONAL,
+               "list extended volume fields");
+#ifdef FULL_LISTVOL_SWITCH
+    cmd_AddParm(ts, "-format", CMD_FLAG, CMD_OPTIONAL,
+               "machine readable format");
+#endif /* FULL_LISTVOL_SWITCH */
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("syncvldb", SyncVldb, 0,
+                         "synchronize VLDB with server");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-volume", CMD_SINGLE, CMD_OPTIONAL, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("syncserv", SyncServer, 0,
+                         "synchronize server with VLDB");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("examine", ExamineVolume, 0,
+                         "everything about the volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-extended", CMD_FLAG, CMD_OPTIONAL,
+               "list extended volume fields");
+#ifdef FULL_LISTVOL_SWITCH
+    cmd_AddParm(ts, "-format", CMD_FLAG, CMD_OPTIONAL,
+               "machine readable format");
+#endif /* FULL_LISTVOL_SWITCH */
+    COMMONPARMS;
+    cmd_CreateAlias(ts, "volinfo");
+
+    ts = cmd_CreateSyntax("setfields", SetFields, 0,
+                         "change volume info fields");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-maxquota", CMD_SINGLE, CMD_OPTIONAL, "quota (KB)");
+    cmd_AddParm(ts, "-clearuse", CMD_FLAG, CMD_OPTIONAL, "clear dayUse");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("offline", volOffline, 0, (char *)CMD_HIDDEN);
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "server name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-sleep", CMD_SINGLE, CMD_OPTIONAL, "seconds to sleep");
+    cmd_AddParm(ts, "-busy", CMD_FLAG, CMD_OPTIONAL, "busy volume");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("online", volOnline, 0, (char *)CMD_HIDDEN);
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "server name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("zap", VolumeZap, 0,
+                         "delete the volume, don't bother with VLDB");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume ID");
+    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
+               "force deletion of bad volumes");
+    cmd_AddParm(ts, "-backup", CMD_FLAG, CMD_OPTIONAL,
+               "also delete backup volume if one is found");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("status", VolserStatus, 0,
+                         "report on volser status");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("rename", RenameVolume, 0, "rename a volume");
+    cmd_AddParm(ts, "-oldname", CMD_SINGLE, 0, "old volume name ");
+    cmd_AddParm(ts, "-newname", CMD_SINGLE, 0, "new volume name ");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("listvldb", ListVLDB, 0,
+                         "list volumes in the VLDB");
+    cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "volume name or ID");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-locked", CMD_FLAG, CMD_OPTIONAL, "locked volumes only");
+    cmd_AddParm(ts, "-quiet", CMD_FLAG, CMD_OPTIONAL,
+               "generate minimal information");
+    cmd_AddParm(ts, "-nosort", CMD_FLAG, CMD_OPTIONAL,
+               "do not alphabetically sort the volume names");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("backupsys", BackSys, 0, "en masse backups");
+    cmd_AddParm(ts, "-prefix", CMD_LIST, CMD_OPTIONAL,
+               "common prefix on volume(s)");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-exclude", CMD_FLAG, CMD_OPTIONAL,
+               "exclude common prefix volumes");
+    cmd_AddParm(ts, "-xprefix", CMD_LIST, CMD_OPTIONAL,
+               "negative prefix on volume(s)");
+    cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL, "no action");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("delentry", DeleteEntry, 0,
+                         "delete VLDB entry for a volume");
+    cmd_AddParm(ts, "-id", CMD_LIST, CMD_OPTIONAL, "volume name or ID");
+    cmd_AddParm(ts, "-prefix", CMD_SINGLE, CMD_OPTIONAL,
+               "prefix of the volume whose VLDB entry is to be deleted");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-noexecute", CMD_FLAG, CMD_OPTIONAL | CMD_HIDE,
+               "no execute");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("partinfo", PartitionInfo, 0,
+                         "list partition information");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("unlockvldb", UnlockVLDB, 0,
+                         "unlock all the locked entries in the VLDB");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("lock", LockEntry, 0,
+                         "lock VLDB entry for a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("changeaddr", ChangeAddr, 0,
+                         "change the IP address of a file server");
+    cmd_AddParm(ts, "-oldaddr", CMD_SINGLE, 0, "original IP address");
+    cmd_AddParm(ts, "-newaddr", CMD_SINGLE, CMD_OPTIONAL, "new IP address");
+    cmd_AddParm(ts, "-remove", CMD_FLAG, CMD_OPTIONAL,
+               "remove the IP address from the VLDB");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("listaddrs", ListAddrs, 0,
+                         "list the IP address of all file servers registered in the VLDB");
+    cmd_AddParm(ts, "-uuid", CMD_SINGLE, CMD_OPTIONAL, "uuid of server");
+    cmd_AddParm(ts, "-host", CMD_SINGLE, CMD_OPTIONAL, "address of host");
+    cmd_AddParm(ts, "-noresolve", CMD_FLAG, CMD_OPTIONAL,
+               "don't resolve addresses");
+    cmd_AddParm(ts, "-printuuid", CMD_FLAG, CMD_OPTIONAL,
+               "print uuid of hosts");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("convertROtoRW", ConvertRO, 0,
+                         "convert a RO volume into a RW volume (after loss of old RW volume)");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "don't ask");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("size", Sizes, 0,
+                         "obtain various sizes of the volume.");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name");
+    cmd_AddParm(ts, "-dump", CMD_FLAG, CMD_OPTIONAL,
+               "Obtain the size of the dump");
+    cmd_AddParm(ts, "-time", CMD_SINGLE, CMD_OPTIONAL, "dump from time");
+    COMMONPARMS;
+
+    code = cmd_Dispatch(argc, argv);
+    if (rxInitDone) {
+       /* Shut down the ubik_client and rx connections */
+       if (cstruct) {
+           (void)ubik_ClientDestroy(cstruct);
+           cstruct = 0;
+       }
+       rx_Finalize();
+    }
+
+    exit((code ? -1 : 0));
+}
diff --git a/src/volser/vos.rc b/src/volser/vos.rc
new file mode 100644 (file)
index 0000000..cc39de0
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+/* Define VERSIONINFO resource */
+
+#define  AFS_VERINFO_FILE_DESCRIPTION "AFS Volume Command"
+#define  AFS_VERINFO_NAME "vos"
+#define  AFS_VERINFO_FILENAME "vos.exe"
+
+#include "AFS_component_version_number.h"
+#include "..\config\NTVersioninfo.rc"
diff --git a/src/volser/vsprocs.c b/src/volser/vsprocs.c
new file mode 100644 (file)
index 0000000..7faec41
--- /dev/null
@@ -0,0 +1,6880 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/vsprocs.c,v 1.30 2004/01/08 21:54:10 shadow Exp $");
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef AFS_AIX_ENV
+#include <sys/statfs.h>
+#endif
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <winsock2.h>
+#else
+#include <sys/file.h>
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#include <lock.h>
+#include <afs/voldefs.h>
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <afs/vlserver.h>
+#include <afs/nfs.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/keys.h>
+#include <ubik.h>
+#include <afs/afsint.h>
+#include "volser.h"
+#include "volint.h"
+#include "lockdata.h"
+#include <afs/com_err.h>
+#include <rx/rxkad.h>
+#include <afs/kautils.h>
+#include <afs/cmd.h>
+#include <errno.h>
+#define ERRCODE_RANGE 8                /* from error_table.h */
+#define        CLOCKSKEW   2           /* not really skew, but resolution */
+
+/* for UV_MoveVolume() recovery */
+
+#include <afs/procmgmt.h>      /* signal(), kill(), wait(), etc. */
+#include <setjmp.h>
+
+#include <volser_prototypes.h>
+
+struct ubik_client *cstruct;
+int verbose = 0;
+
+struct release {
+    afs_int32 time;
+    afs_int32 vldbEntryIndex;
+};
+
+/* Utility macros used by rest of this source file */
+#define EPRINT(ec, es) \
+do { \
+       fprintf(STDERR, "\n"); \
+       fprintf(STDERR, (es)); \
+       PrintError("   ",ec); \
+} while (0)
+
+#define EPRINT1(ec, es, ep1) \
+do { \
+       fprintf(STDERR, "\n"); \
+       fprintf(STDERR, (es), (ep1)); \
+       PrintError("   ",ec); \
+} while (0)
+
+#define EPRINT2(ec, es, ep1, ep2) \
+do { \
+       fprintf(STDERR, "\n"); \
+       fprintf(STDERR, (es), (ep1), (ep2)); \
+       PrintError("   ",ec); \
+} while (0)
+
+#define EPRINT3(ec, es, ep1, ep2, ep3) \
+do { \
+       fprintf(STDERR, "\n"); \
+       fprintf(STDERR, (es), (ep1), (ep2), (ep3)); \
+       PrintError("   ",ec); \
+} while (0)
+
+#define EGOTO(where, ec, es) \
+do { \
+       if (ec) { \
+               EPRINT((ec),(es)); \
+               error = (ec); \
+               goto where; \
+       } \
+} while (0)
+
+#define EGOTO1(where, ec, es, ep1) \
+do { \
+       if (ec) { \
+               EPRINT1((ec),(es),(ep1)); \
+               error = (ec); \
+               goto where; \
+       } \
+} while (0)
+
+#define EGOTO2(where, ec, es, ep1, ep2) \
+do { \
+       if (ec) { \
+               EPRINT2((ec),(es),(ep1),(ep2)); \
+               error = (ec); \
+               goto where; \
+       } \
+} while (0)
+
+#define EGOTO3(where, ec, es, ep1, ep2, ep3) \
+do { \
+       if (ec) { \
+               EPRINT3((ec),(es),(ep1),(ep2),(ep3)); \
+               error = (ec); \
+               goto where; \
+       } \
+} while (0)
+
+#define VPRINT(es) \
+       { if (verbose) { fprintf(STDOUT, (es)); fflush(STDOUT); } }
+#define VPRINT1(es, p) \
+       { if (verbose) { fprintf(STDOUT, (es), (p)); fflush(STDOUT); } }
+#define VPRINT2(es, p1, p2) \
+       { if (verbose) { fprintf(STDOUT, (es), (p1), (p2)); fflush(STDOUT); } }
+#define VPRINT3(es, p1, p2, p3) \
+       { if (verbose) { fprintf(STDOUT, (es), (p1), (p2), (p3)); fflush(STDOUT); } }
+#define VDONE \
+       { if (verbose) { fprintf(STDOUT, " done\n"); fflush(STDOUT); } }
+
+
+
+/* getting rid of this */
+#define ERROR_EXIT(code) {error=(code); goto error_exit;}
+
+
+/* Protos for static routines */
+static afs_int32 CheckAndDeleteVolume(struct rx_connection *aconn,
+                                     afs_int32 apart, afs_int32 okvol,
+                                     afs_int32 delvol);
+static int DelVol(struct rx_connection *conn, afs_int32 vid, afs_int32 part,
+                 afs_int32 flags);
+static int GetTrans(struct nvldbentry *vldbEntryPtr, afs_int32 index,
+                   struct rx_connection **connPtr, afs_int32 * transPtr,
+                   afs_int32 * timePtr);
+static int SimulateForwardMultiple(struct rx_connection *fromconn,
+                                  afs_int32 fromtid, afs_int32 fromdate,
+                                  manyDests * tr, afs_int32 flags,
+                                  void *cookie, manyResults * results);
+static int rel_compar(struct release *r1, struct release *r2);
+static afs_int32 CheckVolume(volintInfo * volumeinfo, afs_int32 aserver,
+                            afs_int32 apart, afs_int32 * modentry,
+                            afs_uint32 * maxvolid);
+
+
+/*map the partition <partId> into partition name <partName>*/
+void
+MapPartIdIntoName(afs_int32 partId, char *partName)
+{
+    if (partId < 26) {         /* what if partId > = 26 ? */
+       strcpy(partName, "/vicep");
+       partName[6] = partId + 'a';
+       partName[7] = '\0';
+       return;
+    } else if (partId < VOLMAXPARTS) {
+       strcpy(partName, "/vicep");
+       partId -= 26;
+       partName[6] = 'a' + (partId / 26);
+       partName[7] = 'a' + (partId % 26);
+       partName[8] = '\0';
+       return;
+    }
+}
+
+int
+yesprompt(char *str)
+{
+    int response, c;
+    int code;
+
+    fprintf(STDERR, "Do you want to %s? [yn](n): ", str);
+    response = c = getchar();
+    while (!(c == EOF || c == '\n'))
+       c = getchar();          /*skip to end of line */
+    code = (response == 'y' || response == 'Y');
+    return code;
+}
+
+
+int
+PrintError(char *msg, afs_int32 errcode)
+{
+    fprintf(STDERR, msg);
+    /*replace by a big switch statement */
+    switch (errcode) {
+    case 0:
+       break;
+    case -1:
+       fprintf(STDERR, "Possible communication failure\n");
+       break;
+    case VSALVAGE:
+       fprintf(STDERR, "Volume needs to be salvaged\n");
+       break;
+    case VNOVNODE:
+       fprintf(STDERR, "Bad vnode number quoted\n");
+       break;
+    case VNOVOL:
+       fprintf(STDERR,
+               "Volume not attached, does not exist, or not on line\n");
+       break;
+    case VVOLEXISTS:
+       fprintf(STDERR, "Volume already exists\n");
+       break;
+    case VNOSERVICE:
+       fprintf(STDERR, "Volume is not in service\n");
+       break;
+    case VOFFLINE:
+       fprintf(STDERR, "Volume is off line\n");
+       break;
+    case VONLINE:
+       fprintf(STDERR, "Volume is already on line\n");
+       break;
+    case VDISKFULL:
+       fprintf(STDERR, "Partition is full\n");
+       break;
+    case VOVERQUOTA:
+       fprintf(STDERR, "Volume max quota exceeded\n");
+       break;
+    case VBUSY:
+       fprintf(STDERR, "Volume temporarily unavailable\n");
+       break;
+    case VMOVED:
+       fprintf(STDERR, "Volume has moved to another server\n");
+       break;
+    case VL_IDEXIST:
+       fprintf(STDERR, "VLDB: volume Id exists in the vldb\n");
+       break;
+    case VL_IO:
+       fprintf(STDERR, "VLDB: a read terminated too early\n");
+       break;
+    case VL_NAMEEXIST:
+       fprintf(STDERR, "VLDB: volume entry exists in the vldb\n");
+       break;
+    case VL_CREATEFAIL:
+       fprintf(STDERR, "VLDB: internal creation failure\n");
+       break;
+    case VL_NOENT:
+       fprintf(STDERR, "VLDB: no such entry\n");
+       break;
+    case VL_EMPTY:
+       fprintf(STDERR, "VLDB: vldb database is empty\n");
+       break;
+    case VL_ENTDELETED:
+       fprintf(STDERR, "VLDB: entry is deleted (soft delete)\n");
+       break;
+    case VL_BADNAME:
+       fprintf(STDERR, "VLDB: volume name is illegal\n");
+       break;
+    case VL_BADINDEX:
+       fprintf(STDERR, "VLDB: index was out of range\n");
+       break;
+    case VL_BADVOLTYPE:
+       fprintf(STDERR, "VLDB: bad volume type\n");
+       break;
+    case VL_BADSERVER:
+       fprintf(STDERR, "VLDB: illegal server number (not within limits)\n");
+       break;
+    case VL_BADPARTITION:
+       fprintf(STDERR, "VLDB: bad partition number\n");
+       break;
+    case VL_REPSFULL:
+       fprintf(STDERR, "VLDB: run out of space for replication sites\n");
+       break;
+    case VL_NOREPSERVER:
+       fprintf(STDERR, "VLDB: no such repsite server exists\n");
+       break;
+    case VL_DUPREPSERVER:
+       fprintf(STDERR, "VLDB: replication site server already exists\n");
+       break;
+    case VL_RWNOTFOUND:
+       fprintf(STDERR, "VLDB: parent r/w entry not found\n");
+       break;
+    case VL_BADREFCOUNT:
+       fprintf(STDERR, "VLDB: illegal reference count number\n");
+       break;
+    case VL_SIZEEXCEEDED:
+       fprintf(STDERR, "VLDB: vldb size for attributes exceeded\n");
+       break;
+    case VL_BADENTRY:
+       fprintf(STDERR, "VLDB: bad incoming vldb entry\n");
+       break;
+    case VL_BADVOLIDBUMP:
+       fprintf(STDERR, "VLDB: illegal max volid increment\n");
+       break;
+    case VL_IDALREADYHASHED:
+       fprintf(STDERR, "VLDB: (RO/BACK) Id already hashed\n");
+       break;
+    case VL_ENTRYLOCKED:
+       fprintf(STDERR, "VLDB: vldb entry is already locked\n");
+       break;
+    case VL_BADVOLOPER:
+       fprintf(STDERR, "VLDB: bad volume operation code\n");
+       break;
+    case VL_BADRELLOCKTYPE:
+       fprintf(STDERR, "VLDB: bad release lock type\n");
+       break;
+    case VL_RERELEASE:
+       fprintf(STDERR, "VLDB: status report: last release was aborted\n");
+       break;
+    case VL_BADSERVERFLAG:
+       fprintf(STDERR, "VLDB: invalid replication site server flag\n");
+       break;
+    case VL_PERM:
+       fprintf(STDERR, "VLDB: no permission access for call\n");
+       break;
+    case VOLSERREAD_DUMPERROR:
+       fprintf(STDERR,
+               "VOLSER:  Problems encountered in reading the dump file !\n");
+       break;
+    case VOLSERDUMPERROR:
+       fprintf(STDERR, "VOLSER: Problems encountered in doing the dump !\n");
+       break;
+    case VOLSERATTACH_ERROR:
+       fprintf(STDERR, "VOLSER: Could not attach the volume\n");
+       break;
+    case VOLSERDETACH_ERROR:
+       fprintf(STDERR, "VOLSER: Could not detach the volume\n");
+       break;
+    case VOLSERILLEGAL_PARTITION:
+       fprintf(STDERR, "VOLSER: encountered illegal partition number\n");
+       break;
+    case VOLSERBAD_ACCESS:
+       fprintf(STDERR, "VOLSER: permission denied, not a super user\n");
+       break;
+    case VOLSERVLDB_ERROR:
+       fprintf(STDERR, "VOLSER: error detected in the VLDB\n");
+       break;
+    case VOLSERBADNAME:
+       fprintf(STDERR, "VOLSER: error in volume name\n");
+       break;
+    case VOLSERVOLMOVED:
+       fprintf(STDERR, "VOLSER: volume has moved\n");
+       break;
+    case VOLSERBADOP:
+       fprintf(STDERR, "VOLSER: illegal operation\n");
+       break;
+    case VOLSERBADRELEASE:
+       fprintf(STDERR, "VOLSER: release could not be completed\n");
+       break;
+    case VOLSERVOLBUSY:
+       fprintf(STDERR, "VOLSER: volume is busy\n");
+       break;
+    case VOLSERNO_MEMORY:
+       fprintf(STDERR, "VOLSER: volume server is out of memory\n");
+       break;
+    case VOLSERNOVOL:
+       fprintf(STDERR,
+               "VOLSER: no such volume - location specified incorrectly or volume does not exist\n");
+       break;
+    case VOLSERMULTIRWVOL:
+       fprintf(STDERR,
+               "VOLSER: multiple RW volumes with same ID, one of which should be deleted\n");
+       break;
+    case VOLSERFAILEDOP:
+       fprintf(STDERR,
+               "VOLSER: not all entries were successfully processed\n");
+       break;
+    default:
+       {
+
+           afs_int32 offset;
+
+           initialize_KA_error_table();
+           initialize_RXK_error_table();
+           initialize_KTC_error_table();
+           initialize_ACFG_error_table();
+           initialize_CMD_error_table();
+           initialize_VL_error_table();
+
+           offset = errcode & ((1 << ERRCODE_RANGE) - 1);
+           fprintf(STDERR, "%s: %s\n", error_table_name(errcode),
+                   error_message(errcode));
+           break;
+       }
+    }
+    return 0;
+}
+
+
+static struct rx_securityClass *uvclass = 0;
+static int uvindex = -1;
+/* called by VLDBClient_Init to set the security module to be used in the RPC */
+int
+UV_SetSecurity(register struct rx_securityClass *as, afs_int32 aindex)
+{
+    uvindex = aindex;
+    uvclass = as;
+    return 0;
+}
+
+/* bind to volser on <port> <aserver> */
+/* takes server address in network order, port in host order.  dumb */
+struct rx_connection *
+UV_Bind(afs_int32 aserver, afs_int32 port)
+{
+    register struct rx_connection *tc;
+
+    tc = rx_NewConnection(aserver, htons(port), VOLSERVICE_ID, uvclass,
+                         uvindex);
+    return tc;
+}
+
+/* if <okvol> is allright(indicated by beibg able to
+ * start a transaction, delete the <delvol> */
+static afs_int32
+CheckAndDeleteVolume(struct rx_connection *aconn, afs_int32 apart,
+                    afs_int32 okvol, afs_int32 delvol)
+{
+    afs_int32 error, code, tid, rcode;
+
+    error = 0;
+    code = 0;
+
+    if (okvol == 0) {
+       code = AFSVolTransCreate(aconn, delvol, apart, ITOffline, &tid);
+       if (!error && code)
+           error = code;
+       code = AFSVolDeleteVolume(aconn, tid);
+       if (!error && code)
+           error = code;
+       code = AFSVolEndTrans(aconn, tid, &rcode);
+       if (!code)
+           code = rcode;
+       if (!error && code)
+           error = code;
+       return error;
+    } else {
+       code = AFSVolTransCreate(aconn, okvol, apart, ITOffline, &tid);
+       if (!code) {
+           code = AFSVolEndTrans(aconn, tid, &rcode);
+           if (!code)
+               code = rcode;
+           if (!error && code)
+               error = code;
+           code = AFSVolTransCreate(aconn, delvol, apart, ITOffline, &tid);
+           if (!error && code)
+               error = code;
+           code = AFSVolDeleteVolume(aconn, tid);
+           if (!error && code)
+               error = code;
+           code = AFSVolEndTrans(aconn, tid, &rcode);
+           if (!code)
+               code = rcode;
+           if (!error && code)
+               error = code;
+       } else
+           error = code;
+       return error;
+    }
+}
+
+/* called by EmuerateEntry, show vldb entry in a reasonable format */
+void
+SubEnumerateEntry(struct nvldbentry *entry)
+{
+    int i;
+    char pname[10];
+    int isMixed = 0;
+
+#ifdef notdef
+    fprintf(STDOUT, "  readWriteID %-10u ", entry->volumeId[RWVOL]);
+    if (entry->flags & RW_EXISTS)
+       fprintf(STDOUT, " valid \n");
+    else
+       fprintf(STDOUT, " invalid \n");
+    fprintf(STDOUT, "  readOnlyID  %-10u ", entry->volumeId[ROVOL]);
+    if (entry->flags & RO_EXISTS)
+       fprintf(STDOUT, " valid \n");
+    else
+       fprintf(STDOUT, " invalid \n");
+    fprintf(STDOUT, "  backUpID    %-10u ", entry->volumeId[BACKVOL]);
+    if (entry->flags & BACK_EXISTS)
+       fprintf(STDOUT, " valid \n");
+    else
+       fprintf(STDOUT, " invalid \n");
+    if ((entry->cloneId != 0) && (entry->flags & RO_EXISTS))
+       fprintf(STDOUT, "    releaseClone %-10u \n", entry->cloneId);
+#else
+    if (entry->flags & RW_EXISTS)
+       fprintf(STDOUT, "    RWrite: %-10u", entry->volumeId[RWVOL]);
+    if (entry->flags & RO_EXISTS)
+       fprintf(STDOUT, "    ROnly: %-10u", entry->volumeId[ROVOL]);
+    if (entry->flags & BACK_EXISTS)
+       fprintf(STDOUT, "    Backup: %-10u", entry->volumeId[BACKVOL]);
+    if ((entry->cloneId != 0) && (entry->flags & RO_EXISTS))
+       fprintf(STDOUT, "    RClone: %-10lu", (unsigned long)entry->cloneId);
+    fprintf(STDOUT, "\n");
+#endif
+    fprintf(STDOUT, "    number of sites -> %lu\n",
+           (unsigned long)entry->nServers);
+    for (i = 0; i < entry->nServers; i++) {
+       if (entry->serverFlags[i] & NEW_REPSITE)
+           isMixed = 1;
+    }
+    for (i = 0; i < entry->nServers; i++) {
+       MapPartIdIntoName(entry->serverPartition[i], pname);
+       fprintf(STDOUT, "       server %s partition %s ",
+               hostutil_GetNameByINet(entry->serverNumber[i]), pname);
+       if (entry->serverFlags[i] & ITSRWVOL)
+           fprintf(STDOUT, "RW Site ");
+       else
+           fprintf(STDOUT, "RO Site ");
+       if (isMixed) {
+           if (entry->serverFlags[i] & NEW_REPSITE)
+               fprintf(STDOUT, " -- New release");
+           else
+               fprintf(STDOUT, " -- Old release");
+       } else {
+           if (entry->serverFlags[i] & RO_DONTUSE)
+               fprintf(STDOUT, " -- Not released");
+       }
+       fprintf(STDOUT, "\n");
+    }
+
+    return;
+
+}
+
+/*enumerate the vldb entry corresponding to <entry> */
+void
+EnumerateEntry(struct nvldbentry *entry)
+{
+
+    fprintf(STDOUT, "\n");
+    fprintf(STDOUT, "%s \n", entry->name);
+    SubEnumerateEntry(entry);
+    return;
+}
+
+/* forcibly remove a volume.  Very dangerous call */
+int
+UV_NukeVolume(afs_int32 server, afs_int32 partid, afs_int32 volid)
+{
+    register struct rx_connection *tconn;
+    register afs_int32 code;
+
+    tconn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    if (tconn) {
+       code = AFSVolNukeVolume(tconn, partid, volid);
+       rx_DestroyConnection(tconn);
+    } else
+       code = 0;
+    return code;
+}
+
+/* like df. Return usage of <pname> on <server> in <partition> */
+int
+UV_PartitionInfo(afs_int32 server, char *pname,
+                struct diskPartition *partition)
+{
+    register struct rx_connection *aconn;
+    afs_int32 code;
+
+    code = 0;
+    aconn = (struct rx_connection *)0;
+    aconn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    code = AFSVolPartitionInfo(aconn, pname, partition);
+    if (code) {
+       fprintf(STDERR, "Could not get information on partition %s\n", pname);
+       PrintError("", code);
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    return code;
+}
+
+/* old interface to create volume */
+int
+UV_CreateVolume(afs_int32 aserver, afs_int32 apart, char *aname,
+               afs_int32 * anewid)
+{
+    afs_int32 code;
+    code = UV_CreateVolume2(aserver, apart, aname, 5000, 0, 0, 0, 0, anewid);
+    return code;
+}
+
+/* create a volume, given a server, partition number, volume name --> sends
+* back new vol id in <anewid>*/
+int
+UV_CreateVolume2(afs_int32 aserver, afs_int32 apart, char *aname,
+                afs_int32 aquota, afs_int32 aspare1, afs_int32 aspare2,
+                afs_int32 aspare3, afs_int32 aspare4, afs_int32 * anewid)
+{
+
+    register struct rx_connection *aconn;
+    afs_int32 tid;
+    register afs_int32 code;
+    afs_int32 error;
+    afs_int32 rcode, vcode;
+    struct nvldbentry entry, storeEntry;       /*the new vldb entry */
+    struct volintInfo tstatus;
+
+    tid = 0;
+    aconn = (struct rx_connection *)0;
+    error = 0;
+    memset(&tstatus, 0, sizeof(struct volintInfo));
+    tstatus.dayUse = -1;
+    tstatus.maxquota = aquota;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+    /* next the next 3 available ids from the VLDB */
+    vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 3, anewid);
+    EGOTO1(cfail, vcode, "Could not get an Id for volume %s\n", aname);
+
+    code =
+       AFSVolCreateVolume(aconn, apart, aname, volser_RW, 0, anewid, &tid);
+    EGOTO2(cfail, vcode, "Failed to create the volume %s %u \n", aname,
+          *anewid);
+
+    code = AFSVolSetInfo(aconn, tid, &tstatus);
+    if (code)
+       EPRINT(code, "Could not change quota (error %d), continuing...\n");
+
+    code = AFSVolSetFlags(aconn, tid, 0);      /* bring it online (mark it InService */
+    EGOTO2(cfail, vcode, "Could not bring the volume %s %u online \n", aname,
+          *anewid);
+
+    VPRINT2("Volume %s %u created and brought online\n", aname, *anewid);
+
+    /* set up the vldb entry for this volume */
+    strncpy(entry.name, aname, VOLSER_OLDMAXVOLNAME);
+    entry.nServers = 1;
+    entry.serverNumber[0] = aserver;   /* this should have another 
+                                        * level of indirection later */
+    entry.serverPartition[0] = apart;  /* this should also have 
+                                        * another indirection level */
+    entry.flags = RW_EXISTS;   /* this records that rw volume exists */
+    entry.serverFlags[0] = ITSRWVOL;   /*this rep site has rw  vol */
+    entry.volumeId[RWVOL] = *anewid;
+    entry.volumeId[ROVOL] = *anewid + 1;       /* rw,ro, bk id are related in the default case */
+    entry.volumeId[BACKVOL] = *anewid + 2;
+    entry.cloneId = 0;
+    /*map into right byte order, before passing to xdr, the stuff has to be in host
+     * byte order. Xdr converts it into network order */
+    MapNetworkToHost(&entry, &storeEntry);
+    /* create the vldb entry */
+    vcode = VLDB_CreateEntry(&storeEntry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not create a VLDB entry for the volume %s %lu\n",
+               aname, (unsigned long)*anewid);
+       /*destroy the created volume */
+       VPRINT1("Deleting the newly created volume %u\n", *anewid);
+       AFSVolDeleteVolume(aconn, tid);
+       error = vcode;
+       goto cfail;
+    }
+    VPRINT2("Created the VLDB entry for the volume %s %u\n", aname, *anewid);
+    /* volume created, now terminate the transaction and release the connection */
+    code = AFSVolEndTrans(aconn, tid, &rcode); /*if it crashes before this
+                                                * the volume will come online anyway when transaction timesout , so if
+                                                * vldb entry exists then the volume is guaranteed to exist too wrt create */
+    tid = 0;
+    if (code) {
+       fprintf(STDERR,
+               "Failed to end the transaction on the volume %s %lu\n", aname,
+               (unsigned long)*anewid);
+       error = code;
+       goto cfail;
+    }
+
+  cfail:
+    if (tid) {
+       code = AFSVolEndTrans(aconn, tid, &rcode);
+       if (code)
+           fprintf(STDERR, "WARNING: could not end transaction\n");
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    PrintError("", error);
+    return error;
+
+
+}
+
+/* create a volume, given a server, partition number, volume name --> sends
+* back new vol id in <anewid>*/
+int
+UV_AddVLDBEntry(afs_int32 aserver, afs_int32 apart, char *aname,
+               afs_int32 aid)
+{
+    register struct rx_connection *aconn;
+    afs_int32 error;
+    afs_int32 vcode;
+    struct nvldbentry entry, storeEntry;       /*the new vldb entry */
+
+    aconn = (struct rx_connection *)0;
+    error = 0;
+
+    /* set up the vldb entry for this volume */
+    strncpy(entry.name, aname, VOLSER_OLDMAXVOLNAME);
+    entry.nServers = 1;
+    entry.serverNumber[0] = aserver;   /* this should have another 
+                                        * level of indirection later */
+    entry.serverPartition[0] = apart;  /* this should also have 
+                                        * another indirection level */
+    entry.flags = RW_EXISTS;   /* this records that rw volume exists */
+    entry.serverFlags[0] = ITSRWVOL;   /*this rep site has rw  vol */
+    entry.volumeId[RWVOL] = aid;
+#ifdef notdef
+    entry.volumeId[ROVOL] = anewid + 1;        /* rw,ro, bk id are related in the default case */
+    entry.volumeId[BACKVOL] = *anewid + 2;
+#else
+    entry.volumeId[ROVOL] = 0;
+    entry.volumeId[BACKVOL] = 0;
+#endif
+    entry.cloneId = 0;
+    /*map into right byte order, before passing to xdr, the stuff has to be in host
+     * byte order. Xdr converts it into network order */
+    MapNetworkToHost(&entry, &storeEntry);
+    /* create the vldb entry */
+    vcode = VLDB_CreateEntry(&storeEntry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not create a VLDB entry for the  volume %s %lu\n",
+               aname, (unsigned long)aid);
+       error = vcode;
+       goto cfail;
+    }
+    VPRINT2("Created the VLDB entry for the volume %s %u\n", aname, aid);
+
+  cfail:
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    PrintError("", error);
+    return error;
+}
+
+/* Delete the volume <volid>on <aserver> <apart>
+ * the physical entry gets removed from the vldb only if the ref count 
+ * becomes zero
+ */
+int
+UV_DeleteVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid)
+{
+    struct rx_connection *aconn = (struct rx_connection *)0;
+    afs_int32 ttid = 0;
+    afs_int32 code, rcode;
+    afs_int32 error = 0;
+    struct nvldbentry entry, storeEntry;
+    int islocked = 0;
+    afs_int32 avoltype = -1, vtype;
+    int notondisk = 0, notinvldb = 0;
+
+    /* Find and read bhe VLDB entry for this volume */
+    code = ubik_Call(VL_SetLock, cstruct, 0, avolid, avoltype, VLOP_DELETE);
+    if (code) {
+       if (code != VL_NOENT) {
+           EGOTO1(error_exit, code,
+                  "Could not lock VLDB entry for the volume %u\n", avolid);
+       }
+       notinvldb = 1;
+    } else {
+       islocked = 1;
+
+       code = VLDB_GetEntryByID(avolid, avoltype, &entry);
+       EGOTO1(error_exit, code, "Could not fetch VLDB entry for volume %u\n",
+              avolid);
+       MapHostToNetwork(&entry);
+
+       if (verbose)
+           EnumerateEntry(&entry);
+    }
+
+    /* Whether volume is in the VLDB or not. Delete the volume on disk */
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+    code = AFSVolTransCreate(aconn, avolid, apart, ITOffline, &ttid);
+    if (code) {
+       if (code == VNOVOL) {
+           notondisk = 1;
+       } else {
+           EGOTO1(error_exit, code, "Transaction on volume %u failed\n",
+                  avolid);
+       }
+    } else {
+       VPRINT1("Trying to delete the volume %u ...", avolid);
+
+       code = AFSVolDeleteVolume(aconn, ttid);
+       EGOTO1(error_exit, code, "Could not delete the volume %u \n", avolid);
+
+       code = AFSVolEndTrans(aconn, ttid, &rcode);
+       code = (code ? code : rcode);
+       ttid = 0;
+       EGOTO1(error_exit, code,
+              "Could not end the transaction for the volume %u \n", avolid);
+       VDONE;
+    }
+
+    /* Now update the VLDB entry.
+     * But first, verify we have a VLDB entry.
+     * Whether volume is on disk or not. Delete the volume in VLDB.
+     */
+    if (notinvldb)
+       ERROR_EXIT(0);
+
+    if (avolid == entry.volumeId[BACKVOL]) {
+       /* Its a backup volume, modify the VLDB entry. Check that the
+        * backup volume is on the server/partition we asked to delete.
+        */
+       if (!(entry.flags & BACK_EXISTS) || !Lp_Match(aserver, apart, &entry)) {
+           notinvldb = 2;      /* Not on this server and partition */
+           ERROR_EXIT(0);
+       }
+
+       VPRINT1("Marking the backup volume %u deleted in the VLDB\n", avolid);
+
+       entry.flags &= ~BACK_EXISTS;
+       vtype = BACKVOL;
+    }
+
+    else if (avolid == entry.volumeId[ROVOL]) {
+       /* Its a read-only volume, modify the VLDB entry. Check that the
+        * readonly volume is on the server/partition we asked to delete.
+        * If flags does not have RO_EIXSTS set, then this may mean the RO 
+        * hasn't been released (and could exist in VLDB).
+        */
+       if (!Lp_ROMatch(aserver, apart, &entry)) {
+           notinvldb = 2;      /* Not found on this server and partition */
+           ERROR_EXIT(0);
+       }
+
+       if (verbose)
+           fprintf(STDOUT,
+                   "Marking the readonly volume %lu deleted in the VLDB\n",
+                   (unsigned long)avolid);
+
+       Lp_SetROValue(&entry, aserver, apart, 0, 0);    /* delete the site */
+       entry.nServers--;
+       if (!Lp_ROMatch(0, 0, &entry))
+           entry.flags &= ~RO_EXISTS;  /* This was the last ro volume */
+       vtype = ROVOL;
+    }
+
+    else if (avolid == entry.volumeId[RWVOL]) {
+       /* It's a rw volume, delete the backup volume, modify the VLDB entry.
+        * Check that the readwrite volumes is on the server/partition we
+        * asked to delete.
+        */
+       if (!(entry.flags & RW_EXISTS) || !Lp_Match(aserver, apart, &entry)) {
+           notinvldb = 2;      /* Not found on this server and partition */
+           ERROR_EXIT(0);
+       }
+
+       /* Delete backup if it exists */
+       code =
+           AFSVolTransCreate(aconn, entry.volumeId[BACKVOL], apart,
+                             ITOffline, &ttid);
+       if (!code) {
+           if (verbose) {
+               fprintf(STDOUT, "Trying to delete the backup volume %u ...",
+                       entry.volumeId[BACKVOL]);
+               fflush(STDOUT);
+           }
+           code = AFSVolDeleteVolume(aconn, ttid);
+           EGOTO1(error_exit, code, "Could not delete the volume %u \n",
+                  entry.volumeId[BACKVOL]);
+
+           code = AFSVolEndTrans(aconn, ttid, &rcode);
+           ttid = 0;
+           code = (code ? code : rcode);
+           EGOTO1(error_exit, code,
+                  "Could not end the transaction for the volume %u \n",
+                  entry.volumeId[BACKVOL]);
+           if (verbose)
+               fprintf(STDOUT, " done\n");
+       }
+
+       if (verbose)
+           fprintf(STDOUT,
+                   "Marking the readwrite volume %lu%s deleted in the VLDB\n",
+                   (unsigned long)avolid,
+                   ((entry.
+                     flags & BACK_EXISTS) ? ", and its backup volume," :
+                    ""));
+
+       Lp_SetRWValue(&entry, aserver, apart, 0L, 0L);
+       entry.nServers--;
+       entry.flags &= ~(BACK_EXISTS | RW_EXISTS);
+       vtype = RWVOL;
+
+       if (entry.flags & RO_EXISTS)
+           fprintf(STDERR, "WARNING: ReadOnly copy(s) may still exist\n");
+    }
+
+    else {
+       notinvldb = 2;          /* Not found on this server and partition */
+       ERROR_EXIT(0);
+    }
+
+    /* Either delete or replace the VLDB entry */
+    if ((entry.nServers <= 0) || !(entry.flags & (RO_EXISTS | RW_EXISTS))) {
+       if (verbose)
+           fprintf(STDOUT,
+                   "Last reference to the VLDB entry for %lu - deleting entry\n",
+                   (unsigned long)avolid);
+       code = ubik_Call(VL_DeleteEntry, cstruct, 0, avolid, vtype);
+       EGOTO1(error_exit, code,
+              "Could not delete the VLDB entry for the volume %u \n",
+              avolid);
+    } else {
+       MapNetworkToHost(&entry, &storeEntry);
+       code =
+           VLDB_ReplaceEntry(avolid, vtype, &storeEntry,
+                             (LOCKREL_OPCODE | LOCKREL_AFSID |
+                              LOCKREL_TIMESTAMP));
+       EGOTO1(error_exit, code,
+              "Could not update the VLDB entry for the volume %u \n",
+              avolid);
+    }
+    islocked = 0;
+
+  error_exit:
+    if (error)
+       EPRINT(error, "\n");
+
+    if (notondisk && notinvldb) {
+       EPRINT2(VOLSERNOVOL, "Volume %u does not exist %s\n", avolid,
+               ((notinvldb == 2) ? "on server and partition" : ""));
+       if (!error)
+           error = VOLSERNOVOL;
+    } else if (notondisk) {
+       fprintf(STDERR,
+               "WARNING: Volume %lu did not exist on the partition\n",
+               (unsigned long)avolid);
+    } else if (notinvldb) {
+       fprintf(STDERR, "WARNING: Volume %lu does not exist in VLDB %s\n",
+               (unsigned long)avolid,
+               ((notinvldb == 2) ? "on server and partition" : ""));
+    }
+
+    if (ttid) {
+       code = AFSVolEndTrans(aconn, ttid, &rcode);
+       code = (code ? code : rcode);
+       if (code) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu\n",
+                   (unsigned long)avolid);
+           PrintError("", code);
+           if (!error)
+               error = code;
+       }
+    }
+
+    if (islocked) {
+       code =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, avolid, -1,
+                     (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
+       if (code) {
+           EPRINT1(code,
+                   "Could not release the lock on the VLDB entry for the volume %u \n",
+                   avolid);
+           if (!error)
+               error = code;
+       }
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    return error;
+}
+
+/* add recovery to UV_MoveVolume */
+
+#define TESTC  0               /* set to test recovery code, clear for production */
+
+jmp_buf env;
+int interrupt = 0;
+
+void
+sigint_handler(int x)
+{
+    if (interrupt)
+       longjmp(env, 0);
+
+    fprintf(STDOUT, "\nSIGINT handler: vos move operation in progress\n");
+    fprintf(STDOUT,
+           "WARNING: may leave AFS storage and metadata in indeterminate state\n");
+    fprintf(STDOUT, "enter second control-c to exit\n");
+    fflush(STDOUT);
+
+    interrupt = 1;
+    (void)signal(SIGINT, sigint_handler);
+
+    return;
+}
+
+/* Move volume <afromvol> on <afromserver> <afrompart> to <atoserver>
+ * <atopart>.  The operation is almost idempotent.  The following
+ * flags are recognized:
+ * 
+ *     RV_NOCLONE - don't use a copy clone
+ */
+
+int
+UV_MoveVolume2(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+              afs_int32 atoserver, afs_int32 atopart, int flags)
+{
+    struct rx_connection *toconn, *fromconn;
+    afs_int32 fromtid, totid, clonetid;
+    char vname[64];
+    char *volName = 0;
+    char tmpName[VOLSER_MAXVOLNAME + 1];
+    afs_int32 rcode;
+    afs_int32 fromDate;
+    struct restoreCookie cookie;
+    register afs_int32 vcode, code;
+    afs_int32 newVol, volid, backupId;
+    struct volser_status tstatus;
+    struct destServer destination;
+
+    struct nvldbentry entry, storeEntry;
+    int i, islocked, pntg;
+    afs_int32 error;
+    char in, lf;               /* for test code */
+    int same;
+
+#ifdef ENABLE_BUGFIX_1165
+    volEntries volumeInfo;
+    struct volintInfo *infop = 0;
+#endif
+
+    islocked = 0;
+    fromconn = (struct rx_connection *)0;
+    toconn = (struct rx_connection *)0;
+    fromtid = 0;
+    totid = 0;
+    clonetid = 0;
+    error = 0;
+    volid = 0;
+    pntg = 0;
+    backupId = 0;
+    newVol = 0;
+
+    /* support control-c processing */
+    if (setjmp(env))
+       goto mfail;
+    (void)signal(SIGINT, sigint_handler);
+
+    if (TESTC) {
+       fprintf(STDOUT,
+               "\nThere are three tests points - verifies all code paths through recovery.\n");
+       fprintf(STDOUT, "First test point - operation not started.\n");
+       fprintf(STDOUT, "...test here (y, n)? ");
+       fflush(STDOUT);
+       fscanf(stdin, "%c", &in);
+       fscanf(stdin, "%c", &lf);       /* toss away */
+       if (in == 'y') {
+           fprintf(STDOUT, "type control-c\n");
+           while (1) {
+               fprintf(stdout, ".");
+               fflush(stdout);
+               sleep(1);
+           }
+       }
+       /* or drop through */
+    }
+
+    vcode = VLDB_GetEntryByID(afromvol, -1, &entry);
+    EGOTO1(mfail, vcode,
+          "Could not fetch the entry for the volume  %u from the VLDB \n",
+          afromvol);
+
+    if (entry.volumeId[RWVOL] != afromvol) {
+       fprintf(STDERR, "Only RW volume can be moved\n");
+       exit(1);
+    }
+
+    vcode = ubik_Call(VL_SetLock, cstruct, 0, afromvol, RWVOL, VLOP_MOVE);
+    EGOTO1(mfail, vcode, "Could not lock entry for volume %u \n", afromvol);
+    islocked = 1;
+
+    vcode = VLDB_GetEntryByID(afromvol, RWVOL, &entry);
+    EGOTO1(mfail, vcode,
+          "Could not fetch the entry for the volume  %u from the VLDB \n",
+          afromvol);
+
+    backupId = entry.volumeId[BACKVOL];
+    MapHostToNetwork(&entry);
+
+    if (!Lp_Match(afromserver, afrompart, &entry)) {
+       /* the from server and partition do not exist in the vldb entry corresponding to volid */
+       if (!Lp_Match(atoserver, atopart, &entry)) {
+           /* the to server and partition do not exist in the vldb entry corresponding to volid */
+           fprintf(STDERR, "The volume %lu is not on the specified site. \n",
+                   (unsigned long)afromvol);
+           fprintf(STDERR, "The current site is :");
+           for (i = 0; i < entry.nServers; i++) {
+               if (entry.serverFlags[i] == ITSRWVOL) {
+                   char pname[10];
+                   MapPartIdIntoName(entry.serverPartition[i], pname);
+                   fprintf(STDERR, " server %s partition %s \n",
+                           hostutil_GetNameByINet(entry.serverNumber[i]),
+                           pname);
+               }
+           }
+           vcode =
+               ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1,
+                         (LOCKREL_OPCODE | LOCKREL_AFSID |
+                          LOCKREL_TIMESTAMP));
+           EGOTO1(mfail, vcode,
+                  " Could not release lock on the VLDB entry for the volume %u \n",
+                  afromvol);
+
+           return VOLSERVOLMOVED;
+       }
+
+       /* delete the volume afromvol on src_server */
+       /* from-info does not exist but to-info does =>
+        * we have already done the move, but the volume
+        * may still be existing physically on from fileserver
+        */
+       fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+       fromtid = 0;
+       pntg = 1;
+
+       code =
+           AFSVolTransCreate(fromconn, afromvol, afrompart, ITOffline,
+                             &fromtid);
+       if (!code) {            /* volume exists - delete it */
+           VPRINT1("Setting flags on leftover source volume %u ...",
+                   afromvol);
+           code =
+               AFSVolSetFlags(fromconn, fromtid,
+                              VTDeleteOnSalvage | VTOutOfService);
+           EGOTO1(mfail, code,
+                  "Failed to set flags on the leftover source volume %u\n",
+                  afromvol);
+           VDONE;
+
+           VPRINT1("Deleting leftover source volume %u ...", afromvol);
+           code = AFSVolDeleteVolume(fromconn, fromtid);
+           EGOTO1(mfail, code,
+                  "Failed to delete the leftover source volume %u\n",
+                  afromvol);
+           VDONE;
+
+           VPRINT1("Ending transaction on leftover source volume %u ...",
+                   afromvol);
+           code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+           fromtid = 0;
+           if (!code)
+               code = rcode;
+           EGOTO1(mfail, code,
+                  "Could not end the transaction for the leftover source volume %u \n",
+                  afromvol);
+           VDONE;
+       }
+
+       /*delete the backup volume now */
+       fromtid = 0;
+       code =
+           AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline,
+                             &fromtid);
+       if (!code) {            /* backup volume exists - delete it */
+           VPRINT1("Setting flags on leftover backup volume %u ...",
+                   backupId);
+           code =
+               AFSVolSetFlags(fromconn, fromtid,
+                              VTDeleteOnSalvage | VTOutOfService);
+           EGOTO1(mfail, code,
+                  "Failed to set flags on the backup volume %u\n", backupId);
+           VDONE;
+
+           VPRINT1("Deleting leftover backup volume %u ...", backupId);
+           code = AFSVolDeleteVolume(fromconn, fromtid);
+           EGOTO1(mfail, code,
+                  "Could not delete the leftover backup volume %u\n",
+                  backupId);
+           VDONE;
+
+           VPRINT1("Ending transaction on leftover backup volume %u ...",
+                   backupId);
+           code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+           fromtid = 0;
+           if (!code)
+               code = rcode;
+           EGOTO1(mfail, code,
+                  "Could not end the transaction for the leftover backup volume %u\n",
+                  backupId);
+           VDONE;
+       }
+
+       fromtid = 0;
+       error = 0;
+       goto mfail;
+    }
+
+    /* From-info matches the vldb info about volid,
+     * its ok start the move operation, the backup volume 
+     * on the old site is deleted in the process 
+     */
+    if (afrompart == atopart) {
+       same = VLDB_IsSameAddrs(afromserver, atoserver, &error);
+       EGOTO2(mfail, error,
+              "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n",
+              afromserver, error);
+
+       if (same) {
+           EGOTO1(mfail, VOLSERVOLMOVED,
+                  "Warning: Moving volume %u to its home partition ignored!\n",
+                  afromvol);
+       }
+    }
+
+    pntg = 1;
+    toconn = UV_Bind(atoserver, AFSCONF_VOLUMEPORT);   /* get connections to the servers */
+    fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+    fromtid = totid = 0;       /* initialize to uncreated */
+
+    /* ***
+     * clone the read/write volume locally.
+     * ***/
+
+    VPRINT1("Starting transaction on source volume %u ...", afromvol);
+    code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
+    EGOTO1(mfail, code, "Failed to create transaction on the volume %u\n",
+          afromvol);
+    VDONE;
+
+    if (!(flags & RV_NOCLONE)) {
+       /* Get a clone id */
+       VPRINT1("Allocating new volume id for clone of volume %u ...",
+               afromvol);
+       newVol = 0;
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &newVol);
+       EGOTO1(mfail, vcode,
+              "Could not get an ID for the clone of volume %u from the VLDB\n",
+              afromvol);
+       VDONE;
+
+       /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
+       VPRINT1("Cloning source volume %u ...", afromvol);
+       strcpy(vname, "move-clone-temp");
+       code =
+           AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname, &newVol);
+       EGOTO1(mfail, code, "Failed to clone the source volume %u\n",
+              afromvol);
+       VDONE;
+    }
+
+    /* lookup the name of the volume we just cloned */
+    volid = afromvol;
+    code = AFSVolGetName(fromconn, fromtid, &volName);
+    EGOTO1(mfail, code, "Failed to get the name of the volume %u\n",
+          afromvol);
+
+    VPRINT1("Ending the transaction on the source volume %u ...", afromvol);
+    rcode = 0;
+    code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+    fromtid = 0;
+    if (!code)
+       code = rcode;
+    EGOTO1(mfail, code,
+          "Failed to end the transaction on the source volume %u\n",
+          afromvol);
+    VDONE;
+
+    /* ***
+     * Create the destination volume
+     * ***/
+
+    if (!(flags & RV_NOCLONE)) {
+       /* All of this is to get the fromDate */
+       VPRINT1("Starting transaction on the cloned volume %u ...", newVol);
+       code =
+           AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline,
+                             &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              newVol);
+       VDONE;
+
+       VPRINT1("Setting flags on cloned volume %u ...", newVol);
+       code =
+           AFSVolSetFlags(fromconn, clonetid,
+                          VTDeleteOnSalvage | VTOutOfService); /*redundant */
+       EGOTO1(mfail, code, "Could not set flags on the cloned volume %u\n",
+              newVol);
+       VDONE;
+
+       /* remember time from which we've dumped the volume */
+       VPRINT1("Getting status of cloned volume %u ...", newVol);
+       code = AFSVolGetStatus(fromconn, clonetid, &tstatus);
+       EGOTO1(mfail, code,
+              "Failed to get the status of the cloned volume %u\n",
+              newVol);
+       VDONE;
+
+       fromDate = tstatus.creationDate - CLOCKSKEW;
+    } else {
+       /* With RV_NOCLONE, just do a full copy from the source */
+       fromDate = 0;
+    }
+
+
+#ifdef ENABLE_BUGFIX_1165
+    /*
+     * Get the internal volume state from the source volume. We'll use such info (i.e. dayUse)
+     * to copy it to the new volume (via AFSSetInfo later on) so that when we move volumes we
+     * don't use this information...
+     */
+    volumeInfo.volEntries_val = (volintInfo *) 0;      /*this hints the stub to allocate space */
+    volumeInfo.volEntries_len = 0;
+    code = AFSVolListOneVolume(fromconn, afrompart, afromvol, &volumeInfo);
+    EGOTO1(mfail, code,
+          "Failed to get the volint Info of the cloned volume %u\n",
+          afromvol);
+
+    infop = (volintInfo *) volumeInfo.volEntries_val;
+    infop->maxquota = -1;      /* Else it will replace the default quota */
+#endif
+
+    /* create a volume on the target machine */
+    volid = afromvol;
+    code = AFSVolTransCreate(toconn, volid, atopart, ITOffline, &totid);
+    if (!code) {
+       /* Delete the existing volume.
+        * While we are deleting the volume in these steps, the transaction
+        * we started against the cloned volume (clonetid above) will be
+        * sitting idle. It will get cleaned up after 600 seconds
+        */
+       VPRINT1("Deleting pre-existing volume %u on destination ...", volid);
+       code = AFSVolDeleteVolume(toconn, totid);
+       EGOTO1(mfail, code,
+              "Could not delete the pre-existing volume %u on destination\n",
+              volid);
+       VDONE;
+
+       VPRINT1
+           ("Ending transaction on pre-existing volume %u on destination ...",
+            volid);
+       code = AFSVolEndTrans(toconn, totid, &rcode);
+       totid = 0;
+       if (!code)
+           code = rcode;
+       EGOTO1(mfail, code,
+              "Could not end the transaction on pre-existing volume %u on destination\n",
+              volid);
+       VDONE;
+    }
+
+    VPRINT1("Creating the destination volume %u ...", volid);
+    code =
+       AFSVolCreateVolume(toconn, atopart, volName, volser_RW, volid, &volid,
+                          &totid);
+    EGOTO1(mfail, code, "Failed to create the destination volume %u\n",
+          volid);
+    VDONE;
+
+    strncpy(tmpName, volName, VOLSER_OLDMAXVOLNAME);
+    free(volName);
+    volName = NULL;
+
+    VPRINT1("Setting volume flags on destination volume %u ...", volid);
+    code =
+       AFSVolSetFlags(toconn, totid, (VTDeleteOnSalvage | VTOutOfService));
+    EGOTO1(mfail, code,
+          "Failed to set the flags on the destination volume %u\n", volid);
+    VDONE;
+
+    /***
+     * Now dump the clone to the new volume
+     ***/
+
+    destination.destHost = ntohl(atoserver);
+    destination.destPort = AFSCONF_VOLUMEPORT;
+    destination.destSSID = 1;
+
+    strncpy(cookie.name, tmpName, VOLSER_OLDMAXVOLNAME);
+    cookie.type = RWVOL;
+    cookie.parent = entry.volumeId[RWVOL];
+    cookie.clone = 0;
+
+    if (!(flags & RV_NOCLONE)) {
+       /* Copy the clone to the new volume */
+       VPRINT2("Dumping from clone %u on source to volume %u on destination ...",
+               newVol, afromvol);
+       code =
+           AFSVolForward(fromconn, clonetid, 0, &destination, totid,
+                         &cookie);
+       EGOTO1(mfail, code, "Failed to move data for the volume %u\n", volid);
+       VDONE;
+
+       VPRINT1("Ending transaction on cloned volume %u ...", newVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              newVol);
+       VDONE;
+    }
+
+    /* ***
+     * reattach to the main-line volume, and incrementally dump it.
+     * ***/
+
+    VPRINT1("Starting transaction on source volume %u ...", afromvol);
+    code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
+    EGOTO1(mfail, code,
+          "Failed to create a transaction on the source volume %u\n",
+          afromvol);
+    VDONE;
+
+    /* now do the incremental */
+    VPRINT2
+       ("Doing the%s dump from source to destination for volume %u ... ",
+        (flags & RV_NOCLONE) ? "" : " incremental",
+        afromvol);
+    code =
+       AFSVolForward(fromconn, fromtid, fromDate, &destination, totid,
+                     &cookie);
+    EGOTO1(mfail, code,
+          "Failed to do the%s dump from rw volume on old site to rw volume on newsite\n",
+         (flags & RV_NOCLONE) ? "" : " incremental");
+    VDONE;
+
+    /* now adjust the flags so that the new volume becomes official */
+    VPRINT1("Setting volume flags on old source volume %u ...", afromvol);
+    code = AFSVolSetFlags(fromconn, fromtid, VTOutOfService);
+    EGOTO(mfail, code,
+         "Failed to set the flags to make old source volume offline\n");
+    VDONE;
+
+    VPRINT1("Setting volume flags on new source volume %u ...", afromvol);
+    code = AFSVolSetFlags(toconn, totid, 0);
+    EGOTO(mfail, code,
+         "Failed to set the flags to make new source volume online\n");
+    VDONE;
+
+#ifdef ENABLE_BUGFIX_1165
+    VPRINT1("Setting volume status on destination volume %u ...", volid);
+    code = AFSVolSetInfo(toconn, totid, infop);
+    EGOTO1(mfail, code,
+          "Failed to set volume status on the destination volume %u\n",
+          volid);
+    VDONE;
+#endif
+
+    /* put new volume online */
+    VPRINT1("Ending transaction on destination volume %u ...", afromvol);
+    code = AFSVolEndTrans(toconn, totid, &rcode);
+    totid = 0;
+    if (!code)
+       code = rcode;
+    EGOTO1(mfail, code,
+          "Failed to end the transaction on the volume %u on the new site\n",
+          afromvol);
+    VDONE;
+
+    Lp_SetRWValue(&entry, afromserver, afrompart, atoserver, atopart);
+    MapNetworkToHost(&entry, &storeEntry);
+    storeEntry.flags &= ~BACK_EXISTS;
+
+    if (TESTC) {
+       fprintf(STDOUT,
+               "Second test point - operation in progress but not complete.\n");
+       fprintf(STDOUT, "...test here (y, n)? ");
+       fflush(STDOUT);
+       fscanf(stdin, "%c", &in);
+       fscanf(stdin, "%c", &lf);       /* toss away */
+       if (in == 'y') {
+           fprintf(STDOUT, "type control-c\n");
+           while (1) {
+               fprintf(stdout, ".");
+               fflush(stdout);
+               sleep(1);
+           }
+       }
+       /* or drop through */
+    }
+
+    VPRINT1("Releasing lock on VLDB entry for volume %u ...", afromvol);
+    vcode =
+       VLDB_ReplaceEntry(afromvol, -1, &storeEntry,
+                         (LOCKREL_OPCODE | LOCKREL_AFSID |
+                          LOCKREL_TIMESTAMP));
+    if (vcode) {
+       fprintf(STDERR,
+               " Could not release the lock on the VLDB entry for the volume %s %lu \n",
+               storeEntry.name, (unsigned long)afromvol);
+       error = vcode;
+       goto mfail;
+    }
+    islocked = 0;
+    VDONE;
+
+    if (TESTC) {
+       fprintf(STDOUT,
+               "Third test point - operation complete but no cleanup.\n");
+       fprintf(STDOUT, "...test here (y, n)? ");
+       fflush(STDOUT);
+       fscanf(stdin, "%c", &in);
+       fscanf(stdin, "%c", &lf);       /* toss away */
+       if (in == 'y') {
+           fprintf(STDOUT, "type control-c\n");
+           while (1) {
+               fprintf(stdout, ".");
+               fflush(stdout);
+               sleep(1);
+           }
+       }
+       /* or drop through */
+    }
+#ifdef notdef
+    /* This is tricky.  File server is very stupid, and if you mark the volume
+     * as VTOutOfService, it may mark the *good* instance (if you're moving
+     * between partitions on the same machine) as out of service.  Since
+     * we're cleaning this code up in DEcorum, we're just going to kludge around
+     * it for now by removing this call. */
+    /* already out of service, just zap it now */
+    code =
+       AFSVolSetFlags(fromconn, fromtid, VTDeleteOnSalvage | VTOutOfService);
+    if (code) {
+       fprintf(STDERR,
+               "Failed to set the flags to make the old source volume offline\n");
+       goto mfail;
+    }
+#endif
+    if (atoserver != afromserver) {
+       /* set forwarding pointer for moved volumes */
+       VPRINT1("Setting forwarding pointer for volume %u ...", afromvol);
+       code = AFSVolSetForwarding(fromconn, fromtid, atoserver);
+       EGOTO1(mfail, code,
+              "Failed to set the forwarding pointer for the volume %u\n",
+              afromvol);
+       VDONE;
+    }
+
+    VPRINT1("Deleting old volume %u on source ...", afromvol);
+    code = AFSVolDeleteVolume(fromconn, fromtid);      /* zap original volume */
+    EGOTO1(mfail, code, "Failed to delete the old volume %u on source\n",
+          afromvol);
+    VDONE;
+
+    VPRINT1("Ending transaction on old volume %u on the source ...",
+           afromvol);
+    code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+    fromtid = 0;
+    if (!code)
+       code = rcode;
+    EGOTO1(mfail, code,
+          "Failed to end the transaction on the old volume %u on the source\n",
+          afromvol);
+    VDONE;
+
+    /* Delete the backup volume on the original site */
+    VPRINT1("Creating transaction for backup volume %u on source ...",
+           backupId);
+    code =
+       AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline, &fromtid);
+    VDONE;
+    if (!code) {
+       VPRINT1("Setting flags on backup volume %u on source ...", backupId);
+       code =
+           AFSVolSetFlags(fromconn, fromtid,
+                          VTDeleteOnSalvage | VTOutOfService);
+       EGOTO1(mfail, code,
+              "Failed to set the flags on the backup volume %u on the source\n",
+              backupId);
+       VDONE;
+
+       VPRINT1("Deleting the backup volume %u on the source ...", backupId);
+       code = AFSVolDeleteVolume(fromconn, fromtid);
+       EGOTO1(mfail, code,
+              "Failed to delete the backup volume %u on the source\n",
+              backupId);
+       VDONE;
+
+       VPRINT1("Ending transaction on backup volume %u on source ...",
+               backupId);
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       fromtid = 0;
+       if (!code)
+           code = rcode;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the backup volume %u on the source\n",
+              backupId);
+       VDONE;
+    } else
+       code = 0;               /* no backup volume? that's okay */
+
+    fromtid = 0;
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on the cloned volume %u ...", newVol);
+       code =
+           AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline,
+                             &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              newVol);
+       VDONE;
+
+       /* now delete the clone */
+       VPRINT1("Deleting the cloned volume %u ...", newVol);
+       code = AFSVolDeleteVolume(fromconn, clonetid);
+       EGOTO1(mfail, code, "Failed to delete the cloned volume %u\n",
+              newVol);
+       VDONE;
+
+       VPRINT1("Ending transaction on cloned volume %u ...", newVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              newVol);
+       VDONE;
+    }
+
+    /* fall through */
+    /* END OF MOVE */
+
+    if (TESTC) {
+       fprintf(STDOUT, "Fourth test point - operation complete.\n");
+       fprintf(STDOUT, "...test here (y, n)? ");
+       fflush(STDOUT);
+       fscanf(stdin, "%c", &in);
+       fscanf(stdin, "%c", &lf);       /* toss away */
+       if (in == 'y') {
+           fprintf(STDOUT, "type control-c\n");
+           while (1) {
+               fprintf(stdout, ".");
+               fflush(stdout);
+               sleep(1);
+           }
+       }
+       /* or drop through */
+    }
+
+    /* normal cleanup code */
+
+    if (entry.flags & RO_EXISTS)
+       fprintf(STDERR, "WARNING : readOnly copies still exist \n");
+
+    if (islocked) {
+       VPRINT1("Cleanup: Releasing VLDB lock on volume %u ...", afromvol);
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1,
+                     (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
+       if (vcode) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   " Could not release the lock on the VLDB entry for the volume %lu \n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = vcode;
+       }
+       VDONE;
+    }
+
+    if (fromtid) {
+       VPRINT1("Cleanup: Ending transaction on source volume %u ...",
+               afromvol);
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       if (code || rcode) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   "Could not end transaction on the source volume %lu\n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+
+    if (clonetid) {
+       VPRINT1("Cleanup: Ending transaction on clone volume %u ...", newVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (code || rcode) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   "Could not end transaction on the source's clone volume %lu\n",
+                   (unsigned long)newVol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+
+    if (totid) {
+       VPRINT1("Cleanup: Ending transaction on destination volume %u ...",
+               afromvol);
+       code = AFSVolEndTrans(toconn, totid, &rcode);
+       if (code) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   "Could not end transaction on destination volume %lu\n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+    if (volName)
+       free(volName);
+#ifdef ENABLE_BUGFIX_1165
+    if (infop)
+       free(infop);
+#endif
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+    if (toconn)
+       rx_DestroyConnection(toconn);
+    PrintError("", error);
+    return error;
+
+    /* come here only when the sky falls */
+  mfail:
+
+    if (pntg) {
+       fprintf(STDOUT,
+               "vos move: operation interrupted, cleanup in progress...\n");
+       fprintf(STDOUT, "clear transaction contexts\n");
+       fflush(STDOUT);
+    }
+
+    /* unlock VLDB entry */
+    if (islocked) {
+       VPRINT1("Recovery: Releasing VLDB lock on volume %u ...", afromvol);
+       ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1,
+                 (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
+       VDONE;
+    }
+
+    if (clonetid) {
+       VPRINT("Recovery: Ending transaction on clone volume ...");
+       AFSVolEndTrans(fromconn, clonetid, &rcode);
+       VDONE;
+    }
+    if (totid) {
+       VPRINT("Recovery: Ending transaction on destination volume ...");
+       AFSVolEndTrans(toconn, totid, &rcode);
+       VDONE;
+    }
+    if (fromtid) {             /* put it on-line */
+       VPRINT("Recovery: Setting volume flags on source volume ...");
+       AFSVolSetFlags(fromconn, fromtid, 0);
+       VDONE;
+
+       VPRINT("Recovery: Ending transaction on source volume ...");
+       AFSVolEndTrans(fromconn, fromtid, &rcode);
+       VDONE;
+    }
+
+    VPRINT("Recovery: Accessing VLDB.\n");
+    vcode = VLDB_GetEntryByID(afromvol, -1, &entry);
+    if (vcode) {
+       fprintf(STDOUT, "FATAL: VLDB access error: abort cleanup\n");
+       fflush(STDOUT);
+       goto done;
+    }
+    MapHostToNetwork(&entry);
+
+    /* Delete either the volume on the source location or the target location. 
+     * If the vldb entry still points to the source location, then we know the
+     * volume move didn't finish so we remove the volume from the target 
+     * location. Otherwise, we remove the volume from the source location.
+     */
+    if (Lp_Match(afromserver, afrompart, &entry)) {    /* didn't move - delete target volume */
+       if (pntg) {
+           fprintf(STDOUT,
+                   "move incomplete - attempt cleanup of target partition - no guarantee\n");
+           fflush(STDOUT);
+       }
+
+       if (volid && toconn) {
+           VPRINT1
+               ("Recovery: Creating transaction for destination volume %u ...",
+                volid);
+           code =
+               AFSVolTransCreate(toconn, volid, atopart, ITOffline, &totid);
+
+           if (!code) {
+               VDONE;
+
+               VPRINT1
+                   ("Recovery: Setting flags on destination volume %u ...",
+                    volid);
+               AFSVolSetFlags(toconn, totid,
+                              VTDeleteOnSalvage | VTOutOfService);
+               VDONE;
+
+               VPRINT1("Recovery: Deleting destination volume %u ...",
+                       volid);
+               AFSVolDeleteVolume(toconn, totid);
+               VDONE;
+
+               VPRINT1
+                   ("Recovery: Ending transaction on destination volume %u ...",
+                    volid);
+               AFSVolEndTrans(toconn, totid, &rcode);
+               VDONE;
+           } else {
+               VPRINT1
+                   ("\nRecovery: Unable to start transaction on destination volume %u.\n",
+                    afromvol);
+           }
+       }
+
+       /* put source volume on-line */
+       if (fromconn) {
+           VPRINT1("Recovery: Creating transaction on source volume %u ...",
+                   afromvol);
+           code =
+               AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy,
+                                 &fromtid);
+           if (!code) {
+               VDONE;
+
+               VPRINT1("Recovery: Setting flags on source volume %u ...",
+                       afromvol);
+               AFSVolSetFlags(fromconn, fromtid, 0);
+               VDONE;
+
+               VPRINT1
+                   ("Recovery: Ending transaction on source volume %u ...",
+                    afromvol);
+               AFSVolEndTrans(fromconn, fromtid, &rcode);
+               VDONE;
+           } else {
+               VPRINT1
+                   ("\nRecovery: Unable to start transaction on source volume %u.\n",
+                    afromvol);
+           }
+       }
+    } else {                   /* yep, move complete */
+       if (pntg) {
+           fprintf(STDOUT,
+                   "move complete - attempt cleanup of source partition - no guarantee\n");
+           fflush(STDOUT);
+       }
+
+       /* delete backup volume */
+       if (fromconn) {
+           VPRINT1("Recovery: Creating transaction on backup volume %u ...",
+                   backupId);
+           code =
+               AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline,
+                                 &fromtid);
+           if (!code) {
+               VDONE;
+
+               VPRINT1("Recovery: Setting flags on backup volume %u ...",
+                       backupId);
+               AFSVolSetFlags(fromconn, fromtid,
+                              VTDeleteOnSalvage | VTOutOfService);
+               VDONE;
+
+               VPRINT1("Recovery: Deleting backup volume %u ...", backupId);
+               AFSVolDeleteVolume(fromconn, fromtid);
+               VDONE;
+
+               VPRINT1
+                   ("Recovery: Ending transaction on backup volume %u ...",
+                    backupId);
+               AFSVolEndTrans(fromconn, fromtid, &rcode);
+               VDONE;
+           } else {
+               VPRINT1
+                   ("\nRecovery: Unable to start transaction on backup volume %u.\n",
+                    backupId);
+           }
+
+           /* delete source volume */
+           VPRINT1("Recovery: Creating transaction on source volume %u ...",
+                   afromvol);
+           code =
+               AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy,
+                                 &fromtid);
+           if (!code) {
+               VDONE;
+
+               VPRINT1("Recovery: Setting flags on backup volume %u ...",
+                       afromvol);
+               AFSVolSetFlags(fromconn, fromtid,
+                              VTDeleteOnSalvage | VTOutOfService);
+               VDONE;
+
+               if (atoserver != afromserver) {
+                   VPRINT("Recovery: Setting volume forwarding pointer ...");
+                   AFSVolSetForwarding(fromconn, fromtid, atoserver);
+                   VDONE;
+               }
+
+               VPRINT1("Recovery: Deleting source volume %u ...", afromvol);
+               AFSVolDeleteVolume(fromconn, fromtid);
+               VDONE;
+
+               VPRINT1
+                   ("Recovery: Ending transaction on source volume %u ...",
+                    afromvol);
+               AFSVolEndTrans(fromconn, fromtid, &rcode);
+               VDONE;
+           } else {
+               VPRINT1
+                   ("\nRecovery: Unable to start transaction on source volume %u.\n",
+                    afromvol);
+           }
+       }
+    }
+
+    /* common cleanup - delete local clone */
+    if (newVol) {
+       VPRINT1("Recovery: Creating transaction on clone volume %u ...",
+               newVol);
+       code =
+           AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline,
+                             &clonetid);
+       if (!code) {
+           VDONE;
+
+           VPRINT1("Recovery: Deleting clone volume %u ...", newVol);
+           AFSVolDeleteVolume(fromconn, clonetid);
+           VDONE;
+
+           VPRINT1("Recovery: Ending transaction on clone volume %u ...",
+                   newVol);
+           AFSVolEndTrans(fromconn, clonetid, &rcode);
+           VDONE;
+       } else {
+           VPRINT1
+               ("\nRecovery: Unable to start transaction on source volume %u.\n",
+                afromvol);
+       }
+    }
+
+    /* unlock VLDB entry */
+    VPRINT1("Recovery: Releasing lock on VLDB entry for volume %u ...",
+           afromvol);
+    ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1,
+             (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
+    VDONE;
+
+  done:                        /* routine cleanup */
+    if (volName)
+       free(volName);
+#ifdef ENABLE_BUGFIX_1165
+    if (infop)
+       free(infop);
+#endif
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+    if (toconn)
+       rx_DestroyConnection(toconn);
+
+    if (pntg) {
+       fprintf(STDOUT, "cleanup complete - user verify desired result\n");
+       fflush(STDOUT);
+    }
+    exit(1);
+}
+
+
+int
+UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+             afs_int32 atoserver, afs_int32 atopart)
+{
+    return UV_MoveVolume2(afromvol, afromserver, afrompart,
+                         atoserver, atopart, 0);
+}
+
+
+/* Copy volume <afromvol> from <afromserver> <afrompart> to <atoserver>
+ * <atopart>.  The new volume is named by <atovolname>.  The new volume
+ * has ID <atovolid> if that is nonzero; otherwise a new ID is allocated
+ * from the VLDB.  the following flags are supported:
+ * 
+ *     RV_RDONLY  - target volume is RO
+ *     RV_OFFLINE - leave target volume offline
+ *     RV_CPINCR  - do incremental dump if target exists
+ *     RV_NOVLDB  - don't create/update VLDB entry
+ *     RV_NOCLONE - don't use a copy clone
+ */
+int
+UV_CopyVolume2(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+              char *atovolname, afs_int32 atoserver, afs_int32 atopart,
+              afs_int32 atovolid, int flags)
+{
+    struct rx_connection *toconn, *fromconn;
+    afs_int32 fromtid, totid, clonetid;
+    char vname[64];
+    afs_int32 rcode;
+    afs_int32 fromDate, cloneFromDate;
+    struct restoreCookie cookie;
+    register afs_int32 vcode, code;
+    afs_int32 cloneVol, newVol, volflag;
+    struct volser_status tstatus;
+    struct destServer destination;
+
+    struct nvldbentry entry, newentry, storeEntry;
+    int islocked, pntg;
+    afs_int32 error;
+    int justclone = 0;
+
+    islocked = 0;
+    fromconn = (struct rx_connection *)0;
+    toconn = (struct rx_connection *)0;
+    fromtid = 0;
+    totid = 0;
+    clonetid = 0;
+    error = 0;
+    pntg = 0;
+    newVol = 0;
+
+    /* support control-c processing */
+    if (setjmp(env))
+       goto mfail;
+    (void)signal(SIGINT, sigint_handler);
+
+    vcode = VLDB_GetEntryByID(afromvol, -1, &entry);
+    EGOTO1(mfail, vcode,
+          "Could not fetch the entry for the volume  %u from the VLDB \n",
+          afromvol);
+    MapHostToNetwork(&entry);
+
+    pntg = 1;
+    toconn = UV_Bind(atoserver, AFSCONF_VOLUMEPORT);   /* get connections to the servers */
+    fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+    fromtid = totid = 0;       /* initialize to uncreated */
+
+
+    /* check if we can shortcut and use a local clone instead of a full copy */
+    if (afromserver == atoserver && afrompart == atopart) {
+       justclone = 1;
+    }
+
+    /* ***
+     * clone the read/write volume locally.
+     * ***/
+
+    cloneVol = 0;
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on source volume %u ...", afromvol);
+       code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy,
+                                &fromtid);
+       EGOTO1(mfail, code, "Failed to create transaction on the volume %u\n",
+              afromvol);
+       VDONE;
+
+       /* Get a clone id */
+       VPRINT1("Allocating new volume id for clone of volume %u ...",
+               afromvol);
+       cloneVol = 0;
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &cloneVol);
+       EGOTO1(mfail, vcode,
+          "Could not get an ID for the clone of volume %u from the VLDB\n",
+          afromvol);
+       VDONE;
+    }
+
+    if (atovolid) {
+       newVol = atovolid;
+    } else {
+       /* Get a new volume id */
+       VPRINT1("Allocating new volume id for copy of volume %u ...", afromvol);
+       newVol = 0;
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &newVol);
+       EGOTO1(mfail, vcode,
+              "Could not get an ID for the copy of volume %u from the VLDB\n",
+              afromvol);
+       VDONE;
+    }
+
+    if (!(flags & RV_NOCLONE)) {
+       /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
+       VPRINT1("Cloning source volume %u ...", afromvol);
+       strcpy(vname, "copy-clone-temp");
+       code =
+           AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname,
+                       &cloneVol);
+       EGOTO1(mfail, code, "Failed to clone the source volume %u\n",
+              afromvol);
+       VDONE;
+
+       VPRINT1("Ending the transaction on the source volume %u ...", afromvol);
+       rcode = 0;
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       fromtid = 0;
+       if (!code)
+           code = rcode;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the source volume %u\n",
+              afromvol);
+       VDONE;
+    }
+
+    /* ***
+     * Create the destination volume
+     * ***/
+
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on the cloned volume %u ...", cloneVol);
+       code =
+           AFSVolTransCreate(fromconn, cloneVol, afrompart, ITOffline,
+                         &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              cloneVol);
+       VDONE;
+
+       VPRINT1("Setting flags on cloned volume %u ...", cloneVol);
+       code =
+           AFSVolSetFlags(fromconn, clonetid,
+                          VTDeleteOnSalvage | VTOutOfService); /*redundant */
+       EGOTO1(mfail, code, "Could not set flags on the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+
+       /* remember time from which we've dumped the volume */
+       VPRINT1("Getting status of cloned volume %u ...", cloneVol);
+       code = AFSVolGetStatus(fromconn, clonetid, &tstatus);
+       EGOTO1(mfail, code,
+              "Failed to get the status of the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+
+       fromDate = tstatus.creationDate - CLOCKSKEW;
+    } else {
+       fromDate = 0;
+    }
+
+    /* create a volume on the target machine */
+    cloneFromDate = 0;
+    code = AFSVolTransCreate(toconn, newVol, atopart, ITOffline, &totid);
+    if (!code) {
+       if ((flags & RV_CPINCR)) {
+           VPRINT1("Getting status of pre-existing volume %u ...", newVol);
+           code = AFSVolGetStatus(toconn, totid, &tstatus);
+           EGOTO1(mfail, code,
+                  "Failed to get the status of the pre-existing volume %u\n",
+                  newVol);
+           VDONE;
+
+           /* Using the update date should be OK here, but add some fudge */
+           cloneFromDate = tstatus.updateDate - CLOCKSKEW;
+           if ((flags & RV_NOCLONE))
+               fromDate = cloneFromDate;
+
+           /* XXX We should check that the source volume's creationDate is
+            * XXX not newer than the existing target volume, and if not,
+            * XXX throw away the existing target and do a full dump. */
+
+           goto cpincr;
+       }
+
+       /* Delete the existing volume.
+        * While we are deleting the volume in these steps, the transaction
+        * we started against the cloned volume (clonetid above) will be
+        * sitting idle. It will get cleaned up after 600 seconds
+        */
+       VPRINT1("Deleting pre-existing volume %u on destination ...", newVol);
+       code = AFSVolDeleteVolume(toconn, totid);
+       EGOTO1(mfail, code,
+              "Could not delete the pre-existing volume %u on destination\n",
+              newVol);
+       VDONE;
+
+       VPRINT1
+           ("Ending transaction on pre-existing volume %u on destination ...",
+            newVol);
+       code = AFSVolEndTrans(toconn, totid, &rcode);
+       totid = 0;
+       if (!code)
+           code = rcode;
+       EGOTO1(mfail, code,
+              "Could not end the transaction on pre-existing volume %u on destination\n",
+              newVol);
+       VDONE;
+    }
+
+    VPRINT1("Creating the destination volume %u ...", newVol);
+    code =
+       AFSVolCreateVolume(toconn, atopart, atovolname,
+                          (flags & RV_RDONLY) ? volser_RO : volser_RW,
+                          newVol, &newVol, &totid);
+    EGOTO1(mfail, code, "Failed to create the destination volume %u\n",
+          newVol);
+    VDONE;
+
+    VPRINT1("Setting volume flags on destination volume %u ...", newVol);
+    code =
+       AFSVolSetFlags(toconn, totid, (VTDeleteOnSalvage | VTOutOfService));
+    EGOTO1(mfail, code,
+          "Failed to set the flags on the destination volume %u\n", newVol);
+    VDONE;
+
+cpincr:
+
+    destination.destHost = ntohl(atoserver);
+    destination.destPort = AFSCONF_VOLUMEPORT;
+    destination.destSSID = 1;
+
+    strncpy(cookie.name, atovolname, VOLSER_OLDMAXVOLNAME);
+    cookie.type = (flags & RV_RDONLY) ? ROVOL : RWVOL;
+    cookie.parent = 0;
+    cookie.clone = 0;
+
+    /***
+     * Now dump the clone to the new volume
+     ***/
+
+    if (!(flags & RV_NOCLONE)) {
+       /* XXX probably should have some code here that checks to see if
+        * XXX we are copying to same server and partition - if so, just
+        * XXX use a clone to save disk space */
+
+       /* Copy the clone to the new volume */
+       VPRINT2("Dumping from clone %u on source to volume %u on destination ...",
+           cloneVol, newVol);
+       code =
+           AFSVolForward(fromconn, clonetid, cloneFromDate, &destination,
+                         totid, &cookie);
+       EGOTO1(mfail, code, "Failed to move data for the volume %u\n",
+              newVol);
+       VDONE;
+
+       VPRINT1("Ending transaction on cloned volume %u ...", cloneVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+    }
+
+    /* ***
+     * reattach to the main-line volume, and incrementally dump it.
+     * ***/
+
+    VPRINT1("Starting transaction on source volume %u ...", afromvol);
+    code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
+    EGOTO1(mfail, code,
+          "Failed to create a transaction on the source volume %u\n",
+          afromvol);
+    VDONE;
+
+    /* now do the incremental */
+    VPRINT2
+       ("Doing the%s dump from source to destination for volume %u ... ",
+        (flags & RV_NOCLONE) ? "" : " incremental",
+        afromvol);
+    code =
+       AFSVolForward(fromconn, fromtid, fromDate, &destination, totid,
+                     &cookie);
+    EGOTO1(mfail, code,
+          "Failed to do the%s dump from old site to new site\n",
+          afromvol);
+    VDONE;
+
+    VPRINT1("Setting volume flags on destination volume %u ...", newVol);
+    volflag = ((flags & RV_OFFLINE) ? VTOutOfService : 0);     /* off or on-line */
+    code = AFSVolSetFlags(toconn, totid, volflag);
+    EGOTO(mfail, code,
+         "Failed to set the flags to make destination volume online\n");
+    VDONE;
+
+    /* put new volume online */
+    VPRINT1("Ending transaction on destination volume %u ...", newVol);
+    code = AFSVolEndTrans(toconn, totid, &rcode);
+    totid = 0;
+    if (!code)
+       code = rcode;
+    EGOTO1(mfail, code,
+          "Failed to end the transaction on the destination volume %u\n",
+          newVol);
+    VDONE;
+
+    VPRINT1("Ending transaction on source volume %u ...", afromvol);
+    code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+    fromtid = 0;
+    if (!code)
+       code = rcode;
+    EGOTO1(mfail, code,
+          "Failed to end the transaction on the source volume %u\n",
+          afromvol);
+    VDONE;
+
+    fromtid = 0;
+
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on the cloned volume %u ...", cloneVol);
+       code =
+           AFSVolTransCreate(fromconn, cloneVol, afrompart, ITOffline,
+                             &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              cloneVol);
+       VDONE;
+
+       /* now delete the clone */
+       VPRINT1("Deleting the cloned volume %u ...", cloneVol);
+       code = AFSVolDeleteVolume(fromconn, clonetid);
+       EGOTO1(mfail, code, "Failed to delete the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+
+       VPRINT1("Ending transaction on cloned volume %u ...", cloneVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+    }
+
+    if (!(flags & RV_NOVLDB)) {
+       /* create the vldb entry for the copied volume */
+       strncpy(newentry.name, atovolname, VOLSER_OLDMAXVOLNAME);
+       newentry.nServers = 1;
+       newentry.serverNumber[0] = atoserver;
+       newentry.serverPartition[0] = atopart;
+       newentry.flags = (flags & RV_RDONLY) ? RO_EXISTS : RW_EXISTS;
+       newentry.serverFlags[0] = (flags & RV_RDONLY) ? ITSROVOL : ITSRWVOL;
+       newentry.volumeId[RWVOL] = newVol;
+       newentry.volumeId[ROVOL] = (flags & RV_RDONLY) ? newVol : 0;
+       newentry.volumeId[BACKVOL] = 0;
+       newentry.cloneId = 0;
+       /*map into right byte order, before passing to xdr, the stuff has to be in host
+        * byte order. Xdr converts it into network order */
+       MapNetworkToHost(&newentry, &storeEntry);
+       /* create the vldb entry */
+       vcode = VLDB_CreateEntry(&storeEntry);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not create a VLDB entry for the volume %s %lu\n",
+                   atovolname, (unsigned long)newVol);
+           /*destroy the created volume */
+           VPRINT1("Deleting the newly created volume %u\n", newVol);
+           AFSVolDeleteVolume(toconn, totid);
+           error = vcode;
+           goto mfail;
+       }
+       VPRINT2("Created the VLDB entry for the volume %s %u\n", atovolname,
+               newVol);
+    }
+
+    /* normal cleanup code */
+
+    if (fromtid) {
+       VPRINT1("Cleanup: Ending transaction on source volume %u ...",
+               afromvol);
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       if (code || rcode) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   "Could not end transaction on the source volume %lu\n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+
+    if (clonetid) {
+       VPRINT1("Cleanup: Ending transaction on clone volume %u ...",
+               cloneVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (code || rcode) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   "Could not end transaction on the source's clone volume %lu\n",
+                   (unsigned long)cloneVol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+
+    if (totid) {
+       VPRINT1("Cleanup: Ending transaction on destination volume %u ...",
+               newVol);
+       code = AFSVolEndTrans(toconn, totid, &rcode);
+       if (code) {
+           VPRINT("\n");
+           fprintf(STDERR,
+                   "Could not end transaction on destination volume %lu\n",
+                   (unsigned long)newVol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+    if (toconn)
+       rx_DestroyConnection(toconn);
+    PrintError("", error);
+    return error;
+
+    /* come here only when the sky falls */
+  mfail:
+
+    if (pntg) {
+       fprintf(STDOUT,
+               "vos copy: operation interrupted, cleanup in progress...\n");
+       fprintf(STDOUT, "clear transaction contexts\n");
+       fflush(STDOUT);
+    }
+
+    if (clonetid) {
+       VPRINT("Recovery: Ending transaction on clone volume ...");
+       AFSVolEndTrans(fromconn, clonetid, &rcode);
+       VDONE;
+    }
+    if (totid) {
+       VPRINT("Recovery: Ending transaction on destination volume ...");
+       AFSVolEndTrans(toconn, totid, &rcode);
+       VDONE;
+    }
+    if (fromtid) {             /* put it on-line */
+       VPRINT("Recovery: Ending transaction on source volume ...");
+       AFSVolEndTrans(fromconn, fromtid, &rcode);
+       VDONE;
+    }
+
+    VPRINT("Recovery: Accessing VLDB.\n");
+    vcode = VLDB_GetEntryByID(afromvol, -1, &entry);
+    if (vcode) {
+       fprintf(STDOUT, "FATAL: VLDB access error: abort cleanup\n");
+       fflush(STDOUT);
+       goto done;
+    }
+    MapHostToNetwork(&entry);
+
+    /* common cleanup - delete local clone */
+    if (cloneVol) {
+       VPRINT1("Recovery: Creating transaction on clone volume %u ...",
+               cloneVol);
+       code =
+           AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline,
+                             &clonetid);
+       if (!code) {
+           VDONE;
+
+           VPRINT1("Recovery: Deleting clone volume %u ...", cloneVol);
+           AFSVolDeleteVolume(fromconn, clonetid);
+           VDONE;
+
+           VPRINT1("Recovery: Ending transaction on clone volume %u ...",
+                   cloneVol);
+           AFSVolEndTrans(fromconn, clonetid, &rcode);
+           VDONE;
+       } else {
+           VPRINT1
+               ("\nRecovery: Unable to start transaction on clone volume %u.\n",
+                cloneVol);
+       }
+    }
+
+  done:                        /* routine cleanup */
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+    if (toconn)
+       rx_DestroyConnection(toconn);
+
+    if (pntg) {
+       fprintf(STDOUT, "cleanup complete - user verify desired result\n");
+       fflush(STDOUT);
+    }
+    exit(1);
+}
+
+
+int
+UV_CopyVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+             char *atovolname, afs_int32 atoserver, afs_int32 atopart)
+{
+    return UV_CopyVolume2(afromvol, afromserver, afrompart,
+                          atovolname, atoserver, atopart, 0, 0);
+}
+
+
+
+/* Make a new backup of volume <avolid> on <aserver> and <apart> 
+ * if one already exists, update it 
+ */
+
+int
+UV_BackupVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid)
+{
+    struct rx_connection *aconn = (struct rx_connection *)0;
+    afs_int32 ttid = 0, btid = 0;
+    afs_int32 backupID;
+    afs_int32 code = 0, rcode = 0;
+    char vname[VOLSER_MAXVOLNAME + 1];
+    struct nvldbentry entry, storeEntry;
+    afs_int32 error = 0;
+    int vldblocked = 0, vldbmod = 0, backexists = 1;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+    /* the calls to VLDB will succeed only if avolid is a RW volume,
+     * since we are following the RW hash chain for searching */
+    code = VLDB_GetEntryByID(avolid, RWVOL, &entry);
+    if (code) {
+       fprintf(STDERR,
+               "Could not fetch the entry for the volume %lu from the VLDB \n",
+               (unsigned long)avolid);
+       error = code;
+       goto bfail;
+    }
+    MapHostToNetwork(&entry);
+
+    /* These operations require the VLDB be locked since it means the VLDB
+     * will change or the vldb is already locked.
+     */
+    if (!(entry.flags & BACK_EXISTS) ||        /* backup volume doesnt exist */
+       (entry.flags & VLOP_ALLOPERS) ||        /* vldb lock already held */
+       (entry.volumeId[BACKVOL] == INVALID_BID)) {     /* no assigned backup volume id */
+
+       code = ubik_Call(VL_SetLock, cstruct, 0, avolid, RWVOL, VLOP_BACKUP);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not lock the VLDB entry for the volume %lu\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+       vldblocked = 1;
+
+       /* Reread the vldb entry */
+       code = VLDB_GetEntryByID(avolid, RWVOL, &entry);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not fetch the entry for the volume %lu from the VLDB \n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+       MapHostToNetwork(&entry);
+    }
+
+    if (!ISNAMEVALID(entry.name)) {
+       fprintf(STDERR, "Name of the volume %s exceeds the size limit\n",
+               entry.name);
+       error = VOLSERBADNAME;
+       goto bfail;
+    }
+
+    backupID = entry.volumeId[BACKVOL];
+    if (backupID == INVALID_BID) {
+       /* Get a backup volume id from the VLDB and update the vldb
+        * entry with it. 
+        */
+       code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &backupID);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not allocate ID for the backup volume of  %lu from the VLDB\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+       entry.volumeId[BACKVOL] = backupID;
+       vldbmod = 1;
+    }
+
+    /* Test to see if the backup volume exists by trying to create
+     * a transaction on the backup volume. We've assumed the backup exists.
+     */
+    code = AFSVolTransCreate(aconn, backupID, apart, ITOffline, &btid);
+    if (code) {
+       if (code != VNOVOL) {
+           fprintf(STDERR, "Could not reach the backup volume %lu\n",
+                   (unsigned long)backupID);
+           error = code;
+           goto bfail;
+       }
+       backexists = 0;         /* backup volume does not exist */
+    }
+    if (btid) {
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       btid = 0;
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction on the previous backup volume %lu\n",
+                   (unsigned long)backupID);
+           error = (code ? code : rcode);
+           goto bfail;
+       }
+    }
+
+    /* Now go ahead and try to clone the RW volume.
+     * First start a transaction on the RW volume 
+     */
+    code = AFSVolTransCreate(aconn, avolid, apart, ITBusy, &ttid);
+    if (code) {
+       fprintf(STDERR, "Could not start a transaction on the volume %lu\n",
+               (unsigned long)avolid);
+       error = code;
+       goto bfail;
+    }
+
+    /* Clone or reclone the volume, depending on whether the backup 
+     * volume exists or not
+     */
+    if (backexists) {
+       VPRINT1("Re-cloning backup volume %u ...", backupID);
+
+       code = AFSVolReClone(aconn, ttid, backupID);
+       if (code) {
+           fprintf(STDERR, "Could not re-clone backup volume %lu\n",
+                   (unsigned long)backupID);
+           error = code;
+           goto bfail;
+       }
+    } else {
+       VPRINT1("Creating a new backup clone %u ...", backupID);
+
+       strcpy(vname, entry.name);
+       strcat(vname, ".backup");
+
+       code = AFSVolClone(aconn, ttid, 0, backupVolume, vname, &backupID);
+       if (code) {
+           fprintf(STDERR, "Failed to clone the volume %lu\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+    }
+
+    /* End the transaction on the RW volume */
+    code = AFSVolEndTrans(aconn, ttid, &rcode);
+    ttid = 0;
+    if (code || rcode) {
+       fprintf(STDERR,
+               "Failed to end the transaction on the rw volume %lu\n",
+               (unsigned long)avolid);
+       error = (code ? code : rcode);
+       goto bfail;
+    }
+
+    /* Mork vldb as backup exists */
+    if (!(entry.flags & BACK_EXISTS)) {
+       entry.flags |= BACK_EXISTS;
+       vldbmod = 1;
+    }
+
+    /* Now go back to the backup volume and bring it on line */
+    code = AFSVolTransCreate(aconn, backupID, apart, ITOffline, &btid);
+    if (code) {
+       fprintf(STDERR,
+               "Failed to start a transaction on the backup volume %lu\n",
+               (unsigned long)backupID);
+       error = code;
+       goto bfail;
+    }
+
+    code = AFSVolSetFlags(aconn, btid, 0);
+    if (code) {
+       fprintf(STDERR, "Could not mark the backup volume %lu on line \n",
+               (unsigned long)backupID);
+       error = code;
+       goto bfail;
+    }
+
+    code = AFSVolEndTrans(aconn, btid, &rcode);
+    btid = 0;
+    if (code || rcode) {
+       fprintf(STDERR,
+               "Failed to end the transaction on the backup volume %lu\n",
+               (unsigned long)backupID);
+       error = (code ? code : rcode);
+       goto bfail;
+    }
+
+    VDONE;
+
+    /* Will update the vldb below */
+
+  bfail:
+    if (ttid) {
+       code = AFSVolEndTrans(aconn, ttid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu\n",
+                   (unsigned long)avolid);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (btid) {
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction the backup volume %lu\n",
+                   (unsigned long)backupID);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    /* Now update the vldb - if modified */
+    if (vldblocked) {
+       if (vldbmod) {
+           MapNetworkToHost(&entry, &storeEntry);
+           code =
+               VLDB_ReplaceEntry(avolid, RWVOL, &storeEntry,
+                                 (LOCKREL_OPCODE | LOCKREL_AFSID |
+                                  LOCKREL_TIMESTAMP));
+           if (code) {
+               fprintf(STDERR,
+                       "Could not update the VLDB entry for the volume %lu \n",
+                       (unsigned long)avolid);
+               if (!error)
+                   error = code;
+           }
+       } else {
+           code =
+               ubik_Call(VL_ReleaseLock, cstruct, 0, avolid, RWVOL,
+                         (LOCKREL_OPCODE | LOCKREL_AFSID |
+                          LOCKREL_TIMESTAMP));
+           if (code) {
+               fprintf(STDERR,
+                       "Could not unlock the VLDB entry for the volume %lu \n",
+                       (unsigned long)avolid);
+               if (!error)
+                   error = code;
+           }
+       }
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+
+    PrintError("", error);
+    return error;
+}
+
+/* Make a new clone of volume <avolid> on <aserver> and <apart> 
+ * using volume ID <acloneid>, or a new ID allocated from the VLDB.
+ * The new volume is named by <aname>, or by appending ".clone" to
+ * the existing name if <aname> is NULL.  The following flags are
+ * supported:
+ * 
+ *     RV_RDONLY  - target volume is RO
+ *     RV_OFFLINE - leave target volume offline
+ */
+
+int
+UV_CloneVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid,
+              afs_int32 acloneid, char *aname, int flags)
+{
+    struct rx_connection *aconn = (struct rx_connection *)0;
+    afs_int32 ttid = 0, btid = 0;
+    afs_int32 code = 0, rcode = 0;
+    char vname[VOLSER_MAXVOLNAME + 1];
+    afs_int32 error = 0;
+    int backexists = 1;
+    volEntries volumeInfo;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+    if (!aname) {
+       volumeInfo.volEntries_val = (volintInfo *) 0;
+       volumeInfo.volEntries_len = 0;
+       code = AFSVolListOneVolume(aconn, apart, avolid, &volumeInfo);
+       if (code) {
+           fprintf(stderr, "Could not get info for volume %lu\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+       strncpy(vname, volumeInfo.volEntries_val[0].name,
+               VOLSER_OLDMAXVOLNAME - 7);
+       vname[VOLSER_OLDMAXVOLNAME - 7] = 0;
+       strcat(vname, ".clone");
+       aname = vname;
+       if (volumeInfo.volEntries_val)
+           free(volumeInfo.volEntries_val);
+    }
+
+    if (!acloneid) {
+       /* Get a clone id */
+       VPRINT1("Allocating new volume id for clone of volume %u ...",
+               avolid);
+       code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &acloneid);
+       EGOTO1(bfail, code,
+          "Could not get an ID for the clone of volume %u from the VLDB\n",
+          avolid);
+       VDONE;
+    }
+
+    /* Test to see if the clone volume exists by trying to create
+     * a transaction on the clone volume. We've assumed the clone exists.
+     */
+    /* XXX I wonder what happens if the clone has some other parent... */
+    code = AFSVolTransCreate(aconn, acloneid, apart, ITOffline, &btid);
+    if (code) {
+       if (code != VNOVOL) {
+           fprintf(STDERR, "Could not reach the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+       backexists = 0;         /* backup volume does not exist */
+    }
+    if (btid) {
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       btid = 0;
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction on the previous clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = (code ? code : rcode);
+           goto bfail;
+       }
+    }
+
+    /* Now go ahead and try to clone the RW volume.
+     * First start a transaction on the RW volume 
+     */
+    code = AFSVolTransCreate(aconn, avolid, apart, ITBusy, &ttid);
+    if (code) {
+       fprintf(STDERR, "Could not start a transaction on the volume %lu\n",
+               (unsigned long)avolid);
+       error = code;
+       goto bfail;
+    }
+
+    /* Clone or reclone the volume, depending on whether the backup 
+     * volume exists or not
+     */
+    if (backexists) {
+       VPRINT1("Re-cloning clone volume %u ...", acloneid);
+
+       code = AFSVolReClone(aconn, ttid, acloneid);
+       if (code) {
+           fprintf(STDERR, "Could not re-clone backup volume %lu\n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+    } else {
+       VPRINT1("Creating a new clone %u ...", acloneid);
+
+       code = AFSVolClone(aconn, ttid, 0,
+                          (flags & RV_RDONLY) ? readonlyVolume : backupVolume,
+                          aname, &acloneid);
+       if (code) {
+           fprintf(STDERR, "Failed to clone the volume %lu\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+    }
+
+    /* End the transaction on the RW volume */
+    code = AFSVolEndTrans(aconn, ttid, &rcode);
+    ttid = 0;
+    if (code || rcode) {
+       fprintf(STDERR,
+               "Failed to end the transaction on the rw volume %lu\n",
+               (unsigned long)avolid);
+       error = (code ? code : rcode);
+       goto bfail;
+    }
+
+    /* Now go back to the backup volume and bring it on line */
+    if (!(flags & RV_OFFLINE)) {
+       code = AFSVolTransCreate(aconn, acloneid, apart, ITOffline, &btid);
+       if (code) {
+           fprintf(STDERR,
+                   "Failed to start a transaction on the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+
+       code = AFSVolSetFlags(aconn, btid, 0);
+       if (code) {
+           fprintf(STDERR, "Could not mark the clone volume %lu on line \n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       btid = 0;
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Failed to end the transaction on the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = (code ? code : rcode);
+           goto bfail;
+       }
+    }
+
+    VDONE;
+
+  bfail:
+    if (ttid) {
+       code = AFSVolEndTrans(aconn, ttid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu\n",
+                   (unsigned long)avolid);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (btid) {
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction on the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+
+    PrintError("", error);
+    return error;
+}
+
+static int
+DelVol(struct rx_connection *conn, afs_int32 vid, afs_int32 part,
+       afs_int32 flags)
+{
+    afs_int32 acode, ccode, rcode, tid;
+    ccode = rcode = tid = 0;
+
+    acode = AFSVolTransCreate(conn, vid, part, flags, &tid);
+    if (!acode) {              /* It really was there */
+       acode = AFSVolDeleteVolume(conn, tid);
+       if (acode) {
+           fprintf(STDERR, "Failed to delete volume %lu.\n",
+                   (unsigned long)vid);
+           PrintError("", acode);
+       }
+       ccode = AFSVolEndTrans(conn, tid, &rcode);
+       if (!ccode)
+           ccode = rcode;
+       if (ccode) {
+           fprintf(STDERR, "Failed to end transaction on volume %lu.\n",
+                   (unsigned long)vid);
+           PrintError("", ccode);
+       }
+    }
+
+    return acode;
+}
+
+#define ONERROR(ec, ep, es) if (ec) { fprintf(STDERR, (es), (ep)); error = (ec); goto rfail; }
+#define ERROREXIT(ec) { error = (ec); goto rfail; }
+
+/* Get a "transaction" on this replica.  Create the volume 
+ * if necessary.  Return the time from which a dump should
+ * be made (0 if it's a new volume)
+ */
+static int
+GetTrans(struct nvldbentry *vldbEntryPtr, afs_int32 index,
+        struct rx_connection **connPtr, afs_int32 * transPtr,
+        afs_int32 * timePtr)
+{
+    afs_int32 volid;
+    struct volser_status tstatus;
+    int code, rcode, tcode;
+
+    *connPtr = (struct rx_connection *)0;
+    *timePtr = 0;
+    *transPtr = 0;
+
+    /* get connection to the replication site */
+    *connPtr = UV_Bind(vldbEntryPtr->serverNumber[index], AFSCONF_VOLUMEPORT);
+    if (!*connPtr)
+       goto fail;              /* server is down */
+
+    volid = vldbEntryPtr->volumeId[ROVOL];
+    if (volid)
+       code =
+           AFSVolTransCreate(*connPtr, volid,
+                             vldbEntryPtr->serverPartition[index], ITOffline,
+                             transPtr);
+
+    /* If the volume does not exist, create it */
+    if (!volid || code) {
+       char volname[64];
+
+       if (volid && (code != VNOVOL)) {
+           PrintError("Failed to start a transaction on the RO volume.\n",
+                      code);
+           goto fail;
+       }
+
+       strcpy(volname, vldbEntryPtr->name);
+       strcat(volname, ".readonly");
+
+       if (verbose) {
+           fprintf(STDOUT,
+                   "Creating new volume %lu on replication site %s: ",
+                   (unsigned long)volid,
+                   hostutil_GetNameByINet(vldbEntryPtr->
+                                          serverNumber[index]));
+           fflush(STDOUT);
+       }
+
+       code =
+           AFSVolCreateVolume(*connPtr, vldbEntryPtr->serverPartition[index],
+                              volname, volser_RO,
+                              vldbEntryPtr->volumeId[RWVOL], &volid,
+                              transPtr);
+       if (code) {
+           PrintError("Failed to create the ro volume: ", code);
+           goto fail;
+       }
+       vldbEntryPtr->volumeId[ROVOL] = volid;
+
+       VDONE;
+
+       /* The following is a bit redundant, since create sets these flags by default */
+       code =
+           AFSVolSetFlags(*connPtr, *transPtr,
+                          VTDeleteOnSalvage | VTOutOfService);
+       if (code) {
+           PrintError("Failed to set flags on the ro volume: ", code);
+           goto fail;
+       }
+    }
+
+    /* Otherwise, the transaction did succeed, so get the creation date of the
+     * latest RO volume on the replication site 
+     */
+    else {
+       VPRINT2("Updating existing ro volume %u on %s ...\n", volid,
+               hostutil_GetNameByINet(vldbEntryPtr->serverNumber[index]));
+
+       code = AFSVolGetStatus(*connPtr, *transPtr, &tstatus);
+       if (code) {
+           PrintError("Failed to get status of volume on destination: ",
+                      code);
+           goto fail;
+       }
+       *timePtr = tstatus.creationDate - CLOCKSKEW;
+    }
+
+    return 0;
+
+  fail:
+    if (*transPtr) {
+       tcode = AFSVolEndTrans(*connPtr, *transPtr, &rcode);
+       *transPtr = 0;
+       if (!tcode)
+           tcode = rcode;
+       if (tcode)
+           PrintError("Could not end transaction on a ro volume: ", tcode);
+    }
+
+    return code;
+}
+
+static int
+SimulateForwardMultiple(struct rx_connection *fromconn, afs_int32 fromtid,
+                       afs_int32 fromdate, manyDests * tr, afs_int32 flags,
+                       void *cookie, manyResults * results)
+{
+    int i;
+
+    for (i = 0; i < tr->manyDests_len; i++) {
+       results->manyResults_val[i] =
+           AFSVolForward(fromconn, fromtid, fromdate,
+                         &(tr->manyDests_val[i].server),
+                         tr->manyDests_val[i].trans, cookie);
+    }
+    return 0;
+}
+
+
+static int
+rel_compar(struct release *r1, struct release *r2)
+{
+    return (r1->time - r2->time);
+}
+
+/* UV_ReleaseVolume()
+ *    Release volume <afromvol> on <afromserver> <afrompart> to all
+ *    its RO sites (full release). Unless the previous release was
+ *    incomplete: in which case we bring the remaining incomplete
+ *    volumes up to date with the volumes that were released
+ *    successfully.
+ *    forceflag: Performs a full release.
+ *
+ *    Will create a clone from the RW, then dump the clone out to 
+ *    the remaining replicas. If there is more than 1 RO sites,
+ *    ensure that the VLDB says at least one RO is available all
+ *    the time: Influences when we write back the VLDB entry.
+ */
+
+int
+UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver,
+                afs_int32 afrompart, int forceflag)
+{
+    char vname[64];
+    afs_int32 code, vcode, rcode, tcode;
+    afs_int32 cloneVolId, roVolId;
+    struct replica *replicas = 0;
+    struct nvldbentry entry, storeEntry;
+    int i, volcount, m, fullrelease, vldbindex;
+    int failure;
+    struct restoreCookie cookie;
+    struct rx_connection **toconns = 0;
+    struct release *times = 0;
+    int nservers = 0;
+    struct rx_connection *fromconn = (struct rx_connection *)0;
+    afs_int32 error = 0;
+    int islocked = 0;
+    afs_int32 clonetid = 0, onlinetid;
+    afs_int32 fromtid = 0;
+    afs_uint32 fromdate, thisdate;
+    int s;
+    manyDests tr;
+    manyResults results;
+    int rwindex, roindex, roclone, roexists;
+    afs_int32 rwcrdate, clcrdate;
+    struct rtime {
+       int validtime;
+       afs_uint32 time;
+    } remembertime[NMAXNSERVERS];
+    int releasecount = 0;
+    struct volser_status volstatus;
+
+    memset((char *)remembertime, 0, sizeof(remembertime));
+    memset((char *)&results, 0, sizeof(results));
+
+    vcode = ubik_Call(VL_SetLock, cstruct, 0, afromvol, RWVOL, VLOP_RELEASE);
+    if (vcode != VL_RERELEASE)
+       ONERROR(vcode, afromvol,
+               "Could not lock the VLDB entry for the volume %u.\n");
+    islocked = 1;
+
+    /* Get the vldb entry in readable format */
+    vcode = VLDB_GetEntryByID(afromvol, RWVOL, &entry);
+    ONERROR(vcode, afromvol,
+           "Could not fetch the entry for the volume %u from the VLDB.\n");
+    MapHostToNetwork(&entry);
+
+    if (verbose)
+       EnumerateEntry(&entry);
+
+    if (!ISNAMEVALID(entry.name))
+       ONERROR(VOLSERBADOP, entry.name,
+               "Volume name %s is too long, rename before releasing.\n");
+    if (entry.volumeId[RWVOL] != afromvol)
+       ONERROR(VOLSERBADOP, afromvol,
+               "The volume %u being released is not a read-write volume.\n");
+    if (entry.nServers <= 1)
+       ONERROR(VOLSERBADOP, afromvol,
+               "Volume %u has no replicas - release operation is meaningless!\n");
+    if (strlen(entry.name) > (VOLSER_OLDMAXVOLNAME - 10))
+       ONERROR(VOLSERBADOP, entry.name,
+               "RO volume name %s exceeds (VOLSER_OLDMAXVOLNAME - 10) character limit\n");
+
+    /* roclone is true if one of the RO volumes is on the same
+     * partition as the RW volume. In this case, we make the RO volume
+     * on the same partition a clone instead of a complete copy.
+     */
+
+    roindex = Lp_ROMatch(afromserver, afrompart, &entry) - 1;
+    roclone = ((roindex == -1) ? 0 : 1);
+    rwindex = Lp_GetRwIndex(&entry);
+    if (rwindex < 0)
+       ONERROR(VOLSERNOVOL, 0, "There is no RW volume \n");
+
+    /* Make sure we have a RO volume id to work with */
+    if (entry.volumeId[ROVOL] == INVALID_BID) {
+       /* need to get a new RO volume id */
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &roVolId);
+       ONERROR(vcode, entry.name, "Cant allocate ID for RO volume of %s\n");
+
+       entry.volumeId[ROVOL] = roVolId;
+       MapNetworkToHost(&entry, &storeEntry);
+       vcode = VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, 0);
+       ONERROR(vcode, entry.name, "Could not update vldb entry for %s.\n");
+    }
+
+    /* Will we be completing a previously unfinished release. -force overrides */
+    for (fullrelease = 1, i = 0; (fullrelease && (i < entry.nServers)); i++) {
+       if (entry.serverFlags[i] & NEW_REPSITE)
+           fullrelease = 0;
+    }
+    if (forceflag && !fullrelease)
+       fullrelease = 1;
+
+    /* Determine which volume id to use and see if it exists */
+    cloneVolId =
+       ((fullrelease
+         || (entry.cloneId == 0)) ? entry.volumeId[ROVOL] : entry.cloneId);
+    code = VolumeExists(afromserver, afrompart, cloneVolId);
+    roexists = ((code == ENODEV) ? 0 : 1);
+
+    fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+    if (!fromconn)
+       ONERROR(-1, afromserver,
+               "Cannot establish connection with server 0x%x\n");
+
+    if (!fullrelease) {
+       if (!roexists)
+           fullrelease = 1;    /* Do a full release if RO clone does not exist */
+       else {
+           /* Begin transaction on RW and mark it busy while we query it */
+           code = AFSVolTransCreate(
+                       fromconn, afromvol, afrompart, ITBusy, &fromtid
+                  );
+           ONERROR(code, afromvol,
+                   "Failed to start transaction on RW volume %u\n");
+
+           /* Query the creation date for the RW */
+           code = AFSVolGetStatus(fromconn, fromtid, &volstatus);
+           ONERROR(code, afromvol,
+                   "Failed to get the status of RW volume %u\n");
+           rwcrdate = volstatus.creationDate;
+
+           /* End transaction on RW */
+           code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+           fromtid = 0;
+           ONERROR((code ? code : rcode), afromvol,
+                   "Failed to end transaction on RW volume %u\n");
+
+           /* Begin transaction on clone and mark it busy while we query it */
+           code = AFSVolTransCreate(
+                       fromconn, cloneVolId, afrompart, ITBusy, &clonetid
+                  );
+           ONERROR(code, cloneVolId,
+                   "Failed to start transaction on RW clone %u\n");
+
+           /* Query the creation date for the clone */
+           code = AFSVolGetStatus(fromconn, clonetid, &volstatus);
+           ONERROR(code, cloneVolId,
+                   "Failed to get the status of RW clone %u\n");
+           clcrdate = volstatus.creationDate;
+
+           /* End transaction on RW */
+           code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+           clonetid = 0;
+           ONERROR((code ? code : rcode), cloneVolId,
+                   "Failed to end transaction on RW volume %u\n");
+
+           if (rwcrdate > clcrdate)
+               fullrelease = 2;/* Do a full release if RO clone older than RW */
+       }
+    }
+
+    if (verbose) {
+       switch (fullrelease) {
+           case 2:
+               fprintf(STDOUT, "RW %lu changed, doing a complete release\n",
+                       (unsigned long)afromvol);
+               break;
+           case 1:
+               fprintf(STDOUT, "This is a complete release of volume %lu\n",
+                       (unsigned long)afromvol);
+               break;
+           case 0:
+               fprintf(STDOUT, "This is a completion of a previous release\n");
+               break;
+       }
+    }
+
+    if (fullrelease) {
+       /* If the RO clone exists, then if the clone is a temporary
+        * clone, delete it. Or if the RO clone is marked RO_DONTUSE
+        * (it was recently added), then also delete it. We do not
+        * want to "reclone" a temporary RO clone.
+        */
+       if (roexists
+           && (!roclone || (entry.serverFlags[roindex] & RO_DONTUSE))) {
+           code = DelVol(fromconn, cloneVolId, afrompart, ITOffline);
+           if (code && (code != VNOVOL))
+               ERROREXIT(code);
+           roexists = 0;
+       }
+
+       /* Mark all the ROs in the VLDB entry as RO_DONTUSE. We don't
+        * write this entry out to the vlserver until after the first
+        * RO volume is released (temp RO clones don't count).
+        */
+       for (i = 0; i < entry.nServers; i++) {
+           entry.serverFlags[i] &= ~NEW_REPSITE;
+           entry.serverFlags[i] |= RO_DONTUSE;
+       }
+       entry.serverFlags[rwindex] |= NEW_REPSITE;
+       entry.serverFlags[rwindex] &= ~RO_DONTUSE;
+
+       /* Begin transaction on RW and mark it busy while we clone it */
+       code =
+           AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy,
+                             &clonetid);
+       ONERROR(code, afromvol, "Failed to start transaction on volume %u\n");
+
+       /* Clone or reclone the volume */
+       if (roexists) {
+           VPRINT1("Recloning RW volume %u...", cloneVolId);
+           code = AFSVolReClone(fromconn, clonetid, cloneVolId);
+           ONERROR(code, afromvol, "Failed to reclone the RW volume %u\n");
+           VDONE;
+       } else {
+           if (roclone) {
+               strcpy(vname, entry.name);
+               strcat(vname, ".readonly");
+               VPRINT("Cloning RW volume %u to permanent RO...");
+           } else {
+               strcpy(vname, "readonly-clone-temp");
+               VPRINT("Cloning RW volume %u to temporary RO...");
+           }
+           code =
+               AFSVolClone(fromconn, clonetid, 0, readonlyVolume, vname,
+                           &cloneVolId);
+           ONERROR(code, afromvol, "Failed to clone the RW volume %u\n");
+           VDONE;
+       }
+
+       /* Get the time the RW was created for future information */
+       VPRINT1("Getting status of RW volume %u...", cloneVolId);
+       code = AFSVolGetStatus(fromconn, clonetid, &volstatus);
+       ONERROR(code, cloneVolId,
+               "Failed to get the status of the RW volume %u\n");
+       VDONE;
+       rwcrdate = volstatus.creationDate;
+
+       /* End the transaction on the RW volume */
+       VPRINT1("Ending cloning transaction on RW volume %u...", cloneVolId);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       clonetid = 0;
+       ONERROR((code ? code : rcode), cloneVolId,
+               "Failed to end cloning transaction on RW %u\n");
+       VDONE;
+
+       /* Remember clone volume ID in case we fail or are interrupted */
+       entry.cloneId = cloneVolId;
+
+       if (roclone) {
+           /* Bring the RO clone online - though not if it's a temporary clone */
+           VPRINT1("Starting transaction on RO clone volume %u...",
+                   cloneVolId);
+           code =
+               AFSVolTransCreate(fromconn, cloneVolId, afrompart, ITOffline,
+                                 &onlinetid);
+           ONERROR(code, cloneVolId,
+                   "Failed to start transaction on volume %u\n");
+           VDONE;
+
+           VPRINT1("Setting volume flags for volume %u...", cloneVolId);
+           tcode = AFSVolSetFlags(fromconn, onlinetid, 0);
+           VDONE;
+
+           VPRINT1("Ending transaction on volume %u...", cloneVolId);
+           code = AFSVolEndTrans(fromconn, onlinetid, &rcode);
+           ONERROR((code ? code : rcode), cloneVolId,
+                   "Failed to end transaction on RO clone %u\n");
+           VDONE;
+
+           ONERROR(tcode, cloneVolId, "Could not bring volume %u on line\n");
+
+           /* Sleep so that a client searching for an online volume won't
+            * find the clone offline and then the next RO offline while the 
+            * release brings the clone online and the next RO offline (race).
+            * There is a fix in the 3.4 client that does not need this sleep
+            * anymore, but we don't know what clients we have.
+            */
+           if (entry.nServers > 2)
+               sleep(5);
+
+           /* Mark the RO clone in the VLDB as a good site (already released) */
+           entry.serverFlags[roindex] |= NEW_REPSITE;
+           entry.serverFlags[roindex] &= ~RO_DONTUSE;
+           entry.flags |= RO_EXISTS;
+
+           releasecount++;
+
+           /* Write out the VLDB entry only if the clone is not a temporary
+            * clone. If we did this to a temporary clone then we would end
+            * up marking all the ROs as "old release" making the ROs
+            * temporarily unavailable.
+            */
+           MapNetworkToHost(&entry, &storeEntry);
+           VPRINT1("Replacing VLDB entry for %s...", entry.name);
+           vcode = VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, 0);
+           ONERROR(vcode, entry.name,
+                   "Could not update vldb entry for %s.\n");
+           VDONE;
+       }
+    }
+
+    /* Now we will release from the clone to the remaining RO replicas.
+     * The first 2 ROs (counting the non-temporary RO clone) are released
+     * individually: releasecount. This is to reduce the race condition
+     * of clients trying to find an on-line RO volume. The remaining ROs
+     * are released in parallel but no more than half the number of ROs
+     * (rounded up) at a time: nservers.
+     */
+
+    strcpy(vname, entry.name);
+    strcat(vname, ".readonly");
+    memset(&cookie, 0, sizeof(cookie));
+    strncpy(cookie.name, vname, VOLSER_OLDMAXVOLNAME);
+    cookie.type = ROVOL;
+    cookie.parent = entry.volumeId[RWVOL];
+    cookie.clone = 0;
+
+    nservers = entry.nServers / 2;     /* how many to do at once, excluding clone */
+    replicas =
+       (struct replica *)malloc(sizeof(struct replica) * nservers + 1);
+    times = (struct release *)malloc(sizeof(struct release) * nservers + 1);
+    toconns =
+       (struct rx_connection **)malloc(sizeof(struct rx_connection *) *
+                                       nservers + 1);
+    results.manyResults_val =
+       (afs_int32 *) malloc(sizeof(afs_int32) * nservers + 1);
+    if (!replicas || !times || !!!results.manyResults_val || !toconns)
+       ONERROR(ENOMEM, 0,
+               "Failed to create transaction on the release clone\n");
+
+    memset(replicas, 0, (sizeof(struct replica) * nservers + 1));
+    memset(times, 0, (sizeof(struct release) * nservers + 1));
+    memset(toconns, 0, (sizeof(struct rx_connection *) * nservers + 1));
+    memset(results.manyResults_val, 0, (sizeof(afs_int32) * nservers + 1));
+
+    /* Create a transaction on the cloned volume */
+    VPRINT1("Starting transaction on cloned volume %u...", cloneVolId);
+    code =
+       AFSVolTransCreate(fromconn, cloneVolId, afrompart, ITBusy, &fromtid);
+    if (!fullrelease && code)
+       ONERROR(VOLSERNOVOL, afromvol,
+               "Old clone is inaccessible. Try vos release -f %u.\n");
+    ONERROR(code, 0, "Failed to create transaction on the release clone\n");
+    VDONE;
+
+    /* For each index in the VLDB */
+    for (vldbindex = 0; vldbindex < entry.nServers;) {
+
+       /* Get a transaction on the replicas. Pick replacas which have an old release. */
+       for (volcount = 0;
+            ((volcount < nservers) && (vldbindex < entry.nServers));
+            vldbindex++) {
+           /* The first two RO volumes will be released individually.
+            * The rest are then released in parallel. This is a hack
+            * for clients not recognizing right away when a RO volume
+            * comes back on-line.
+            */
+           if ((volcount == 1) && (releasecount < 2))
+               break;
+
+           if (vldbindex == roindex)
+               continue;       /* the clone    */
+           if ((entry.serverFlags[vldbindex] & NEW_REPSITE)
+               && !(entry.serverFlags[vldbindex] & RO_DONTUSE))
+               continue;
+           if (!(entry.serverFlags[vldbindex] & ITSROVOL))
+               continue;       /* not a RO vol */
+
+
+           /* Get a Transaction on this replica. Get a new connection if
+            * necessary.  Create the volume if necessary.  Return the
+            * time from which the dump should be made (0 if it's a new
+            * volume).  Each volume might have a different time. 
+            */
+           replicas[volcount].server.destHost =
+               ntohl(entry.serverNumber[vldbindex]);
+           replicas[volcount].server.destPort = AFSCONF_VOLUMEPORT;
+           replicas[volcount].server.destSSID = 1;
+           times[volcount].vldbEntryIndex = vldbindex;
+
+           code =
+               GetTrans(&entry, vldbindex, &(toconns[volcount]),
+                        &(replicas[volcount].trans),
+                        &(times[volcount].time));
+           if (code)
+               continue;
+
+           /* Thisdate is the date from which we want to pick up all changes */
+           if (forceflag || !fullrelease
+               || (rwcrdate > times[volcount].time)) {
+               /* If the forceflag is set, then we want to do a full dump.
+                * If it's not a full release, we can't be sure that the creation
+                *  date is good (so we also do a full dump).
+                * If the RW volume was replaced (its creation date is newer than
+                *  the last release), then we can't be sure what has changed (so
+                *  we do a full dump).
+                */
+               thisdate = 0;
+           } else if (remembertime[vldbindex].validtime) {
+               /* Trans was prev ended. Use the time from the prev trans
+                * because, prev trans may have created the volume. In which
+                * case time[volcount].time would be now instead of 0.
+                */
+               thisdate =
+                   (remembertime[vldbindex].time <
+                    times[volcount].time) ? remembertime[vldbindex].
+                   time : times[volcount].time;
+           } else {
+               thisdate = times[volcount].time;
+           }
+           remembertime[vldbindex].validtime = 1;
+           remembertime[vldbindex].time = thisdate;
+
+           if (volcount == 0) {
+               fromdate = thisdate;
+           } else {
+               /* Include this volume if it is within 15 minutes of the earliest */
+               if (((fromdate >
+                     thisdate) ? (fromdate - thisdate) : (thisdate -
+                                                          fromdate)) > 900) {
+                   AFSVolEndTrans(toconns[volcount],
+                                  replicas[volcount].trans, &rcode);
+                   replicas[volcount].trans = 0;
+                   break;
+               }
+               if (thisdate < fromdate)
+                   fromdate = thisdate;
+           }
+           volcount++;
+       }
+       if (!volcount)
+           continue;
+
+       if (verbose) {
+           fprintf(STDOUT, "Starting ForwardMulti from %lu to %u on %s",
+                   (unsigned long)cloneVolId, entry.volumeId[ROVOL],
+                   hostutil_GetNameByINet(entry.
+                                          serverNumber[times[0].
+                                                       vldbEntryIndex]));
+
+           for (s = 1; s < volcount; s++) {
+               fprintf(STDOUT, " and %s",
+                       hostutil_GetNameByINet(entry.
+                                              serverNumber[times[s].
+                                                           vldbEntryIndex]));
+           }
+
+           if (fromdate == 0)
+               fprintf(STDOUT, " (full release)");
+           fprintf(STDOUT, ".\n");
+           fflush(STDOUT);
+       }
+
+       /* Release the ones we have collected */
+       tr.manyDests_val = &(replicas[0]);
+       tr.manyDests_len = results.manyResults_len = volcount;
+       code =
+           AFSVolForwardMultiple(fromconn, fromtid, fromdate, &tr,
+                                 0 /*spare */ , &cookie, &results);
+       if (code == RXGEN_OPCODE) {     /* RPC Interface Mismatch */
+           code =
+               SimulateForwardMultiple(fromconn, fromtid, fromdate, &tr,
+                                       0 /*spare */ , &cookie, &results);
+           nservers = 1;
+       }
+
+       if (code) {
+           PrintError("Release failed: ", code);
+       } else {
+           for (m = 0; m < volcount; m++) {
+               if (results.manyResults_val[m]) {
+                   if ((m == 0) || (results.manyResults_val[m] != ENOENT)) {
+                       /* we retry timed out transaction. When it is
+                        * not the first volume and the transaction wasn't found
+                        * (assume it timed out and was garbage collected by volser).
+                        */
+                       PrintError
+                           ("Failed to dump volume from clone to a ro site: ",
+                            results.manyResults_val[m]);
+                   }
+                   continue;
+               }
+
+               code =
+                   AFSVolSetIdsTypes(toconns[m], replicas[m].trans, vname,
+                                     ROVOL, entry.volumeId[RWVOL], 0, 0);
+               if (code) {
+                   if ((m == 0) || (code != ENOENT)) {
+                       PrintError("Failed to set correct names and ids: ",
+                                  code);
+                   }
+                   continue;
+               }
+
+               /* have to clear dest. flags to ensure new vol goes online:
+                * because the restore (forwarded) operation copied
+                * the V_inService(=0) flag over to the destination. 
+                */
+               code = AFSVolSetFlags(toconns[m], replicas[m].trans, 0);
+               if (code) {
+                   if ((m == 0) || (code != ENOENT)) {
+                       PrintError("Failed to set flags on ro volume: ",
+                                  code);
+                   }
+                   continue;
+               }
+
+               entry.serverFlags[times[m].vldbEntryIndex] |= NEW_REPSITE;
+               entry.serverFlags[times[m].vldbEntryIndex] &= ~RO_DONTUSE;
+               entry.flags |= RO_EXISTS;
+               releasecount++;
+           }
+       }
+
+       /* End the transactions and destroy the connections */
+       for (s = 0; s < volcount; s++) {
+           if (replicas[s].trans)
+               code = AFSVolEndTrans(toconns[s], replicas[s].trans, &rcode);
+           replicas[s].trans = 0;
+           if (!code)
+               code = rcode;
+           if (code) {
+               if ((s == 0) || (code != ENOENT)) {
+                   PrintError("Could not end transaction on a ro volume: ",
+                              code);
+               } else {
+                   PrintError
+                       ("Transaction timed out on a ro volume. Will retry.\n",
+                        0);
+                   if (times[s].vldbEntryIndex < vldbindex)
+                       vldbindex = times[s].vldbEntryIndex;
+               }
+           }
+
+           if (toconns[s])
+               rx_DestroyConnection(toconns[s]);
+           toconns[s] = 0;
+       }
+
+       MapNetworkToHost(&entry, &storeEntry);
+       vcode = VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, 0);
+       ONERROR(vcode, afromvol,
+               " Could not update VLDB entry for volume %u\n");
+    }                          /* for each index in the vldb */
+
+    /* End the transaction on the cloned volume */
+    code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+    fromtid = 0;
+    if (!code)
+       code = rcode;
+    if (code)
+       PrintError("Failed to end transaction on rw volume: ", code);
+
+    /* Figure out if any volume were not released and say so */
+    for (failure = 0, i = 0; i < entry.nServers; i++) {
+       if (!(entry.serverFlags[i] & NEW_REPSITE))
+           failure++;
+    }
+    if (failure) {
+       char pname[10];
+       fprintf(STDERR,
+               "The volume %lu could not be released to the following %d sites:\n",
+               (unsigned long)afromvol, failure);
+       for (i = 0; i < entry.nServers; i++) {
+           if (!(entry.serverFlags[i] & NEW_REPSITE)) {
+               MapPartIdIntoName(entry.serverPartition[i], pname);
+               fprintf(STDERR, "\t%35s %s\n",
+                       hostutil_GetNameByINet(entry.serverNumber[i]), pname);
+           }
+       }
+
+       MapNetworkToHost(&entry, &storeEntry);
+       vcode =
+           VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry,
+                             LOCKREL_TIMESTAMP);
+       ONERROR(vcode, afromvol,
+               " Could not update VLDB entry for volume %u\n");
+
+       ERROREXIT(VOLSERBADRELEASE);
+    }
+
+    /* All the ROs were release successfully. Remove the temporary clone */
+    if (!roclone) {
+       if (verbose) {
+           fprintf(STDOUT, "Deleting the releaseClone %lu ...",
+                   (unsigned long)cloneVolId);
+           fflush(STDOUT);
+       }
+       code = DelVol(fromconn, cloneVolId, afrompart, ITOffline);
+       ONERROR(code, cloneVolId, "Failed to delete volume %u.\n");
+       VDONE;
+    }
+    entry.cloneId = 0;
+
+    for (i = 0; i < entry.nServers; i++)
+       entry.serverFlags[i] &= ~NEW_REPSITE;
+
+    /* Update the VLDB */
+    VPRINT("updating VLDB ...");
+
+    MapNetworkToHost(&entry, &storeEntry);
+    vcode =
+       VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry,
+                         LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+    ONERROR(vcode, afromvol, " Could not update VLDB entry for volume %u\n");
+    VDONE;
+
+  rfail:
+    if (clonetid) {
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       clonetid = 0;
+       if (code) {
+           fprintf(STDERR,
+                   "Failed to end cloning transaction on the RW volume %lu\n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = code;
+       }
+    }
+    if (fromtid) {
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       fromtid = 0;
+       if (code) {
+           fprintf(STDERR,
+                   "Failed to end transaction on the release clone %lu\n",
+                   (unsigned long)cloneVolId);
+           if (!error)
+               error = code;
+       }
+    }
+    for (i = 0; i < nservers; i++) {
+       if (replicas && replicas[i].trans) {
+           code = AFSVolEndTrans(toconns[i], replicas[i].trans, &rcode);
+           replicas[i].trans = 0;
+           if (code) {
+               fprintf(STDERR,
+                       "Failed to end transaction on ro volume %u at server %s\n",
+                       entry.volumeId[ROVOL],
+                       hostutil_GetNameByINet(htonl
+                                              (replicas[i].server.
+                                               destHost)));
+               if (!error)
+                   error = code;
+           }
+       }
+       if (toconns && toconns[i]) {
+           rx_DestroyConnection(toconns[i]);
+           toconns[i] = 0;
+       }
+    }
+    if (islocked) {
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not release lock on the VLDB entry for volume %lu\n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = vcode;
+       }
+    }
+
+    PrintError("", error);
+
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+    if (results.manyResults_val)
+       free(results.manyResults_val);
+    if (replicas)
+       free(replicas);
+    if (toconns)
+       free(toconns);
+    if (times)
+       free(times);
+    return error;
+}
+
+
+void
+dump_sig_handler(int x)
+{
+    fprintf(STDERR, "\nSignal handler: vos dump operation\n");
+    longjmp(env, 0);
+}
+
+/* Dump the volume <afromvol> on <afromserver> and
+ * <afrompart> to <afilename> starting from <fromdate>.
+ * DumpFunction does the real work behind the scenes after
+ * extracting parameters from the rock 
+ */
+int
+UV_DumpVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+             afs_int32 fromdate, afs_int32(*DumpFunction) (), char *rock)
+{
+    struct rx_connection *fromconn = (struct rx_connection *)0;
+    struct rx_call *fromcall = (struct rx_call *)0;
+    afs_int32 fromtid = 0, rxError = 0, rcode = 0;
+    afs_int32 code, error = 0;
+
+    if (setjmp(env))
+       ERROR_EXIT(EPIPE);
+#ifndef AFS_NT40_ENV
+    (void)signal(SIGPIPE, dump_sig_handler);
+#endif
+    (void)signal(SIGINT, dump_sig_handler);
+
+    if (!fromdate) {
+       VPRINT("Full Dump ...\n");
+    } else {
+       VPRINT1("Incremental Dump (as of %.24s)...\n",
+               ctime((time_t *) & fromdate));
+    }
+
+    /* get connections to the servers */
+    fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+
+    VPRINT1("Starting transaction on volume %u...", afromvol);
+    code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
+    EGOTO1(error_exit, code,
+          "Could not start transaction on the volume %u to be dumped\n",
+          afromvol);
+    VDONE;
+
+    fromcall = rx_NewCall(fromconn);
+
+    VPRINT1("Starting volume dump on volume %u...", afromvol);
+    code = StartAFSVolDump(fromcall, fromtid, fromdate);
+    EGOTO(error_exit, code, "Could not start the dump process \n");
+    VDONE;
+
+    VPRINT1("Dumping volume %u...", afromvol);
+    code = DumpFunction(fromcall, rock);
+    EGOTO(error_exit, code, "Error while dumping volume \n");
+    VDONE;
+
+  error_exit:
+    if (fromcall) {
+       code = rx_EndCall(fromcall, rxError);
+       if (code) {
+           fprintf(STDERR, "Error in rx_EndCall\n");
+           if (!error)
+               error = code;
+       }
+    }
+    if (fromtid) {
+       VPRINT1("Ending transaction on volume %u...", afromvol);
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu\n",
+                   (unsigned long)afromvol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+
+    PrintError("", error);
+    return (error);
+}
+
+/* Clone the volume <afromvol> on <afromserver> and
+ * <afrompart>, and then dump the clone volume to 
+ * <afilename> starting from <fromdate>.
+ * DumpFunction does the real work behind the scenes after
+ * extracting parameters from the rock 
+ */
+int
+UV_DumpClonedVolume(afs_int32 afromvol, afs_int32 afromserver,
+                   afs_int32 afrompart, afs_int32 fromdate,
+                   afs_int32(*DumpFunction) (), char *rock)
+{
+    struct rx_connection *fromconn = (struct rx_connection *)0;
+    struct rx_call *fromcall = (struct rx_call *)0;
+    afs_int32 fromtid = 0, rxError = 0, rcode = 0;
+    afs_int32 clonetid = 0;
+    afs_int32 code = 0, vcode = 0, error = 0;
+    afs_int32 clonevol = 0;
+    char vname[64];
+
+    if (setjmp(env))
+       ERROR_EXIT(EPIPE);
+#ifndef AFS_NT40_ENV
+    (void)signal(SIGPIPE, dump_sig_handler);
+#endif
+    (void)signal(SIGINT, dump_sig_handler);
+
+    if (!fromdate) {
+       VPRINT("Full Dump ...\n");
+    } else {
+       VPRINT1("Incremental Dump (as of %.24s)...\n",
+               ctime((time_t *) & fromdate));
+    }
+
+    /* get connections to the servers */
+    fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+
+    VPRINT1("Starting transaction on volume %u...", afromvol);
+    code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
+    EGOTO1(error_exit, code,
+          "Could not start transaction on the volume %u to be dumped\n",
+          afromvol);
+    VDONE;
+
+    /* Get a clone id */
+    VPRINT1("Allocating new volume id for clone of volume %u ...", afromvol);
+    code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &clonevol);
+    EGOTO1(error_exit, code,
+          "Could not get an ID for the clone of volume %u from the VLDB\n",
+          afromvol);
+    VDONE;
+
+    /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
+    VPRINT2("Cloning source volume %u to clone volume %u...", afromvol,
+           clonevol);
+    strcpy(vname, "dump-clone-temp");
+    code =
+       AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname, &clonevol);
+    EGOTO1(error_exit, code, "Failed to clone the source volume %u\n",
+          afromvol);
+    VDONE;
+
+    VPRINT1("Ending the transaction on the volume %u ...", afromvol);
+    rcode = 0;
+    code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+    fromtid = 0;
+    if (!code)
+       code = rcode;
+    EGOTO1(error_exit, code,
+          "Failed to end the transaction on the volume %u\n", afromvol);
+    VDONE;
+
+
+    VPRINT1("Starting transaction on the cloned volume %u ...", clonevol);
+    code =
+       AFSVolTransCreate(fromconn, clonevol, afrompart, ITOffline,
+                         &clonetid);
+    EGOTO1(error_exit, code,
+          "Failed to start a transaction on the cloned volume%u\n",
+          clonevol);
+    VDONE;
+
+    VPRINT1("Setting flags on cloned volume %u ...", clonevol);
+    code = AFSVolSetFlags(fromconn, clonetid, VTDeleteOnSalvage | VTOutOfService);     /*redundant */
+    EGOTO1(error_exit, code, "Could not set falgs on the cloned volume %u\n",
+          clonevol);
+    VDONE;
+
+
+    fromcall = rx_NewCall(fromconn);
+
+    VPRINT1("Starting volume dump from cloned volume %u...", clonevol);
+    code = StartAFSVolDump(fromcall, clonetid, fromdate);
+    EGOTO(error_exit, code, "Could not start the dump process \n");
+    VDONE;
+
+    VPRINT1("Dumping volume %u...", afromvol);
+    code = DumpFunction(fromcall, rock);
+    EGOTO(error_exit, code, "Error while dumping volume \n");
+    VDONE;
+
+  error_exit:
+    /* now delete the clone */
+    VPRINT1("Deleting the cloned volume %u ...", clonevol);
+    code = AFSVolDeleteVolume(fromconn, clonetid);
+    if (code) {
+       fprintf(STDERR, "Failed to delete the cloned volume %lu\n",
+               (unsigned long)clonevol);
+    } else {
+       VDONE;
+    }
+
+    if (fromcall) {
+       code = rx_EndCall(fromcall, rxError);
+       if (code) {
+           fprintf(STDERR, "Error in rx_EndCall\n");
+           if (!error)
+               error = code;
+       }
+    }
+    if (clonetid) {
+       VPRINT1("Ending transaction on cloned volume %u...", clonevol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction on the cloned volume %lu\n",
+                   (unsigned long)clonevol);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+    if (fromconn)
+       rx_DestroyConnection(fromconn);
+
+    PrintError("", error);
+    return (error);
+}
+
+
+
+/*
+ * Restore a volume <tovolid> <tovolname> on <toserver> <topart> from
+ * the dump file <afilename>. WriteData does all the real work
+ * after extracting params from the rock 
+ */
+int
+UV_RestoreVolume(afs_int32 toserver, afs_int32 topart, afs_int32 tovolid,
+                char tovolname[], int flags, afs_int32(*WriteData) (),
+                char *rock)
+{
+    struct rx_connection *toconn, *tempconn;
+    struct rx_call *tocall;
+    afs_int32 totid, code, rcode, vcode, terror = 0;
+    afs_int32 rxError = 0;
+    struct volser_status tstatus;
+    char partName[10];
+    afs_int32 pvolid;
+    afs_int32 temptid;
+    int success;
+    struct nvldbentry entry, storeEntry;
+    afs_int32 error;
+    int islocked;
+    struct restoreCookie cookie;
+    int reuseID;
+    afs_int32 newDate, volflag, voltype, volsertype;
+    int index, same, errcode;
+    char apartName[10];
+
+
+    memset(&cookie, 0, sizeof(cookie));
+    islocked = 0;
+    success = 0;
+    error = 0;
+    reuseID = 1;
+    tocall = (struct rx_call *)0;
+    toconn = (struct rx_connection *)0;
+    tempconn = (struct rx_connection *)0;
+    totid = 0;
+    temptid = 0;
+
+    if (flags & RV_RDONLY) {
+       voltype = ROVOL;
+       volsertype = volser_RO;
+    } else {
+       voltype = RWVOL;
+       volsertype = volser_RW;
+    }
+
+    pvolid = tovolid;
+    toconn = UV_Bind(toserver, AFSCONF_VOLUMEPORT);
+    if (pvolid == 0) {         /*alot a new id if needed */
+       vcode = VLDB_GetEntryByName(tovolname, &entry);
+       if (vcode == VL_NOENT) {
+           vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &pvolid);
+           if (vcode) {
+               fprintf(STDERR, "Could not get an Id for the volume %s\n",
+                       tovolname);
+               error = vcode;
+               goto refail;
+           }
+           reuseID = 0;
+       } else if (flags & RV_RDONLY) {
+           if (entry.flags & RW_EXISTS) {
+               fprintf(STDERR,
+                       "Entry for ReadWrite volume %s already exists!\n",
+                       entry.name);
+               error = VOLSERBADOP;
+               goto refail;
+           }
+           if (!entry.volumeId[ROVOL]) {
+               fprintf(STDERR,
+                       "Existing entry for volume %s has no ReadOnly ID\n",
+                       tovolname);
+               error = VOLSERBADOP;
+               goto refail;
+           }
+           pvolid = entry.volumeId[ROVOL];
+       } else {
+           pvolid = entry.volumeId[RWVOL];
+       }
+    }
+    /* at this point we have a volume id to use/reuse for the volume to be restored */
+    if (strlen(tovolname) > (VOLSER_OLDMAXVOLNAME - 1)) {
+       EGOTO1(refail, VOLSERBADOP,
+              "The volume name %s exceeds the maximum limit of (VOLSER_OLDMAXVOLNAME -1 ) bytes\n",
+              tovolname);
+    }
+    MapPartIdIntoName(topart, partName);
+    fprintf(STDOUT, "Restoring volume %s Id %lu on server %s partition %s ..",
+           tovolname, (unsigned long)pvolid,
+           hostutil_GetNameByINet(toserver), partName);
+    fflush(STDOUT);
+    code =
+       AFSVolCreateVolume(toconn, topart, tovolname, volsertype, 0, &pvolid,
+                          &totid);
+    if (code) {
+       if (flags & RV_FULLRST) {       /* full restore: delete then create anew */
+           VPRINT1("Deleting the previous volume %u ...", pvolid);
+
+           code =
+               AFSVolTransCreate(toconn, pvolid, topart, ITOffline, &totid);
+           EGOTO1(refail, code, "Failed to start transaction on %u\n",
+                  pvolid);
+
+           code =
+               AFSVolSetFlags(toconn, totid,
+                              VTDeleteOnSalvage | VTOutOfService);
+           EGOTO1(refail, code, "Could not set flags on volume %u \n",
+                  pvolid);
+
+           code = AFSVolDeleteVolume(toconn, totid);
+           EGOTO1(refail, code, "Could not delete volume %u\n", pvolid);
+
+           code = AFSVolEndTrans(toconn, totid, &rcode);
+           totid = 0;
+           if (!code)
+               code = rcode;
+           EGOTO1(refail, code, "Could not end transaction on %u\n", pvolid);
+
+           VDONE;
+
+           code =
+               AFSVolCreateVolume(toconn, topart, tovolname, volsertype, 0,
+                                  &pvolid, &totid);
+           EGOTO1(refail, code, "Could not create new volume %u\n", pvolid);
+
+           newDate = 0;
+       } else {
+           code =
+               AFSVolTransCreate(toconn, pvolid, topart, ITOffline, &totid);
+           EGOTO1(refail, code, "Failed to start transaction on %u\n",
+                  pvolid);
+
+           code = AFSVolGetStatus(toconn, totid, &tstatus);
+           EGOTO1(refail, code, "Could not get timestamp from volume %u\n",
+                  pvolid);
+           newDate = tstatus.creationDate;
+       }
+    }
+    cookie.parent = pvolid;
+    cookie.type = voltype;
+    cookie.clone = 0;
+    strncpy(cookie.name, tovolname, VOLSER_OLDMAXVOLNAME);
+
+    tocall = rx_NewCall(toconn);
+    terror = StartAFSVolRestore(tocall, totid, 1, &cookie);
+    if (terror) {
+       fprintf(STDERR, "Volume restore Failed \n");
+       error = terror;
+       goto refail;
+    }
+    code = WriteData(tocall, rock);
+    if (code) {
+       fprintf(STDERR, "Could not transmit data\n");
+       error = code;
+       goto refail;
+    }
+    terror = rx_EndCall(tocall, rxError);
+    tocall = (struct rx_call *)0;
+    if (terror) {
+       fprintf(STDERR, "rx_EndCall Failed \n");
+       error = terror;
+       goto refail;
+    }
+    code = AFSVolGetStatus(toconn, totid, &tstatus);
+    if (code) {
+       fprintf(STDERR,
+               "Could not get status information about the volume %lu\n",
+               (unsigned long)pvolid);
+       error = code;
+       goto refail;
+    }
+    code = AFSVolSetIdsTypes(toconn, totid, tovolname, voltype, pvolid, 0, 0);
+    if (code) {
+       fprintf(STDERR, "Could not set the right type and ID on %lu\n",
+               (unsigned long)pvolid);
+       error = code;
+       goto refail;
+    }
+    if (!newDate)
+       newDate = time(0);
+    code = AFSVolSetDate(toconn, totid, newDate);
+    if (code) {
+       fprintf(STDERR, "Could not set the date on %lu\n",
+               (unsigned long)pvolid);
+       error = code;
+       goto refail;
+    }
+
+    volflag = ((flags & RV_OFFLINE) ? VTOutOfService : 0);     /* off or on-line */
+    code = AFSVolSetFlags(toconn, totid, volflag);
+    if (code) {
+       fprintf(STDERR, "Could not mark %lu online\n", (unsigned long)pvolid);
+       error = code;
+       goto refail;
+    }
+
+/* It isn't handled right in refail */
+    code = AFSVolEndTrans(toconn, totid, &rcode);
+    totid = 0;
+    if (!code)
+       code = rcode;
+    if (code) {
+       fprintf(STDERR, "Could not end transaction on %lu\n",
+               (unsigned long)pvolid);
+       error = code;
+       goto refail;
+    }
+
+    success = 1;
+    fprintf(STDOUT, " done\n");
+    fflush(STDOUT);
+    if (success && (!reuseID || (flags & RV_FULLRST))) {
+       /* Volume was restored on the file server, update the 
+        * VLDB to reflect the change.
+        */
+       vcode = VLDB_GetEntryByID(pvolid, voltype, &entry);
+       if (vcode && vcode != VL_NOENT && vcode != VL_ENTDELETED) {
+           fprintf(STDERR,
+                   "Could not fetch the entry for volume number %lu from VLDB \n",
+                   (unsigned long)pvolid);
+           error = vcode;
+           goto refail;
+       }
+       if (!vcode)
+           MapHostToNetwork(&entry);
+       if (vcode == VL_NOENT) {        /* it doesnot exist already */
+           /*make the vldb return this indication specifically */
+           VPRINT("------- Creating a new VLDB entry ------- \n");
+           strcpy(entry.name, tovolname);
+           entry.nServers = 1;
+           entry.serverNumber[0] = toserver;   /*should be indirect */
+           entry.serverPartition[0] = topart;
+           entry.serverFlags[0] = (flags & RV_RDONLY) ? ITSROVOL : ITSRWVOL;
+           entry.flags = (flags & RV_RDONLY) ? RO_EXISTS : RW_EXISTS;
+           if (flags & RV_RDONLY)
+               entry.volumeId[ROVOL] = pvolid;
+           else if (tstatus.cloneID != 0) {
+               entry.volumeId[ROVOL] = tstatus.cloneID;        /*this should come from status info on the volume if non zero */
+           } else
+               entry.volumeId[ROVOL] = INVALID_BID;
+           entry.volumeId[RWVOL] = pvolid;
+           entry.cloneId = 0;
+           if (tstatus.backupID != 0) {
+               entry.volumeId[BACKVOL] = tstatus.backupID;
+               /*this should come from status info on the volume if non zero */
+           } else
+               entry.volumeId[BACKVOL] = INVALID_BID;
+           MapNetworkToHost(&entry, &storeEntry);
+           vcode = VLDB_CreateEntry(&storeEntry);
+           if (vcode) {
+               fprintf(STDERR,
+                       "Could not create the VLDB entry for volume number %lu  \n",
+                       (unsigned long)pvolid);
+               error = vcode;
+               goto refail;
+           }
+           islocked = 0;
+           if (verbose)
+               EnumerateEntry(&entry);
+       } else {                /*update the existing entry */
+           if (verbose) {
+               fprintf(STDOUT, "Updating the existing VLDB entry\n");
+               fprintf(STDOUT, "------- Old entry -------\n");
+               EnumerateEntry(&entry);
+               fprintf(STDOUT, "------- New entry -------\n");
+           }
+           vcode =
+               ubik_Call(VL_SetLock, cstruct, 0, pvolid, voltype,
+                         VLOP_RESTORE);
+           if (vcode) {
+               fprintf(STDERR,
+                       "Could not lock the entry for volume number %lu \n",
+                       (unsigned long)pvolid);
+               error = vcode;
+               goto refail;
+           }
+           islocked = 1;
+           strcpy(entry.name, tovolname);
+
+           /* Update the vlentry with the new information */
+           if (flags & RV_RDONLY)
+               index = Lp_ROMatch(toserver, topart, &entry) - 1;
+           else
+               index = Lp_GetRwIndex(&entry);
+           if (index == -1) {
+               /* Add the new site for the volume being restored */
+               entry.serverNumber[entry.nServers] = toserver;
+               entry.serverPartition[entry.nServers] = topart;
+               entry.serverFlags[entry.nServers] =
+                   (flags & RV_RDONLY) ? ITSROVOL : ITSRWVOL;
+               entry.nServers++;
+           } else {
+               /* This volume should be deleted on the old site
+                * if its different from new site.
+                */
+               same =
+                   VLDB_IsSameAddrs(toserver, entry.serverNumber[index],
+                                    &errcode);
+               EPRINT2(errcode,
+                       "Failed to get info about server's %d address(es) from vlserver (err=%d)\n",
+                       toserver, errcode);
+               if ((!errcode && !same)
+                   || (entry.serverPartition[index] != topart)) {
+                   tempconn =
+                       UV_Bind(entry.serverNumber[index],
+                               AFSCONF_VOLUMEPORT);
+
+                   MapPartIdIntoName(entry.serverPartition[index],
+                                     apartName);
+                   VPRINT3
+                       ("Deleting the previous volume %u on server %s, partition %s ...",
+                        pvolid,
+                        hostutil_GetNameByINet(entry.serverNumber[index]),
+                        apartName);
+                   code =
+                       AFSVolTransCreate(tempconn, pvolid,
+                                         entry.serverPartition[index],
+                                         ITOffline, &temptid);
+                   if (!code) {
+                       code =
+                           AFSVolSetFlags(tempconn, temptid,
+                                          VTDeleteOnSalvage |
+                                          VTOutOfService);
+                       if (code) {
+                           fprintf(STDERR,
+                                   "Could not set flags on volume %lu on the older site\n",
+                                   (unsigned long)pvolid);
+                           error = code;
+                           goto refail;
+                       }
+                       code = AFSVolDeleteVolume(tempconn, temptid);
+                       if (code) {
+                           fprintf(STDERR,
+                                   "Could not delete volume %lu on the older site\n",
+                                   (unsigned long)pvolid);
+                           error = code;
+                           goto refail;
+                       }
+                       code = AFSVolEndTrans(tempconn, temptid, &rcode);
+                       temptid = 0;
+                       if (!code)
+                           code = rcode;
+                       if (code) {
+                           fprintf(STDERR,
+                                   "Could not end transaction on volume %lu on the older site\n",
+                                   (unsigned long)pvolid);
+                           error = code;
+                           goto refail;
+                       }
+                       VDONE;
+                       MapPartIdIntoName(entry.serverPartition[index],
+                                         partName);
+                   }
+               }
+               entry.serverNumber[index] = toserver;
+               entry.serverPartition[index] = topart;
+           }
+
+           entry.flags |= (flags & RV_RDONLY) ? RO_EXISTS : RW_EXISTS;
+           MapNetworkToHost(&entry, &storeEntry);
+           vcode =
+               VLDB_ReplaceEntry(pvolid, voltype, &storeEntry,
+                                 LOCKREL_OPCODE | LOCKREL_AFSID |
+                                 LOCKREL_TIMESTAMP);
+           if (vcode) {
+               fprintf(STDERR,
+                       "Could not update the entry for volume number %lu  \n",
+                       (unsigned long)pvolid);
+               error = vcode;
+               goto refail;
+           }
+           islocked = 0;
+           if (verbose)
+               EnumerateEntry(&entry);
+       }
+
+
+    }
+  refail:
+    if (tocall) {
+       code = rx_EndCall(tocall, rxError);
+       if (!error)
+           error = code;
+    }
+    if (islocked) {
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, pvolid, voltype,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not release lock on the VLDB entry for the volume %lu\n",
+                   (unsigned long)pvolid);
+           if (!error)
+               error = vcode;
+       }
+    }
+    if (totid) {
+       code = AFSVolEndTrans(toconn, totid, &rcode);
+       if (!code)
+           code = rcode;
+       if (code) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu \n",
+                   (unsigned long)pvolid);
+           if (!error)
+               error = code;
+       }
+    }
+    if (temptid) {
+       code = AFSVolEndTrans(toconn, temptid, &rcode);
+       if (!code)
+           code = rcode;
+       if (code) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu \n",
+                   (unsigned long)pvolid);
+           if (!error)
+               error = code;
+       }
+    }
+    if (tempconn)
+       rx_DestroyConnection(tempconn);
+    if (toconn)
+       rx_DestroyConnection(toconn);
+    PrintError("", error);
+    return error;
+}
+
+
+/*unlocks the vldb entry associated with <volid> */
+int
+UV_LockRelease(afs_int32 volid)
+{
+
+
+    afs_int32 vcode;
+
+    VPRINT("Binding to the VLDB server\n");
+    vcode =
+       ubik_Call(VL_ReleaseLock, cstruct, 0, volid, -1,
+                 LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not unlock the entry for volume number %lu in VLDB \n",
+               (unsigned long)volid);
+       PrintError("", vcode);
+       return (vcode);
+    }
+    VPRINT("VLDB updated\n");
+    return 0;
+
+}
+
+/*adds <server> and <part> as a readonly replication site for <volid>
+*in vldb */
+int
+UV_AddSite(afs_int32 server, afs_int32 part, afs_int32 volid)
+{
+    int j, nro = 0, islocked = 0;
+    struct nvldbentry entry, storeEntry;
+    afs_int32 vcode, error = 0;
+    char apartName[10];
+
+    error = ubik_Call(VL_SetLock, cstruct, 0, volid, RWVOL, VLOP_ADDSITE);
+    if (error) {
+       fprintf(STDERR,
+               " Could not lock the VLDB entry for the volume %lu \n",
+               (unsigned long)volid);
+       goto asfail;
+    }
+    islocked = 1;
+
+    error = VLDB_GetEntryByID(volid, RWVOL, &entry);
+    if (error) {
+       fprintf(STDERR,
+               "Could not fetch the VLDB entry for volume number %lu  \n",
+               (unsigned long)volid);
+       goto asfail;
+
+    }
+    if (!ISNAMEVALID(entry.name)) {
+       fprintf(STDERR,
+               "Volume name %s is too long, rename before adding site\n",
+               entry.name);
+       error = VOLSERBADOP;
+       goto asfail;
+    }
+    MapHostToNetwork(&entry);
+
+    /* See if it's too many entries */
+    if (entry.nServers >= NMAXNSERVERS) {
+       fprintf(STDERR, "Total number of entries will exceed %u\n",
+               NMAXNSERVERS);
+       error = VOLSERBADOP;
+       goto asfail;
+    }
+
+    /* See if it's on the same server */
+    for (j = 0; j < entry.nServers; j++) {
+       if (entry.serverFlags[j] & ITSROVOL) {
+           nro++;
+           if (VLDB_IsSameAddrs(server, entry.serverNumber[j], &error)) {
+               if (error) {
+                   fprintf(STDERR,
+                           "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n",
+                           server, error);
+               } else {
+                   MapPartIdIntoName(entry.serverPartition[j], apartName);
+                   fprintf(STDERR,
+                           "RO already exists on partition %s. Multiple ROs on a single server aren't allowed\n",
+                           apartName);
+                   error = VOLSERBADOP;
+               }
+               goto asfail;
+           }
+       }
+    }
+
+    /* See if it's too many RO sites - leave one for the RW */
+    if (nro >= NMAXNSERVERS - 1) {
+       fprintf(STDERR, "Total number of sites will exceed %u\n",
+               NMAXNSERVERS - 1);
+       error = VOLSERBADOP;
+       goto asfail;
+    }
+
+    VPRINT("Adding a new site ...");
+    entry.serverNumber[entry.nServers] = server;
+    entry.serverPartition[entry.nServers] = part;
+    entry.serverFlags[entry.nServers] = (ITSROVOL | RO_DONTUSE);
+    entry.nServers++;
+
+    MapNetworkToHost(&entry, &storeEntry);
+    error =
+       VLDB_ReplaceEntry(volid, RWVOL, &storeEntry,
+                         LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+    if (error) {
+       fprintf(STDERR, "Could not update entry for volume %lu \n",
+               (unsigned long)volid);
+       goto asfail;
+    }
+    islocked = 0;
+    VDONE;
+
+  asfail:
+    if (islocked) {
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not release lock on volume entry for %lu \n",
+                   (unsigned long)volid);
+           PrintError("", vcode);
+       }
+    }
+
+    PrintError("", error);
+    return error;
+}
+
+/*removes <server> <part> as read only site for <volid> from the vldb */
+int
+UV_RemoveSite(afs_int32 server, afs_int32 part, afs_int32 volid)
+{
+    afs_int32 vcode;
+    struct nvldbentry entry, storeEntry;
+    int islocked;
+
+    vcode = ubik_Call(VL_SetLock, cstruct, 0, volid, RWVOL, VLOP_ADDSITE);
+    if (vcode) {
+       fprintf(STDERR, " Could not lock the VLDB entry for volume %lu \n",
+               (unsigned long)volid);
+       PrintError("", vcode);
+       return (vcode);
+    }
+    islocked = 1;
+    vcode = VLDB_GetEntryByID(volid, RWVOL, &entry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not fetch the entry for volume number %lu from VLDB \n",
+               (unsigned long)volid);
+       PrintError("", vcode);
+       return (vcode);
+    }
+    MapHostToNetwork(&entry);
+    if (!Lp_ROMatch(server, part, &entry)) {
+       /*this site doesnot exist  */
+       fprintf(STDERR, "This site is not a replication site \n");
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR, "Could not update entry for volume %lu \n",
+                   (unsigned long)volid);
+           PrintError("", vcode);
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+           return (vcode);
+       }
+       return VOLSERBADOP;
+    } else {                   /*remove the rep site */
+       Lp_SetROValue(&entry, server, part, 0, 0);
+       entry.nServers--;
+       if ((entry.nServers == 1) && (entry.flags & RW_EXISTS))
+           entry.flags &= ~RO_EXISTS;
+       if (entry.nServers < 1) {       /*this is the last ref */
+           VPRINT1("Deleting the VLDB entry for %u ...", volid);
+           fflush(STDOUT);
+           vcode = ubik_Call(VL_DeleteEntry, cstruct, 0, volid, ROVOL);
+           if (vcode) {
+               fprintf(STDERR,
+                       "Could not delete VLDB entry for volume %lu \n",
+                       (unsigned long)volid);
+               PrintError("", vcode);
+               return (vcode);
+           }
+           VDONE;
+       }
+       MapNetworkToHost(&entry, &storeEntry);
+       fprintf(STDOUT, "Deleting the replication site for volume %lu ...",
+               (unsigned long)volid);
+       fflush(STDOUT);
+       vcode =
+           VLDB_ReplaceEntry(volid, RWVOL, &storeEntry,
+                             LOCKREL_OPCODE | LOCKREL_AFSID |
+                             LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not release lock on volume entry for %lu \n",
+                   (unsigned long)volid);
+           PrintError("", vcode);
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+           return (vcode);
+       }
+       VDONE;
+    }
+    return 0;
+}
+
+/*sets <server> <part> as read/write site for <volid> in the vldb */
+int
+UV_ChangeLocation(afs_int32 server, afs_int32 part, afs_int32 volid)
+{
+    afs_int32 vcode;
+    struct nvldbentry entry, storeEntry;
+    int index;
+
+    vcode = ubik_Call(VL_SetLock, cstruct, 0, volid, RWVOL, VLOP_ADDSITE);
+    if (vcode) {
+       fprintf(STDERR, " Could not lock the VLDB entry for volume %lu \n",
+               (unsigned long)volid);
+       PrintError("", vcode);
+       return (vcode);
+    }
+    vcode = VLDB_GetEntryByID(volid, RWVOL, &entry);
+    if (vcode) {
+       fprintf(STDERR,
+               "Could not fetch the entry for volume number %lu from VLDB \n",
+               (unsigned long)volid);
+       PrintError("", vcode);
+       return (vcode);
+    }
+    MapHostToNetwork(&entry);
+    index = Lp_GetRwIndex(&entry);
+    if (index < 0) {
+       /* no RW site exists  */
+       fprintf(STDERR, "No existing RW site for volume %lu",
+               (unsigned long)volid);
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not release lock on entry for volume %lu \n",
+                   (unsigned long)volid);
+           PrintError("", vcode);
+           return (vcode);
+       }
+       return VOLSERBADOP;
+    } else {                   /* change the RW site */
+       entry.serverNumber[index] = server;
+       entry.serverPartition[index] = part;
+       MapNetworkToHost(&entry, &storeEntry);
+       vcode =
+           VLDB_ReplaceEntry(volid, RWVOL, &storeEntry,
+                             LOCKREL_OPCODE | LOCKREL_AFSID |
+                             LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR, "Could not update entry for volume %lu \n",
+                   (unsigned long)volid);
+           PrintError("", vcode);
+           ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+           return (vcode);
+       }
+       VDONE;
+    }
+    return 0;
+}
+
+/*list all the partitions on <aserver> */
+int
+UV_ListPartitions(afs_int32 aserver, struct partList *ptrPartList,
+                 afs_int32 * cntp)
+{
+    struct rx_connection *aconn;
+    struct pIDs partIds;
+    struct partEntries partEnts;
+    register int i, j = 0, code;
+
+    *cntp = 0;
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+    partEnts.partEntries_len = 0;
+    partEnts.partEntries_val = NULL;
+    code = AFSVolXListPartitions(aconn, &partEnts);    /* this is available only on new servers */
+    if (code == RXGEN_OPCODE) {
+       for (i = 0; i < 26; i++)        /* try old interface */
+           partIds.partIds[i] = -1;
+       code = AFSVolListPartitions(aconn, &partIds);
+       if (!code) {
+           for (i = 0; i < 26; i++) {
+               if ((partIds.partIds[i]) != -1) {
+                   ptrPartList->partId[j] = partIds.partIds[i];
+                   ptrPartList->partFlags[j] = PARTVALID;
+                   j++;
+               } else
+                   ptrPartList->partFlags[i] = 0;
+           }
+           *cntp = j;
+       }
+    } else if (!code) {
+       *cntp = partEnts.partEntries_len;
+       if (*cntp > VOLMAXPARTS) {
+           fprintf(STDERR,
+                   "Warning: number of partitions on the server too high %d (process only %d)\n",
+                   *cntp, VOLMAXPARTS);
+           *cntp = VOLMAXPARTS;
+       }
+       for (i = 0; i < *cntp; i++) {
+           ptrPartList->partId[i] = partEnts.partEntries_val[i];
+           ptrPartList->partFlags[i] = PARTVALID;
+       }
+       free(partEnts.partEntries_val);
+    }
+
+   /* out: */
+    if (code)
+       fprintf(STDERR,
+               "Could not fetch the list of partitions from the server\n");
+    PrintError("", code);
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    return code;
+}
+
+
+/*zap the list of volumes specified by volPtrArray (the volCloneId field).
+ This is used by the backup system */
+int
+UV_ZapVolumeClones(afs_int32 aserver, afs_int32 apart,
+                  struct volDescription *volPtr, afs_int32 arraySize)
+{
+    struct rx_connection *aconn;
+    struct volDescription *curPtr;
+    int curPos;
+    afs_int32 code = 0;
+    afs_int32 rcode = 0;
+    afs_int32 success = 1;
+    afs_int32 tid;
+
+    aconn = (struct rx_connection *)0;
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+    curPos = 0;
+    for (curPtr = volPtr; curPos < arraySize; curPtr++) {
+       if (curPtr->volFlags & CLONEVALID) {
+           curPtr->volFlags &= ~CLONEZAPPED;
+           success = 1;
+           code =
+               AFSVolTransCreate(aconn, curPtr->volCloneId, apart, ITOffline,
+                                 &tid);
+           if (code)
+               success = 0;
+           else {
+               code = AFSVolDeleteVolume(aconn, tid);
+               if (code)
+                   success = 0;
+               code = AFSVolEndTrans(aconn, tid, &rcode);
+               if (code || rcode)
+                   success = 0;
+           }
+           if (success)
+               curPtr->volFlags |= CLONEZAPPED;
+           if (!success)
+               fprintf(STDERR, "Could not zap volume %lu\n",
+                       (unsigned long)curPtr->volCloneId);
+           if (success)
+               VPRINT2("Clone of %s %u deleted\n", curPtr->volName,
+                       curPtr->volCloneId);
+           curPos++;
+           tid = 0;
+       }
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    return 0;
+}
+
+/*return a list of clones of the volumes specified by volPtrArray. Used by the 
+ backup system */
+int
+UV_GenerateVolumeClones(afs_int32 aserver, afs_int32 apart,
+                       struct volDescription *volPtr, afs_int32 arraySize)
+{
+    struct rx_connection *aconn;
+    struct volDescription *curPtr;
+    int curPos;
+    afs_int32 code = 0;
+    afs_int32 rcode = 0;
+    afs_int32 tid;
+    int reuseCloneId = 0;
+    afs_int32 curCloneId = 0;
+    char cloneName[256];       /*max vol name */
+
+    aconn = (struct rx_connection *)0;
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+    curPos = 0;
+    if ((volPtr->volFlags & REUSECLONEID) && (volPtr->volFlags & ENTRYVALID))
+       reuseCloneId = 1;
+    else {                     /*get a bunch of id's from vldb */
+       code =
+           ubik_Call(VL_GetNewVolumeId, cstruct, 0, arraySize, &curCloneId);
+       if (code) {
+           fprintf(STDERR, "Could not get ID's for the clone from VLDB\n");
+           PrintError("", code);
+           return code;
+       }
+    }
+
+    for (curPtr = volPtr; curPos < arraySize; curPtr++) {
+       if (curPtr->volFlags & ENTRYVALID) {
+
+           curPtr->volFlags |= CLONEVALID;
+           /*make a clone of curParentId and record as curPtr->volCloneId */
+           code =
+               AFSVolTransCreate(aconn, curPtr->volId, apart, ITOffline,
+                                 &tid);
+           if (code)
+               VPRINT2("Clone for volume %s %u failed \n", curPtr->volName,
+                       curPtr->volId);
+           if (code) {
+               curPtr->volFlags &= ~CLONEVALID;        /*cant clone */
+               curPos++;
+               continue;
+           }
+           if (strlen(curPtr->volName) < (VOLSER_OLDMAXVOLNAME - 9)) {
+               strcpy(cloneName, curPtr->volName);
+               strcat(cloneName, "-tmpClone-");
+           } else
+               strcpy(cloneName, "-tmpClone");
+           if (reuseCloneId) {
+               curPtr->volCloneId = curCloneId;
+               curCloneId++;
+           }
+
+           code =
+               AFSVolClone(aconn, tid, 0, readonlyVolume, cloneName,
+                           &(curPtr->volCloneId));
+           if (code) {
+               curPtr->volFlags &= ~CLONEVALID;
+               curPos++;
+               fprintf(STDERR, "Could not clone %s due to error %lu\n",
+                       curPtr->volName, (unsigned long)code);
+               code = AFSVolEndTrans(aconn, tid, &rcode);
+               if (code)
+                   fprintf(STDERR, "WARNING: could not end transaction\n");
+               continue;
+           }
+           VPRINT2("********** Cloned %s temporary %u\n", cloneName,
+                   curPtr->volCloneId);
+           code = AFSVolEndTrans(aconn, tid, &rcode);
+           if (code || rcode) {
+               curPtr->volFlags &= ~CLONEVALID;
+               curPos++;
+               continue;
+           }
+
+           curPos++;
+       }
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    return 0;
+}
+
+
+/*list all the volumes on <aserver> and <apart>. If all = 1, then all the
+* relevant fields of the volume are also returned. This is a heavy weight operation.*/
+int
+UV_ListVolumes(afs_int32 aserver, afs_int32 apart, int all,
+              struct volintInfo **resultPtr, afs_int32 * size)
+{
+    struct rx_connection *aconn;
+    afs_int32 code = 0;
+    volEntries volumeInfo;
+
+    code = 0;
+    *size = 0;
+    *resultPtr = (volintInfo *) 0;
+    volumeInfo.volEntries_val = (volintInfo *) 0;      /*this hints the stub to allocate space */
+    volumeInfo.volEntries_len = 0;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+    code = AFSVolListVolumes(aconn, apart, all, &volumeInfo);
+    if (code) {
+       fprintf(STDERR,
+               "Could not fetch the list of volumes from the server\n");
+    } else {
+       *resultPtr = volumeInfo.volEntries_val;
+       *size = volumeInfo.volEntries_len;
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    PrintError("", code);
+    return code;
+}
+
+/*------------------------------------------------------------------------
+ * EXPORTED UV_XListVolumes
+ *
+ * Description:
+ *     List the extended information for all the volumes on a particular
+ *     File Server and partition.  We may either return the volume's ID
+ *     or all of its extended information.
+ *
+ * Arguments:
+ *     a_serverID         : Address of the File Server for which we want
+ *                             extended volume info.
+ *     a_partID           : Partition for which we want the extended
+ *                             volume info.
+ *     a_all              : If non-zero, fetch ALL the volume info,
+ *                             otherwise just the volume ID.
+ *     a_resultPP         : Ptr to the address of the area containing
+ *                             the returned volume info.
+ *     a_numEntsInResultP : Ptr for the value we set for the number of
+ *                             entries returned.
+ *
+ * Returns:
+ *     0 on success,
+ *     Otherise, the return value of AFSVolXListVolumes.
+ *
+ * Environment:
+ *     This routine is closely related to UV_ListVolumes, which returns
+ *     only the standard level of detail on AFS volumes. It is a
+ *     heavyweight operation, zipping through all the volume entries for
+ *     a given server/partition.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+int
+UV_XListVolumes(afs_int32 a_serverID, afs_int32 a_partID, int a_all,
+               struct volintXInfo **a_resultPP,
+               afs_int32 * a_numEntsInResultP)
+{
+    struct rx_connection *rxConnP;     /*Ptr to the Rx connection involved */
+    afs_int32 code;            /*Error code to return */
+    volXEntries volumeXInfo;   /*Area for returned extended vol info */
+
+    /*
+     * Set up our error code and the area for returned extended volume info.
+     * We set the val field to a null pointer as a hint for the stub to
+     * allocate space.
+     */
+    code = 0;
+    *a_numEntsInResultP = 0;
+    *a_resultPP = (volintXInfo *) 0;
+    volumeXInfo.volXEntries_val = (volintXInfo *) 0;
+    volumeXInfo.volXEntries_len = 0;
+
+    /*
+     * Bind to the Volume Server port on the File Server machine in question,
+     * then go for it.
+     */
+    rxConnP = UV_Bind(a_serverID, AFSCONF_VOLUMEPORT);
+    code = AFSVolXListVolumes(rxConnP, a_partID, a_all, &volumeXInfo);
+    if (code)
+       fprintf(STDERR, "[UV_XListVolumes] Couldn't fetch volume list\n");
+    else {
+       /*
+        * We got the info; pull out the pointer to where the results lie
+        * and how many entries are there.
+        */
+       *a_resultPP = volumeXInfo.volXEntries_val;
+       *a_numEntsInResultP = volumeXInfo.volXEntries_len;
+    }
+
+    /*
+     * If we got an Rx connection, throw it away.
+     */
+    if (rxConnP)
+       rx_DestroyConnection(rxConnP);
+
+    PrintError("", code);
+    return (code);
+}                              /*UV_XListVolumes */
+
+/* get all the information about volume <volid> on <aserver> and <apart> */
+int
+UV_ListOneVolume(afs_int32 aserver, afs_int32 apart, afs_int32 volid,
+                struct volintInfo **resultPtr)
+{
+    struct rx_connection *aconn;
+    afs_int32 code = 0;
+    volEntries volumeInfo;
+
+    code = 0;
+
+    *resultPtr = (volintInfo *) 0;
+    volumeInfo.volEntries_val = (volintInfo *) 0;      /*this hints the stub to allocate space */
+    volumeInfo.volEntries_len = 0;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+    code = AFSVolListOneVolume(aconn, apart, volid, &volumeInfo);
+    if (code) {
+       fprintf(STDERR,
+               "Could not fetch the information about volume %lu from the server\n",
+               (unsigned long)volid);
+    } else {
+       *resultPtr = volumeInfo.volEntries_val;
+
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    PrintError("", code);
+    return code;
+}
+
+/*------------------------------------------------------------------------
+ * EXPORTED UV_XListOneVolume
+ *
+ * Description:
+ *     List the extended information for a volume on a particular File
+ *     Server and partition.
+ *
+ * Arguments:
+ *     a_serverID         : Address of the File Server for which we want
+ *                             extended volume info.
+ *     a_partID           : Partition for which we want the extended
+ *                             volume info.
+ *     a_volID            : Volume ID for which we want the info.
+ *     a_resultPP         : Ptr to the address of the area containing
+ *                             the returned volume info.
+ *
+ * Returns:
+ *     0 on success,
+ *     Otherise, the return value of AFSVolXListOneVolume.
+ *
+ * Environment:
+ *     This routine is closely related to UV_ListOneVolume, which returns
+ *     only the standard level of detail on the chosen AFS volume.
+ *
+ * Side Effects:
+ *     As advertised.
+ *------------------------------------------------------------------------*/
+
+int
+UV_XListOneVolume(afs_int32 a_serverID, afs_int32 a_partID, afs_int32 a_volID,
+                 struct volintXInfo **a_resultPP)
+{
+    struct rx_connection *rxConnP;     /*Rx connection to Volume Server */
+    afs_int32 code;            /*Error code */
+    volXEntries volumeXInfo;   /*Area for returned info */
+
+    /*
+     * Set up our error code, and the area we're in which we are returning
+     * the info.  Setting the val field to a null pointer tells the stub
+     * to allocate space for us.
+     */
+    code = 0;
+    *a_resultPP = (volintXInfo *) 0;
+    volumeXInfo.volXEntries_val = (volintXInfo *) 0;
+    volumeXInfo.volXEntries_len = 0;
+
+    /*
+     * Bind to the Volume Server port on the File Server machine in question,
+     * then go for it.
+     */
+    rxConnP = UV_Bind(a_serverID, AFSCONF_VOLUMEPORT);
+    code = AFSVolXListOneVolume(rxConnP, a_partID, a_volID, &volumeXInfo);
+    if (code)
+       fprintf(STDERR,
+               "[UV_XListOneVolume] Couldn't fetch the volume information\n");
+    else
+       /*
+        * We got the info; pull out the pointer to where the results lie.
+        */
+       *a_resultPP = volumeXInfo.volXEntries_val;
+
+    /*
+     * If we got an Rx connection, throw it away.
+     */
+    if (rxConnP)
+       rx_DestroyConnection(rxConnP);
+
+    PrintError("", code);
+    return code;
+}
+
+/* CheckVolume()
+ *    Given a volume we read from a partition, check if it is 
+ *    represented in the VLDB correctly.
+ * 
+ *    The VLDB is looked up by the RW volume id (not its name).
+ *    The RW contains the true name of the volume (BK and RO set
+ *       the name in the VLDB only on creation of the VLDB entry).
+ *    We want rules strict enough that when we check all volumes
+ *       on one partition, it does not need to be done again. IE:
+ *       two volumes on different partitions won't constantly 
+ *       change a VLDB entry away from what the other set.
+ *    For RW and BK volumes, we will always check the VLDB to see 
+ *       if the two exist on the server/partition. May seem redundant,
+ *       but this is an easy check of the VLDB. IE: if the VLDB entry
+ *       says the BK exists but no BK volume is there, we will detect
+ *       this when we check the RW volume.
+ *    VLDB entries are locked only when a change needs to be done.
+ *    Output changed to look a lot like the "vos syncserv" otuput.
+ */
+static afs_int32
+CheckVolume(volintInfo * volumeinfo, afs_int32 aserver, afs_int32 apart,
+           afs_int32 * modentry, afs_uint32 * maxvolid)
+{
+    int idx, j;
+    afs_int32 code, error = 0;
+    struct nvldbentry entry, storeEntry;
+    char pname[10];
+    int pass = 0, islocked = 0, createentry, addvolume, modified, mod;
+    afs_int32 rwvolid;
+
+    if (modentry)
+       *modentry = 0;
+    rwvolid =
+       ((volumeinfo->type ==
+         RWVOL) ? volumeinfo->volid : volumeinfo->parentID);
+
+  retry:
+    /* Check to see if the VLDB is ok without locking it (pass 1).
+     * If it will change, then lock the VLDB entry, read it again,
+     * then make the changes to it (pass 2).
+     */
+    if (++pass == 2) {
+       code = ubik_Call(VL_SetLock, cstruct, 0, rwvolid, RWVOL, VLOP_DELETE);
+       if (code) {
+           fprintf(STDERR, "Could not lock VLDB entry for %lu\n",
+                   (unsigned long)rwvolid);
+           ERROR_EXIT(code);
+       }
+       islocked = 1;
+    }
+
+    createentry = 0;           /* Do we need to create a VLDB entry */
+    addvolume = 0;             /* Add this volume to the VLDB entry */
+    modified = 0;              /* The VLDB entry was modified */
+
+    /* Read the entry from VLDB by its RW volume id */
+    code = VLDB_GetEntryByID(rwvolid, RWVOL, &entry);
+    if (code) {
+       if (code != VL_NOENT) {
+           fprintf(STDOUT,
+                   "Could not retreive the VLDB entry for volume %lu \n",
+                   (unsigned long)rwvolid);
+           ERROR_EXIT(code);
+       }
+
+       memset(&entry, 0, sizeof(entry));
+       vsu_ExtractName(entry.name, volumeinfo->name);  /* Store name of RW */
+
+       createentry = 1;
+    } else {
+       MapHostToNetwork(&entry);
+    }
+
+    if (verbose && (pass == 1)) {
+       fprintf(STDOUT, "_______________________________\n");
+       fprintf(STDOUT, "\n-- status before -- \n");
+       if (createentry) {
+           fprintf(STDOUT, "\n**does not exist**\n");
+       } else {
+           if ((entry.flags & RW_EXISTS) || (entry.flags & RO_EXISTS)
+               || (entry.flags & BACK_EXISTS))
+               EnumerateEntry(&entry);
+       }
+       fprintf(STDOUT, "\n");
+    }
+
+    if (volumeinfo->type == RWVOL) {   /* RW volume exists */
+       if (createentry) {
+           idx = 0;
+           entry.nServers = 1;
+           addvolume++;
+       } else {
+           /* Check existence of RW and BK volumes */
+           code = CheckVldbRWBK(&entry, &mod);
+           if (code)
+               ERROR_EXIT(code);
+           if (mod)
+               modified++;
+
+           idx = Lp_GetRwIndex(&entry);
+           if (idx == -1) {    /* RW index not found in the VLDB entry */
+               idx = entry.nServers;   /* put it into next index */
+               entry.nServers++;
+               addvolume++;
+           } else {            /* RW index found in the VLDB entry. */
+               /* Verify if this volume's location matches where the VLDB says it is */
+               if (!Lp_Match(aserver, apart, &entry)) {
+                   if (entry.flags & RW_EXISTS) {
+                       /* The RW volume exists elsewhere - report this one a duplicate */
+                       if (pass == 1) {
+                           MapPartIdIntoName(apart, pname);
+                           fprintf(STDERR,
+                                   "*** Warning: Orphaned RW volume %lu exists on %s %s\n",
+                                   (unsigned long)rwvolid,
+                                   hostutil_GetNameByINet(aserver), pname);
+                           MapPartIdIntoName(entry.serverPartition[idx],
+                                             pname);
+                           fprintf(STDERR,
+                                   "    VLDB reports RW volume %lu exists on %s %s\n",
+                                   (unsigned long)rwvolid,
+                                   hostutil_GetNameByINet(entry.
+                                                          serverNumber[idx]),
+                                   pname);
+                       }
+                   } else {
+                       /* The RW volume does not exist - have VLDB point to this one */
+                       addvolume++;
+
+                       /* Check for orphaned BK volume on old partition */
+                       if (entry.flags & BACK_EXISTS) {
+                           if (pass == 1) {
+                               MapPartIdIntoName(entry.serverPartition[idx],
+                                                 pname);
+                               fprintf(STDERR,
+                                       "*** Warning: Orphaned BK volume %u exists on %s %s\n",
+                                       entry.volumeId[BACKVOL],
+                                       hostutil_GetNameByINet(entry.
+                                                              serverNumber
+                                                              [idx]), pname);
+                               MapPartIdIntoName(apart, pname);
+                               fprintf(STDERR,
+                                       "    VLDB reports its RW volume %lu exists on %s %s\n",
+                                       (unsigned long)rwvolid,
+                                       hostutil_GetNameByINet(aserver),
+                                       pname);
+                           }
+                       }
+                   }
+               } else {
+                   /* Volume location matches the VLDB location */
+                   if ((volumeinfo->backupID && !entry.volumeId[BACKVOL])
+                       || (volumeinfo->cloneID && !entry.volumeId[ROVOL])
+                       ||
+                       (strncmp
+                        (entry.name, volumeinfo->name,
+                         VOLSER_OLDMAXVOLNAME) != 0)) {
+                       addvolume++;
+                   }
+               }
+           }
+       }
+
+       if (addvolume) {
+           entry.flags |= RW_EXISTS;
+           entry.volumeId[RWVOL] = rwvolid;
+           if (!entry.volumeId[BACKVOL])
+               entry.volumeId[BACKVOL] = volumeinfo->backupID;
+           if (!entry.volumeId[ROVOL])
+               entry.volumeId[ROVOL] = volumeinfo->cloneID;
+
+           entry.serverFlags[idx] = ITSRWVOL;
+           entry.serverNumber[idx] = aserver;
+           entry.serverPartition[idx] = apart;
+           strncpy(entry.name, volumeinfo->name, VOLSER_OLDMAXVOLNAME);
+
+           modified++;
+
+           /* One last check - to update BK if need to */
+           code = CheckVldbRWBK(&entry, &mod);
+           if (code)
+               ERROR_EXIT(code);
+           if (mod)
+               modified++;
+       }
+    }
+
+    else if (volumeinfo->type == BACKVOL) {    /* A BK volume */
+       if (createentry) {
+           idx = 0;
+           entry.nServers = 1;
+           addvolume++;
+       } else {
+           /* Check existence of RW and BK volumes */
+           code = CheckVldbRWBK(&entry, &mod);
+           if (code)
+               ERROR_EXIT(code);
+           if (mod)
+               modified++;
+
+           idx = Lp_GetRwIndex(&entry);
+           if (idx == -1) {    /* RW index not found in the VLDB entry */
+               idx = entry.nServers;   /* Put it into next index */
+               entry.nServers++;
+               addvolume++;
+           } else {            /* RW index found in the VLDB entry */
+               /* Verify if this volume's location matches where the VLDB says it is */
+               if (!Lp_Match(aserver, apart, &entry)) {
+                   /* VLDB says RW and/or BK is elsewhere - report this BK volume orphaned */
+                   if (pass == 1) {
+                       MapPartIdIntoName(apart, pname);
+                       fprintf(STDERR,
+                               "*** Warning: Orphaned BK volume %lu exists on %s %s\n",
+                               (unsigned long)volumeinfo->volid,
+                               hostutil_GetNameByINet(aserver), pname);
+                       MapPartIdIntoName(entry.serverPartition[idx], pname);
+                       fprintf(STDERR,
+                               "    VLDB reports its RW/BK volume %lu exists on %s %s\n",
+                               (unsigned long)rwvolid,
+                               hostutil_GetNameByINet(entry.
+                                                      serverNumber[idx]),
+                               pname);
+                   }
+               } else {
+                   if (volumeinfo->volid != entry.volumeId[BACKVOL]) {
+                       if (!(entry.flags & BACK_EXISTS)) {
+                           addvolume++;
+                       } else if (volumeinfo->volid >
+                                  entry.volumeId[BACKVOL]) {
+                           addvolume++;
+
+                           if (pass == 1) {
+                               MapPartIdIntoName(entry.serverPartition[idx],
+                                                 pname);
+                               fprintf(STDERR,
+                                       "*** Warning: Orphaned BK volume %u exists on %s %s\n",
+                                       entry.volumeId[BACKVOL],
+                                       hostutil_GetNameByINet(aserver),
+                                       pname);
+                               fprintf(STDERR,
+                                       "    VLDB reports its BK volume ID is %lu\n",
+                                       (unsigned long)volumeinfo->volid);
+                           }
+                       } else {
+                           if (pass == 1) {
+                               MapPartIdIntoName(entry.serverPartition[idx],
+                                                 pname);
+                               fprintf(STDERR,
+                                       "*** Warning: Orphaned BK volume %lu exists on %s %s\n",
+                                       (unsigned long)volumeinfo->volid,
+                                       hostutil_GetNameByINet(aserver),
+                                       pname);
+                               fprintf(STDERR,
+                                       "    VLDB reports its BK volume ID is %u\n",
+                                       entry.volumeId[BACKVOL]);
+                           }
+                       }
+                   } else if (!entry.volumeId[BACKVOL]) {
+                       addvolume++;
+                   }
+               }
+           }
+       }
+       if (addvolume) {
+           entry.flags |= BACK_EXISTS;
+           entry.volumeId[RWVOL] = rwvolid;
+           entry.volumeId[BACKVOL] = volumeinfo->volid;
+
+           entry.serverNumber[idx] = aserver;
+           entry.serverPartition[idx] = apart;
+           entry.serverFlags[idx] = ITSRWVOL;
+
+           modified++;
+       }
+    }
+
+    else if (volumeinfo->type == ROVOL) {      /* A RO volume */
+       if (volumeinfo->volid == entry.volumeId[ROVOL]) {
+           /* This is a quick check to see if the RO entry exists in the 
+            * VLDB so we avoid the CheckVldbRO() call (which checks if each
+            * RO volume listed in the VLDB exists).
+            */
+           idx = Lp_ROMatch(aserver, apart, &entry) - 1;
+           if (idx == -1) {
+               idx = entry.nServers;
+               entry.nServers++;
+               addvolume++;
+           } else {
+               if (!(entry.flags & RO_EXISTS)) {
+                   addvolume++;
+               }
+           }
+       } else {
+           /* Before we correct the VLDB entry, make sure all the
+            * ROs listed in the VLDB exist.
+            */
+           code = CheckVldbRO(&entry, &mod);
+           if (code)
+               ERROR_EXIT(code);
+           if (mod)
+               modified++;
+
+           if (!(entry.flags & RO_EXISTS)) {
+               /* No RO exists in the VLDB entry - add this one */
+               idx = entry.nServers;
+               entry.nServers++;
+               addvolume++;
+           } else if (volumeinfo->volid > entry.volumeId[ROVOL]) {
+               /* The volume headers's RO ID does not match that in the VLDB entry,
+                * and the vol hdr's ID is greater (implies more recent). So delete
+                * all the RO volumes listed in VLDB entry and add this volume.
+                */
+               for (j = 0; j < entry.nServers; j++) {
+                   if (entry.serverFlags[j] & ITSROVOL) {
+                       /* Verify this volume exists and print message we are orphaning it */
+                       if (pass == 1) {
+                           MapPartIdIntoName(apart, pname);
+                           fprintf(STDERR,
+                                   "*** Warning: Orphaned RO volume %u exists on %s %s\n",
+                                   entry.volumeId[ROVOL],
+                                   hostutil_GetNameByINet(entry.
+                                                          serverNumber[j]),
+                                   pname);
+                           fprintf(STDERR,
+                                   "    VLDB reports its RO volume ID is %lu\n",
+                                   (unsigned long)volumeinfo->volid);
+                       }
+
+                       Lp_SetRWValue(entry, entry.serverNumber[idx],
+                                     entry.serverPartition[idx], 0L, 0L);
+                       entry.nServers--;
+                       modified++;
+                       j--;
+                   }
+               }
+
+               idx = entry.nServers;
+               entry.nServers++;
+               addvolume++;
+           } else if (volumeinfo->volid < entry.volumeId[ROVOL]) {
+               /* The volume headers's RO ID does not match that in the VLDB entry,
+                * and the vol hdr's ID is lower (implies its older). So orphan it.
+                */
+               if (pass == 1) {
+                   MapPartIdIntoName(apart, pname);
+                   fprintf(STDERR,
+                           "*** Warning: Orphaned RO volume %lu exists on %s %s\n",
+                           (unsigned long)volumeinfo->volid,
+                           hostutil_GetNameByINet(aserver), pname);
+                   fprintf(STDERR,
+                           "    VLDB reports its RO volume ID is %u\n",
+                           entry.volumeId[ROVOL]);
+               }
+           } else {
+               /* The RO volume ID in the volume header match that in the VLDB entry,
+                * and there exist RO volumes in the VLDB entry. See if any of them
+                * are this one. If not, then we add it.
+                */
+               idx = Lp_ROMatch(aserver, apart, &entry) - 1;
+               if (idx == -1) {
+                   idx = entry.nServers;
+                   entry.nServers++;
+                   addvolume++;
+               }
+           }
+       }
+
+       if (addvolume) {
+           entry.flags |= RO_EXISTS;
+           entry.volumeId[RWVOL] = rwvolid;
+           entry.volumeId[ROVOL] = volumeinfo->volid;
+
+           entry.serverNumber[idx] = aserver;
+           entry.serverPartition[idx] = apart;
+           entry.serverFlags[idx] = ITSROVOL;
+
+           modified++;
+       }
+    }
+
+    /* Remember largest volume id */
+    if (entry.volumeId[ROVOL] > *maxvolid)
+       *maxvolid = entry.volumeId[ROVOL];
+    if (entry.volumeId[BACKVOL] > *maxvolid)
+       *maxvolid = entry.volumeId[BACKVOL];
+    if (entry.volumeId[RWVOL] > *maxvolid)
+       *maxvolid = entry.volumeId[RWVOL];
+
+    if (modified) {
+       MapNetworkToHost(&entry, &storeEntry);
+
+       if (createentry) {
+           code = VLDB_CreateEntry(&storeEntry);
+           if (code) {
+               fprintf(STDOUT,
+                       "Could not create a VLDB entry for the volume %lu\n",
+                       (unsigned long)rwvolid);
+               ERROR_EXIT(code);
+           }
+       } else {
+           if (pass == 1)
+               goto retry;
+           code =
+               VLDB_ReplaceEntry(rwvolid, RWVOL, &storeEntry,
+                                 LOCKREL_OPCODE | LOCKREL_AFSID |
+                                 LOCKREL_TIMESTAMP);
+           if (code) {
+               fprintf(STDERR, "Could not update entry for %lu\n",
+                       (unsigned long)rwvolid);
+               ERROR_EXIT(code);
+           }
+       }
+       if (modentry)
+           *modentry = modified;
+    } else if (pass == 2) {
+       code =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, rwvolid, RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (code) {
+           PrintError("Could not unlock VLDB entry ", code);
+       }
+    }
+
+    if (verbose) {
+       fprintf(STDOUT, "-- status after --\n");
+       if (modified)
+           EnumerateEntry(&entry);
+       else
+           fprintf(STDOUT, "\n**no change**\n");
+    }
+
+  error_exit:
+    VPRINT("\n_______________________________\n");
+    return (error);
+}
+
+int
+sortVolumes(const void *a, const void *b)
+{
+    volintInfo *v1 = (volintInfo *) a;
+    volintInfo *v2 = (volintInfo *) b;
+    afs_int32 rwvolid1, rwvolid2;
+
+    rwvolid1 = ((v1->type == RWVOL) ? v1->volid : v1->parentID);
+    rwvolid2 = ((v2->type == RWVOL) ? v2->volid : v2->parentID);
+
+    if (rwvolid1 > rwvolid2)
+       return -1;              /* lower RW id goes first */
+    if (rwvolid1 < rwvolid2)
+       return 1;
+
+    if (v1->type == RWVOL)
+       return -1;              /* RW vols go first */
+    if (v2->type == RWVOL)
+       return 1;
+
+    if ((v1->type == BACKVOL) && (v2->type == ROVOL))
+       return -1;              /* BK vols next */
+    if ((v1->type == ROVOL) && (v2->type == BACKVOL))
+       return 1;
+
+    if (v1->volid < v2->volid)
+       return 1;               /* larger volids first */
+    if (v1->volid > v2->volid)
+       return -1;
+    return 0;
+}
+
+/* UV_SyncVolume()
+ *      Synchronise <aserver> <apart>(if flags = 1) <avolid>.
+ *      Synchronize an individual volume against a sever and partition.
+ *      Checks the VLDB entry (similar to syncserv) as well as checks
+ *      if the volume exists on specified servers (similar to syncvldb).
+ */
+int
+UV_SyncVolume(afs_int32 aserver, afs_int32 apart, char *avolname, int flags)
+{
+    struct rx_connection *aconn = 0;
+    afs_int32 j, k, code, vcode, error = 0;
+    afs_int32 tverbose, mod, modified = 0;
+    struct nvldbentry vldbentry;
+    afs_int32 volumeid = 0;
+    volEntries volumeInfo;
+    struct partList PartList;
+    afs_int32 pcnt, rv;
+    afs_int32 maxvolid = 0;
+
+    volumeInfo.volEntries_val = (volintInfo *) 0;
+    volumeInfo.volEntries_len = 0;
+
+    if (!aserver && flags) {
+       /* fprintf(STDERR,"Partition option requires a server option\n"); */
+       ERROR_EXIT(EINVAL);
+    }
+
+    /* Turn verbose logging off and do our own verbose logging */
+    tverbose = verbose;
+    verbose = 0;
+
+    /* Read the VLDB entry */
+    vcode = VLDB_GetEntryByName(avolname, &vldbentry);
+    if (vcode && (vcode != VL_NOENT)) {
+       fprintf(STDERR, "Could not access the VLDB for volume %s\n",
+               avolname);
+       ERROR_EXIT(vcode);
+    } else if (!vcode) {
+       MapHostToNetwork(&vldbentry);
+    }
+
+    if (tverbose) {
+       fprintf(STDOUT, "Processing VLDB entry %s ...\n", avolname);
+       fprintf(STDOUT, "_______________________________\n");
+       fprintf(STDOUT, "\n-- status before -- \n");
+       if (vcode) {
+           fprintf(STDOUT, "\n**does not exist**\n");
+       } else {
+           if ((vldbentry.flags & RW_EXISTS) || (vldbentry.flags & RO_EXISTS)
+               || (vldbentry.flags & BACK_EXISTS))
+               EnumerateEntry(&vldbentry);
+       }
+       fprintf(STDOUT, "\n");
+    }
+
+    /* Verify that all of the VLDB entries exist on the repective servers 
+     * and partitions (this does not require that avolname be a volume ID).
+     * Equivalent to a syncserv.
+     */
+    if (!vcode) {
+       code = CheckVldb(&vldbentry, &mod);
+       if (code) {
+           fprintf(STDERR, "Could not process VLDB entry for volume %s\n",
+                   vldbentry.name);
+           ERROR_EXIT(code);
+       }
+       if (mod)
+           modified++;
+    }
+
+    /* If aserver is given, we will search for the desired volume on it */
+    if (aserver) {
+       /* Generate array of partitions on the server that we will check */
+       if (!flags) {
+           code = UV_ListPartitions(aserver, &PartList, &pcnt);
+           if (code) {
+               fprintf(STDERR,
+                       "Could not fetch the list of partitions from the server\n");
+               ERROR_EXIT(code);
+           }
+       } else {
+           PartList.partId[0] = apart;
+           pcnt = 1;
+       }
+
+       aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+       /* If a volume ID were given, search for it on each partition */
+       if ((volumeid = atol(avolname))) {
+           for (j = 0; j < pcnt; j++) {
+               code =
+                   AFSVolListOneVolume(aconn, PartList.partId[j], volumeid,
+                                       &volumeInfo);
+               if (code) {
+                   if (code != ENODEV) {
+                       fprintf(STDERR, "Could not query server\n");
+                       ERROR_EXIT(code);
+                   }
+               } else {
+                   /* Found one, sync it with VLDB entry */
+                   code =
+                       CheckVolume(volumeInfo.volEntries_val, aserver,
+                                   PartList.partId[j], &mod, &maxvolid);
+                   if (code)
+                       ERROR_EXIT(code);
+                   if (mod)
+                       modified++;
+               }
+
+               if (volumeInfo.volEntries_val)
+                   free(volumeInfo.volEntries_val);
+               volumeInfo.volEntries_val = (volintInfo *) 0;
+               volumeInfo.volEntries_len = 0;
+           }
+       }
+
+       /* Check to see if the RW, BK, and RO IDs exist on any
+        * partitions. We get the volume IDs from the VLDB.
+        */
+       rv = 1;                 /* Read the VLDB entry ? */
+       for (j = 0; j < MAXTYPES; j++) {        /* for RW, RO, and BK IDs */
+           if (rv) {
+               vcode = VLDB_GetEntryByName(avolname, &vldbentry);
+               if (vcode) {
+                   if (vcode == VL_NOENT)
+                       break;
+                   fprintf(STDERR,
+                           "Could not access the VLDB for volume %s\n",
+                           avolname);
+                   ERROR_EXIT(vcode);
+               }
+               rv = 0;
+           }
+
+           if (vldbentry.volumeId[j] == 0)
+               continue;
+
+           for (k = 0; k < pcnt; k++) {        /* For each partition */
+               volumeInfo.volEntries_val = (volintInfo *) 0;
+               volumeInfo.volEntries_len = 0;
+               code =
+                   AFSVolListOneVolume(aconn, PartList.partId[k],
+                                       vldbentry.volumeId[j], &volumeInfo);
+               if (code) {
+                   if (code != ENODEV) {
+                       fprintf(STDERR, "Could not query server\n");
+                       ERROR_EXIT(code);
+                   }
+               } else {
+                   /* Found one, sync it with VLDB entry */
+                   code =
+                       CheckVolume(volumeInfo.volEntries_val, aserver,
+                                   PartList.partId[k], &mod, &maxvolid);
+                   if (code)
+                       ERROR_EXIT(code);
+                   if (mod)
+                       modified++, rv++;
+               }
+
+               if (volumeInfo.volEntries_val)
+                   free(volumeInfo.volEntries_val);
+               volumeInfo.volEntries_val = (volintInfo *) 0;
+               volumeInfo.volEntries_len = 0;
+           }
+       }
+    }
+
+    /* if (aserver) */
+    /* If verbose output, print a summary of what changed */
+    if (tverbose) {
+       fprintf(STDOUT, "-- status after --\n");
+       code = VLDB_GetEntryByName(avolname, &vldbentry);
+       if (code && (code != VL_NOENT)) {
+           fprintf(STDERR, "Could not access the VLDB for volume %s\n",
+                   avolname);
+           ERROR_EXIT(code);
+       }
+       if (modified && (code == VL_NOENT)) {
+           fprintf(STDOUT, "\n**entry deleted**\n");
+       } else if (modified) {
+           EnumerateEntry(&vldbentry);
+       } else {
+           fprintf(STDOUT, "\n**no change**\n");
+       }
+       fprintf(STDOUT, "\n_______________________________\n");
+    }
+
+  error_exit:
+    /* Now check if the maxvolid is larger than that stored in the VLDB */
+    if (maxvolid) {
+       afs_int32 maxvldbid = 0;
+       code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 0, &maxvldbid);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not get the highest allocated volume id from the VLDB\n");
+           if (!error)
+               error = code;
+       } else if (maxvolid > maxvldbid) {
+           afs_uint32 id, nid;
+           id = maxvolid - maxvldbid + 1;
+           code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, id, &nid);
+           if (code) {
+               fprintf(STDERR,
+                       "Error in increasing highest allocated volume id in VLDB\n");
+               if (!error)
+                   error = code;
+           }
+       }
+    }
+
+    verbose = tverbose;
+    if (verbose) {
+       if (error)
+           fprintf(STDOUT, "...error encountered");
+       else
+           fprintf(STDOUT, "...done entry\n");
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    if (volumeInfo.volEntries_val)
+       free(volumeInfo.volEntries_val);
+
+    PrintError("", error);
+    return error;
+}
+
+/* UV_SyncVldb()
+ *      Synchronise vldb with the file server <aserver> and,
+ *      optionally, <apart>.
+ */
+int
+UV_SyncVldb(afs_int32 aserver, afs_int32 apart, int flags, int force)
+{
+    struct rx_connection *aconn;
+    afs_int32 code, error = 0;
+    int i, j, pfail;
+    volEntries volumeInfo;
+    struct partList PartList;
+    afs_int32 pcnt;
+    char pname[10];
+    volintInfo *vi;
+    afs_int32 failures = 0, modifications = 0, tentries = 0;
+    afs_int32 modified;
+    afs_uint32 maxvolid = 0;
+
+    volumeInfo.volEntries_val = (volintInfo *) 0;
+    volumeInfo.volEntries_len = 0;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+    /* Generate array of partitions to check */
+    if (!flags) {
+       code = UV_ListPartitions(aserver, &PartList, &pcnt);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not fetch the list of partitions from the server\n");
+           ERROR_EXIT(code);
+       }
+    } else {
+       PartList.partId[0] = apart;
+       pcnt = 1;
+    }
+
+    VPRINT("Processing volume entries ...\n");
+
+    /* Step through the array of partitions */
+    for (i = 0; i < pcnt; i++) {
+       apart = PartList.partId[i];
+       MapPartIdIntoName(apart, pname);
+
+       volumeInfo.volEntries_val = (volintInfo *) 0;
+       volumeInfo.volEntries_len = 0;
+       code = AFSVolListVolumes(aconn, apart, 1, &volumeInfo);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not fetch the list of volumes from the server\n");
+           ERROR_EXIT(code);
+       }
+
+       /* May want to sort the entries: RW, BK (high to low), RO (high to low) */
+       qsort((char *)volumeInfo.volEntries_val, volumeInfo.volEntries_len,
+             sizeof(volintInfo), sortVolumes);
+
+       pfail = 0;
+       for (vi = volumeInfo.volEntries_val, j = 0;
+            j < volumeInfo.volEntries_len; j++, vi++) {
+           if (!vi->status)
+               continue;
+
+           tentries++;
+
+           if (verbose) {
+               fprintf(STDOUT,
+                       "Processing volume entry %d: %s (%lu) on server %s %s...\n",
+                       j + 1, vi->name, (unsigned long)vi->volid,
+                       hostutil_GetNameByINet(aserver), pname);
+               fflush(STDOUT);
+           }
+
+           code = CheckVolume(vi, aserver, apart, &modified, &maxvolid);
+           if (code) {
+               PrintError("", code);
+               failures++;
+               pfail++;
+           } else if (modified) {
+               modifications++;
+           }
+
+           if (verbose) {
+               if (code) {
+                   fprintf(STDOUT, "...error encountered\n\n");
+               } else {
+                   fprintf(STDOUT, "...done entry %d\n\n", j + 1);
+               }
+           }
+       }
+
+       if (pfail) {
+           fprintf(STDERR,
+                   "Could not process entries on server %s partition %s\n",
+                   hostutil_GetNameByINet(aserver), pname);
+       }
+       if (volumeInfo.volEntries_val) {
+           free(volumeInfo.volEntries_val);
+           volumeInfo.volEntries_val = 0;
+       }
+
+    }                          /* thru all partitions */
+
+    VPRINT3("Total entries: %u, Failed to process %d, Changed %d\n", tentries,
+           failures, modifications);
+
+  error_exit:
+    /* Now check if the maxvolid is larger than that stored in the VLDB */
+    if (maxvolid) {
+       afs_uint32 maxvldbid = 0;
+       code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 0, &maxvldbid);
+       if (code) {
+           fprintf(STDERR,
+                   "Could not get the highest allocated volume id from the VLDB\n");
+           if (!error)
+               error = code;
+       } else if (maxvolid > maxvldbid) {
+           afs_uint32 id, nid;
+           id = maxvolid - maxvldbid + 1;
+           code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, id, &nid);
+           if (code) {
+               fprintf(STDERR,
+                       "Error in increasing highest allocated volume id in VLDB\n");
+               if (!error)
+                   error = code;
+           }
+       }
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    if (volumeInfo.volEntries_val)
+       free(volumeInfo.volEntries_val);
+    PrintError("", error);
+    return (error);
+}
+
+/* VolumeExists()
+ *      Determine if a volume exists on a server and partition.
+ *      Try creating a transaction on the volume. If we can,
+ *      the volume exists, if not, then return the error code.
+ *      Some error codes mean the volume is unavailable but
+ *      still exists - so we catch these error codes.
+ */
+afs_int32
+VolumeExists(afs_int32 server, afs_int32 partition, afs_int32 volumeid)
+{
+    struct rx_connection *conn = (struct rx_connection *)0;
+    afs_int32 code = -1;
+    volEntries volumeInfo;
+
+    conn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    if (conn) {
+       volumeInfo.volEntries_val = (volintInfo *) 0;
+       volumeInfo.volEntries_len = 0;
+       code = AFSVolListOneVolume(conn, partition, volumeid, &volumeInfo);
+       if (volumeInfo.volEntries_val)
+           free(volumeInfo.volEntries_val);
+       if (code == VOLSERILLEGAL_PARTITION)
+           code = ENODEV;
+       rx_DestroyConnection(conn);
+    }
+    return code;
+}
+
+/* CheckVldbRWBK()
+ *
+ */
+afs_int32
+CheckVldbRWBK(struct nvldbentry * entry, afs_int32 * modified)
+{
+    int modentry = 0;
+    int idx;
+    afs_int32 code, error = 0;
+    char pname[10];
+
+    if (modified)
+       *modified = 0;
+    idx = Lp_GetRwIndex(entry);
+
+    /* Check to see if the RW volume exists and set the RW_EXISTS
+     * flag accordingly.
+     */
+    if (idx == -1) {           /* Did not find a RW entry */
+       if (entry->flags & RW_EXISTS) { /* ... yet entry says RW exists */
+           entry->flags &= ~RW_EXISTS; /* ... so say RW does not exist */
+           modentry++;
+       }
+    } else {
+       code =
+           VolumeExists(entry->serverNumber[idx],
+                        entry->serverPartition[idx], entry->volumeId[RWVOL]);
+       if (code == 0) {        /* RW volume exists */
+           if (!(entry->flags & RW_EXISTS)) {  /* ... yet entry says RW does not exist */
+               entry->flags |= RW_EXISTS;      /* ... so say RW does exist */
+               modentry++;
+           }
+       } else if (code == ENODEV) {    /* RW volume does not exist */
+           if (entry->flags & RW_EXISTS) {     /* ... yet entry says RW exists */
+               entry->flags &= ~RW_EXISTS;     /* ... so say RW does not exist */
+               modentry++;
+           }
+       } else {
+           /* If VLDB says it didn't exist, then ignore error */
+           if (entry->flags & RW_EXISTS) {
+               MapPartIdIntoName(entry->serverPartition[idx], pname);
+               fprintf(STDERR,
+                       "Transaction call failed for RW volume %u on server %s %s\n",
+                       entry->volumeId[RWVOL],
+                       hostutil_GetNameByINet(entry->serverNumber[idx]),
+                       pname);
+               ERROR_EXIT(code);
+           }
+       }
+    }
+
+    /* Check to see if the BK volume exists and set the BACK_EXISTS
+     * flag accordingly. idx already ponts to the RW entry.
+     */
+    if (idx == -1) {           /* Did not find a RW entry */
+       if (entry->flags & BACK_EXISTS) {       /* ... yet entry says BK exists */
+           entry->flags &= ~BACK_EXISTS;       /* ... so say BK does not exist */
+           modentry++;
+       }
+    } else {                   /* Found a RW entry */
+       code =
+           VolumeExists(entry->serverNumber[idx],
+                        entry->serverPartition[idx],
+                        entry->volumeId[BACKVOL]);
+       if (code == 0) {        /* BK volume exists */
+           if (!(entry->flags & BACK_EXISTS)) {        /* ... yet entry says BK does not exist */
+               entry->flags |= BACK_EXISTS;    /* ... so say BK does exist */
+               modentry++;
+           }
+       } else if (code == ENODEV) {    /* BK volume does not exist */
+           if (entry->flags & BACK_EXISTS) {   /* ... yet entry says BK exists */
+               entry->flags &= ~BACK_EXISTS;   /* ... so say BK does not exist */
+               modentry++;
+           }
+       } else {
+           /* If VLDB says it didn't exist, then ignore error */
+           if (entry->flags & BACK_EXISTS) {
+               MapPartIdIntoName(entry->serverPartition[idx], pname);
+               fprintf(STDERR,
+                       "Transaction call failed for BK volume %u on server %s %s\n",
+                       entry->volumeId[BACKVOL],
+                       hostutil_GetNameByINet(entry->serverNumber[idx]),
+                       pname);
+               ERROR_EXIT(code);
+           }
+       }
+    }
+
+    /* If there is an idx but the BK and RW volumes no
+     * longer exist, then remove the RW entry.
+     */
+    if ((idx != -1) && !(entry->flags & RW_EXISTS)
+       && !(entry->flags & BACK_EXISTS)) {
+       Lp_SetRWValue(entry, entry->serverNumber[idx],
+                     entry->serverPartition[idx], 0L, 0L);
+       entry->nServers--;
+       modentry++;
+    }
+
+  error_exit:
+    if (modified)
+       *modified = modentry;
+    return (error);
+}
+
+int
+CheckVldbRO(struct nvldbentry *entry, afs_int32 * modified)
+{
+    int idx;
+    int foundro = 0, modentry = 0;
+    afs_int32 code, error = 0;
+    char pname[10];
+
+    if (modified)
+       *modified = 0;
+
+    /* Check to see if the RO volumes exist and set the RO_EXISTS
+     * flag accordingly. 
+     */
+    for (idx = 0; idx < entry->nServers; idx++) {
+       if (!(entry->serverFlags[idx] & ITSROVOL)) {
+           continue;           /* not a RO */
+       }
+
+       code =
+           VolumeExists(entry->serverNumber[idx],
+                        entry->serverPartition[idx], entry->volumeId[ROVOL]);
+       if (code == 0) {        /* RO volume exists */
+           foundro++;
+       } else if (code == ENODEV) {    /* RW volume does not exist */
+           Lp_SetROValue(entry, entry->serverNumber[idx],
+                         entry->serverPartition[idx], 0L, 0L);
+           entry->nServers--;
+           idx--;
+           modentry++;
+       } else {
+           MapPartIdIntoName(entry->serverPartition[idx], pname);
+           fprintf(STDERR,
+                   "Transaction call failed for RO %u on server %s %s\n",
+                   entry->volumeId[ROVOL],
+                   hostutil_GetNameByINet(entry->serverNumber[idx]), pname);
+           ERROR_EXIT(code);
+       }
+    }
+
+    if (foundro) {             /* A RO volume exists */
+       if (!(entry->flags & RO_EXISTS)) {      /* ... yet entry says RW does not exist */
+           entry->flags |= RO_EXISTS;  /* ... so say RW does exist */
+           modentry++;
+       }
+    } else {                   /* A RO volume does not exist */
+       if (entry->flags & RO_EXISTS) { /* ... yet entry says RO exists */
+           entry->flags &= ~RO_EXISTS; /* ... so say RO does not exist */
+           modentry++;
+       }
+    }
+
+  error_exit:
+    if (modified)
+       *modified = modentry;
+    return (error);
+}
+
+/* CheckVldb()
+ *      Ensure that <entry> matches with the info on file servers
+ */
+afs_int32
+CheckVldb(struct nvldbentry * entry, afs_int32 * modified)
+{
+    afs_int32 code, error = 0;
+    struct nvldbentry storeEntry;
+    int islocked = 0, mod, modentry, delentry = 0;
+    int pass = 0;
+
+    if (modified)
+       *modified = 0;
+    if (verbose) {
+       fprintf(STDOUT, "_______________________________\n");
+       fprintf(STDOUT, "\n-- status before -- \n");
+       if ((entry->flags & RW_EXISTS) || (entry->flags & RO_EXISTS)
+           || (entry->flags & BACK_EXISTS))
+           EnumerateEntry(entry);
+       fprintf(STDOUT, "\n");
+    }
+
+    if (strlen(entry->name) > (VOLSER_OLDMAXVOLNAME - 10)) {
+       fprintf(STDERR, "Volume name %s exceeds limit of %d characters\n",
+               entry->name, VOLSER_OLDMAXVOLNAME - 10);
+    }
+
+  retry:
+    /* Check to see if the VLDB is ok without locking it (pass 1).
+     * If it will change, then lock the VLDB entry, read it again,
+     * then make the changes to it (pass 2).
+     */
+    if (++pass == 2) {
+       code =
+           ubik_Call(VL_SetLock, cstruct, 0, entry->volumeId[RWVOL], RWVOL,
+                     VLOP_DELETE);
+       if (code) {
+           fprintf(STDERR, "Could not lock VLDB entry for %u \n",
+                   entry->volumeId[RWVOL]);
+           ERROR_EXIT(code);
+       }
+       islocked = 1;
+
+       code = VLDB_GetEntryByID(entry->volumeId[RWVOL], RWVOL, entry);
+       if (code) {
+           fprintf(STDERR, "Could not read VLDB entry for volume %s\n",
+                   entry->name);
+           ERROR_EXIT(code);
+       } else {
+           MapHostToNetwork(entry);
+       }
+    }
+
+    modentry = 0;
+
+    /* Check if the RW and BK entries are ok */
+    code = CheckVldbRWBK(entry, &mod);
+    if (code)
+       ERROR_EXIT(code);
+    if (mod && (pass == 1))
+       goto retry;
+    if (mod)
+       modentry++;
+
+    /* Check if the RO volumes entries are ok */
+    code = CheckVldbRO(entry, &mod);
+    if (code)
+       ERROR_EXIT(code);
+    if (mod && (pass == 1))
+       goto retry;
+    if (mod)
+       modentry++;
+
+    /* The VLDB entry has been updated. If it as been modified, then 
+     * write the entry back out the the VLDB.
+     */
+    if (modentry) {
+       if (pass == 1)
+           goto retry;
+
+       if (!(entry->flags & RW_EXISTS) && !(entry->flags & BACK_EXISTS)
+           && !(entry->flags & RO_EXISTS)) {
+           /* The RW, BK, nor RO volumes do not exist. Delete the VLDB entry */
+           code =
+               ubik_Call(VL_DeleteEntry, cstruct, 0, entry->volumeId[RWVOL],
+                         RWVOL);
+           if (code) {
+               fprintf(STDERR,
+                       "Could not delete VLDB entry for volume %u \n",
+                       entry->volumeId[RWVOL]);
+               ERROR_EXIT(code);
+           }
+           delentry = 1;
+       } else {
+           /* Replace old entry with our new one */
+           MapNetworkToHost(entry, &storeEntry);
+           code =
+               VLDB_ReplaceEntry(entry->volumeId[RWVOL], RWVOL, &storeEntry,
+                                 (LOCKREL_OPCODE | LOCKREL_AFSID |
+                                  LOCKREL_TIMESTAMP));
+           if (code) {
+               fprintf(STDERR, "Could not update VLDB entry for volume %u\n",
+                       entry->volumeId[RWVOL]);
+               ERROR_EXIT(code);
+           }
+       }
+       if (modified)
+           *modified = 1;
+       islocked = 0;
+    }
+
+    if (verbose) {
+       fprintf(STDOUT, "-- status after --\n");
+       if (delentry)
+           fprintf(STDOUT, "\n**entry deleted**\n");
+       else if (modentry)
+           EnumerateEntry(entry);
+       else
+           fprintf(STDOUT, "\n**no change**\n");
+    }
+
+  error_exit:
+    VPRINT("\n_______________________________\n");
+
+    if (islocked) {
+       code =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, entry->volumeId[RWVOL],
+                     RWVOL,
+                     (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
+       if (code) {
+           fprintf(STDERR,
+                   "Could not release lock on VLDB entry for volume %u\n",
+                   entry->volumeId[RWVOL]);
+           if (!error)
+               error = code;
+       }
+    }
+    return error;
+}
+
+/* UV_SyncServer()
+ *      Synchronise <aserver> <apart>(if flags = 1) with the VLDB.
+ */
+int
+UV_SyncServer(afs_int32 aserver, afs_int32 apart, int flags, int force)
+{
+    struct rx_connection *aconn;
+    afs_int32 code, error = 0;
+    afs_int32 nentries, tentries = 0;
+    struct VldbListByAttributes attributes;
+    nbulkentries arrayEntries;
+    afs_int32 failures = 0, modified, modifications = 0;
+    struct nvldbentry *vlentry;
+    afs_int32 si, nsi, j;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+    /* Set up attributes to search VLDB  */
+    attributes.server = ntohl(aserver);
+    attributes.Mask = VLLIST_SERVER;
+    if (flags) {
+       attributes.partition = apart;
+       attributes.Mask |= VLLIST_PARTITION;
+    }
+
+    VPRINT("Processing VLDB entries ...\n");
+
+    /* While we need to collect more VLDB entries */
+    for (si = 0; si != -1; si = nsi) {
+       memset(&arrayEntries, 0, sizeof(arrayEntries));
+
+       /* Collect set of VLDB entries */
+       code =
+           VLDB_ListAttributesN2(&attributes, 0, si, &nentries,
+                                 &arrayEntries, &nsi);
+       if (code == RXGEN_OPCODE) {
+           code = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries);
+           nsi = -1;
+       }
+       if (code) {
+           fprintf(STDERR, "Could not access the VLDB for attributes\n");
+           ERROR_EXIT(code);
+       }
+       tentries += nentries;
+
+       for (j = 0; j < nentries; j++) {
+           vlentry = &arrayEntries.nbulkentries_val[j];
+           MapHostToNetwork(vlentry);
+
+           VPRINT1("Processing VLDB entry %d ...\n", j + 1);
+
+           code = CheckVldb(vlentry, &modified);
+           if (code) {
+               PrintError("", code);
+               fprintf(STDERR,
+                       "Could not process VLDB entry for volume %s\n",
+                       vlentry->name);
+               failures++;
+           } else if (modified) {
+               modifications++;
+           }
+
+           if (verbose) {
+               if (code) {
+                   fprintf(STDOUT, "...error encountered\n\n");
+               } else {
+                   fprintf(STDOUT, "...done entry %d\n\n", j + 1);
+               }
+           }
+       }
+
+       if (arrayEntries.nbulkentries_val) {
+           free(arrayEntries.nbulkentries_val);
+           arrayEntries.nbulkentries_val = 0;
+       }
+    }
+
+    VPRINT3("Total entries: %u, Failed to process %d, Changed %d\n", tentries,
+           failures, modifications);
+
+  error_exit:
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    if (arrayEntries.nbulkentries_val)
+       free(arrayEntries.nbulkentries_val);
+
+    if (failures)
+       error = VOLSERFAILEDOP;
+    return error;
+}
+
+/*rename volume <oldname> to <newname>, changing the names of the related 
+ *readonly and backup volumes. This operation is also idempotent.
+ *salvager is capable of recovering from rename operation stopping halfway.
+ *to recover run syncserver on the affected machines,it will force renaming to completion. name clashes should have been detected before calling this proc */
+int
+UV_RenameVolume(struct nvldbentry *entry, char oldname[], char newname[])
+{
+    struct nvldbentry storeEntry;
+    afs_int32 vcode, code, rcode, error;
+    int i, index;
+    char nameBuffer[256];
+    afs_int32 tid;
+    struct rx_connection *aconn;
+    int islocked;
+
+    error = 0;
+    aconn = (struct rx_connection *)0;
+    tid = 0;
+    islocked = 0;
+
+    vcode = ubik_Call(VL_SetLock, cstruct, 0, entry->volumeId[RWVOL], RWVOL, VLOP_ADDSITE);    /*last param is dummy */
+    if (vcode) {
+       fprintf(STDERR,
+               " Could not lock the VLDB entry for the  volume %u \n",
+               entry->volumeId[RWVOL]);
+       error = vcode;
+       goto rvfail;
+    }
+    islocked = 1;
+    strncpy(entry->name, newname, VOLSER_OLDMAXVOLNAME);
+    MapNetworkToHost(entry, &storeEntry);
+    vcode = VLDB_ReplaceEntry(entry->volumeId[RWVOL], RWVOL, &storeEntry, 0);
+    if (vcode) {
+       fprintf(STDERR, "Could not update VLDB entry for %u\n",
+               entry->volumeId[RWVOL]);
+       error = vcode;
+       goto rvfail;
+    }
+    VPRINT1("Recorded the new name %s in VLDB\n", newname);
+    /*at this stage the intent to rename is recorded in the vldb, as far as the vldb 
+     * is concerned, oldname is lost */
+    if (entry->flags & RW_EXISTS) {
+       index = Lp_GetRwIndex(entry);
+       if (index == -1) {      /* there is a serious discrepancy */
+           fprintf(STDERR,
+                   "There is a serious discrepancy in VLDB entry for volume %u\n",
+                   entry->volumeId[RWVOL]);
+           fprintf(STDERR, "try building VLDB from scratch\n");
+           error = VOLSERVLDB_ERROR;
+           goto rvfail;
+       }
+       aconn = UV_Bind(entry->serverNumber[index], AFSCONF_VOLUMEPORT);
+       code =
+           AFSVolTransCreate(aconn, entry->volumeId[RWVOL],
+                             entry->serverPartition[index], ITOffline, &tid);
+       if (code) {             /*volume doesnot exist */
+           fprintf(STDERR,
+                   "Could not start transaction on the rw volume %u\n",
+                   entry->volumeId[RWVOL]);
+           error = code;
+           goto rvfail;
+       } else {                /*volume exists, process it */
+
+           code =
+               AFSVolSetIdsTypes(aconn, tid, newname, RWVOL,
+                                 entry->volumeId[RWVOL],
+                                 entry->volumeId[ROVOL],
+                                 entry->volumeId[BACKVOL]);
+           if (!code) {
+               VPRINT2("Renamed rw volume %s to %s\n", oldname, newname);
+               code = AFSVolEndTrans(aconn, tid, &rcode);
+               tid = 0;
+               if (code) {
+                   fprintf(STDERR,
+                           "Could not  end transaction on volume %s %u\n",
+                           entry->name, entry->volumeId[RWVOL]);
+                   error = code;
+                   goto rvfail;
+               }
+           } else {
+               fprintf(STDERR, "Could not  set parameters on volume %s %u\n",
+                       entry->name, entry->volumeId[RWVOL]);
+               error = code;
+               goto rvfail;
+           }
+       }
+       if (aconn)
+           rx_DestroyConnection(aconn);
+       aconn = (struct rx_connection *)0;
+    }
+    /*end rw volume processing */
+    if (entry->flags & BACK_EXISTS) {  /*process the backup volume */
+       index = Lp_GetRwIndex(entry);
+       if (index == -1) {      /* there is a serious discrepancy */
+           fprintf(STDERR,
+                   "There is a serious discrepancy in the VLDB entry for the backup volume %u\n",
+                   entry->volumeId[BACKVOL]);
+           fprintf(STDERR, "try building VLDB from scratch\n");
+           error = VOLSERVLDB_ERROR;
+           goto rvfail;
+       }
+       aconn = UV_Bind(entry->serverNumber[index], AFSCONF_VOLUMEPORT);
+       code =
+           AFSVolTransCreate(aconn, entry->volumeId[BACKVOL],
+                             entry->serverPartition[index], ITOffline, &tid);
+       if (code) {             /*volume doesnot exist */
+           fprintf(STDERR,
+                   "Could not start transaction on the backup volume  %u\n",
+                   entry->volumeId[BACKVOL]);
+           error = code;
+           goto rvfail;
+       } else {                /*volume exists, process it */
+           if (strlen(newname) > (VOLSER_OLDMAXVOLNAME - 8)) {
+               fprintf(STDERR,
+                       "Volume name %s.backup exceeds the limit of %u characters\n",
+                       newname, VOLSER_OLDMAXVOLNAME);
+               error = code;
+               goto rvfail;
+           }
+           strcpy(nameBuffer, newname);
+           strcat(nameBuffer, ".backup");
+
+           code =
+               AFSVolSetIdsTypes(aconn, tid, nameBuffer, BACKVOL,
+                                 entry->volumeId[RWVOL], 0, 0);
+           if (!code) {
+               VPRINT1("Renamed backup volume to %s \n", nameBuffer);
+               code = AFSVolEndTrans(aconn, tid, &rcode);
+               tid = 0;
+               if (code) {
+                   fprintf(STDERR,
+                           "Could not  end transaction on the backup volume %u\n",
+                           entry->volumeId[BACKVOL]);
+                   error = code;
+                   goto rvfail;
+               }
+           } else {
+               fprintf(STDERR,
+                       "Could not  set parameters on the backup volume %u\n",
+                       entry->volumeId[BACKVOL]);
+               error = code;
+               goto rvfail;
+           }
+       }
+    }                          /* end backup processing */
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    aconn = (struct rx_connection *)0;
+    if (entry->flags & RO_EXISTS) {    /*process the ro volumes */
+       for (i = 0; i < entry->nServers; i++) {
+           if (entry->serverFlags[i] & ITSROVOL) {
+               aconn = UV_Bind(entry->serverNumber[i], AFSCONF_VOLUMEPORT);
+               code =
+                   AFSVolTransCreate(aconn, entry->volumeId[ROVOL],
+                                     entry->serverPartition[i], ITOffline,
+                                     &tid);
+               if (code) {     /*volume doesnot exist */
+                   fprintf(STDERR,
+                           "Could not start transaction on the ro volume %u\n",
+                           entry->volumeId[ROVOL]);
+                   error = code;
+                   goto rvfail;
+               } else {        /*volume exists, process it */
+                   strcpy(nameBuffer, newname);
+                   strcat(nameBuffer, ".readonly");
+                   if (strlen(nameBuffer) > (VOLSER_OLDMAXVOLNAME - 1)) {
+                       fprintf(STDERR,
+                               "Volume name %s exceeds the limit of %u characters\n",
+                               nameBuffer, VOLSER_OLDMAXVOLNAME);
+                       error = code;
+                       goto rvfail;
+                   }
+                   code =
+                       AFSVolSetIdsTypes(aconn, tid, nameBuffer, ROVOL,
+                                         entry->volumeId[RWVOL], 0, 0);
+                   if (!code) {
+                       VPRINT2("Renamed RO volume %s on host %s\n",
+                               nameBuffer,
+                               hostutil_GetNameByINet(entry->
+                                                      serverNumber[i]));
+                       code = AFSVolEndTrans(aconn, tid, &rcode);
+                       tid = 0;
+                       if (code) {
+                           fprintf(STDERR,
+                                   "Could not  end transaction on volume %u\n",
+                                   entry->volumeId[ROVOL]);
+                           error = code;
+                           goto rvfail;
+                       }
+                   } else {
+                       fprintf(STDERR,
+                               "Could not  set parameters on the ro volume %u\n",
+                               entry->volumeId[ROVOL]);
+                       error = code;
+                       goto rvfail;
+                   }
+               }
+               if (aconn)
+                   rx_DestroyConnection(aconn);
+               aconn = (struct rx_connection *)0;
+           }
+       }
+    }
+  rvfail:
+    if (islocked) {
+       vcode =
+           ubik_Call(VL_ReleaseLock, cstruct, 0, entry->volumeId[RWVOL],
+                     RWVOL,
+                     LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not unlock the VLDB entry for the volume %s %u\n",
+                   entry->name, entry->volumeId[RWVOL]);
+           if (!error)
+               error = vcode;
+       }
+    }
+    if (tid) {
+       code = AFSVolEndTrans(aconn, tid, &rcode);
+       if (!code)
+           code = rcode;
+       if (code) {
+           fprintf(STDERR, "Failed to end transaction on a volume \n");
+           if (!error)
+               error = code;
+       }
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    PrintError("", error);
+    return error;
+
+}
+
+/*report on all the active transactions on volser */
+int
+UV_VolserStatus(afs_int32 server, transDebugInfo ** rpntr, afs_int32 * rcount)
+{
+    struct rx_connection *aconn;
+    transDebugEntries transInfo;
+    afs_int32 code = 0;
+
+    aconn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    transInfo.transDebugEntries_val = (transDebugInfo *) 0;
+    transInfo.transDebugEntries_len = 0;
+    code = AFSVolMonitor(aconn, &transInfo);
+    if (code) {
+       fprintf(STDERR,
+               "Could not access status information about the server\n");
+       PrintError("", code);
+       if (transInfo.transDebugEntries_val)
+           free(transInfo.transDebugEntries_val);
+       if (aconn)
+           rx_DestroyConnection(aconn);
+       return code;
+    } else {
+       *rcount = transInfo.transDebugEntries_len;
+       *rpntr = transInfo.transDebugEntries_val;
+       if (aconn)
+           rx_DestroyConnection(aconn);
+       return 0;
+    }
+
+
+}
+
+/*delete the volume without interacting with the vldb */
+int
+UV_VolumeZap(afs_int32 server, afs_int32 part, afs_int32 volid)
+{
+    afs_int32 rcode, ttid, error, code;
+    struct rx_connection *aconn;
+
+    code = 0;
+    error = 0;
+    ttid = 0;
+
+    aconn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    code = AFSVolTransCreate(aconn, volid, part, ITOffline, &ttid);
+    if (code) {
+       fprintf(STDERR, "Could not start transaction on volume %lu\n",
+               (unsigned long)volid);
+       error = code;
+       goto zfail;
+    }
+    code = AFSVolDeleteVolume(aconn, ttid);
+    if (code) {
+       fprintf(STDERR, "Could not delete volume %lu\n",
+               (unsigned long)volid);
+       error = code;
+       goto zfail;
+    }
+    code = AFSVolEndTrans(aconn, ttid, &rcode);
+    ttid = 0;
+    if (!code)
+       code = rcode;
+    if (code) {
+       fprintf(STDERR, "Could not end transaction on volume %lu\n",
+               (unsigned long)volid);
+       error = code;
+       goto zfail;
+    }
+  zfail:
+    if (ttid) {
+       code = AFSVolEndTrans(aconn, ttid, &rcode);
+       if (!code)
+           code = rcode;
+       if (!error)
+           error = code;
+    }
+    PrintError("", error);
+    if (aconn)
+       rx_DestroyConnection(aconn);
+    return error;
+}
+
+int
+UV_SetVolume(afs_int32 server, afs_int32 partition, afs_int32 volid,
+            afs_int32 transflag, afs_int32 setflag, int sleeptime)
+{
+    struct rx_connection *conn = 0;
+    afs_int32 tid = 0;
+    afs_int32 code, error = 0, rcode;
+
+    conn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    if (!conn) {
+       fprintf(STDERR, "SetVolumeStatus: Bind Failed");
+       ERROR_EXIT(-1);
+    }
+
+    code = AFSVolTransCreate(conn, volid, partition, transflag, &tid);
+    if (code) {
+       fprintf(STDERR, "SetVolumeStatus: TransCreate Failed\n");
+       ERROR_EXIT(code);
+    }
+
+    code = AFSVolSetFlags(conn, tid, setflag);
+    if (code) {
+       fprintf(STDERR, "SetVolumeStatus: SetFlags Failed\n");
+       ERROR_EXIT(code);
+    }
+
+    if (sleeptime) {
+#ifdef AFS_PTHREAD_ENV
+       sleep(sleeptime);
+#else
+       IOMGR_Sleep(sleeptime);
+#endif
+    }
+
+  error_exit:
+    if (tid) {
+       rcode = 0;
+       code = AFSVolEndTrans(conn, tid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "SetVolumeStatus: EndTrans Failed\n");
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (conn)
+       rx_DestroyConnection(conn);
+    return (error);
+}
+
+int
+UV_SetVolumeInfo(afs_int32 server, afs_int32 partition, afs_int32 volid,
+                volintInfo * infop)
+{
+    struct rx_connection *conn = 0;
+    afs_int32 tid = 0;
+    afs_int32 code, error = 0, rcode;
+
+    conn = UV_Bind(server, AFSCONF_VOLUMEPORT);
+    if (!conn) {
+       fprintf(STDERR, "SetVolumeInfo: Bind Failed");
+       ERROR_EXIT(-1);
+    }
+
+    code = AFSVolTransCreate(conn, volid, partition, ITOffline, &tid);
+    if (code) {
+       fprintf(STDERR, "SetVolumeInfo: TransCreate Failed\n");
+       ERROR_EXIT(code);
+    }
+
+    code = AFSVolSetInfo(conn, tid, infop);
+    if (code) {
+       fprintf(STDERR, "SetVolumeInfo: SetInfo Failed\n");
+       ERROR_EXIT(code);
+    }
+
+  error_exit:
+    if (tid) {
+       rcode = 0;
+       code = AFSVolEndTrans(conn, tid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "SetVolumeInfo: EndTrans Failed\n");
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (conn)
+       rx_DestroyConnection(conn);
+    return (error);
+}
+
+int
+UV_GetSize(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+          afs_int32 fromdate, struct volintSize *vol_size)
+{
+    struct rx_connection *aconn = (struct rx_connection *)0;
+    afs_int32 tid = 0, rcode = 0;
+    afs_int32 code, error = 0;
+
+
+    /* get connections to the servers */
+    aconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
+
+    VPRINT1("Starting transaction on volume %u...", afromvol);
+    code = AFSVolTransCreate(aconn, afromvol, afrompart, ITBusy, &tid);
+    EGOTO1(error_exit, code,
+          "Could not start transaction on the volume %u to be measured\n",
+          afromvol);
+    VDONE;
+
+    VPRINT1("Getting size of volume on volume %u...", afromvol);
+    code = AFSVolGetSize(aconn, tid, fromdate, vol_size);
+    EGOTO(error_exit, code, "Could not start the measurement process \n");
+    VDONE;
+
+  error_exit:
+    if (tid) {
+       VPRINT1("Ending transaction on volume %u...", afromvol);
+       code = AFSVolEndTrans(aconn, tid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "Could not end transaction on the volume %u\n",
+                   afromvol);
+           fprintf(STDERR, "error codes: %d and %d\n", code, rcode);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+       VDONE;
+    }
+    if (aconn)
+       rx_DestroyConnection(aconn);
+
+    PrintError("", error);
+    return (error);
+}
+
+/*maps the host addresses in <old > (present in network byte order) to
+ that in< new> (present in host byte order )*/
+void
+MapNetworkToHost(struct nvldbentry *old, struct nvldbentry *new)
+{
+    int i, count;
+
+    /*copy all the fields */
+    strcpy(new->name, old->name);
+/*    new->volumeType = old->volumeType;*/
+    new->nServers = old->nServers;
+    count = old->nServers;
+    if (count < NMAXNSERVERS)
+       count++;
+    for (i = 0; i < count; i++) {
+       new->serverNumber[i] = ntohl(old->serverNumber[i]);
+       new->serverPartition[i] = old->serverPartition[i];
+       new->serverFlags[i] = old->serverFlags[i];
+    }
+    new->volumeId[RWVOL] = old->volumeId[RWVOL];
+    new->volumeId[ROVOL] = old->volumeId[ROVOL];
+    new->volumeId[BACKVOL] = old->volumeId[BACKVOL];
+    new->cloneId = old->cloneId;
+    new->flags = old->flags;
+}
+
+/*maps the host entries in <entry> which are present in host byte order to network byte order */
+void
+MapHostToNetwork(struct nvldbentry *entry)
+{
+    int i, count;
+
+    count = entry->nServers;
+    if (count < NMAXNSERVERS)
+       count++;
+    for (i = 0; i < count; i++) {
+       entry->serverNumber[i] = htonl(entry->serverNumber[i]);
+    }
+}
diff --git a/src/volser/vsutils.c b/src/volser/vsutils.c
new file mode 100644 (file)
index 0000000..0f825be
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+RCSID
+    ("$Header: /cvs/openafs/src/volser/vsutils.c,v 1.16 2003/12/07 22:49:46 jaltman Exp $");
+
+#include <afs/stds.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/file.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif /* AFS_NT40_ENV */
+#include <sys/stat.h>
+#ifdef AFS_AIX_ENV
+#include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#include <errno.h>
+#include <lock.h>
+#include <rx/xdr.h>
+#include <rx/rx.h>
+#include <rx/rx_globals.h>
+#include <afs/nfs.h>
+#include <afs/vlserver.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/keys.h>
+#include <ubik.h>
+#include <afs/afsint.h>
+#include <afs/cmd.h>
+#include <rx/rxkad.h>
+#include "volser.h"
+#include "volint.h"
+#include "lockdata.h"
+
+struct ubik_client *cstruct;
+static rxkad_level vsu_rxkad_level = rxkad_clear;
+
+static void
+ovlentry_to_nvlentry(oentryp, nentryp)
+     struct vldbentry *oentryp;
+     struct nvldbentry *nentryp;
+{
+    register int i;
+
+    memset(nentryp, 0, sizeof(struct nvldbentry));
+    strncpy(nentryp->name, oentryp->name, sizeof(nentryp->name));
+    for (i = 0; i < oentryp->nServers; i++) {
+       nentryp->serverNumber[i] = oentryp->serverNumber[i];
+       nentryp->serverPartition[i] = oentryp->serverPartition[i];
+       nentryp->serverFlags[i] = oentryp->serverFlags[i];
+    }
+    nentryp->nServers = oentryp->nServers;
+    for (i = 0; i < MAXTYPES; i++)
+       nentryp->volumeId[i] = oentryp->volumeId[i];
+    nentryp->cloneId = oentryp->cloneId;
+    nentryp->flags = oentryp->flags;
+}
+
+static
+nvlentry_to_ovlentry(nentryp, oentryp)
+     struct nvldbentry *nentryp;
+     struct vldbentry *oentryp;
+{
+    register int i;
+
+    memset(oentryp, 0, sizeof(struct vldbentry));
+    strncpy(oentryp->name, nentryp->name, sizeof(oentryp->name));
+    if (nentryp->nServers > OMAXNSERVERS) {
+       /*
+        * The alternative is to store OMAXSERVERS but it's always better
+        * to know what's going on...
+        */
+       return VL_BADSERVER;
+    }
+    for (i = 0; i < nentryp->nServers; i++) {
+       oentryp->serverNumber[i] = nentryp->serverNumber[i];
+       oentryp->serverPartition[i] = nentryp->serverPartition[i];
+       oentryp->serverFlags[i] = nentryp->serverFlags[i];
+    }
+    oentryp->nServers = i;
+    for (i = 0; i < MAXTYPES; i++)
+       oentryp->volumeId[i] = nentryp->volumeId[i];
+    oentryp->cloneId = nentryp->cloneId;
+    oentryp->flags = nentryp->flags;
+    return 0;
+}
+
+static int newvlserver = 0;
+
+VLDB_CreateEntry(entryp)
+     struct nvldbentry *entryp;
+{
+    struct vldbentry oentry;
+    register int code;
+
+    if (newvlserver == 1) {
+      tryold:
+       code = nvlentry_to_ovlentry(entryp, &oentry);
+       if (code)
+           return code;
+       code = ubik_Call(VL_CreateEntry, cstruct, 0, &oentry);
+       return code;
+    }
+    code = ubik_Call(VL_CreateEntryN, cstruct, 0, entryp);
+    if (!newvlserver) {
+       if (code == RXGEN_OPCODE) {
+           newvlserver = 1;    /* Doesn't support new interface */
+           goto tryold;
+       } else if (!code) {
+           newvlserver = 2;
+       }
+    }
+    return code;
+}
+
+VLDB_GetEntryByID(volid, voltype, entryp)
+     afs_int32 volid, voltype;
+     struct nvldbentry *entryp;
+{
+    struct vldbentry oentry;
+    register int code;
+
+    if (newvlserver == 1) {
+      tryold:
+       code =
+           ubik_Call(VL_GetEntryByID, cstruct, 0, volid, voltype, &oentry);
+       if (!code)
+           ovlentry_to_nvlentry(&oentry, entryp);
+       return code;
+    }
+    code = ubik_Call(VL_GetEntryByIDN, cstruct, 0, volid, voltype, entryp);
+    if (!newvlserver) {
+       if (code == RXGEN_OPCODE) {
+           newvlserver = 1;    /* Doesn't support new interface */
+           goto tryold;
+       } else if (!code) {
+           newvlserver = 2;
+       }
+    }
+    return code;
+}
+
+VLDB_GetEntryByName(namep, entryp)
+     char *namep;
+     struct nvldbentry *entryp;
+{
+    struct vldbentry oentry;
+    register int code;
+
+    if (newvlserver == 1) {
+      tryold:
+       code = ubik_Call(VL_GetEntryByNameO, cstruct, 0, namep, &oentry);
+       if (!code)
+           ovlentry_to_nvlentry(&oentry, entryp);
+       return code;
+    }
+    code = ubik_Call(VL_GetEntryByNameN, cstruct, 0, namep, entryp);
+    if (!newvlserver) {
+       if (code == RXGEN_OPCODE) {
+           newvlserver = 1;    /* Doesn't support new interface */
+           goto tryold;
+       } else if (!code) {
+           newvlserver = 2;
+       }
+    }
+    return code;
+}
+
+VLDB_ReplaceEntry(volid, voltype, entryp, releasetype)
+     afs_int32 volid, voltype, releasetype;
+     struct nvldbentry *entryp;
+{
+    struct vldbentry oentry;
+    register int code;
+
+    if (newvlserver == 1) {
+      tryold:
+       code = nvlentry_to_ovlentry(entryp, &oentry);
+       if (code)
+           return code;
+       code =
+           ubik_Call(VL_ReplaceEntry, cstruct, 0, volid, voltype, &oentry,
+                     releasetype);
+       return code;
+    }
+    code =
+       ubik_Call(VL_ReplaceEntryN, cstruct, 0, volid, voltype, entryp,
+                 releasetype);
+    if (!newvlserver) {
+       if (code == RXGEN_OPCODE) {
+           newvlserver = 1;    /* Doesn't support new interface */
+           goto tryold;
+       } else if (!code) {
+           newvlserver = 2;
+       }
+    }
+    return code;
+}
+
+
+
+VLDB_ListAttributes(attrp, entriesp, blkentriesp)
+     VldbListByAttributes *attrp;
+     afs_int32 *entriesp;
+     nbulkentries *blkentriesp;
+{
+    bulkentries arrayEntries;
+    register int code, i;
+
+    if (newvlserver == 1) {
+      tryold:
+       memset(&arrayEntries, 0, sizeof(arrayEntries)); /*initialize to hint the stub  to alloc space */
+       code =
+           ubik_Call(VL_ListAttributes, cstruct, 0, attrp, entriesp,
+                     &arrayEntries);
+       if (!code) {
+           blkentriesp->nbulkentries_val =
+               (nvldbentry *) malloc(*entriesp * sizeof(struct nvldbentry));
+           for (i = 0; i < *entriesp; i++) {   /* process each entry */
+               ovlentry_to_nvlentry(&arrayEntries.bulkentries_val[i],
+                                    &blkentriesp->nbulkentries_val[i]);
+           }
+       }
+       if (arrayEntries.bulkentries_val)
+           free(arrayEntries.bulkentries_val);
+       return code;
+    }
+    code =
+       ubik_Call(VL_ListAttributesN, cstruct, 0, attrp, entriesp,
+                 blkentriesp);
+    if (!newvlserver) {
+       if (code == RXGEN_OPCODE) {
+           newvlserver = 1;    /* Doesn't support new interface */
+           goto tryold;
+       } else if (!code) {
+           newvlserver = 2;
+       }
+    }
+    return code;
+}
+
+VLDB_ListAttributesN2(attrp, name, thisindex, nentriesp, blkentriesp,
+                     nextindexp)
+     VldbListByAttributes *attrp;
+     char *name;
+     afs_int32 thisindex;
+     afs_int32 *nentriesp;
+     nbulkentries *blkentriesp;
+     afs_int32 *nextindexp;
+{
+    afs_int32 code;
+
+    code =
+       ubik_Call(VL_ListAttributesN2, cstruct, 0, attrp, (name ? name : ""),
+                 thisindex, nentriesp, blkentriesp, nextindexp);
+    return code;
+}
+
+
+static int vlserverv4 = -1;
+struct cacheips {
+    afs_int32 server;
+    afs_int32 count;
+    afs_uint32 addrs[16];
+};
+/*
+ * Increase cache size.  This avoids high CPU usage by the vlserver
+ * in environments where there are more than 16 fileservers in the
+ * cell.
+ */
+#define GETADDRUCACHESIZE             64
+struct cacheips cacheips[GETADDRUCACHESIZE];
+int cacheip_index = 0;
+
+VLDB_IsSameAddrs(serv1, serv2, errorp)
+     afs_int32 serv1, serv2, *errorp;
+{
+    register int code;
+    ListAddrByAttributes attrs;
+    bulkaddrs addrs;
+    afs_uint32 *addrp, nentries, unique, i, j, f1, f2;
+    afsUUID uuid;
+    static int initcache = 0;
+
+    *errorp = 0;
+
+    if (serv1 == serv2)
+       return 1;
+    if (vlserverv4 == 1) {
+       return 0;
+    }
+    if (!initcache) {
+       for (i = 0; i < GETADDRUCACHESIZE; i++) {
+           cacheips[i].server = cacheips[i].count = 0;
+       }
+       initcache = 1;
+    }
+
+    /* See if it's cached */
+    for (i = 0; i < GETADDRUCACHESIZE; i++) {
+       f1 = f2 = 0;
+       for (j = 0; j < cacheips[i].count; j++) {
+           if (serv1 == cacheips[i].addrs[j])
+               f1 = 1;
+           else if (serv2 == cacheips[i].addrs[j])
+               f2 = 1;
+
+           if (f1 && f2)
+               return 1;
+       }
+       if (f1 || f2)
+           return 0;
+       if (cacheips[i].server == serv1)
+           return 0;
+    }
+
+    memset(&attrs, 0, sizeof(attrs));
+    attrs.Mask = VLADDR_IPADDR;
+    attrs.ipaddr = serv1;
+    memset(&addrs, 0, sizeof(addrs));
+    memset(&uuid, 0, sizeof(uuid));
+    code =
+       ubik_Call(VL_GetAddrsU, cstruct, 0, &attrs, &uuid, &unique, &nentries,
+                 &addrs);
+    if (vlserverv4 == -1) {
+       if (code == RXGEN_OPCODE) {
+           vlserverv4 = 1;     /* Doesn't support new interface */
+           return 0;
+       } else if (!code) {
+           vlserverv4 = 2;
+       }
+    }
+    if (code == VL_NOENT)
+       return 0;
+    if (code) {
+       *errorp = code;
+       return 0;
+    }
+
+    code = 0;
+    if (nentries > GETADDRUCACHESIZE)
+       nentries = GETADDRUCACHESIZE;   /* safety check; should not happen */
+    if (++cacheip_index >= GETADDRUCACHESIZE)
+       cacheip_index = 0;
+    cacheips[cacheip_index].server = serv1;
+    cacheips[cacheip_index].count = nentries;
+    addrp = addrs.bulkaddrs_val;
+    for (i = 0; i < nentries; i++, addrp++) {
+       cacheips[cacheip_index].addrs[i] = *addrp;
+       if (serv2 == *addrp) {
+           code = 1;
+       }
+    }
+    return code;
+}
+
+
+#ifdef notdef
+afs_int32
+subik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
+          p11, p12, p13, p14, p15, p16)
+     register struct ubik_client *aclient;
+     int (*aproc) ();
+     afs_int32 aflags;
+     afs_int32 p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14,
+        p15, p16;
+{
+    struct vldbentry vldbentry;
+    register int code, (*nproc) ();
+
+    if (newvlserver == 1) {
+    }
+    code =
+       ubik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9,
+                 p10, p11, p12, p13, p14, p15, p16);
+    if (!newvlserver) {
+       if (code == RXGEN_OPCODE) {
+           newvlserver = 1;    /* Doesn't support new interface */
+       } else if (!code) {
+           newvlserver = 2;
+       }
+    }
+}
+#endif /* notdef */
+
+
+/*
+  Set encryption.  If 'cryptflag' is nonzero, encrpytion is turned on
+  for authenticated connections; if zero, encryption is turned off.
+  Calling this function always results in a level of at least rxkad_auth;
+  to get a rxkad_clear connection, simply don't call this.
+*/
+void
+vsu_SetCrypt(cryptflag)
+     int cryptflag;
+{
+    if (cryptflag) {
+       vsu_rxkad_level = rxkad_crypt;
+    } else {
+       vsu_rxkad_level = rxkad_auth;
+    }
+}
+
+
+/*
+  Get the appropriate type of ubik client structure out from the system.
+*/
+afs_int32
+vsu_ClientInit(noAuthFlag, confDir, cellName, sauth, uclientp, secproc)
+     int noAuthFlag;
+     int (*secproc) ();
+     char *cellName;
+     struct ubik_client **uclientp;
+     char *confDir;
+     afs_int32 sauth;
+{
+    afs_int32 code, scIndex, i;
+    struct afsconf_cell info;
+    struct afsconf_dir *tdir;
+    struct ktc_principal sname;
+    struct ktc_token ttoken;
+    struct rx_securityClass *sc;
+    static struct rx_connection *serverconns[VLDB_MAXSERVERS];
+    char cellstr[64];
+
+
+    code = rx_Init(0);
+    if (code) {
+       fprintf(STDERR, "vsu_ClientInit: could not initialize rx.\n");
+       return code;
+    }
+    rx_SetRxDeadTime(90);
+
+    if (sauth) {               /* -localauth */
+       tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
+       if (!tdir) {
+           fprintf(STDERR,
+                   "vsu_ClientInit: Could not process files in configuration directory (%s).\n",
+                   AFSDIR_SERVER_ETC_DIRPATH);
+           return -1;
+       }
+       code = afsconf_ClientAuth(tdir, &sc, &scIndex); /* sets sc,scIndex */
+       if (code) {
+           fprintf(STDERR,
+                   "vsu_ClientInit: Could not get security object for -localAuth\n");
+           return -1;
+       }
+       code =
+           afsconf_GetCellInfo(tdir, tdir->cellName, AFSCONF_VLDBSERVICE,
+                               &info);
+       if (code) {
+           fprintf(STDERR,
+                   "vsu_ClientInit: can't find cell %s's hosts in %s/%s\n",
+                   cellName, AFSDIR_SERVER_ETC_DIRPATH,
+                   AFSDIR_CELLSERVDB_FILE);
+           exit(1);
+       }
+    } else {                   /* not -localauth */
+       tdir = afsconf_Open(confDir);
+       if (!tdir) {
+           fprintf(STDERR,
+                   "vsu_ClientInit: Could not process files in configuration directory (%s).\n",
+                   confDir);
+           return -1;
+       }
+
+       if (!cellName) {
+           code = afsconf_GetLocalCell(tdir, cellstr, sizeof(cellstr));
+           if (code) {
+               fprintf(STDERR,
+                       "vsu_ClientInit: can't get local cellname, check %s/%s\n",
+                       confDir, AFSDIR_THISCELL_FILE);
+               exit(1);
+           }
+           cellName = cellstr;
+       }
+
+       code =
+           afsconf_GetCellInfo(tdir, cellName, AFSCONF_VLDBSERVICE, &info);
+       if (code) {
+           fprintf(STDERR,
+                   "vsu_ClientInit: can't find cell %s's hosts in %s/%s\n",
+                   cellName, confDir, AFSDIR_CELLSERVDB_FILE);
+           exit(1);
+       }
+       if (noAuthFlag)         /* -noauth */
+           scIndex = 0;
+       else {                  /* not -noauth */
+           strcpy(sname.cell, info.name);
+           sname.instance[0] = 0;
+           strcpy(sname.name, "afs");
+           code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
+           if (code) {         /* did not get ticket */
+               fprintf(STDERR,
+                       "vsu_ClientInit: Could not get afs tokens, running unauthenticated.\n");
+               scIndex = 0;
+           } else {            /* got a ticket */
+               scIndex = 2;
+               if ((ttoken.kvno < 0) || (ttoken.kvno > 255)) {
+                   fprintf(STDERR,
+                           "vsu_ClientInit: funny kvno (%d) in ticket, proceeding\n",
+                           ttoken.kvno);
+               }
+           }
+       }
+
+       switch (scIndex) {
+       case 0:
+           sc = rxnull_NewClientSecurityObject();
+           break;
+       case 2:
+           sc = rxkad_NewClientSecurityObject(vsu_rxkad_level,
+                                              &ttoken.sessionKey,
+                                              ttoken.kvno, ttoken.ticketLen,
+                                              ttoken.ticket);
+           break;
+       default:
+           fprintf(STDERR, "vsu_ClientInit: unsupported security index %d\n",
+                   scIndex);
+           exit(1);
+           break;
+       }
+    }
+
+    afsconf_Close(tdir);
+
+    if (secproc)               /* tell UV module about default authentication */
+       (*secproc) (sc, scIndex);
+    if (info.numServers > VLDB_MAXSERVERS) {
+       fprintf(STDERR,
+               "vsu_ClientInit: info.numServers=%d (> VLDB_MAXSERVERS=%d)\n",
+               info.numServers, VLDB_MAXSERVERS);
+       exit(1);
+    }
+    for (i = 0; i < info.numServers; i++) {
+       serverconns[i] =
+           rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
+                            info.hostAddr[i].sin_port, USER_SERVICE_ID, sc,
+                            scIndex);
+    }
+    *uclientp = 0;
+    code = ubik_ClientInit(serverconns, uclientp);
+    if (code) {
+       fprintf(STDERR, "vsu_ClientInit: ubik client init failed.\n");
+       return code;
+    }
+    return 0;
+}
+
+
+/*extract the name of volume <name> without readonly or backup suffixes
+ * and return the result as <rname>.
+ */
+int
+vsu_ExtractName(rname, name)
+     char rname[], name[];
+{
+    char sname[VOLSER_OLDMAXVOLNAME + 1];
+    int total;
+
+    strncpy(sname, name, sizeof(sname));
+    sname[sizeof(sname) - 1] = '\0';
+    total = strlen(sname);
+    if (!strcmp(&sname[total - 9], ".readonly")) {
+       /*discard the last 8 chars */
+       sname[total - 9] = '\0';
+       strcpy(rname, sname);
+       return 0;
+    } else if (!strcmp(&sname[total - 7], ".backup")) {
+       /*discard last 6 chars */
+       sname[total - 7] = '\0';
+       strcpy(rname, sname);
+       return 0;
+    } else {
+       strncpy(rname, name, VOLSER_OLDMAXVOLNAME);
+       rname[VOLSER_OLDMAXVOLNAME] = '\0';
+       return -1;
+    }
+}
+
+
+/* returns 0 if failed */
+afs_uint32
+vsu_GetVolumeID(astring, acstruct, errp)
+     struct ubik_client *acstruct;
+     afs_int32 *errp;
+     char *astring;
+{
+    afs_uint32 tc, value;
+
+    char *str, *ptr, volname[VOLSER_OLDMAXVOLNAME + 1];
+    int tryname, curval;
+    struct nvldbentry entry;
+    afs_int32 vcode = 0;
+    int total;
+
+    *errp = 0;
+    total = strlen(astring);
+    str = astring;
+    ptr = astring;
+    tryname = 0;
+    while ((curval = *str++)) {
+       if (curval < '0' || curval > '9')
+           tryname = 1;
+    }
+
+    if (tryname) {
+       vsu_ExtractName(volname, astring);
+       vcode = VLDB_GetEntryByName(volname, &entry);
+       if (!vcode) {
+           if (!strcmp(&astring[total - 9], ".readonly"))
+               return entry.volumeId[ROVOL];
+           else if ((!strcmp(&astring[total - 7], ".backup")))
+               return entry.volumeId[BACKVOL];
+           else
+               return (entry.volumeId[RWVOL]);
+       } else {
+           *errp = vcode;
+           return 0;           /* can't find volume */
+       }
+    }
+
+    value = 0;
+    while ((tc = *astring++)) {
+       if (tc & 0x80) {
+           if (!tryname)
+               fprintf(STDERR, "goofed in volid \n");
+           else {
+               fprintf(STDERR, "Could not get entry from vldb for %s\n",
+                       ptr);
+               PrintError("", vcode);
+           }
+           *errp = EINVAL;
+           return 0;
+       }
+       if (tc < '0' || tc > '9') {
+           if (!tryname)
+               fprintf(STDERR,
+                       "internal error: out of range char in vol ID\n");
+           else {
+               fprintf(STDERR, "Could not get entry from vldb for %s\n",
+                       ptr);
+               PrintError("", vcode);
+           }
+           *errp = ERANGE;
+           return 0;
+       }
+       value *= 10;
+       value += (tc - '0');
+    }
+    return value;
+}