afsio is a command to pipe data into or out of afs files
authorHartmut Reuter <reuter@rzg.mpg.de>
Wed, 30 Sep 2009 08:06:14 +0000 (10:06 +0200)
committerDerrick Brashear <shadow|account-1000005@unknown>
Mon, 5 Oct 2009 09:14:34 +0000 (02:14 -0700)
afsio bypasses the cache manager to achieve higher throughput.
However, it uses the cache manager to stat or create files over the
AFS path.

Besides 'apropos' and 'help' there are 3 subcommands:

'write' pipes data into a new or empty AFS file

'append' pipes data at the end of an existing AFS file

'read' pipes data out of an AFS file

for 'write' there is an option '-md5' which calculates on the fly
the md5 chscksum and prints it to stdout. (Useful when you create
long time archives to keep it separately for later ...)

for 'write' there is also an option '-synthesize <size>' to do just
performance tests. It creates a file which contains at the
begin of each 4 KB block the offset printed in ascii.

for all subcommands exists a '-verbose' option which writes to
stderr data rate and timing information.

All three subcommands may also be used prefixed with 'fid' accepting
then a Fid instead of an AFS path. With the 'fid' prefix also a '-cell'
option is allowed.

Reviewed-on: http://gerrit.openafs.org/555
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: Derrick Brashear <shadow@dementia.org>

src/config/stds.h
src/util/afsutil_prototypes.h
src/util/volparse.c
src/venus/Makefile.in
src/venus/afsio.c [new file with mode: 0644]

index be65339..9087c71 100644 (file)
@@ -283,10 +283,12 @@ typedef struct afsUUID afsUUID;
  */
 #ifdef AFS_NT40_ENV
 #define AFS_INT64_FMT "I64d"
+#define AFS_UINT64_FMT "I64u"
 #define AFS_PTR_FMT   "Ip"
 #define AFS_SIZET_FMT "Iu"
 #else
 #define AFS_INT64_FMT "lld"
+#define AFS_UINT64_FMT "llu"
 #define AFS_PTR_FMT   "p"
 #define AFS_SIZET_FMT "u"
 #endif
index 9d0645d..a05a64e 100644 (file)
@@ -201,6 +201,8 @@ extern afs_int32 volutil_PartitionName2_r(afs_int32 part, char *tbuffer, size_t
 extern char *volutil_PartitionName(int avalue);
 extern afs_int32 util_GetInt32(register char *as, afs_int32 * aval);
 extern afs_uint32 util_GetUInt32(register char *as, afs_uint32 * aval);
+extern afs_int64 util_GetInt64(char *as, afs_int64 * aval);
+extern afs_uint64 util_GetUInt64(char *as, afs_uint64 * aval);
 extern afs_int32 util_GetHumanInt32(register char *as, afs_int32 * aval);
 
 /* winsock_nt.c */
index 6ba1892..cb96935 100644 (file)
@@ -341,3 +341,92 @@ util_GetHumanInt32(register char *as, afs_int32 * aval)
 
     return 0;
 }
+
+afs_int64
+util_GetInt64(char *as, afs_int64 * aval)
+{
+    afs_int64 total;
+    int tc;
+    int base;
+    int negative;
+
+    total = 0; /* initialize things */
+    negative = 0;
+
+    /* skip over leading spaces */
+    while ((tc = *as)) {
+       if (tc != ' ' && tc != '\t')
+           break;
+    }
+
+    /* compute sign */
+    if (*as == '-') {
+       negative = 1;
+       as++; /* skip over character */
+    }
+
+    /* compute the base */
+    if (*as == '0') {
+       as++;
+       if (*as == 'x' || *as == 'X') {
+           base = 16;
+           as++;
+       } else
+           base = 8;
+    } else
+       base = 10;
+
+    /* compute the # itself */
+    while ((tc = *as)) {
+       if (!ismeta(tc, base))
+           return -1;
+       total *= base;
+       total += getmeta(tc);
+       as++;
+    }
+
+    if (negative)
+       *aval = -total;
+    else
+       *aval = total;
+    return 0;
+}
+
+afs_uint64
+util_GetUInt64(char *as, afs_uint64 * aval)
+{
+    afs_uint64 total;
+    int tc;
+    int base;
+
+    total = 0; /* initialize things */
+
+    /* skip over leading spaces */
+    while ((tc = *as)) {
+       if (tc != ' ' && tc != '\t')
+           break;
+    }
+
+    /* compute the base */
+    if (*as == '0') {
+       as++;
+       if (*as == 'x' || *as == 'X') {
+           base = 16;
+           as++;
+       } else
+           base = 8;
+    } else
+       base = 10;
+
+    /* compute the # itself */
+    while ((tc = *as)) {
+       if (!ismeta(tc, base))
+           return -1;
+       total *= base;
+       total += getmeta(tc);
+       as++;
+    }
+
+    *aval = total;
+    return 0;
+}
index 0247e98..c571d31 100644 (file)
@@ -66,6 +66,11 @@ fs.o: fs.c ${INCLS} AFS_component_version_number.c
 fs: fs.o $(LIBS)
        ${CC} ${CFLAGS} -o fs fs.o ${TOP_LIBDIR}/libprot.a $(LIBS) ${XLIBS}
 
+afsio.o: afsio.c ${INCLS} AFS_component_version_number.c
+
+afsio: afsio.o $(LIBS)
+       ${CC} ${CFLAGS} -o afsio afsio.o ${TOP_LIBDIR}/libprot.a ${TOP_LIBDIR}/libafsint.a $(LIBS) ${XLIBS}
+
 livesys.o: livesys.c ${INCLS} AFS_component_version_number.c
 
 livesys: livesys.c $(LIBS)
diff --git a/src/venus/afsio.c b/src/venus/afsio.c
new file mode 100644 (file)
index 0000000..94bde8f
--- /dev/null
@@ -0,0 +1,1509 @@
+/*
+ * Copyright (c) 2007, Hartmut Reuter,
+ * RZG, Max-Planck-Institut f. Plasmaphysik.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in
+ *      the documentation and/or other materials provided with the
+ *      distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef AFS_NT40_ENV
+#include <fcntl.h>
+#else
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <fcntl.h>
+#endif
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <afs/vice.h>
+#include <afs/cmd.h>
+#include <afs/auth.h>
+#include <afs/cellconfig.h>
+#include <afs/afsutil.h>
+#include <rx/rx.h>
+#include <rx/xdr.h>
+#include <afs/venus.h>
+#include <afs/afscbint.h>
+#define FSINT_COMMON_XG 1
+#include <afs/afsint.h>
+#include <afs/vldbint.h>
+#include <afs/vlserver.h>
+#include <afs/volser.h>
+#include <afs/ptint.h>
+#include <afs/dir.h>
+#include <afs/nfs.h>
+#include <afs/ihandle.h>
+#include <afs/namei_ops.h>
+#include <afs/vnode.h>
+#include <afs/com_err.h>
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+#include <sys/time.h>
+#include <netdb.h>
+#ifdef AFS_DARWIN_ENV
+#include <sys/malloc.h>
+#else
+#include <malloc.h>
+#endif
+#include <afs/errors.h>
+#include <afs/sys_prototypes.h>
+#include <des_prototypes.h>
+#include <rx_prototypes.h>
+#include "../rxkad/md5.h"
+#define        MAXHOSTS 13
+#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 */
+#ifdef AFS_PTHREAD_ENV
+#include <assert.h>
+pthread_key_t uclient_key;
+#endif
+
+int readFile(struct cmd_syndesc *as, void *);
+int writeFile(struct cmd_syndesc *as, void *);
+struct rx_connection *FindRXConnection(afs_uint32 host, u_short port, u_short service, struct rx_securityClass *securityObject, int serviceSecurityIndex);
+struct cellLookup * FindCell(char *cellName);
+
+char pnp[255];
+int rxInitDone = 0;
+static int verbose = 0;                /* Set if -verbose option given */
+static int CBServiceNeeded = 0;
+static struct timeval starttime, opentime, readtime, writetime;
+afs_uint64 xfered=0, oldxfered=0;
+static struct timeval now;
+struct timezone Timezone;
+static float seconds, datarate, oldseconds;
+extern int rxInitDone;
+afs_uint64 transid = 0;
+afs_uint32 expires = 0;
+afs_uint32 server_List[MAXHOSTSPERCELL];
+char tmpstr[1024];
+char tmpstr2[1024];
+static struct ubik_client *uclient;
+#define BUFFLEN 65536
+#define WRITEBUFFLEN 1024*1024*64
+
+afsUUID uuid;
+MD5_CTX md5;
+int md5sum = 0;
+
+struct wbuf {
+    struct wbuf *next;
+    afs_uint32 offset;         /* offset inside the buffer */
+    afs_uint32 buflen;         /* total length == BUFFLEN */
+    afs_uint32 used;           /* bytes used inside buffer */
+    char buf[BUFFLEN];
+};
+
+struct connectionLookup {
+    afs_uint32 host;
+    u_short port;
+    struct rx_connection *conn;
+};
+
+struct cellLookup {
+    struct cellLookup *next;
+    struct afsconf_cell info;
+    struct rx_securityClass *sc[3];
+    afs_int32 scIndex;
+};
+
+struct dirLookup {
+    struct dirLookup *next;
+    struct dirLookup *prev;
+    afs_int32 host;
+    struct cellLookup *cell;
+    AFSFid fid;
+    char name[VL_MAXNAMELEN];
+};
+
+struct cellLookup *Cells = 0;
+struct dirLookup  *Dirs = 0;
+char cellFname[256];
+
+#define MAX_HOSTS 256
+static struct connectionLookup ConnLookup[MAX_HOSTS];
+static int ConnLookupInitialized = 0;
+
+struct FsCmdInputs PioctlInputs;
+struct FsCmdOutputs PioctlOutputs;
+
+void
+printDatarate(void)
+{
+    seconds = now.tv_sec + now.tv_usec *.000001
+       -opentime.tv_sec - opentime.tv_usec *.000001;
+    if ((seconds - oldseconds) > 30.) {
+       afs_int64 tmp;
+       tmp = xfered - oldxfered;
+        datarate = ((afs_uint32) (tmp >> 20)) / (seconds - oldseconds);
+        fprintf(stderr,"%llu MB transferred, present date rate = %.0f MB/sec.\n",
+               xfered >> 20, datarate);
+       oldxfered = xfered;
+       oldseconds = seconds;
+    }
+}
+
+void
+SetCellFname(char *name)
+{
+    struct afsconf_dir *tdir;
+
+    strcpy((char *) &cellFname,"/afs/");
+    if (name)
+       strcat((char *) &cellFname, name);
+    else {
+       tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
+       afsconf_GetLocalCell(tdir, &cellFname[5], MAXCELLCHARS);
+    }
+}
+
+afs_int32
+main (int argc, char **argv)
+{
+    afs_int32 code;
+    struct cmd_syndesc *ts;
+
+    strcpy(pnp, argv[0]);
+
+#ifdef AFS_PTHREAD_ENV
+    assert(pthread_key_create(&uclient_key, NULL) == 0);
+#endif
+    ts = cmd_CreateSyntax("read", readFile, CMD_REQUIRED,
+                         "read a file from AFS");
+    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "AFS-filename");
+    cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *) 0);
+    cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
+
+    ts = cmd_CreateSyntax("fidread", readFile, CMD_REQUIRED,
+                         "read on a non AFS-client a file from AFS");
+    cmd_IsAdministratorCommand(ts);
+    cmd_AddParm(ts, "-fid", CMD_SINGLE, CMD_REQUIRED, "volume.vnode.uniquifier");
+    cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *) 0);
+    cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
+
+    ts = cmd_CreateSyntax("write", writeFile, CMD_REQUIRED,
+                         "write a file into AFS");
+    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "AFS-filename");
+    cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *) 0);
+    cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
+    cmd_AddParm(ts, "-synthesize", CMD_SINGLE, CMD_OPTIONAL, "create data pattern of specified length instead reading from stdin");
+
+    ts = cmd_CreateSyntax("fidwrite", writeFile, CMD_REQUIRED,
+                         "write a file into AFS");
+    cmd_IsAdministratorCommand(ts);
+    cmd_AddParm(ts, "-vnode", CMD_SINGLE, CMD_REQUIRED, "volume.vnode.uniquifier");
+    cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *) 0);
+    cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
+
+    ts = cmd_CreateSyntax("append", writeFile, CMD_REQUIRED,
+                         "append to a file in AFS");
+    cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "AFS-filename");
+    cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *) 0);
+
+    ts = cmd_CreateSyntax("fidappend", writeFile, CMD_REQUIRED,
+                         "append to a file in AFS");
+    cmd_IsAdministratorCommand(ts);
+    cmd_AddParm(ts, "-vnode", CMD_SINGLE, CMD_REQUIRED, "volume.vnode.uniquifier");
+    cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
+    cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *) 0);
+
+    code = cmd_Dispatch(argc, argv);
+    exit (0);
+}
+
+AFS_UNUSED
+afs_int32
+HandleLocalAuth(struct rx_securityClass **sc[3], afs_int32 *scIndex)
+{
+    static struct afsconf_dir *tdir = NULL;
+    struct ktc_principal sname;
+    struct ktc_token ttoken;
+    int kvno;
+    struct ktc_encryptionKey key;
+    afs_uint32 host = 0;
+    char *cell;
+    afs_int32 code;
+
+    tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
+    if (!tdir) {
+        fprintf(stderr,"Could not open configuration directory: %s.\n",
+               AFSDIR_SERVER_ETC_DIRPATH);
+        return -1;
+    }
+    cell = tdir->cellName;
+    strcpy(sname.cell, cell);
+    sname.instance[0] = 0;
+    strcpy(sname.name, "afs");
+    code=afsconf_GetLatestKey(tdir, &kvno, &key);
+    if (code) {
+        fprintf(stderr,"afsconf_GetLatestKey returned %d\n", code);
+        return -1;
+    }
+    ttoken.kvno = kvno;
+    des_init_random_number_generator(ktc_to_cblock(&key));
+    code = des_random_key(ktc_to_cblock(&ttoken.sessionKey));
+    if (code) {
+        fprintf(stderr,"des_random_key returned %d\n", code);
+        return -1;
+    }
+    ttoken.ticketLen = MAXKTCTICKETLEN;
+    code = tkt_MakeTicket(ttoken.ticket, &ttoken.ticketLen, &key,
+                         AUTH_SUPERUSER, "", sname.cell,
+                         0, 0xffffffff,
+                         &ttoken.sessionKey, host,
+                         sname.name, sname.instance);
+    if (code)
+        *scIndex = 0;
+    else {
+       *scIndex = 2;
+        *sc[2] = (struct rx_securityClass *)
+           rxkad_NewClientSecurityObject(rxkad_clear,
+                                         &ttoken.sessionKey, ttoken.kvno,
+                                         ttoken.ticketLen, ttoken.ticket);
+    }
+    if (*scIndex == 0)
+       *sc[0] = (struct rx_securityClass *) rxnull_NewClientSecurityObject();
+    return 0;
+}
+
+afs_int32
+AFS_Lookup(struct rx_connection *conn, AFSFid *dirfid, char *name,
+          AFSFid *outfid, AFSFetchStatus *outstatus, AFSFetchStatus
+          *dirstatus, AFSCallBack *callback, AFSVolSync *sync)
+{
+    afs_int32 code = VBUSY;
+    while (code == VBUSY) {
+       code = RXAFS_Lookup(conn, dirfid, name, outfid, outstatus, dirstatus,
+                           callback, sync);
+       if (code == VBUSY) {
+           fprintf(stderr, "waiting for busy AFS volume %u.\n",
+                   dirfid->Volume);
+#ifdef AFS_PTHREAD_ENV
+           sleep(10);
+#else
+           IOMGR_Sleep(10);
+#endif
+       }
+    }
+    return code;
+}
+
+afs_int32
+AFS_FetchStatus(struct rx_connection *conn, AFSFid *fid, AFSFetchStatus
+               *Status, AFSCallBack *callback, AFSVolSync *sync)
+{
+    afs_int32 code = VBUSY;
+
+    while (code == VBUSY) {
+        code = RXAFS_FetchStatus(conn, fid, Status, callback, sync);
+       if (code == VBUSY) {
+           fprintf(stderr, "waiting for busy AFS volume %u.\n",
+                   fid->Volume);
+#ifdef AFS_PTHREAD_ENV
+           sleep(10);
+#else
+           IOMGR_Sleep(10);
+#endif
+       }
+    }
+    return code;
+}
+
+afs_int32
+StartAFS_FetchData(struct rx_call *call, AFSFid *fid, afs_int32 pos,
+                  afs_int32 len)
+{
+    afs_int32 code = VBUSY;
+    while (code == VBUSY) {
+       code = StartRXAFS_FetchData (call, fid, pos, len);
+       if (code == VBUSY) {
+           fprintf(stderr, "waiting for busy AFS volume %u.\n",
+                   fid->Volume);
+#ifdef AFS_PTHREAD_ENV
+           sleep(10);
+#else
+           IOMGR_Sleep(10);
+#endif
+       }
+    }
+    return code;
+}
+
+afs_int32
+StartAFS_FetchData64(struct rx_call *call, AFSFid *fid, afs_int64 pos,
+                     afs_int64 len)
+{
+    afs_int32 code = VBUSY;
+    while (code == VBUSY) {
+       code = StartRXAFS_FetchData64 (call, fid, pos, len);
+       if (code == VBUSY) {
+           fprintf(stderr, "waiting for busy AFS volume %u.\n",
+                   fid->Volume);
+#ifdef AFS_PTHREAD_ENV
+           sleep(10);
+#else
+           IOMGR_Sleep(10);
+#endif
+       }
+    }
+    return code;
+}
+
+afs_int32
+StartAFS_StoreData(struct rx_call *call, AFSFid *fid, AFSStoreStatus *status,
+                  afs_int32 pos, afs_int32 len, afs_int32 len2)
+{
+    afs_int32 code = VBUSY;
+    while (code == VBUSY) {
+       code = StartRXAFS_StoreData (call, fid, status, pos, len, len2);
+       if (code == VBUSY) {
+           fprintf(stderr, "waiting for busy AFS volume %u.\n",
+                   fid->Volume);
+#ifdef AFS_PTHREAD_ENV
+           sleep(10);
+#else
+           IOMGR_Sleep(10);
+#endif
+       }
+    }
+    return code;
+}
+
+afs_uint32
+StartAFS_StoreData64(struct rx_call *call, AFSFid *fid, AFSStoreStatus *status,
+                    afs_int64 pos, afs_int64 len, afs_int64 len2)
+{
+    afs_int32 code = VBUSY;
+    while (code == VBUSY) {
+       code = StartRXAFS_StoreData64 (call, fid, status, pos, len, len2);
+       if (code == VBUSY) {
+           fprintf(stderr, "waiting for busy AFS volume %u.\n",
+                   fid->Volume);
+#ifdef AFS_PTHREAD_ENV
+           sleep(10);
+#else
+           IOMGR_Sleep(10);
+#endif
+       }
+    }
+    return code;
+}
+
+afs_int32
+SRXAFSCB_CallBack(struct rx_call *rxcall, AFSCBFids *Fids_Array,
+                 AFSCBs *CallBack_Array)
+{
+    return 0;
+}
+
+afs_int32
+SRXAFSCB_InitCallBackState(struct rx_call *rxcall)
+{
+    return 0;
+}
+
+afs_int32
+SRXAFSCB_Probe(struct rx_call *rxcall)
+{
+    return 0;
+}
+
+afs_int32
+SRXAFSCB_GetCE(struct rx_call *rxcall,
+              afs_int32 index,
+              AFSDBCacheEntry * ce)
+{
+    return(0);
+}
+
+afs_int32
+SRXAFSCB_GetLock(struct rx_call *rxcall,
+                afs_int32 index,
+                AFSDBLock * lock)
+{
+    return(0);
+}
+
+afs_int32
+SRXAFSCB_XStatsVersion(struct rx_call *rxcall,
+                      afs_int32 * versionNumberP)
+{
+    return(0);
+}
+
+afs_int32
+SRXAFSCB_GetXStats(struct rx_call *rxcall,
+                  afs_int32 clientVersionNumber,
+                  afs_int32 collectionNumber,
+                  afs_int32 * srvVersionNumberP,
+                  afs_int32 * timeP,
+                  AFSCB_CollData * dataP)
+{
+    return(0);
+}
+
+afs_int32
+SRXAFSCB_ProbeUuid(struct rx_call *a_call, afsUUID *a_uuid)
+{
+    return(0);
+}
+
+
+afs_int32
+SRXAFSCB_WhoAreYou(struct rx_call *a_call, struct interfaceAddr *addr)
+{
+    int code = 0;
+
+    addr->numberOfInterfaces = 0;
+    addr->uuid = uuid;
+#ifdef notdef
+    /* return all network interface addresses */
+    addr->numberOfInterfaces = afs_cb_interface.numberOfInterfaces;
+    for ( i=0; i < afs_cb_interface.numberOfInterfaces; i++) {
+        addr->addr_in[i] = ntohl(afs_cb_interface.addr_in[i]);
+        addr->subnetmask[i] = ntohl(afs_cb_interface.subnetmask[i]);
+        addr->mtu[i] = ntohl(afs_cb_interface.mtu[i]);
+    }
+#endif
+
+    return code;
+}
+
+afs_int32
+SRXAFSCB_InitCallBackState2(struct rx_call *a_call, struct interfaceAddr *
+                           addr)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_InitCallBackState3(struct rx_call *a_call, afsUUID *a_uuid)
+{
+    return 0;
+}
+
+afs_int32
+SRXAFSCB_GetCacheConfig(struct rx_call *a_call, afs_uint32 callerVersion,
+                       afs_uint32 *serverVersion, afs_uint32 *configCount,
+                       cacheConfig *config)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_GetLocalCell(struct rx_call *a_call, char **a_name)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_GetCellServDB(struct rx_call *a_call, afs_int32 a_index,
+                      char **a_name, serverList *a_hosts)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_GetServerPrefs(struct rx_call *a_call, afs_int32 a_index,
+                       afs_int32 *a_srvr_addr, afs_int32 *a_srvr_rank)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_TellMeAboutYourself(struct rx_call *a_call, struct interfaceAddr *
+                            addr, Capabilities *capabilities)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_GetCellByNum(struct rx_call *a_call, afs_int32 a_cellnum,
+                     char **a_name, serverList *a_hosts)
+{
+    return RXGEN_OPCODE;
+}
+
+afs_int32
+SRXAFSCB_GetCE64(struct rx_call *a_call, afs_int32 a_index,
+                struct AFSDBCacheEntry64 *a_result)
+{
+    return RXGEN_OPCODE;
+}
+
+void *
+InitializeCBService_LWP(void *unused)
+{
+    struct rx_securityClass *CBsecobj;
+    struct rx_service *CBService;
+
+    afs_uuid_create(&uuid);
+
+    CBsecobj = (struct rx_securityClass *)rxnull_NewServerSecurityObject();
+    if (!CBsecobj) {
+       fprintf(stderr,"rxnull_NewServerSecurityObject failed for callback service.\n");
+       exit(1);
+    }
+    CBService = rx_NewService(0, 1, "afs", &CBsecobj, 1,
+                             RXAFSCB_ExecuteRequest);
+    if (!CBService) {
+       fprintf(stderr,"rx_NewService failed for callback service.\n");
+       exit(1);
+    }
+    rx_StartServer(1);
+    return 0;
+}
+
+
+int
+InitializeCBService(void)
+{
+#define RESTOOL_CBPORT 7102
+#define MAX_PORT_TRIES 1000
+#define LWP_STACK_SIZE (16 * 1024)
+    afs_int32 code;
+#ifdef AFS_PTHREAD_ENV
+    pthread_t CBservicePid, parentPid;
+    pthread_attr_t tattr;
+#else
+    PROCESS CBServiceLWP_ID, parentPid;
+#endif
+    int InitialCBPort;
+    int CBPort;
+
+#ifndef NO_AFS_CLIENT
+    if (!CBServiceNeeded)
+        return 0;
+#endif
+#ifndef AFS_PTHREAD_ENV
+    code = LWP_InitializeProcessSupport(LWP_MAX_PRIORITY - 2, &parentPid);
+    if (code != LWP_SUCCESS) {
+       fprintf(stderr,"Unable to initialize LWP support, code %d\n",
+               code);
+       exit(1);
+    }
+#endif
+
+#if defined(AFS_AIX_ENV) || defined(AFS_SUN_ENV) || defined(AFS_DEC_ENV) || defined(AFS_OSF_ENV) || defined(AFS_SGI_ENV)
+    srandom(getpid());
+    InitialCBPort = RESTOOL_CBPORT + random() % 1000;
+#else /* AFS_AIX_ENV || AFS_SUN_ENV || AFS_OSF_ENV || AFS_SGI_ENV */
+#if defined(AFS_HPUX_ENV)
+    srand48(getpid());
+    InitialCBPort = RESTOOL_CBPORT + lrand48() % 1000;
+#else /* AFS_HPUX_ENV */
+    srand(getpid());
+    InitialCBPort = RESTOOL_CBPORT + rand() % 1000;
+#endif /* AFS_HPUX_ENV */
+#endif /* AFS_AIX_ENV || AFS_SUN_ENV || AFS_OSF_ENV || AFS_SGI_ENV */
+
+    CBPort = InitialCBPort;
+    do {
+       code = rx_Init(htons(CBPort));
+       if (code) {
+           if ((code == RX_ADDRINUSE) &&
+               (CBPort < MAX_PORT_TRIES + InitialCBPort)) {
+               CBPort++;
+           } else if (CBPort < MAX_PORT_TRIES + InitialCBPort) {
+               fprintf(stderr, "rx_Init didn't succeed for callback service."
+                       " Tried port numbers %d through %d\n",
+                       InitialCBPort, CBPort);
+               exit(1);
+           } else {
+               fprintf(stderr,"Couldn't initialize callback service "
+                       "because too many users are running this program. "
+                       "Try again later.\n");
+               exit(1);
+           }
+       }
+    } while(code);
+#ifdef AFS_PTHREAD_ENV
+    assert(pthread_attr_init(&tattr) == 0);
+    assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0);
+    assert(pthread_create(
+              &CBservicePid, &tattr, InitializeCBService_LWP, 0)
+          == 0);
+#else
+    code = LWP_CreateProcess(InitializeCBService_LWP, LWP_STACK_SIZE,
+                            LWP_MAX_PRIORITY - 2, (int *) 0, "CBService",
+                            &CBServiceLWP_ID);
+    if (code != LWP_SUCCESS) {
+       fprintf(stderr,"Unable to create the callback service LWP, code %d\n",
+               code);
+       exit(1);
+    }
+#endif
+    return 0;
+}
+
+int
+ScanVnode(char *fname, char *cell)
+{
+    afs_int32 i, code = 0;
+
+    SetCellFname(cell);
+    i = sscanf(fname, "%u.%u.%u",
+              &PioctlInputs.fid.Volume,
+              &PioctlInputs.fid.Vnode,
+              &PioctlInputs.fid.Unique);
+    if (i != 3) {
+       PioctlInputs.fid.Volume = 0;
+       PioctlInputs.fid.Vnode = 0;
+       PioctlInputs.fid.Unique = 0;
+        fprintf(stderr,"fs: invalid vnode triple: %s\n", fname);
+        code = EINVAL;
+    }
+    /*
+     * The following is used to handle the case of unknown uniquifier. We
+     * just need a valid reference to the volume to direct the RPC to the
+     * right fileserver. Therefore we take the root directory of the volume.
+     */
+    if (PioctlInputs.fid.Unique == 0) {
+       PioctlInputs.int32s[0] = PioctlInputs.fid.Vnode;
+       PioctlInputs.fid.Vnode = 1;
+       PioctlInputs.fid.Unique = 1;
+    }
+    return code;
+}
+
+int
+VLDBInit(int noAuthFlag, struct afsconf_cell *info)
+{
+    afs_int32 code;
+
+    code = ugen_ClientInit(noAuthFlag, (char *) AFSDIR_CLIENT_ETC_DIRPATH,
+                           info->name, 0, &uclient,
+                           NULL, pnp, rxkad_clear,
+                           VLDB_MAXSERVERS, AFSCONF_VLDBSERVICE, 50,
+                           0, 0, USER_SERVICE_ID);
+    rxInitDone = 1;
+    return code;
+}
+
+afs_int32
+get_vnode_hosts(char *fname, char **cellp, afs_int32 *hosts, AFSFid *Fid,
+               int onlyRW)
+{
+    struct afsconf_dir *tdir;
+    struct vldbentry vldbEntry;
+    afs_int32 i, j, code, *h, len;
+    struct afsconf_cell info;
+    afs_int32 mask;
+
+    tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
+    if (!tdir) {
+        fprintf(stderr,"Could not process files in configuration directory "
+               "(%s).\n",AFSDIR_CLIENT_ETC_DIRPATH);
+        return -1;
+    }
+    if (!*cellp) {
+       len = MAXCELLCHARS;
+       *cellp = (char *) malloc(MAXCELLCHARS);
+       code = afsconf_GetLocalCell(tdir, *cellp, len);
+       if (code) return code;
+    }
+    code = afsconf_GetCellInfo(tdir, *cellp, AFSCONF_VLDBSERVICE, &info);
+    if (code) {
+        fprintf(stderr,"fs: cell %s not in %s/CellServDB\n",
+               *cellp, AFSDIR_CLIENT_ETC_DIRPATH);
+        return code;
+    }
+
+    i = sscanf(fname, "%u.%u.%u", &Fid->Volume, &Fid->Vnode, &Fid->Unique);
+    if (i != 3) {
+       fprintf(stderr,"fs: invalid vnode triple: %s\n", fname);
+       return 1;
+    }
+    code = VLDBInit(1, &info);
+    if (code == 0) {
+        code = ubik_VL_GetEntryByID(uclient, 0, Fid->Volume,
+                                   -1, &vldbEntry);
+        if (code == VL_NOENT)
+            fprintf(stderr,"fs: volume %u does not exist in this cell.\n",
+                   Fid->Volume);
+        if (code) return code;
+    }
+    h = hosts;
+    mask = VLSF_RWVOL;
+    if (!onlyRW) mask |= VLSF_RWVOL;
+    for (i=0, j=0; j<vldbEntry.nServers; j++) {
+        if (vldbEntry.serverFlags[j] & mask) {
+            *h++ = ntohl(vldbEntry.serverNumber[j]);
+            i++;
+        }
+    }
+    for (; i<MAXHOSTS; i++) *h++ = 0;
+    return 0;
+}
+
+/* get_file_cell()
+ *     Determine which AFS cell file 'fn' lives in, the list of servers that
+ *     offer it, and the FID.
+ */
+afs_int32
+get_file_cell(char *fn, char **cellp, afs_int32 hosts[MAXHOSTS], AFSFid *Fid,
+             struct AFSFetchStatus *Status, afs_int32 create)
+{
+    afs_int32 code;
+    char buf[256];
+    struct ViceIoctl status;
+    int j;
+    afs_int32 *Tmpafs_int32;
+
+    memset((char *) Status, 0, sizeof(struct AFSFetchStatus));
+    memset(buf, 0, sizeof(buf));
+    status.in_size = 0;
+    status.out_size = sizeof(buf);
+    status.in = buf;
+    status.out = buf;
+    errno = 0;
+    code = pioctl(fn, VIOC_FILE_CELL_NAME, &status, 0);
+    if (code && create) {
+       char *c;
+       int fd;
+       strcpy(buf,fn);
+       if ((c = strrchr(buf,'/'))) {
+           *c = 0;
+           code = pioctl(buf,VIOC_FILE_CELL_NAME, &status, 0);
+           if (!code) {
+               fd = open(fn, O_CREAT, 0644);
+               close(fd);
+           }
+           code = pioctl(fn, VIOC_FILE_CELL_NAME, &status, 0);
+       }
+    }
+    if (code) {
+       fprintf(stderr, "Unable to determine cell for %s\n", fn);
+       if (errno) {
+           perror(fn);
+           if (errno == EINVAL)
+               fprintf(stderr, "(File might not be in AFS)\n");
+       } else
+           afs_com_err(pnp, code, (char *) 0);
+    } else {
+       *cellp = (char *) malloc(strlen(buf)+1);
+       strcpy(*cellp, buf);
+       SetCellFname(*cellp);
+       memset(buf, 0, sizeof(buf));
+       status.in = 0;
+       status.in_size = 0;
+       status.out = buf;
+       status.out_size = sizeof(buf);
+       code = pioctl(fn, VIOCWHEREIS, &status, 0);
+       if (code) {
+           fprintf(stderr, "Unable to determine fileservers for %s\n", fn);
+           if (errno) {
+               perror(fn);
+           }
+           else
+               afs_com_err(pnp, code, (char *) 0);
+       } else {
+           Tmpafs_int32 = (afs_int32 *)buf;
+           for (j=0;j<MAXHOSTS;++j) {
+               hosts[j] = Tmpafs_int32[j];
+               if (!Tmpafs_int32[j])
+                   break;
+           }
+       }
+       memset(buf, 0, sizeof(buf));
+       status.in_size = 0;
+       status.out_size = sizeof(buf);
+       status.in = 0;
+       status.out = buf;
+       code = pioctl(fn, VIOCGETFID, &status, 0);
+       if (code) {
+           fprintf(stderr, "Unable to determine FID for %s\n", fn);
+           if (errno) {
+               perror(fn);
+           } else {
+               afs_com_err(pnp, code, (char *) 0);
+           }
+       } else {
+           afs_int32 saveCommand, saveVolume;
+
+           Tmpafs_int32 = (afs_int32 *)buf;
+           Fid->Volume = Tmpafs_int32[1];
+           Fid->Vnode = Tmpafs_int32[2];
+           Fid->Unique = Tmpafs_int32[3];
+           status.in_size = sizeof(struct FsCmdInputs);
+           status.out_size = sizeof(struct FsCmdOutputs);
+           status.in = (char *) &PioctlInputs;
+           status.out = (char *) &PioctlOutputs;
+           saveCommand = PioctlInputs.command;
+           saveVolume = PioctlInputs.fid.Volume;
+           PioctlInputs.command = 0;
+           PioctlInputs.fid.Volume = 0;
+           if (!pioctl(fn, VIOC_FS_CMD, &status, 0))
+               memcpy((char *)Status, &PioctlOutputs.status,
+                     sizeof(struct AFSFetchStatus));
+           PioctlInputs.command = saveCommand;
+           PioctlInputs.fid.Volume = saveVolume;
+           if (create && (Status->Length || Status->Length_hi)) {
+                fprintf(stderr,"AFS file %s not empty, request aborted.\n",
+                       fn);
+                exit(-5);
+           }
+       }
+    }
+    return code;
+}
+
+int
+DestroyConnections(void)
+{
+    int i;
+
+    if (!ConnLookupInitialized) return 0;
+    for (i = 0; i < MAX_HOSTS; i++) {
+        if (!ConnLookup[i].conn) break;
+       RXAFS_GiveUpAllCallBacks(ConnLookup[i].conn);
+       rx_DestroyConnection(ConnLookup[i].conn);
+    }
+    if (!rxInitDone)
+       rx_Finalize();
+    return 0;
+}
+
+
+int
+LogErrors (int level, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    return vfprintf(stderr, fmt, ap);
+}
+
+int
+readFile(struct cmd_syndesc *as, void *unused)
+{
+    char *fname;
+    char *cell = 0;
+    afs_int32 code;
+    afs_int32 hosts[MAXHOSTS];
+    AFSFid Fid;
+    int i, j;
+    struct rx_connection *RXConn;
+    struct cellLookup *cl;
+    struct rx_call *tcall;
+    struct AFSVolSync tsync;
+    struct AFSFetchStatus OutStatus;
+    struct AFSCallBack CallBack;
+    afs_int64 Pos;
+    afs_int32 len;
+    afs_int64 length, Len;
+    u_char vnode = 0;
+    u_char first = 1;
+    int bytes;
+    int worstCode = 0;
+    char *buf = 0;
+    int bufflen = BUFFLEN;
+
+    i=0;
+
+    if (as->name[0] == 'f')
+       vnode = 1;
+    if (as->parms[2].items)
+       verbose = 1;
+    if (as->parms[3].items) {
+       md5sum = 1;
+       MD5_Init(&md5);
+    }
+
+    CBServiceNeeded = 1;
+    InitializeCBService();
+
+    gettimeofday (&starttime, &Timezone);
+    fname = as->parms[0].items->data;
+    cell = 0;
+    if (as->parms[1].items)
+       cell = as->parms[1].items->data;
+    if (vnode)
+       code = get_vnode_hosts(fname, &cell, hosts, &Fid, 1);
+    else
+        code = get_file_cell(fname, &cell, hosts, &Fid, &OutStatus, 0);
+    if (code) {
+        fprintf(stderr,"File not found %s\n", fname);
+        return code;
+    }
+    if (Fid.Vnode & 1) {
+       fprintf(stderr,"%s is a directory, not a file\n", fname);
+       return ENOENT;
+    }
+    cl = FindCell(cell);
+    for (j=0;j<MAXHOSTS;++j) {
+       int useHost;
+
+        if (first && as->parms[6].items) {
+           afs_uint32 fields, ip1, ip2, ip3, ip4;
+           fields = sscanf(as->parms[6].items->data, "%d.%d.%d.%d",
+                           &ip1, &ip2, &ip3, &ip4);
+           useHost = (ip1 << 24) + (ip2 << 16) + (ip3 << 8) + ip4;
+           j--;
+        } else {
+            if (!hosts[j])
+                break;
+           useHost = hosts[j];
+       }
+       first = 0;
+        RXConn = FindRXConnection(useHost, htons(AFSCONF_FILEPORT), 1,
+                                 cl->sc[cl->scIndex], cl->scIndex);
+        if (!RXConn) {
+            fprintf(stderr,"rx_NewConnection failed to server 0x%X\n",
+                    useHost);
+            continue;
+        }
+#ifndef NO_AFS_CLIENT
+       if (vnode) {
+#endif /* NO_AFS_CLIENT */
+            code = AFS_FetchStatus(RXConn, &Fid, &OutStatus, &CallBack, &tsync);
+            if (code) {
+                fprintf(stderr,"RXAFS_FetchStatus failed to server 0x%X for"
+                       " file %s, code was %d\n",
+                       useHost, fname, code);
+               continue;
+           }
+#ifndef NO_AFS_CLIENT
+        }
+#endif /* NO_AFS_CLIENT */
+        gettimeofday(&opentime, &Timezone);
+       if (verbose) {
+            seconds = opentime.tv_sec + opentime.tv_usec *.000001
+               -starttime.tv_sec - starttime.tv_usec *.000001;
+           fprintf(stderr,"Startup to find the file took %.3f sec.\n",
+                   seconds);
+       }
+       Len = OutStatus.Length_hi;
+       Len <<= 32;
+       Len += OutStatus.Length;
+       ZeroInt64(Pos);
+       {
+           afs_uint32 high, low;
+
+            tcall = rx_NewCall(RXConn);
+            code = StartAFS_FetchData64 (tcall, &Fid, Pos, Len);
+            if (code == RXGEN_OPCODE) {
+               afs_uint32 tmpPos,  tmpLen;
+               tmpPos = Pos; tmpLen = Len;
+                code = StartAFS_FetchData (tcall, &Fid, Pos, Len);
+               bytes = rx_Read(tcall, (char *)&low, sizeof(afs_int32));
+               length = ntohl(low);
+               if (bytes != 4) code = -3;
+            } else if (!code) {
+                bytes = rx_Read(tcall, (char *)&high, 4);
+               length = ntohl(high);
+               length <<= 32;
+                bytes += rx_Read(tcall, (char *)&low, 4);
+               length += ntohl(low);
+               if (bytes != 8) code = -3;
+            }
+            if (code) {
+                if (code == RXGEN_OPCODE) {
+                    fprintf(stderr, "File server for %s might not be running a"
+                           " multi-resident AFS server\n",
+                           fname);
+               } else {
+                    fprintf(stderr, "%s for %s ended with error code %d\n",
+                            (char *) &as->name, fname, code);
+                   exit(1);
+               }
+            }
+           if (length > bufflen)
+               len = bufflen;
+           else
+               len = length;
+           buf = (char *)malloc(len);
+           if (!buf) {
+               fprintf(stderr, "couldn't allocate buffer\n");
+               exit(1);
+           }
+           while (!code && NonZeroInt64(length)) {
+               if (length > bufflen)
+                   len = bufflen;
+               else
+                   len = length;
+               bytes = rx_Read(tcall, (char *) buf, len);
+               if (bytes != len) {
+                   code = -3;
+               }
+               if (md5sum)
+                   MD5_Update(&md5, buf, len);
+               if (!code)
+                   write(1, buf, len);
+               length -= len;
+               xfered += len;
+               gettimeofday(&now, &Timezone);
+               if (verbose)
+                   printDatarate();
+           }
+           worstCode = code;
+           code = EndRXAFS_FetchData (tcall, &OutStatus, &CallBack, &tsync);
+           rx_EndCall(tcall, 0);
+           if (!worstCode)
+               worstCode = code;
+       }
+        break;
+    }
+    gettimeofday(&readtime, &Timezone);
+    if (worstCode) {
+       fprintf(stderr,"%s failed with code %d\n",
+               (char *) &as->name, worstCode);
+    } else {
+        if (md5sum) {
+           afs_uint32 md5int[4];
+           char *p;
+           MD5_Final((char *) &md5int[0], &md5);
+           p = fname + strlen(fname);
+            while (p > fname) {
+               if (*(--p) == '/') {
+                   ++p;
+                   break;
+               }
+           }
+           fprintf(stderr, "%08x%08x%08x%08x  %s\n",
+                   htonl(md5int[0]), htonl(md5int[1]),
+                   htonl(md5int[2]), htonl(md5int[3]), p);
+        }
+       if(verbose) {
+            seconds = readtime.tv_sec + readtime.tv_usec *.000001
+               -opentime.tv_sec - opentime.tv_usec *.000001;
+            fprintf(stderr,"Transfer of %llu bytes took %.3f sec.\n",
+                   xfered, seconds);
+            datarate = (xfered >> 20) / seconds;
+            fprintf(stderr,"Total data rate = %.0f MB/sec. for read\n",
+                   datarate);
+       }
+    }
+    DestroyConnections();
+    return worstCode;
+}
+
+int
+writeFile(struct cmd_syndesc *as, void *unused)
+{
+    char *fname = NULL;
+    char *cell = 0;
+    afs_int32 code, localcode = 0;
+    afs_int32 hosts[MAXHOSTS];
+    afs_uint32 useHost;
+    AFSFid Fid;
+    int i;
+    struct rx_connection *RXConn;
+    struct cellLookup *cl;
+    struct rx_call *tcall;
+    struct AFSVolSync tsync;
+    struct AFSFetchStatus OutStatus;
+    struct AFSStoreStatus InStatus;
+    afs_int64 Pos;
+    afs_int64 length, Len, synthlength = 0, offset = 0;
+    u_char vnode = 0;
+    int bytes;
+    int worstCode = 0;
+    int append = 0;
+    int synthesize = 0;
+    afs_int32 byteswritten;
+    struct wbuf *bufchain = 0;
+    struct wbuf *previous, *tbuf;
+
+    i=0;
+
+    if (as->name[0] == 'f') {
+       vnode = 1;
+        if (as->name[3] == 'a')
+           append = 1;
+    } else
+        if (as->name[0] == 'a')
+           append = 1;
+    if (as->parms[2].items)
+       verbose = 1;
+    if (as->parms[3].items)
+       md5sum = 1;
+    if (as->parms[4].items) {
+       code = util_GetInt64(as->parms[4].items->data, &synthlength);
+       if (code) {
+           fprintf(stderr, "Invalid value for synthesize length %s\n",
+                   as->parms[4].items->data);
+           return code;
+       }
+       synthesize = 1;
+    }
+    CBServiceNeeded = 1;
+    InitializeCBService();
+
+    if (as->parms[0].items)
+       fname = as->parms[0].items->data;
+
+    cell = 0;
+    if (as->parms[1].items) cell = as->parms[1].items->data;
+    if (vnode) {
+       code = get_vnode_hosts(fname, &cell, hosts, &Fid, 1);
+       if (code)
+           return code;
+    } else
+        code = get_file_cell(fname, &cell, hosts, &Fid, &OutStatus, append ? 0 : 1);
+    if (code) {
+      if (code != -5)
+            fprintf(stderr,"File or directory not found: %s\n",
+                    fname);
+        return code;
+    }
+    if (Fid.Vnode & 1) {
+       fprintf(stderr,"%s is a directory, not a file\n", fname);
+       return ENOENT;
+    }
+    if (!hosts[0]) {
+       fprintf(stderr,"AFS file not found: %s\n", fname);
+       return ENOENT;
+    }
+    cl = FindCell(cell);
+    gettimeofday (&starttime, &Timezone);
+    useHost = hosts[0];
+    RXConn = FindRXConnection(useHost, htons(AFSCONF_FILEPORT), 1,
+                             cl->sc[cl->scIndex], cl->scIndex);
+    if (!RXConn) {
+        fprintf(stderr,"rx_NewConnection failed to server 0x%X\n",
+               hosts[0]);
+        return -1;
+    }
+    InStatus.Mask = AFS_SETMODE + AFS_FSYNC;
+    InStatus.UnixModeBits = 0644;
+    if (append) {
+       Pos = OutStatus.Length_hi;
+       Pos = (Pos << 32) | OutStatus.Length;
+    } else
+        Pos = 0;
+    previous = (struct wbuf *)&bufchain;
+    if (md5sum)
+       MD5_Init(&md5);
+
+    Len = 0;
+    while (Len<WRITEBUFFLEN) {
+       tbuf = (struct wbuf *)malloc(sizeof(struct wbuf));
+       if (!tbuf) {
+           if (!bufchain) {
+               fprintf(stderr, "Couldn't allocate buffer, aborting\n");
+               exit(1);
+           }
+           break;
+       }
+       memset(tbuf, 0, sizeof(struct wbuf));
+       tbuf->buflen = BUFFLEN;
+       if (synthesize) {
+           afs_int32 ll, l = tbuf->buflen;
+           if (l > synthlength)
+               l = synthlength;
+           for (ll = 0; ll < l; ll += 4096) {
+                sprintf(&tbuf->buf[ll],"Offset (0x%x, 0x%x)\n",
+                       (unsigned int)(offset >> 32),
+                       (unsigned int)(offset & 0xffffffff) + ll);
+           }
+           offset += l;
+           synthlength -= l;
+           tbuf->used = l;
+       } else
+           tbuf->used = read(0, &tbuf->buf, tbuf->buflen);
+       if (!tbuf->used) {
+           free(tbuf);
+           break;
+       }
+       if (md5sum)
+           MD5_Update(&md5, &tbuf->buf, tbuf->used);
+       previous->next = tbuf;
+       previous = tbuf;
+       Len += tbuf->used;
+    }
+    gettimeofday(&opentime, &Timezone);
+    if (verbose) {
+        seconds = opentime.tv_sec + opentime.tv_usec *.000001
+           -starttime.tv_sec - starttime.tv_usec *.000001;
+        fprintf(stderr,"Startup to find the file took %.3f sec.\n",
+               seconds);
+    }
+    bytes = Len;
+    while (!code && bytes) {
+        afs_int32 code2;
+        Len = bytes;
+    restart:
+        tcall = rx_NewCall(RXConn);
+        code = StartAFS_StoreData64 (tcall, &Fid, &InStatus, Pos, Len, Pos+Len);
+        if (code == RXGEN_OPCODE) {
+           afs_uint32 tmpLen, tmpPos;
+           tmpPos = Pos;
+           tmpLen = Len;
+           if (Pos+Len > 0x7fffffff) {
+               fprintf(stderr,"AFS fileserver does not support files >= 2 GB\n");
+               return EFBIG;
+           }
+           code = StartAFS_StoreData (tcall, &Fid, &InStatus, tmpPos, tmpLen,
+                                      tmpPos+tmpLen);
+        }
+        if (code) {
+            fprintf(stderr, "StartRXAFS_StoreData had error code %d\n", code);
+           return code;
+        }
+        length = Len;
+       tbuf = bufchain;
+       if (Len) {
+            for (tbuf= bufchain; tbuf; tbuf=tbuf->next) {
+               if (!tbuf->used)
+                   break;
+               byteswritten = rx_Write(tcall, tbuf->buf, tbuf->used);
+               if (byteswritten != tbuf->used) {
+                   fprintf(stderr,"Only %d instead of %" AFS_INT64_FMT " bytes transferred by rx_Write()\n", byteswritten, length);
+                   fprintf(stderr, "At %" AFS_UINT64_FMT " bytes from the end\n", length);
+                   code = -4;
+                   break;
+               }
+               xfered += tbuf->used;
+               gettimeofday(&now, &Timezone);
+               if (verbose)
+                   printDatarate();
+               length -= tbuf->used;
+            }
+       }
+        worstCode = code;
+        code = EndRXAFS_StoreData64 (tcall, &OutStatus, &tsync);
+       if (code) {
+           fprintf(stderr, "EndRXAFS_StoreData64 returned %d\n", code);
+            worstCode = code;
+        }
+        code2 = rx_Error(tcall);
+       if (code2) {
+           fprintf(stderr, "rx_Error returned %d\n", code2);
+            worstCode = code2;
+        }
+        code2 = rx_EndCall(tcall, localcode);
+       if (code2) {
+           fprintf(stderr, "rx_EndCall returned %d\n", code2);
+            worstCode = code2;
+        }
+       code = worstCode;
+       if (code == 110) {
+           fprintf(stderr, "Waiting for busy volume\n");
+           sleep(10);
+           goto restart;
+       }
+       Pos += Len;
+       bytes = 0;
+       if (!code) {
+            for (tbuf = bufchain; tbuf; tbuf=tbuf->next) {
+               tbuf->offset = 0;
+               if (synthesize) {
+                   afs_int32 ll, l = tbuf->buflen;
+                   if (l > synthlength)
+                       l = synthlength;
+                   for (ll = 0; ll < l; ll += 4096) {
+                     sprintf(&tbuf->buf[ll],"Offset (0x%x, 0x%x)\n",
+                             (unsigned int)(offset >> 32),
+                             (unsigned int)(offset & 0xffffffff) + ll);
+                   }
+                   offset += l;
+                   synthlength -= l;
+                   tbuf->used = l;
+               } else
+                   tbuf->used = read(0, &tbuf->buf, tbuf->buflen);
+               if (!tbuf->used)
+                   break;
+               if (md5sum)
+                   MD5_Update(&md5, &tbuf->buf, tbuf->used);
+               Len += tbuf->used;
+               bytes += tbuf->used;
+            }
+        }
+    }
+    gettimeofday(&writetime, &Timezone);
+    if (worstCode) {
+       fprintf(stderr,"%s failed with code %d\n", as->name, worstCode);
+    } else if(verbose) {
+        seconds = writetime.tv_sec + writetime.tv_usec *.000001
+           -opentime.tv_sec - opentime.tv_usec *.000001;
+        fprintf(stderr,"Transfer of %llu bytes took %.3f sec.\n",
+               xfered, seconds);
+        datarate = (xfered >> 20) / seconds;
+        fprintf(stderr,"Total data rate = %.0f MB/sec. for write\n",
+               datarate);
+    }
+    while (bufchain) {
+       tbuf = bufchain;
+       bufchain = tbuf->next;
+       free(tbuf);
+    }
+    DestroyConnections();
+    if (md5sum) {
+       afs_uint32 md5int[4];
+       char *p;
+       MD5_Final((char *) &md5int[0], &md5);
+       p = fname + strlen(fname);
+        while (p > fname) {
+           if (*(--p) == '/') {
+               ++p;
+               break;
+           }
+       }
+       fprintf(stdout, "%08x%08x%08x%08x  %s\n",
+               htonl(md5int[0]), htonl(md5int[1]),
+               htonl(md5int[2]), htonl(md5int[3]), p);
+    }
+    return worstCode;
+}
+
+struct cellLookup *
+FindCell(char *cellName)
+{
+    char name[MAXCELLCHARS];
+    char *np;
+    struct cellLookup *p, *p2;
+    static struct afsconf_dir *tdir;
+    struct ktc_principal sname;
+    struct ktc_token ttoken;
+    afs_int32 len, code;
+
+    if (cellName) {
+       np = cellName;
+    } else {
+        if (!tdir)
+           tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
+       len = MAXCELLCHARS;
+       afsconf_GetLocalCell(tdir, name, len);
+       np = (char *) &name;
+    }
+    SetCellFname(np);
+
+    p2 = (struct cellLookup *) &Cells;
+    for (p = Cells; p; p = p->next) {
+       if (!strcmp((char *)&p->info.name, np)) {
+#ifdef NO_AFS_CLIENT
+           if (!strcmp((char *)&lastcell, np))
+               code = VLDBInit(1, &p->info);
+#endif
+           return p;
+       }
+       p2 = p;
+    }
+    p2->next = (struct cellLookup *) malloc(sizeof(struct cellLookup));
+    p = p2->next;
+    memset(p, 0, sizeof(struct cellLookup));
+    p->next = (struct cellLookup *) 0;
+    if (!tdir)
+       tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
+    if (afsconf_GetCellInfo(tdir, np, AFSCONF_VLDBSERVICE, &p->info)) {
+       p2->next = (struct cellLookup *) 0;
+       free(p);
+       p = (struct cellLookup *) 0;
+    } else {
+#ifdef NO_AFS_CLIENT
+       if (code = VLDBInit(1, &p->info))
+            fprintf(stderr,"VLDBInit failed for cell %s\n", p->info.name);
+#endif
+        strcpy((char *)&sname.cell, (char *)&p->info.name);
+        sname.instance[0] = 0;
+        strcpy(sname.name, "afs");
+        code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
+        if (code)
+            p->scIndex = 0;
+        else {
+            if ((ttoken.kvno >= 0) && (ttoken.kvno <= 255))
+               /* this is a kerberos ticket, set scIndex accordingly */
+               p->scIndex = 2;
+           else {
+               fprintf(stderr,"funny kvno (%d) in ticket, proceeding\n",
+                       ttoken.kvno);
+               p->scIndex = 2;
+           }
+           p->sc[2] = (struct rx_securityClass *)
+               rxkad_NewClientSecurityObject(rxkad_clear, &ttoken.sessionKey,
+                                             ttoken.kvno, ttoken.ticketLen,
+                                             ttoken.ticket);
+        }
+        if (p->scIndex == 0)
+            p->sc[0] = (struct rx_securityClass *)
+               rxnull_NewClientSecurityObject();
+    }
+
+    if (p)
+        return p;
+    else
+       return 0;
+}
+
+struct rx_connection *
+FindRXConnection(afs_uint32 host, u_short port, u_short service,
+                struct rx_securityClass *securityObject,
+                int serviceSecurityIndex)
+{
+    int i;
+
+    if (!ConnLookupInitialized) {
+       memset(ConnLookup, 0, MAX_HOSTS * sizeof(struct connectionLookup));
+       ConnLookupInitialized = 1;
+    }
+
+    for (i = 0; i < MAX_HOSTS; i++) {
+        if ((ConnLookup[i].host == host) && (ConnLookup[i].port == port))
+           return ConnLookup[i].conn;
+       if (!ConnLookup[i].conn)
+           break;
+    }
+
+    if (i >= MAX_HOSTS)
+       return 0;
+
+    ConnLookup[i].conn = rx_NewConnection(host, port, service, securityObject, serviceSecurityIndex);
+    if (ConnLookup[i].conn) {
+       ConnLookup[i].host = host;
+       ConnLookup[i].port = port;
+    }
+
+    return ConnLookup[i].conn;
+}