vol-dump-tool-20030824
authorNathan Neulinger <nneul@umr.edu>
Sun, 24 Aug 2003 22:22:39 +0000 (22:22 +0000)
committerDerrick Brashear <shadow@dementia.org>
Sun, 24 Aug 2003 22:22:39 +0000 (22:22 +0000)
FIXES 2008

create direct-from-disk volume dump tool

src/vol/Makefile.in
src/vol/vol-dump.c [new file with mode: 0644]

index 1f733fb..81368a6 100644 (file)
@@ -23,13 +23,14 @@ VLIBOBJS=vnode.o volume.o vutil.o partition.o fssync.o purge.o \
         clone.o nuke.o devname.o listinodes.o common.o ihandle.o \
         namei_ops.o
 
-OBJECTS=${VLIBOBJS} physio.o vol-salvage.o vol-info.o 
+OBJECTS=${VLIBOBJS} physio.o vol-salvage.o vol-info.o vol-dump.o
 
 all: gi \
        ${TOP_LIBDIR}/vlib.a \
        ${TOP_LIBDIR}/libvlib.a \
        salvager \
        volinfo \
+       voldump \
        $(FS_CONV_OSF40D) \
        $(XFS_SIZE_CHECK) \
        $(FS_CONV_SOL26) \
@@ -51,6 +52,7 @@ install: \
        ${DESTDIR}${libdir}/afs/libvlib.a \
        ${DESTDIR}${afssrvlibexecdir}/salvager \
        ${DESTDIR}${afssrvsbindir}/volinfo \
+       ${DESTDIR}${afssrvsbindir}/voldump \
        $(install_FS_CONV_OSF40D) \
        $(install_XFS_SIZE_CHECK) \
        $(install_FS_CONV_SOL26) \
@@ -159,15 +161,12 @@ gi: ${TOP_LIBDIR}/libsys.a
         esac
 
 volinfo: vol-info.o physio.o ihandle.o ${LIBS}
-       case ${SYS_NAME} in \
-               *linux* | *fbsd*) \
-                       ${CC} ${CFLAGS} -o volinfo vol-info.o physio.o \
-                               ihandle.o ${LIBS} ${XLIBS} ;; \
-               *) \
-                       ${CC} ${CFLAGS} -o volinfo vol-info.o physio.o \
-                                ihandle.o ${LIBS} ${XLIBS} ;; \
-       esac
+       ${CC} ${CFLAGS} -o volinfo vol-info.o physio.o \
+               ihandle.o ${LIBS} ${XLIBS}
 
+voldump: vol-dump.o physio.o ihandle.o ${LIBS}
+       ${CC} ${CFLAGS} -o voldump vol-dump.o physio.o \
+                ihandle.o ${LIBS} ${XLIBS}
 
 fs_conv_dux40D: fs_conv_411.o
        ${CC} ${CFLAGS} ${TOP_LIBDIR}/libcmd.a -o fs_conv_dux40D fs_conv_411.o  ${LIBS} ${XLIBS}
@@ -186,7 +185,7 @@ xfs_size_check: xfs_size_check.c
 #
 clean:
        $(RM) -f *.o *.a AFS_component_version_number.c
-       $(RM) -f ${SCMPROGS} ${STAGEPROGS} core salvager volinfo gi fs_conv_sol26 fs_conv_dux40D
+       $(RM) -f ${SCMPROGS} ${STAGEPROGS} core salvager volinfo voldump gi fs_conv_sol26 fs_conv_dux40D
 
 test:
        cd test; $(MAKE)
@@ -210,6 +209,9 @@ ${DESTDIR}${afssrvlibexecdir}/salvager: salvager
 ${DESTDIR}${afssrvsbindir}/volinfo: volinfo
        ${INSTALL} -s $? $@
 
+${DESTDIR}${afssrvsbindir}/voldump: voldump
+       ${INSTALL} -s $? $@
+
 ${DESTDIR}${includedir}/afs/nfs.h: nfs.h
        ${INSTALL} $? $@
 
@@ -269,6 +271,7 @@ dest: \
        ${DEST}/lib/afs/libvlib.a \
        ${DEST}/root.server/usr/afs/bin/salvager \
        ${DEST}/root.server/usr/afs/bin/volinfo \
+       ${DEST}/root.server/usr/afs/bin/voldump \
        $(dest_FS_CONV_OSF40D) \
        $(dest_XFS_SIZE_CHECK) \
        $(dest_FS_CONV_SOL26) \
diff --git a/src/vol/vol-dump.c b/src/vol/vol-dump.c
new file mode 100644 (file)
index 0000000..cdb2430
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+ * 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$");
+
+#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 "volinodes.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 */
+}