test-suite-pull-tools-directly-in-20020114
authorDerrick Brashear <shadow@dementia.org>
Tue, 15 Jan 2002 04:14:51 +0000 (04:14 +0000)
committerDerrick Brashear <shadow@dementia.org>
Tue, 15 Jan 2002 04:14:51 +0000 (04:14 +0000)
move dump tools directly into test suite as that is their intended use

canonical versions of these tools will be distributed otherwise
and may be updated but these provide the minimum functionality

37 files changed:
Makefile.in
configure.in
src/tests/Makefile.in
src/tests/README.dumptool [new file with mode: 0644]
src/tests/TEMPLATE [new file with mode: 0644]
src/tests/afsdump_dirlist.c [new file with mode: 0644]
src/tests/afsdump_extract.c [new file with mode: 0644]
src/tests/afsdump_scan.c [new file with mode: 0644]
src/tests/afsdump_xsed.c [new file with mode: 0644]
src/tests/backuphdr.c [new file with mode: 0644]
src/tests/directory.c [new file with mode: 0644]
src/tests/dump.c [new file with mode: 0644]
src/tests/dumpfmt.h [new file with mode: 0644]
src/tests/dumpscan.h [new file with mode: 0644]
src/tests/dumpscan_errs.et [new file with mode: 0644]
src/tests/dumptool.c [new file with mode: 0644]
src/tests/int64.c [new file with mode: 0644]
src/tests/intNN.h [new file with mode: 0644]
src/tests/internal.h [new file with mode: 0644]
src/tests/null-search.c [new file with mode: 0644]
src/tests/parsedump.c [new file with mode: 0644]
src/tests/parsetag.c [new file with mode: 0644]
src/tests/parsevnode.c [new file with mode: 0644]
src/tests/parsevol.c [new file with mode: 0644]
src/tests/pathname.c [new file with mode: 0644]
src/tests/primitive.c [new file with mode: 0644]
src/tests/repair.c [new file with mode: 0644]
src/tests/stagehdr.c [new file with mode: 0644]
src/tests/stagehdr.h [new file with mode: 0644]
src/tests/util.c [new file with mode: 0644]
src/tests/xf_errs.et [new file with mode: 0644]
src/tests/xf_files.c [new file with mode: 0644]
src/tests/xf_printf.c [new file with mode: 0644]
src/tests/xf_profile.c [new file with mode: 0644]
src/tests/xf_rxcall.c [new file with mode: 0644]
src/tests/xfiles.c [new file with mode: 0644]
src/tests/xfiles.h [new file with mode: 0644]

index 364456e..5ac9f28 100644 (file)
@@ -215,9 +215,6 @@ tviced: project viced vlserver libafsrpc libafsauthent
 volser: project tviced usd kauth audit
        ${COMPILE_PART1} volser ${COMPILE_PART2}
 
-tools: volser
-       ${COMPILE_PART1} tools ${COMPILE_PART2}
-
 venus: project volser ptserver
        ${COMPILE_PART1} venus ${COMPILE_PART2}
        ${COMPILE_PART1} venus/test ${COMPILE_PART2}
@@ -439,13 +436,13 @@ libadmin: libafsauthent bozo
 
 finale: project cmd comerr afsd allrcmds butc tbutc @ENABLE_KERNEL_MODULE@ libuafs audit kauth log package \
        ptserver scout bu_utils ubik uss bozo vfsck volser \
-       venus update xstat afsmonitor dauth tools rxdebug libafsrpc \
+       venus update xstat afsmonitor dauth rxdebug libafsrpc \
        libafsauthent libadmin
        ${COMPILE_PART1} finale ${COMPILE_PART2}
 
 finale_nolibafs: project cmd comerr afsd allrcmds butc tbutc libuafs audit kauth log package \
        ptserver scout bu_utils ubik uss bozo vfsck volser \
-       venus update xstat afsmonitor dauth tools rxdebug libafsrpc \
+       venus update xstat afsmonitor dauth rxdebug libafsrpc \
        libafsauthent libadmin
        ${COMPILE_PART1} finale ${COMPILE_PART2}
 
@@ -536,7 +533,7 @@ clean2:
        -${COMPILE_PART1} bucoord ${COMPILE_CLEAN}
        -${COMPILE_PART1} xstat ${COMPILE_CLEAN}
        -${COMPILE_PART1} afsmonitor ${COMPILE_CLEAN}
-       -${COMPILE_PART1} tools ${COMPILE_CLEAN}
+       -${COMPILE_PART1} tests ${COMPILE_CLEAN}
        -${COMPILE_PART1} rxdebug ${COMPILE_CLEAN}
        -${COMPILE_PART1} libafsrpc ${COMPILE_CLEAN}
        -${COMPILE_PART1} libafsauthent ${COMPILE_CLEAN}
@@ -665,7 +662,8 @@ distclean: clean
        src/sia/Makefile \
        src/sys/Makefile \
        src/tbutc/Makefile \
-       src/tools/Makefile \
+       src/tests/Makefile \
+       src/tests/Dirpath.pm \
        src/tsm41/Makefile \
        src/tviced/Makefile \
        src/ubik/Makefile \
index 10615e2..f5672fd 100644 (file)
@@ -107,7 +107,6 @@ src/sys/Makefile \
 src/tbutc/Makefile \
 src/tests/Makefile \
 src/tests/Dirpath.pm \
-src/tools/Makefile \
 src/tsm41/Makefile \
 src/tviced/Makefile \
 src/ubik/Makefile \
index b402189..b1ae44d 100644 (file)
@@ -6,6 +6,105 @@ SHELL         = /bin/sh
 CFLAGS         = -I. -I${srcdir} ${DBG} ${OPTMZ} -I${TOP_OBJDIR}/src/config -I${TOP_INCDIR} ${XCFLAGS}
 LDFLAGS=${DBG} ${OPTMZ} ${XLDFLAGS}
 
+INCDIRS=-I${TOP_OBJDIR}/src/config -I${TOP_INCDIR}/afs -I${TOP_INCDIR}
+INCLIBS=-L${SRCDIR}/lib/afs -L${TOP_LIBDIR}
+
+LIBS=\
+       ${TOP_LIBDIR}/libdumpscan.a \
+       ${TOP_LIBDIR}/libxfiles.a \
+       ${TOP_LIBDIR}/libauth.a \
+       ${TOP_LIBDIR}/libaudit.a \
+       ${TOP_LIBDIR}/libvolser.a \
+       ${TOP_LIBDIR}/libvldb.a \
+       ${TOP_LIBDIR}/libubik.a \
+       ${TOP_LIBDIR}/librxkad.a \
+       ${TOP_LIBDIR}/libsys.a \
+       ${TOP_LIBDIR}/librx.a \
+       ${TOP_LIBDIR}/liblwp.a \
+       ${TOP_LIBDIR}/util.a \
+       ${TOP_LIBDIR}/libcom_err.a \
+       ${XLIBS}
+
+OBJS_afsdump_scan    = afsdump_scan.o repair.o
+OBJS_afsdump_xsed    = afsdump_xsed.o repair.o
+OBJS_libxfiles.a     = xfiles.o xf_errs.o xf_printf.o int64.o \
+                       xf_files.o xf_rxcall.o xf_profile.o
+OBJS_libdumpscan.a   = primitive.o util.o dumpscan_errs.o parsetag.o \
+                       parsedump.o parsevol.o parsevnode.o dump.o \
+                       directory.o pathname.o backuphdr.o stagehdr.o
+
+TARGETS = libxfiles.a libdumpscan.a \
+          afsdump_scan afsdump_dirlist afsdump_extract dumptool
+
+afsdump_scan: libxfiles.a libdumpscan.a $(OBJS_afsdump_scan)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_scan $(OBJS_afsdump_scan) $(LIBS)
+
+afsdump_xsed: libxfiles.a libdumpscan.a $(OBJS_afsdump_xsed)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_xsed $(OBJS_afsdump_xsed) $(LIBS)
+
+afsdump_dirlist: libxfiles.a libdumpscan.a afsdump_dirlist.o
+       $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_dirlist afsdump_dirlist.o $(LIBS)
+
+afsdump_extract: libxfiles.a libdumpscan.a afsdump_extract.o
+       $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_extract afsdump_extract.o $(LIBS)
+
+null-search: libxfiles.a libdumpscan.a null-search.c
+       $(CC) $(CFLAGS) $(LDFLAGS) -o null-search null-search.c $(LIBS)
+
+dumptool: dumptool.c
+       $(CC) $(CFLAGS) $(LDFLAGS) -o dumptool dumptool.c
+
+libxfiles.a: $(OBJS_libxfiles.a)
+       -rm -f libxfiles.a
+       $(AR) r libxfiles.a $(OBJS_libxfiles.a)
+       $(RANLIB) libxfiles.a
+
+libdumpscan.a: $(OBJS_libdumpscan.a)
+       -rm -f libdumpscan.a
+       $(AR) r libdumpscan.a $(OBJS_libdumpscan.a)
+       $(RANLIB) libdumpscan.a
+
+xf_errs.c xf_errs.h: xf_errs.et
+       $(COMPILE_ET) xf_errs.et
+
+dumpscan_errs.c dumpscan_errs.h: dumpscan_errs.et
+       $(COMPILE_ET) dumpscan_errs.et
+
+util.o xfiles.o xf_files.o: xf_errs.h
+backuphdr.o directory.o parsedump.o parsetag.o: dumpscan_errs.h
+parsevnode.o parsevol.o pathname.o repair.o:    dumpscan_errs.h
+stagehdr.o util.o:                              dumpscan_errs.h
+
+${DEST}/etc/afsdump_scan: afsdump_scan
+       ${INSTALL} $? $@
+
+${DEST}/etc/afsdump_dirlist: afsdump_dirlist
+       ${INSTALL} $? $@
+
+${DEST}/etc/afsdump_extract: afsdump_extract
+       ${INSTALL} $? $@
+
+${DEST}/etc/dumptool: dumptool
+       ${INSTALL} $? $@
+
+${DESTDIR}${sbindir}/afsdump_scan: afsdump_scan
+       ${INSTALL} $? $@
+
+${DESTDIR}${sbindir}/afsdump_dirlist: afsdump_dirlist
+       ${INSTALL} $? $@
+
+${DESTDIR}${sbindir}/afsdump_extract: afsdump_extract
+       ${INSTALL} $? $@
+
+${DESTDIR}${sbindir}/dumptool: dumptool
+       ${INSTALL} $? $@
+
+${TOP_LIBDIR}/libxfiles.a: libxfiles.a
+       ${INSTALL} $? $@
+
+${TOP_LIBDIR}/libdumpscan.a: libdumpscan.a
+       ${INSTALL} $? $@
+
 SYS_LIBS       = ${TOP_LIBDIR}/libsys.a ${TOP_LIBDIR}/librx.a ${TOP_LIBDIR}/liblwp.a ${TOP_LIBDIR}/util.a
 
 AUTH_LIBS      = ${TOP_LIBDIR}/libauth.a ${SYS_LIBS}
@@ -65,8 +164,6 @@ TEST_SRCS     = write-ro-file.c read-vs-mmap.c read-vs-mmap2.c                  \
 
 EXTRA_OBJS = err.o errx.o warn.o warnx.o
 
-all: run-tests $(TEST_PROGRAMS) OS.pm
-
 OS.pm: OS-$(MKAFS_OSTYPE).pm
        $(CP) OS-$(MKAFS_OSTYPE).pm OS.pm
 
@@ -256,13 +353,21 @@ mountpoint:   mountpoint.in
        sed -e "s!%bindir%!$(bindir)!" $(srcdir)/mountpoint.in > $@
        chmod +x mountpoint
 
-clean:
-       rm -f run-tests $(TEST_PROGRAMS) *.o *~ OS.pm
+dest:
 
 install:
 
 uninstall:
 
+all: run-tests $(TEST_PROGRAMS) OS.pm ${TOP_LIBDIR}/libxfiles.a \
+       ${TOP_LIBDIR}/libdumpscan.a \
+       afsdump_scan afsdump_dirlist afsdump_extract dumptool
+
+clean:
+       -rm xf_errs.c xf_errs.h dumpscan_errs.c dumpscan_errs.h *.o \
+       $(TARGETS) run-tests $(TEST_PROGRAMS) OS.pm
+
+include ../config/Makefile.version
 
 TAGS:  $(TEST_SRCS)
        etags $(TEST_SRCS)
diff --git a/src/tests/README.dumptool b/src/tests/README.dumptool
new file mode 100644 (file)
index 0000000..69c28dd
--- /dev/null
@@ -0,0 +1,152 @@
+$Id$
+
+This is the README for dumptool, a program to interactively restore
+AFS volume dump files.
+
+
+INTRODUCTION
+
+Dumptool arose out of a need here at NRL to perform maintenance of
+MR-AFS (Multi-Resident AFS) volumes.  After it was written, we found
+that it worked great on standard AFS volumes as well, and relatively
+few changes were required to make it compile with a standard AFS
+installation.
+
+Dumptool provides an interface similar to the interactive Unix restore;
+given a dump file, a user can navigate through the filesystem inside
+the dump using familiar commands such as "cd" and "ls".  Also provided
+is a "cp" command to copy individual files out of the dump into a
+normal filesystem space.  This eliminates the need to restore an
+entire volume just to retrieve a single file.
+
+Dumptool was written at the Naval Research Laboratory by Ken Hornstein
+<kenh@cmf.nrl.navy.mil>.  The latest and greatest version of dumptool
+can always be found in the AFS contrib directory at:
+
+/afs/transarc.com/public/afs-contrib/dumptool/
+
+
+INSTALLATION & OPERATION
+
+The standard Makefile target will build a dumptool for a vanilla AFS
+installation.  The "mrafs" target will build a dumptool that can
+operate on MR-AFS dumps.  In either case, you may need to change some
+of the Makefile variables to reflect your site; see the Makefile for
+more information.
+
+Once dumptool is built successfully, you can run it on any AFS dump
+file.  Without any additional arguments, dumptool will scan the dump file,
+build indexes of all listed vnodes, and present a prompt (">") that
+accepts the following commands:
+
+       ls      Lists files in the current directory.  Filename globbing
+               (e.g., wildcards such as * ?) are supported via the system
+               fnmatch() function.  Accepts the following flags:
+
+           -l  Generates a "long" listing, similar to the -l switch for
+               the Unix ls.  Displays Unix mode mask, owner, group,
+               and file size.
+           -i  Displays volume, vnode, and uniquifier for each matching
+               file in the format volume.vnode.uniquifier.  Note that
+               the volume displayed is that of the _parent_ volume,
+               which in the case of a backup volume is the _original_
+               volume from which it was generated.
+           -F  Append / to filenames for directories, @ for symlinks,
+               and * for files which have the execute bits set.
+           -R  Recurse through all subdirectories.
+       
+       cd      Change the current directory
+
+       cp      Copy a file from the dump.  Note that globbing is NOT
+               supported, and you must give a filename (the Unix
+               idiom of just giving a destination directory for the
+               second argument to cp will NOT work).
+       
+       vcp     Copy a file from the dump, by the vnode.  The first
+               argument is the vnode number, optionally followed by
+               the uniqifier.  E.g:
+
+               vcp 126278 /tmp/file1
+               vcp 126278.43289 /tmp/file2
+       
+       quit    Exits dumptool.
+       exit
+
+
+ADDITIONAL OPTIONS TO DUMPTOOL
+
+Dumptool supports a number of command-line options.  They are detailed
+below:
+
+       -v      Verbose mode.  Output additional information during dump
+               processing.  Each -v will increate output.
+
+       -f      Force dump processing.  Attempt to continue processing
+               a dump even when some errors are detected.  Not completely
+               tested.
+       
+       -i      Inode dump.  Dump a list of all files in the volume,
+               with their volume/vnode/uniqifier information.
+
+
+SUPPORT FOR MR-AFS
+
+Dumptool also supports the extra information in MR-AFS dumps, and
+provides some extra commands/options for dealing with MR-AFS dumps:
+
+Additional command line options:
+
+       -d      Dump all residency filenames in the dump file to standard
+               output.
+       
+       -t      Residency tag information.  Allows a user to specify the
+               tag of a residency if the rsserver is not available.
+               Format of option is Residency/Tag
+       
+       -r      Residency filesystem information.  Allows a user to specify
+               the residency filesystem information if dumptool is not
+               run on the same host as the residency in the dump.  Format
+               of option is Residency/Type/Size/Algorithm.
+
+Additional commands:
+
+       file    Displays to standard output the residency filename of a
+               given dump filename.  All residencies available are shown.
+
+
+CAVEATS
+
+The user interface needs some work.  "ls" doesn't support nearly as many
+options as the standard Unix one.  "cp" also doesn't have all of the features
+found in common Unix variants.
+
+Right now two passes are done through the dump file to scan all vnodes.
+With some clever work this could be sped up somewhat and changed to only
+do a single scan.
+
+
+MODIFYING DUMPTOOL
+
+I welcome changes to dumptool, but I have some guidelines.
+
+First, I DEMAND that the changes be sent in context diff format.  I prefer
+unidiff (diff -u), but standard context diffs are okay.  It's extremely
+difficult to deal with new code in any other way.
+
+If the changes you want to do require some significant
+rearchitecturing, it might be a good idea to contact me first.  That
+way we can coordinate the modifications in a meaningful way (I might
+have made changes since the last released version).
+
+If you're making MR-AFS specific changes, please follow the example
+I've set and protect them with #ifdef RESIDENCY.
+
+And please ... follow my code style, okay?  I always try to follow
+whatever wacky code style I find in source code that I modify, and I
+think it's only polite for you to do the same.
+
+
+CONTACT INFORMATION
+
+As always, please send comments, suggestions, or new features to me,
+Ken Hornstein <kenh@cmf.nrl.navy.mil>.  Shar and enjoy.
diff --git a/src/tests/TEMPLATE b/src/tests/TEMPLATE
new file mode 100644 (file)
index 0000000..6448750
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
diff --git a/src/tests/afsdump_dirlist.c b/src/tests/afsdump_dirlist.c
new file mode 100644 (file)
index 0000000..78d6037
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* afsdump_dirlist.c - List an AFS directory file */
+
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "dumpscan.h"
+
+extern int optind;
+extern char *optarg;
+
+char *argv0;
+static char *input_path;
+static int quiet, verbose, error_count;
+
+static path_hashinfo phi;
+static dump_parser dp;
+
+
+/* Print a usage message and exit */
+static void usage(int status, char *msg)
+{
+  if (msg) fprintf(stderr, "%s: %s\n", argv0, msg);
+  fprintf(stderr, "Usage: %s [options] [file]\n", argv0);
+  fprintf(stderr, "  -h     Print this help message\n");
+  fprintf(stderr, "  -q     Quiet mode (don't print errors)\n");
+  fprintf(stderr, "  -v     Verbose mode\n");
+  exit(status);
+}
+
+
+/* Parse the command-line options */
+static void parse_options(int argc, char **argv)
+{
+  int c;
+
+  /* Set the program name */
+  if (argv0 = strrchr(argv[0], '/')) argv0++;
+  else argv0 = argv[0];
+
+  /* Initialize options */
+  input_path = 0;
+  quiet = verbose = 0;
+
+  /* Initialize other stuff */
+  error_count = 0;
+
+  /* Parse the options */
+  while ((c = getopt(argc, argv, "hqv")) != EOF) {
+    switch (c) {
+      case 'q': quiet        = 1;                         continue;
+      case 'v': verbose      = 1;                         continue;
+      case 'h': usage(0, 0);
+      default:  usage(1, "Invalid option!");
+    }
+  }
+
+  if (quiet && verbose) usage(1, "Can't specify both -q and -v");
+
+  /* Parse non-option arguments */
+  if (argc - optind > 1) usage(1, "Too many arguments!");
+  input_path = (argc == optind) ? "-" : argv[optind];
+}
+
+
+/* A callback to count and print errors */
+static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...)
+{
+  va_list alist;
+
+  error_count++;
+  if (!quiet) {
+    va_start(alist, msg);
+    com_err_va(argv0, code, msg, alist);
+    va_end(alist);
+  }
+}
+
+
+/* Main program */
+void main(int argc, char **argv)
+{
+  XFILE input_file;
+  afs_uint32 r;
+
+  parse_options(argc, argv);
+  initialize_acfg_error_table();
+  initialize_AVds_error_table();
+  initialize_rxk_error_table();
+  initialize_u_error_table();
+  initialize_vl_error_table();
+  initialize_vols_error_table();
+  initialize_xFil_error_table();
+  r = xfopen(&input_file, O_RDONLY, input_path);
+  if (r) {
+    com_err(argv0, r, "opening %s", input_path);
+    exit(2);
+  }
+
+  memset(&dp, 0, sizeof(dp));
+  dp.cb_error     = my_error_cb;
+  dp.print_flags  = DSPRINT_DIR;
+  if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK;
+
+  r = ParseDirectory(&input_file, &dp, 0, 1);
+  xfclose(&input_file);
+
+  if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count);
+  if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r));
+  exit(0);
+}
diff --git a/src/tests/afsdump_extract.c b/src/tests/afsdump_extract.c
new file mode 100644 (file)
index 0000000..e92c66e
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* afsdump_extract.c - Extract files from an AFS dump */
+
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+
+#define COPYBUFSIZE (256*1024)
+
+extern int optind;
+extern char *optarg;
+
+char *argv0;
+static char **file_names;
+static int *file_vnums, name_count, vnum_count;
+
+static char *input_path, *target;
+static int quiet, verbose, error_count, dirs_done, extract_all;
+static int nomode, use_realpath, use_vnum;
+static int do_acls, do_headers;
+
+static path_hashinfo phi;
+static dump_parser dp;
+
+/* Print a usage message and exit */
+static void usage(int status, char *msg)
+{
+  if (msg) fprintf(stderr, "%s: %s\n", argv0, msg);
+  fprintf(stderr, "Usage: %s [options] dumpfile [dest [files...]]\n", argv0);
+  fprintf(stderr, "  -A     Save ACL's\n");
+  fprintf(stderr, "  -H     Save headers\n");
+  fprintf(stderr, "  -h     Print this help message\n");
+  fprintf(stderr, "  -i     Use vnode numbers\n");
+  fprintf(stderr, "  -n     Don't actually create files\n");
+  fprintf(stderr, "  -p     Use real pathnames internally\n");
+  fprintf(stderr, "  -q     Quiet mode (don't print errors)\n");
+  fprintf(stderr, "  -v     Verbose mode\n");
+  fprintf(stderr, "The destination directory defaults to .\n");
+  fprintf(stderr, "Files may be vnode numbers or volume-relative paths;\n");
+  fprintf(stderr, "If vnode numbers are used, files will be extracted\n");
+  fprintf(stderr, "a name generated from the vnode number and uniqifier.\n");
+  fprintf(stderr, "If paths are used, -p is implied and files will be\n");
+  fprintf(stderr, "into correctly-named files.\n");
+  exit(status);
+}
+
+
+/* Parse the command-line options */
+static void parse_options(int argc, char **argv)
+{
+  int c, i, i_name, i_vnum;
+
+  /* Set the program name */
+  if (argv0 = strrchr(argv[0], '/')) argv0++;
+  else argv0 = argv[0];
+
+  /* Initialize options */
+  input_path = 0;
+  quiet = verbose = nomode = 0;
+  use_realpath = use_vnum = do_acls = do_headers = extract_all = 0;
+
+  /* Initialize other stuff */
+  error_count = 0;
+
+  /* Parse the options */
+  while ((c = getopt(argc, argv, "AHhinpqv")) != EOF) {
+    switch (c) {
+      case 'A': do_acls      = 1;                         continue;
+      case 'H': do_headers   = 1;                         continue;
+      case 'i': use_vnum     = 1;                         continue;
+      case 'n': nomode       = 1;                         continue;
+      case 'p': use_realpath = 1;                         continue;
+      case 'q': quiet        = 1;                         continue;
+      case 'v': verbose      = 1;                         continue;
+      case 'h': usage(0, 0);
+      default:  usage(1, "Invalid option!");
+    }
+  }
+
+  if (quiet && verbose) usage(1, "Can't specify both -q and -v");
+
+  /* Parse non-option arguments */
+  if (argc - optind < 1) usage(1, "Dumpfile name required!");
+  input_path = argv[optind];
+
+  if (argc - optind < 2) target = ".";
+  target = argv[optind + 1];
+
+  vnum_count = name_count = 0;
+  if (argc - optind < 3) extract_all = 1;
+  else {
+    argv += optind + 2;
+    argc -= optind + 2;
+    for (i = 0; i < argc; i++) {
+      if (argv[i][0] == '/') name_count++;
+      else                   vnum_count++;
+    }
+    file_names = (char **)malloc(name_count + sizeof(char *));
+    file_vnums = (afs_uint32 *)malloc(vnum_count + sizeof(afs_uint32));
+    if (name_count) use_realpath = 1;
+
+    i_name = i_vnum = 0;
+    for (i = 0; i < argc; i++) {
+      if (argv[i][0] == '/') file_names[i_name++] = argv[i];
+      else                   file_vnums[i_vnum++] = strtol(argv[i], 0, 0);
+    }
+  }
+}
+
+
+static int mkdirp(char *path)
+{
+  char *x = path, slash;
+  struct stat statbuf;
+
+  for (;;) {
+    while (*x && *x != '/') x++;
+    slash = *x;
+    *x = 0;
+
+    if (stat(path, &statbuf)) {
+      if (errno == ENOENT) {
+        if (verbose) printf("> mkdir %s\n", path);
+        if (!mkdir(path, 0755)) errno = 0;
+      }
+    }
+    if (!slash) break;
+    *x++ = '/';
+    if (errno) return errno;
+  }
+
+  return 0;
+}
+
+
+static char *modestr(int mode)
+{
+  static char str[10];
+  int i;
+
+  strcpy(str, "rwxrwxrwx");
+  for (i = 0; i < 9; i++) {
+    if (!(mode & (1 << i)))
+      str[8 - i] = '-';
+  }
+  if (mode & 01000) str[8] = (str[8] == '-') ? 'T' : 't';
+  if (mode & 02000) str[5] = (str[5] == '-') ? 'S' : 's';
+  if (mode & 04000) str[2] = (str[2] == '-') ? 'S' : 's';
+  return str;
+}
+
+
+static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+static char *datestr(time_t date)
+{
+  static char str[13];
+  time_t clock = time(0);
+  struct tm *now, *then;
+  int diff;
+
+  now = localtime(&clock);
+  then = localtime(&date);
+
+  diff = now->tm_mon - then->tm_mon;
+  if (then->tm_year == now->tm_year - 1) diff += 12;
+  if (then->tm_year == now->tm_year + 1) diff -= 12;
+
+  if (diff < 5 || diff > 5)
+    sprintf(str, "%3s %2d  %4d", month[then->tm_mon], then->tm_mday,
+            then->tm_year + 1900);
+  else
+    sprintf(str, "%3s %2d %2d:%2d", month[then->tm_mon], then->tm_mday,
+            then->tm_hour, then->tm_min);
+  return str;
+}
+
+
+/* Should we use this vnode?
+ * Return 0 if no, non-0 if yes
+ */
+static int usevnode(XFILE *X, afs_uint32 vnum, char *vnodepath)
+{
+  int vl, vpl, r, i;
+
+  /* Special case */
+  if (extract_all || !strcmp(vnodepath, "/"))
+    return 1;
+
+  for (i = 0; i < vnum_count; i++)
+    if (vnum == file_vnums[i]) return 2;
+
+  vl = strlen(vnodepath);
+/*fprintf(stderr, "++ checking %s\n", vnodepath);*/
+  for (i = 0; i < name_count; i++) {
+    vpl = strlen(file_names[i]);
+/*  fprintf(stderr, "   %s\n", file_names[i]);*/
+
+    if (vl > vpl) {
+      r = !strncmp(file_names[i], vnodepath, vpl) && vnodepath[vpl] == '/';
+    } else if (vl < vpl) {
+      r = !strncmp(file_names[i], vnodepath, vl) && file_names[i][vl] == '/';
+    } else {
+      r = !strcmp(file_names[i], vnodepath);
+    }
+    if (r) return 1;
+  }
+  return 0;
+}
+
+
+static int copyfile(XFILE *in, XFILE *out, int size)
+{
+  static char buf[COPYBUFSIZE];
+  int nr, nw, r;
+
+  while (size) {
+    nr = (size > COPYBUFSIZE) ? COPYBUFSIZE : size;
+    if (r = xfread(in, buf, nr)) return r;
+    if (r = xfwrite(out, buf, nr)) return r;
+    size -= nr;
+  }
+  return 0;
+}
+
+
+/* A callback to count and print errors */
+static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...)
+{
+  va_list alist;
+
+  error_count++;
+  if (!quiet) {
+    va_start(alist, msg);
+    com_err_va(argv0, code, msg, alist);
+    va_end(alist);
+  }
+}
+
+
+static afs_uint32 dumphdr_cb(afs_dump_header *hdr, XFILE *X, void *refcon)
+{
+  return 0;
+}
+
+
+static afs_uint32 volhdr_cb(afs_vol_header *hdr, XFILE *X, void *refcon)
+{
+  return 0;
+}
+
+
+static afs_uint32 directory_cb(afs_vnode *v, XFILE *X, void *refcon)
+{
+  char *vnodepath;
+  int r, use;
+
+  /* Should we even use this? */
+  if (!use_vnum) {
+    if (r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath))
+      return r;
+    if (!(use = usevnode(X, v->vnode, vnodepath))) {
+      free(vnodepath);
+      return 0;
+    }
+  }
+
+  /* Print it out */
+  if (verbose) {
+    if (use_vnum) 
+      printf("d%s %3d %-11d %11d %s #%d:%d\n",
+             modestr(v->mode), v->nlinks, v->owner, v->size,
+             datestr(v->server_date), v->vnode, v->vuniq);
+    else
+      printf("d%s %3d %-11d %11d %s %s\n",
+             modestr(v->mode), v->nlinks, v->owner, v->size,
+             datestr(v->server_date), vnodepath);
+  }
+  else if (!quiet && !use_vnum)
+    printf("%s\n", vnodepath);
+
+  /* Make the directory, if needed */
+  if (!nomode && !use_vnum && use != 2) {
+    if (strcmp(vnodepath, "/")
+      && (r = mkdirp(vnodepath + 1))) {
+      free(vnodepath);
+      return r;
+    }
+    if (do_acls) {
+      /* XXX do ACL's later */
+    }
+  }
+  if (!use_vnum) free(vnodepath);
+  return 0;
+}
+
+
+static afs_uint32 file_cb(afs_vnode *v, XFILE *X, void *refcon)
+{
+  char *vnodepath, vnpx[30];
+  u_int64 where;
+  XFILE OX;
+  int r, use;
+
+  if (!dirs_done) {
+    dirs_done = 1;
+    if (verbose) printf("* Extracting files...\n");
+  }
+
+  /* Should we even use this? */
+  if (!use_vnum) {
+    if (r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath))
+      return r;
+    if (!(use = usevnode(X, v->vnode, vnodepath))) {
+      free(vnodepath);
+      return 0;
+    }
+    if (use == 2) {
+      free(vnodepath);
+      sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
+      vnodepath = vnpx;
+    }
+  } else {
+    sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
+    vnodepath = vnpx;
+  }
+
+  /* Print it out */
+  if (verbose) {
+    printf("-%s %3d %-11d %11d %s %s\n",
+           modestr(v->mode), v->nlinks, v->owner, v->size,
+           datestr(v->server_date), vnodepath);
+  } else if (!quiet) {
+    printf("%s\n", vnodepath);
+  }
+
+  if (!nomode) {
+    if ((r = xftell(X, &where))
+    ||  (r = xfseek(X, &v->d_offset))
+    ||  (r = xfopen_path(&OX, O_RDWR|O_CREAT|O_TRUNC, vnodepath + 1, 0644))) {
+      if (!use_vnum) free(vnodepath);
+      return r;
+    }
+    r = copyfile(X, &OX, v->size);
+    xfclose(&OX);
+    xfseek(X, &where);
+  } else r = 0;
+
+  if (!use_vnum && use != 2) free(vnodepath);
+  return r;
+}
+
+
+static afs_uint32 symlink_cb(afs_vnode *v, XFILE *X, void *refcon)
+{
+  char *vnodepath, *linktarget, vnpx[30];
+  u_int64 where;
+  int r, use;
+
+  if (!dirs_done) {
+    dirs_done = 1;
+    if (verbose) printf("* Extracting files...\n");
+  }
+
+  /* Should we even use this? */
+  if (!use_vnum) {
+    if (r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath))
+      return r;
+    if (!(use = usevnode(X, v->vnode, vnodepath))) {
+      free(vnodepath);
+      return 0;
+    }
+    if (use == 2) {
+      free(vnodepath);
+      sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
+      vnodepath = vnpx;
+    }
+  } else {
+    sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
+    vnodepath = vnpx;
+  }
+
+  if (!(linktarget = (char *)malloc(v->size + 1))) {
+    if (!use_vnum && use != 2) free(vnodepath);
+    return DSERR_MEM;
+  }
+  if ((r = xftell(X, &where))
+  ||  (r = xfseek(X, &v->d_offset))
+  ||  (r = xfread(X, linktarget, v->size))) {
+    if (!use_vnum && use != 2) free(vnodepath);
+    free(linktarget);
+    return r;
+  }
+  xfseek(X, &where);
+  linktarget[v->size] = 0;
+
+  /* Print it out */
+  if (verbose)
+    printf("l%s %3d %-11d %11d %s %s -> %s\n",
+           modestr(v->mode), v->nlinks, v->owner, v->size,
+           datestr(v->server_date), vnodepath, linktarget);
+  else if (!quiet)
+    printf("%s\n", vnodepath);
+
+  r = 0;
+  if (!nomode) {
+    if (symlink(linktarget, vnodepath + 1))
+      r = errno;
+  }
+
+  free(linktarget);
+  if (!use_vnum && use != 2) free(vnodepath);
+  return r;
+}
+
+
+static afs_uint32 lose_cb(afs_vnode *v, XFILE *F, void *refcon)
+{
+  int r;
+
+  if (!dirs_done) {
+    dirs_done = 1;
+    if (verbose) printf("* Extracting files...\n");
+  }
+
+  return 0;
+}
+
+
+/* Main program */
+void main(int argc, char **argv)
+{
+  XFILE input_file;
+  afs_uint32 r;
+
+  parse_options(argc, argv);
+  initialize_acfg_error_table();
+  initialize_AVds_error_table();
+  initialize_rxk_error_table();
+  initialize_u_error_table();
+  initialize_vl_error_table();
+  initialize_vols_error_table();
+  initialize_xFil_error_table();
+  r = xfopen(&input_file, O_RDONLY, input_path);
+  if (r) {
+    com_err(argv0, r, "opening %s", input_path);
+    exit(2);
+  }
+
+  memset(&dp, 0, sizeof(dp));
+  dp.cb_error       = my_error_cb;
+  if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK;
+  dirs_done = 0;
+
+  if (!use_vnum) {
+    u_int64 where;
+
+    memset(&phi, 0, sizeof(phi));
+    phi.p = &dp;
+
+    if (verbose) printf("* Building pathname info...\n");
+    if ((r = xftell(&input_file, &where))
+    ||  (r = Path_PreScan(&input_file, &phi, 1))
+    ||  (r = xfseek(&input_file, &where))) {
+      com_err(argv0, r, "- path initialization failed");
+      xfclose(&input_file);
+      exit(1);
+    }
+  }
+
+  dp.cb_vnode_dir   = directory_cb;
+  dp.cb_vnode_file  = file_cb;
+  dp.cb_vnode_link  = symlink_cb;
+  dp.cb_vnode_empty = lose_cb;
+  dp.cb_vnode_wierd = lose_cb;
+  if (do_headers) {
+    dp.cb_dumphdr   = dumphdr_cb;
+    dp.cb_volhdr    = volhdr_cb;
+  }
+
+  if (!nomode) {
+    mkdir(target, 0755);
+    if (chdir(target)) {
+      fprintf(stderr, "chdir %s failed: %s\n", target, strerror(errno));
+      exit(1);
+    }
+  }
+  r = ParseDumpFile(&input_file, &dp);
+
+  if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count);
+  if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r));
+  exit(0);
+}
diff --git a/src/tests/afsdump_scan.c b/src/tests/afsdump_scan.c
new file mode 100644 (file)
index 0000000..ae95d8b
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* afsdump_scan.c - General-purpose dump scanner */
+
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "dumpscan.h"
+
+extern int optind;
+extern char *optarg;
+
+extern XFILE repair_output;
+extern afs_uint32 repair_dumphdr_cb(afs_dump_header *, XFILE *, void *);
+extern afs_uint32 repair_volhdr_cb(afs_vol_header *, XFILE *, void *);
+extern afs_uint32 repair_vnode_cb(afs_vnode *, XFILE *, void *);
+
+char *argv0;
+static char *input_path, *gendump_path;
+static afs_uint32 printflags, repairflags;
+static int quiet, verbose, error_count;
+
+static path_hashinfo phi;
+static dump_parser dp;
+
+
+/* Print a usage message and exit */
+static void usage(int status, char *msg)
+{
+  if (msg) fprintf(stderr, "%s: %s\n", argv0, msg);
+  fprintf(stderr, "Usage: %s [options] [file]\n", argv0);
+  fprintf(stderr, "  -Pxxx  Set print options:\n");
+  fprintf(stderr, "          B = Print backup system header (if any)\n");
+  fprintf(stderr, "          H = Print AFS dump header\n");
+  fprintf(stderr, "          V = Print AFS volume header\n");
+  fprintf(stderr, "          v = List vnodes\n");
+  fprintf(stderr, "          p = Include path to each vnode\n");
+  fprintf(stderr, "          i = Include info for each vnode\n");
+  fprintf(stderr, "          d = List directory contents\n");
+  fprintf(stderr, "          a = List access control lists\n");
+  fprintf(stderr, "          g = Print debugging info\n");
+  fprintf(stderr, "  -Rxxx  Set repair options:\n");
+  fprintf(stderr, "          0 = Skip null tags\n");
+  fprintf(stderr, "          b = Seek backward to find skipped tags\n");
+  fprintf(stderr, "          d = Resync after vnode data\n");
+  fprintf(stderr, "          v = Resync after corrupted vnodes\n");
+  fprintf(stderr, "  -h     Print this help message\n");
+  fprintf(stderr, "  -gxxx  Generate a new dump in file xxx\n");
+  fprintf(stderr, "  -q     Quiet mode (don't print errors)\n");
+  fprintf(stderr, "  -v     Verbose mode\n");
+  exit(status);
+}
+
+
+/* Parse the argument given to the -P option.
+ * Returns the resulting * dumpscan print flags (DSPRINT_*).
+ * If an unrecognized flag is used, prints an error message and exits.
+ */
+static afs_uint32 parse_printflags(char *flags)
+{
+  afs_uint32 result = 0;
+  char *x;
+
+  for (x = flags; *x; x++) switch (*x) {
+    case 'B': result |= DSPRINT_BCKHDR;  continue;
+    case 'H': result |= DSPRINT_DUMPHDR; continue;
+    case 'V': result |= DSPRINT_VOLHDR;  continue;
+    case 'v': result |= DSPRINT_ITEM;    continue;
+    case 'p': result |= DSPRINT_PATH;    continue;
+    case 'i': result |= DSPRINT_VNODE;   continue;
+    case 'd': result |= DSPRINT_DIR;     continue;
+    case 'a': result |= DSPRINT_ACL;     continue;
+    case 'g': result |= DSPRINT_DEBUG;   continue;
+    default:  usage(1, "Invalid print options!");
+  }
+  return result;
+}
+
+
+/* Parse the argument given to the -R option.
+ * Returns the resulting * dumpscan repair flags (DSFIX_*).
+ * If an unrecognized flag is used, prints an error message and exits.
+ */
+static afs_uint32 parse_repairflags(char *flags)
+{
+  afs_uint32 result = 0;
+  char *x;
+
+  for (x = flags; *x; x++) switch (*x) {
+    case '0': result |= DSFIX_SKIP;   continue;
+    case 'b': result |= DSFIX_RSKIP;  continue;
+    case 'd': result |= DSFIX_VDSYNC; continue;
+    case 'v': result |= DSFIX_VFSYNC; continue;
+    default:  usage(1, "Invalid repair options!");
+  }
+  return result;
+}
+
+
+/* Parse the command-line options */
+static void parse_options(int argc, char **argv)
+{
+  int c;
+
+  /* Set the program name */
+  if (argv0 = strrchr(argv[0], '/')) argv0++;
+  else argv0 = argv[0];
+
+  /* Initialize options */
+  input_path = gendump_path = 0;
+  printflags = repairflags = 0;
+  quiet = verbose = 0;
+
+  /* Initialize other stuff */
+  error_count = 0;
+
+  /* Parse the options */
+  while ((c = getopt(argc, argv, "P:R:g:hqv")) != EOF) {
+    switch (c) {
+      case 'P': printflags   = parse_printflags(optarg);  continue;
+      case 'R': repairflags  = parse_repairflags(optarg); continue;
+      case 'g': gendump_path = optarg;                    continue;
+      case 'q': quiet        = 1;                         continue;
+      case 'v': verbose      = 1;                         continue;
+      case 'h': usage(0, 0);
+      default:  usage(1, "Invalid option!");
+    }
+  }
+
+  if (quiet && verbose) usage(1, "Can't specify both -q and -v");
+
+  /* Parse non-option arguments */
+  if (argc - optind > 1) usage(1, "Too many arguments!");
+  input_path = (argc == optind) ? "-" : argv[optind];
+}
+
+
+/* A callback to count and print errors */
+static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...)
+{
+  va_list alist;
+
+  error_count++;
+  if (!quiet) {
+    va_start(alist, msg);
+    com_err_va(argv0, code, msg, alist);
+    va_end(alist);
+  }
+}
+
+
+/* A callback to print the path of a vnode. */
+static afs_uint32 print_vnode_path(afs_vnode *v, XFILE *X, void *refcon)
+{
+  afs_uint32 r;
+  char *name = 0;
+
+  /* Do repair, but only for known vnode types */
+  if (gendump_path
+  &&  (!(v->field_mask & F_VNODE_TYPE)
+       || v->type != vFile
+       || v->type != vDirectory
+       || v->type != vSymlink)) {
+    r = repair_vnode_cb(v, X, refcon);
+    if (r) return r;
+  }
+  r = Path_Build(X, &phi, v->vnode, &name, 0);
+  if (!r && name) printf(" Path: %s\n", name);
+  if (name) free(name);
+  return r;
+}
+
+
+/* Setup for generating a repaired dump */
+static afs_uint32 setup_repair(void)
+{
+  afs_uint32 r;
+
+  r = xfopen(&repair_output, O_RDWR|O_CREAT|O_TRUNC, gendump_path);
+  if (r) return r;
+
+  dp.cb_dumphdr     = repair_dumphdr_cb;
+  dp.cb_volhdr      = repair_volhdr_cb;
+  dp.cb_vnode_dir   = repair_vnode_cb;
+  dp.cb_vnode_file  = repair_vnode_cb;
+  dp.cb_vnode_link  = repair_vnode_cb;
+  dp.cb_vnode_empty = repair_vnode_cb;
+  return 0;
+}
+
+
+/* Main program */
+void main(int argc, char **argv)
+{
+  XFILE input_file;
+  afs_uint32 r;
+
+  parse_options(argc, argv);
+  initialize_acfg_error_table();
+  initialize_AVds_error_table();
+  initialize_rxk_error_table();
+  initialize_u_error_table();
+  initialize_vl_error_table();
+  initialize_vols_error_table();
+  initialize_xFil_error_table();
+  r = xfopen(&input_file, O_RDONLY, input_path);
+  if (r) {
+    com_err(argv0, r, "opening %s", input_path);
+    exit(2);
+  }
+
+  memset(&dp, 0, sizeof(dp));
+  dp.cb_error     = my_error_cb;
+  dp.repair_flags = repairflags;
+  if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK;
+  else {
+    if (repairflags)
+      fprintf(stderr, "Repair modes available only for seekable dumps\n");
+    if (printflags & DSPRINT_PATH)
+      fprintf(stderr, "Path-printing available only for seekable dumps\n");
+    if (repairflags || (printflags & DSPRINT_PATH))
+      exit(1);
+  }
+
+  if (gendump_path && (r = setup_repair())) {
+    com_err(argv0, r, "setting up repair output");
+    xfclose(&input_file);
+    exit(2);
+  }
+
+  if (printflags & DSPRINT_PATH) {
+    u_int64 where;
+
+    dp.print_flags = printflags & DSPRINT_DEBUG;
+    memset(&phi, 0, sizeof(phi));
+    phi.p = &dp;
+
+    if ((r = xftell(&input_file, &where))
+    ||  (r = Path_PreScan(&input_file, &phi, 0))
+    ||  (r = xfseek(&input_file, &where))) {
+      com_err(argv0, r, "- path initialization failed");
+      xfclose(&input_file);
+      exit(2);
+    }
+
+    dp.cb_vnode_dir   = print_vnode_path;
+    dp.cb_vnode_file  = print_vnode_path;
+    dp.cb_vnode_link  = print_vnode_path;
+    dp.cb_vnode_empty = print_vnode_path;
+    dp.cb_vnode_wierd = print_vnode_path;
+  }
+
+  dp.print_flags  = printflags;
+  r = ParseDumpFile(&input_file, &dp);
+  xfclose(&input_file);
+  if (gendump_path) {
+    if (!r) r = DumpDumpEnd(&repair_output);
+    if (!r) r = xfclose(&repair_output);
+    else xfclose(&repair_output);
+  }
+
+  if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count);
+  if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r));
+  exit(0);
+}
diff --git a/src/tests/afsdump_xsed.c b/src/tests/afsdump_xsed.c
new file mode 100644 (file)
index 0000000..f4f9e0f
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * COPYRIGHT NOTICE
+ * Copyright (c) 1997 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ * 
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ * 
+ * Carnegie Mellon requests users of this software to return to
+ * 
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ * 
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* UB - Unified Backups */
+/* methods/afs/dumpscan/afsdump_scan.c - General-purpose dump scanner */
+
+#include "dumpscan.h"
+#include <sys/fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+extern int opterr, optind;
+extern char *optarg;
+
+extern XFILE repair_output;
+extern afs_uint32 repair_dumphdr_cb(afs_dump_header *, XFILE *, void *);
+extern afs_uint32 repair_volhdr_cb(afs_vol_header *, XFILE *, void *);
+extern afs_uint32 repair_vnode_cb(afs_vnode *, XFILE *, void *);
+
+char *argv0;
+static char *input_path, *gendump_path;
+static afs_uint32 printflags, repairflags, add_admin;
+static int quiet, verbose, error_count;
+
+static path_hashinfo phi;
+static dump_parser dp;
+
+
+/* Print a usage message and exit */
+static void usage(int status, char *msg)
+{
+  if (msg) fprintf(stderr, "%s: %s\n", argv0, msg);
+  fprintf(stderr, "Usage: %s [options] [file]\n", argv0);
+  fprintf(stderr, "  -Pxxx  Set print options:\n");
+  fprintf(stderr, "          B = Print backup system header (if any)\n");
+  fprintf(stderr, "          H = Print AFS dump header\n");
+  fprintf(stderr, "          V = Print AFS volume header\n");
+  fprintf(stderr, "          v = List vnodes\n");
+  fprintf(stderr, "          p = Include path to each vnode\n");
+  fprintf(stderr, "          i = Include info for each vnode\n");
+  fprintf(stderr, "          d = List directory contents\n");
+  fprintf(stderr, "          a = List access control lists\n");
+  fprintf(stderr, "          g = Print debugging info\n");
+  fprintf(stderr, "  -Rxxx  Set repair options:\n");
+  fprintf(stderr, "          0 = Skip null tags\n");
+  fprintf(stderr, "          b = Seek backward to find skipped tags\n");
+  fprintf(stderr, "          d = Resync after vnode data\n");
+  fprintf(stderr, "          v = Resync after corrupted vnodes\n");
+  fprintf(stderr, "  -Annn  Add all rights for ID nnn to every directory\n");
+  fprintf(stderr, "  -h     Print this help message\n");
+  fprintf(stderr, "  -gxxx  Generate a new dump in file xxx\n");
+  fprintf(stderr, "  -q     Quiet mode (don't print errors)\n");
+  fprintf(stderr, "  -v     Verbose mode\n");
+  exit(status);
+}
+
+
+/* Parse the argument given to the -P option.
+ * Returns the resulting * dumpscan print flags (DSPRINT_*).
+ * If an unrecognized flag is used, prints an error message and exits.
+ */
+static afs_uint32 parse_printflags(char *flags)
+{
+  afs_uint32 result = 0;
+  char *x;
+
+  for (x = flags; *x; x++) switch (*x) {
+    case 'B': result |= DSPRINT_BCKHDR;  continue;
+    case 'H': result |= DSPRINT_DUMPHDR; continue;
+    case 'V': result |= DSPRINT_VOLHDR;  continue;
+    case 'v': result |= DSPRINT_ITEM;    continue;
+    case 'p': result |= DSPRINT_PATH;    continue;
+    case 'i': result |= DSPRINT_VNODE;   continue;
+    case 'd': result |= DSPRINT_DIR;     continue;
+    case 'a': result |= DSPRINT_ACL;     continue;
+    case 'g': result |= DSPRINT_DEBUG;   continue;
+    default:  usage(1, "Invalid print options!");
+  }
+  return result;
+}
+
+
+/* Parse the argument given to the -R option.
+ * Returns the resulting * dumpscan repair flags (DSFIX_*).
+ * If an unrecognized flag is used, prints an error message and exits.
+ */
+static afs_uint32 parse_repairflags(char *flags)
+{
+  afs_uint32 result = 0;
+  char *x;
+
+  for (x = flags; *x; x++) switch (*x) {
+    case '0': result |= DSFIX_SKIP;    continue;
+    case 'b': result |= DSFIX_RSKIP;   continue;
+    case 'd': result |= DSFIX_VDSYNC;  continue;
+    case 'v': result |= DSFIX_VFSYNC;  continue;
+    default:  usage(1, "Invalid repair options!");
+  }
+  return result;
+}
+
+
+/* Parse the command-line options */
+static void parse_options(int argc, char **argv)
+{
+  int c;
+
+  /* Set the program name */
+  if (argv0 = strrchr(argv[0], '/')) argv0++;
+  else argv0 = argv[0];
+
+  /* Initialize options */
+  input_path = gendump_path = 0;
+  printflags = repairflags = add_admin = 0;
+  quiet = verbose = 0;
+
+  /* Initialize other stuff */
+  error_count = 0;
+
+  /* Parse the options */
+  while ((c = getopt(argc, argv, "A:P:R:g:hv")) != EOF) {
+    switch (c) {
+      case 'A': add_admin    = atoi(optarg);              continue;
+      case 'P': printflags   = parse_printflags(optarg);  continue;
+      case 'R': repairflags  = parse_repairflags(optarg); continue;
+      case 'g': gendump_path = optarg;                    continue;
+      case 'q': quiet        = 1;                         continue;
+      case 'v': verbose      = 1;                         continue;
+      case 'h': usage(0, 0);
+      default:  usage(1, "Invalid option!");
+    }
+  }
+
+  if (quiet && verbose) usage(1, "Can't specify both -q and -v");
+
+  /* Parse non-option arguments */
+  if (argc - optind > 1) usage(1, "Too many arguments!");
+  input_path = (argc == optind) ? "-" : argv[optind];
+  if (add_admin && !gendump_path) add_admin = 0;
+}
+
+
+/* A callback to count and print errors */
+static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...)
+{
+  va_list alist;
+
+  error_count++;
+  if (!quiet) {
+    va_start(alist, msg);
+    com_err_va(argv0, code, msg, alist);
+    va_end(alist);
+  }
+}
+
+
+/* A callback to print the path of a vnode. */
+static afs_uint32 print_vnode_path(afs_vnode *v, XFILE *X, void *refcon)
+{
+  afs_uint32 r;
+  char *name = 0;
+
+  /* Do repair, but only for known vnode types */
+  if (gendump_path
+  &&  (!(v->field_mask & F_VNODE_TYPE)
+       || v->type != vFile
+       || v->type != vDirectory
+       || v->type != vSymlink)) {
+    r = repair_vnode_cb(v, X, refcon);
+    if (r) return r;
+  }
+  r = Path_Build(X, &phi, v->vnode, &name, 0);
+  if (!r && name) printf(" Path: %s\n", name);
+  if (name) free(name);
+  return r;
+}
+
+
+static afs_uint32 munge_admin_acl(afs_vnode *v, XFILE *X, void *refcon)
+{
+  struct acl_accessList *acl;
+  int add_entry = 1, remove_entry = -1;
+  int i, o, n;
+
+  acl = (struct acl_accessList *)(v->acl);
+  o = n = ntohl(acl->positive);
+  for (i = 0; i < n; i++)
+    if (ntohl(acl->entries[i].id) == add_admin) add_entry = 0;
+  n = ntohl(acl->negative);
+  for (i = o; i < n + o; i++)
+    if (ntohl(acl->entries[i].id) == add_admin) remove_entry = i;
+
+  if (add_entry) {
+    for (i = (remove_entry < 0) ? o + n : remove_entry; i > o; i--) {
+      acl->entries[i].id     = acl->entries[i-1].id;
+      acl->entries[i].rights = acl->entries[i-1].rights;
+    }
+    acl->entries[o].id = htonl(add_admin);
+    acl->entries[o].rights = htonl((PRSFS_READ   | PRSFS_LOOKUP
+                                  | PRSFS_INSERT | PRSFS_DELETE
+                                  | PRSFS_WRITE  | PRSFS_LOCK
+                                  | PRSFS_ADMINISTER));
+    acl->positive = htonl(o + 1);
+    if (remove_entry < 0) acl->total = htonl(o + n + 1);
+    else acl->negative = htonl(n - 1);
+  } else if (remove_entry >= 0) {
+    for (i = remove_entry; i < o + n - 1; i++) {
+      acl->entries[i].id     = acl->entries[i+1].id;
+      acl->entries[i].rights = acl->entries[i+1].rights;
+    }
+    acl->negative = htonl(n - 1);
+    acl->total    = htonl(o + n - 1);
+  }
+  return repair_vnode_cb(v, X, refcon);
+}
+
+
+/* Setup for generating a repaired dump */
+static afs_uint32 setup_repair(void)
+{
+  afs_uint32 r;
+
+  r = xfopen(&repair_output, gendump_path, O_RDWR, 0644);
+  if (r) return r;
+
+  dp.cb_dumphdr     = repair_dumphdr_cb;
+  dp.cb_volhdr      = repair_volhdr_cb;
+  dp.cb_vnode_dir   = repair_vnode_cb;
+  dp.cb_vnode_file  = repair_vnode_cb;
+  dp.cb_vnode_link  = repair_vnode_cb;
+  dp.cb_vnode_empty = repair_vnode_cb;
+  return 0;
+}
+
+
+/* Main program */
+void main(int argc, char **argv)
+{
+  XFILE *X;
+  afs_uint32 r;
+
+  parse_options(argc, argv);
+  initialize_UB_error_table();
+  initialize_UBsp_error_table();
+  initialize_AVds_error_table();
+  r = xfopen(&X, input_path, O_RDONLY, 0);
+  if (r) {
+    com_err(argv0, r, "opening %s", input_path);
+    exit(2);
+  }
+
+  bzero(&dp, sizeof(dp));
+  dp.cb_error     = my_error_cb;
+  dp.repair_flags = repairflags;
+  if (X->is_seekable) dp.flags |= DSFLAG_SEEK;
+  else {
+    if (repairflags)
+      fprintf(stderr, "Repair modes available only for seekable dumps\n");
+    if (printflags & DSPRINT_PATH)
+      fprintf(stderr, "Path-printing available only for seekable dumps\n");
+    if (repairflags || (printflags & DSPRINT_PATH))
+      exit(1);
+  }
+
+  if (gendump_path && (r = setup_repair())) {
+    com_err(argv0, r, "setting up repair output");
+    xfclose(X);
+    exit(2);
+  }
+
+  if (printflags & DSPRINT_PATH) {
+    u_int64 where;
+
+    dp.print_flags = printflags & DSPRINT_DEBUG;
+    bzero(&phi, sizeof(phi));
+    phi.p = &dp;
+
+    if ((r = xftell(X, &where))
+    ||  (r = Path_PreScan(X, &phi, 0))
+    ||  (r = xfseek(X, &where))) {
+      com_err(argv0, r, "- path initialization failed");
+      xfclose(X);
+      exit(2);
+    }
+
+    dp.cb_vnode_dir   = print_vnode_path;
+    dp.cb_vnode_file  = print_vnode_path;
+    dp.cb_vnode_link  = print_vnode_path;
+    dp.cb_vnode_empty = print_vnode_path;
+    dp.cb_vnode_wierd = print_vnode_path;
+  }
+
+  if (add_admin) {
+    dp.cb_vnode_dir = munge_admin_acl;
+  }
+
+  dp.print_flags  = printflags;
+  r = ParseDumpFile(X, &dp);
+  if (gendump_path) {
+    if (!r) r = DumpDumpEnd(&repair_output);
+    if (!r) r = xfclose(&repair_output);
+    else xfclose(&repair_output);
+  }
+
+  if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count);
+  if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r));
+  exit(0);
+}
diff --git a/src/tests/backuphdr.c b/src/tests/backuphdr.c
new file mode 100644 (file)
index 0000000..88c9759
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* backuphdr.c - Parse and print backup system headers */
+
+#include <stdlib.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "stagehdr.h"
+
+afs_uint32 try_backuphdr(XFILE *X, char *tag, tagged_field *field,
+                      afs_uint32 value, tag_parse_info *pi,
+                      void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  backup_system_header bh;
+  u_int64 where;
+  afs_uint32 r;
+
+  /* Which header should we try (if any)? */
+  switch (*tag) {
+    case STAGE_VERSMIN: r = ParseStageHdr(X, tag, &bh); break;
+    default: return DSERR_MAGIC;
+  }
+  if (r) return r;
+
+  /* Do something with it... */
+  if (p->print_flags & DSPRINT_BCKHDR) PrintBackupHdr(&bh);
+  if (p->cb_bckhdr) {
+    r = xftell(X, &where);
+    if (!r && p->cb_bckhdr)
+      r = (p->cb_bckhdr)(&bh, X, p->refcon);
+    if (p->flags & DSFLAG_SEEK) {
+      if (!r) r = xfseek(X, &where);
+      else xfseek(X, &where);
+    }
+  }
+  if (bh.server)  free(bh.server);
+  if (bh.part)    free(bh.part);
+  if (bh.volname) free(bh.volname);
+  return r;
+}
+
+
+void PrintBackupHdr(backup_system_header *hdr)
+{
+  time_t from = hdr->from_date, to = hdr->to_date, dd = hdr->dump_date;
+
+  printf("* BACKUP SYSTEM HEADER\n");
+  printf(" Version:    %d\n", hdr->version);
+  printf(" Volume:     %s (%d)\n", hdr->volname, hdr->volid);
+  printf(" Location:   %s %s\n", hdr->server, hdr->part);
+  printf(" Level:      %d\n", hdr->level);
+  printf(" Range:      %d => %d\n", hdr->from_date, hdr->to_date);
+  printf("          == %s", ctime(&from));
+  printf("          => %s", ctime(&to));
+  printf(" Dump Time:  %d == %s", hdr->dump_date, ctime(&dd));
+  printf(" Dump Flags: 0x%08x\n", hdr->flags);
+  printf(" Length:     %d\n", hdr->dumplen);
+  printf(" File Num:   %d\n", hdr->filenum);
+}
diff --git a/src/tests/directory.c b/src/tests/directory.c
new file mode 100644 (file)
index 0000000..f270723
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* directory.c - Parse an AFS directory */
+/* See the end of this file for a description of the directory format */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "xf_errs.h"
+#include "dumpfmt.h"
+#include "internal.h"
+
+#include <afs/dir.h>
+
+static afs_dir_page page;
+
+#define allocbit(x) (page.header.freebitmap[(x)>>3] & (1 << ((x) & 7)))
+#define DPHE (DHE + 1)
+
+static void fixup(char *name, int l)
+{
+  name += 16;
+  l -= 15;
+
+  while (l-- > 0) {
+    name[0] = name[4];
+    name++;
+  }
+}
+
+afs_uint32 parse_directory(XFILE *X, dump_parser *p, afs_vnode *v,
+                        afs_uint32 size, int toeof)
+{
+  afs_dir_entry de;
+  int pgno, i, j, l, n;
+  afs_uint32 r;
+  u_int64 where;
+
+  if (p->print_flags & DSPRINT_DIR) {
+    printf("  VNode      Uniqifier   Name\n");
+    printf("  ========== ==========  ==============================\n");
+  }
+  if ((p->flags & DSFLAG_SEEK) && (r = xftell(X, &where))) return r;
+  for (pgno = 0; toeof || size; pgno++, size -= (toeof ? 0 : AFS_PAGESIZE)) {
+    if ((p->flags & DSFLAG_SEEK) && (r = xfseek(X, &where))) return r;
+    if (r = xfread(X, &page, AFS_PAGESIZE)) {
+      if (toeof && r == ERROR_XFILE_EOF) break;
+      return r;
+    }
+    if ((p->flags & DSFLAG_SEEK) && (r = xftell(X, &where))) return r;
+    if (page.header.tag != htons(1234)) {
+      if (p->cb_error)
+        (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon,
+                      "Invalid page tag (%d) in page %d",
+                      ntohs(page.header.tag), pgno);
+      return DSERR_MAGIC;
+    }
+    for (i = (pgno ? 1 : DPHE); i < EPP; i++) {
+      if (!allocbit(i)) continue;
+      if (page.entry[i].flag != FFIRST) {
+        if (p->cb_error)
+          (p->cb_error)(DSERR_MAGIC, 0, p->err_refcon,
+                        "Invalid entry flag %d in entry %d/%d; skipping...",
+                        page.entry[i].flag, pgno, i);
+        continue;
+      }
+      n = (EPP - i - 1) * 32 + 16;
+      for (l = 0; n && page.entry[i].name[l]; l++, n--);
+      if (page.entry[i].name[l]) {
+        if (p->cb_error)
+          (p->cb_error)(DSERR_FMT, 0, p->err_refcon,
+                        "Filename too long in entry %d/%d; skipping page",
+                        pgno, i);
+        break;
+      }
+/*    fixup(page.entry[i].name, l); */
+      if (pgno) de.slot = i - 1 + (pgno - 1) * (EPP - 1) + (EPP - DPHE);
+      else de.slot = i - DPHE;
+      de.name  = page.entry[i].name;
+      de.vnode = ntohl(page.entry[i].vnode);
+      de.uniq  = ntohl(page.entry[i].vunique);
+      if (p->print_flags & DSPRINT_DIR)
+        printf("  %10d %10d  %s\n", de.vnode, de.uniq, de.name);
+      if (p->cb_dirent) {
+        r = (p->cb_dirent)(v, &de, X, p->refcon);
+      }
+      if (p->cb_dirent && (r = (p->cb_dirent)(v, &de, X, p->refcon)))
+        return r;
+      i += ((l + 16) >> 5);
+    }
+  }
+  if ((p->flags & DSFLAG_SEEK) && (r = xfseek(X, &where))) return r;
+  return 0;
+}
+
+
+afs_uint32 ParseDirectory(XFILE *X, dump_parser *p, afs_uint32 size, int toeof)
+{
+  afs_uint32 r;
+
+  r = parse_directory(X, p, 0, size, toeof);
+}
+
+
+typedef struct {
+  char **name;
+  afs_uint32 *vnode;
+  afs_uint32 *vuniq;
+} dirlookup_stat;
+
+
+static afs_uint32 dirlookup_cb(afs_vnode *v, afs_dir_entry *de,
+                            XFILE *X, void *refcon)
+{
+  dirlookup_stat *s = (dirlookup_stat *)refcon;
+
+  if (s->name && s->name[0]) {                  /* Search by filename */
+    if (strcmp(de->name, s->name[0])) return 0; /* Not it! */
+    if (s->vnode) s->vnode[0] = de->vnode;
+    if (s->vuniq) s->vuniq[0] = de->uniq;
+  } else if (s->vnode) {                        /* Search by vnode */
+    if (de->vnode != s->vnode[0]) return 0;     /* Not it! */
+    if (s->name) {
+      s->name[0] = (char *)malloc(strlen(de->name) + 1);
+      if (!s->name[0]) return ENOMEM;
+      strcpy(s->name[0], de->name);
+    }
+    if (s->vuniq) s->vuniq[0] = de->uniq;
+  }
+  return DSERR_DONE;
+}
+
+
+/* Look up an entry in a directory, by name or vnode.
+ * If *name is NULL, we are looking up by vnode.
+ * Otherwise, we are looking for a filename.
+ * In any event, any of name, vnode, vuniq that are
+ * neither NULL nor the search key are filled in on
+ * success.
+ *
+ * Call this with X pointing to the start of the directory,
+ * and size set to the length of the directory.
+ * Returns 0 on success, whether or not the entry is found.
+ */
+afs_uint32 DirectoryLookup(XFILE *X, dump_parser *p, afs_uint32 size,
+                    char **name, afs_uint32 *vnode, afs_uint32 *vuniq)
+{
+  dump_parser my_p;
+  dirlookup_stat my_s;
+  afs_uint32 r;
+
+  memset(&my_s, 0, sizeof(my_s));
+  my_s.name  = name;
+  my_s.vnode = vnode;
+  my_s.vuniq = vuniq;
+
+  memset(&my_p, 0, sizeof(my_p));
+  my_p.refcon = (void *)&my_s;
+  my_p.err_refcon = p->err_refcon;
+  my_p.cb_error = p->cb_error;
+  my_p.cb_dirent  = dirlookup_cb;
+
+  r = parse_directory(X, &my_p, 0, size, 0);
+  if (!r) r = DSERR_DONE;
+  return handle_return(r, X, 0, p);
+}
+
+
+/* AFS directory format:
+ * AFS directories are stored in volume dumps in exactly the same format
+ * that is used on disk, which makes them relatively easy to dump and restore,
+ * but means we have to do some work to interpret them.
+ *
+ * The ACL for a directory is stored on disk in the last part of a "large"
+ * (directory) vnode.  This part of the vnode, which has fixed size
+ * SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE, is copied directly into
+ * the dump file with a tag of 'A' (VTAG_ACL).  The structure of this
+ * section is described in <afs/acl.h>.
+ *
+ * The name-to-vnode mappings are also stored exactly as they appear on
+ * disk, using the file data ('f') attribute.  As usual, this attribute
+ * consists of a 32-bit number containing the size, immediately followed
+ * by the data itself.  The interesting structures and constants are
+ * defined in <afs/dir.h>
+ * 
+ * A directory consists of one or more 'pages', each of which is 2K
+ * (AFS_PAGESIZE).  Each page contains EPP (currently 64) 'entries', each
+ * of which is 32 bytes.  The first page begins with a DirHeader, which
+ * is DHE entries long, and includes a PageHeader.  All other pages begin
+ * with just a PageHeader, which is 1 entry long.  Every other entry is
+ * a DirEntry, a DirXEntry (name extension), or unused.
+ *
+ * A Page Header contains the following elements:
+ * - pgcount    contains a count of the number of pages in the directory,
+ *              if the directory is new-style (>128 pages), or 0 if it is
+ *              old-style.  This field is meaningful only in the Dir Header.
+ * - tag        a magic number, which must be 1234
+ * - freecount  apparently unused
+ * - freebitmap A bitmap of free entries.  Each byte corresponds to 8
+ *              entries, with the least significant bit referring to the
+ *              first of those.  Each bit is set iff the corresponding
+ *              entry is allocated.  Entries used by the page and dir
+ *              headers are considered allocated.
+ *
+ * A Dir Header consists of a Page Header, followed by an allocation map
+ * and hash table.  The allocation map contains one byte for each of the
+ * first 128 pages; that byte contains the number of entries in that page
+ * that are allocated.  Every page that actually exists has at peast one
+ * entry allocated (the Page Header); if a byte in this map is 0, it means
+ * that the page does not yet exist.
+ *
+ * Each bucket in the hash table is a linked list, using 'blob numbers'
+ * as pointers.  A blob number is defined as (page# * EPP) + entry#.
+ * The head of each chain is kept in the hash table, and the next pointers
+ * are kept in the 'next' entry of each directory.
+ *
+ * Directory entries themselves contain the following elements:
+ * - flag    Set to FFIRST iff this is the first blob in an entry
+ *           (otherwise it will be a name continuation).  This is
+ *           probably not reliable.
+ * - length  Unused
+ * - next    Pointer to the next element in this hash chain
+ * - fid     FileID (vnode and uniquifier)
+ * - name    Filename (null-terminated)
+ */
diff --git a/src/tests/dump.c b/src/tests/dump.c
new file mode 100644 (file)
index 0000000..8d228fd
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* dump.c - Write out parts of a volume dump */
+
+#include "dumpscan.h"
+#include "dumpfmt.h"
+
+#define COPYBUFSIZE 65536
+
+afs_uint32 DumpDumpHeader(XFILE *OX, afs_dump_header *hdr)
+{
+  afs_uint32 r;
+
+  if (r = WriteTagInt32Pair(OX, TAG_DUMPHEADER, hdr->magic, hdr->version))
+    return r;
+
+  if (hdr->field_mask & F_DUMPHDR_VOLID) {
+    if (r = WriteTagInt32(OX, DHTAG_VOLID, hdr->volid)) return r;
+  }
+  if (hdr->field_mask & F_DUMPHDR_VOLNAME) {
+    if (r = WriteByte(OX, DHTAG_VOLNAME)) return r;
+    if (r = WriteString(OX, hdr->volname)) return r;
+  }
+  if (hdr->field_mask & (F_DUMPHDR_FROM | F_DUMPHDR_TO)) {
+    if (r = WriteTagInt16(OX, DHTAG_DUMPTIMES, 2))
+      return r;
+    if (r = WriteInt32(OX, (hdr->field_mask & F_DUMPHDR_FROM)
+                       ? hdr->from_date : 0))
+      return r;
+    if (r = WriteInt32(OX, (hdr->field_mask & F_DUMPHDR_TO)
+                       ? hdr->to_date : time(0)))
+      return r;
+  }
+  return 0;
+}
+
+
+afs_uint32 DumpVolumeHeader(XFILE *OX, afs_vol_header *hdr)
+{
+  afs_uint32 r;
+  int i;
+
+  if (r = WriteByte(OX, TAG_VOLHEADER)) return r;
+
+  if (hdr->field_mask & F_VOLHDR_VOLID) {
+    if (r = WriteTagInt32(OX, VHTAG_VOLID, hdr->volid)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_VOLVERS) {
+    if (r = WriteTagInt32(OX, VHTAG_VERS, hdr->volvers)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_VOLNAME) {
+    if (r = WriteByte(OX, VHTAG_VOLNAME)) return r;
+    if (r = WriteString(OX, hdr->volname)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_INSERV) {
+    if (r = WriteTagByte(OX, VHTAG_INSERV, hdr->flag_inservice)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_BLESSED) {
+    if (r = WriteTagByte(OX, VHTAG_BLESSED, hdr->flag_blessed)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_VOLUNIQ) {
+    if (r = WriteTagInt32(OX, VHTAG_VUNIQ, hdr->voluniq)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_VOLTYPE) {
+    if (r = WriteTagByte(OX, VHTAG_TYPE, hdr->voltype)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_PARENT) {
+    if (r = WriteTagInt32(OX, VHTAG_PARENT, hdr->parent_volid)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_CLONE) {
+    if (r = WriteTagInt32(OX, VHTAG_CLONE, hdr->clone_volid)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_MAXQ) {
+    if (r = WriteTagInt32(OX, VHTAG_MAXQUOTA, hdr->maxquota)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_MINQ) {
+    if (r = WriteTagInt32(OX, VHTAG_MINQUOTA, hdr->minquota)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_DISKUSED) {
+    if (r = WriteTagInt32(OX, VHTAG_DISKUSED, hdr->diskused)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_NFILES) {
+    if (r = WriteTagInt32(OX, VHTAG_FILECNT, hdr->nfiles)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_ACCOUNT) {
+    if (r = WriteTagInt32(OX, VHTAG_ACCOUNT, hdr->account_no)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_OWNER) {
+    if (r = WriteTagInt32(OX, VHTAG_OWNER, hdr->owner)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_CREATE_DATE) {
+    if (r = WriteTagInt32(OX, VHTAG_CREAT, hdr->create_date)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_ACCESS_DATE) {
+    if (r = WriteTagInt32(OX, VHTAG_ACCESS, hdr->access_date)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_UPDATE_DATE) {
+    if (r = WriteTagInt32(OX, VHTAG_UPDATE, hdr->update_date)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_EXPIRE_DATE) {
+    if (r = WriteTagInt32(OX, VHTAG_EXPIRE, hdr->expire_date)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_BACKUP_DATE) {
+    if (r = WriteTagInt32(OX, VHTAG_BACKUP, hdr->backup_date)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_OFFLINE_MSG) {
+    if (r = WriteTagInt32(OX, VHTAG_OFFLINE, hdr->offline_msg)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_MOTD) {
+    if (r = WriteTagInt32(OX, VHTAG_MOTD, hdr->motd_msg)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_WEEKUSE) {
+    if (r = WriteTagInt16(OX, VHTAG_WEEKUSE, 7)) return r;
+    for (i = 0; i < 7; i++)
+      if (r = WriteInt32(OX, hdr->weekuse[i])) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_DAYUSE_DATE) {
+    if (r = WriteTagInt32(OX, VHTAG_DUDATE, hdr->dayuse_date)) return r;
+  }
+  if (hdr->field_mask & F_VOLHDR_DAYUSE) {
+    if (r = WriteTagInt32(OX, VHTAG_DAYUSE, hdr->dayuse)) return r;
+  }
+  return 0;
+}
+
+
+afs_uint32 DumpVNode(XFILE *OX, afs_vnode *v)
+{
+  afs_uint32 r;
+
+  if (r = WriteTagInt32Pair(OX, TAG_VNODE, v->vnode, v->vuniq)) return r;
+
+  if (v->field_mask & F_VNODE_TYPE) {
+    if (r = WriteTagByte(OX, VTAG_TYPE, v->type)) return r;
+  }
+  if (v->field_mask & F_VNODE_NLINKS) {
+    if (r = WriteTagInt16(OX, VTAG_NLINKS, v->nlinks)) return r;
+  }
+  if (v->field_mask & F_VNODE_DVERS) {
+    if (r = WriteTagInt32(OX, VTAG_DVERS, v->datavers)) return r;
+  }
+  if (v->field_mask & F_VNODE_SDATE) {
+    if (r = WriteTagInt32(OX, VTAG_SERVER_DATE, v->server_date)) return r;
+  }
+  if (v->field_mask & F_VNODE_AUTHOR) {
+    if (r = WriteTagInt32(OX, VTAG_AUTHOR, v->author)) return r;
+  }
+  if (v->field_mask & F_VNODE_OWNER) {
+    if (r = WriteTagInt32(OX, VTAG_OWNER, v->owner)) return r;
+  }
+  if (v->field_mask & F_VNODE_GROUP) {
+    if (r = WriteTagInt32(OX, VTAG_GROUP, v->group)) return r;
+  }
+  if (v->field_mask & F_VNODE_MODE) {
+    if (r = WriteTagInt16(OX, VTAG_MODE, v->mode)) return r;
+  }
+  if (v->field_mask & F_VNODE_PARENT) {
+    if (r = WriteTagInt32(OX, VTAG_PARENT, v->parent)) return r;
+  }
+  if (v->field_mask & F_VNODE_CDATE) {
+    if (r = WriteTagInt32(OX, VTAG_CLIENT_DATE, v->client_date)) return r;
+  }
+  if (v->field_mask & F_VNODE_ACL) {
+    if (r = WriteByte(OX, VTAG_ACL)) return r;
+    if (r = xfwrite(OX, v->acl, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE))
+      return r;
+  }
+  return 0;
+}
+
+
+afs_uint32 DumpVNodeData(XFILE *OX, char *buf, afs_uint32 size)
+{
+  afs_uint32 r;
+
+  if (r = WriteTagInt32(OX, VTAG_DATA, size)) return r;
+  if (r = xfwrite(OX, buf, size)) return r;
+  return 0;
+}
+
+
+afs_uint32 CopyVNodeData(XFILE *OX, XFILE *X, afs_uint32 size)
+{
+  afs_uint32 r, n;
+  static char buf[COPYBUFSIZE];
+
+  if (r = WriteTagInt32(OX, VTAG_DATA, size)) return r;
+  while (size) {
+    n = (size > COPYBUFSIZE) ? COPYBUFSIZE : size;
+    if (r = xfread(X, buf, n)) return r;
+    if (r = xfwrite(OX, buf, n)) return r;
+    size -= n;
+  }
+  return 0;
+}
+
+
+afs_uint32 DumpDumpEnd(XFILE *OX) {
+  afs_uint32 r;
+
+  if (r = WriteTagInt32(OX, TAG_DUMPEND, DUMPENDMAGIC)) return r;
+  return 0;
+}
diff --git a/src/tests/dumpfmt.h b/src/tests/dumpfmt.h
new file mode 100644 (file)
index 0000000..766b697
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* dumpfmt.h - Description of AFS dump format */
+
+#ifndef _DUMPFMT_H_
+#define _DUMPFMT_H_
+
+#include "intNN.h"
+
+/* AFS dump file format:
+ * All data in AFS dumps is tagged; that is, each data item is preceeded
+ * by a 1-byte tag which identifies what the data item is.  There is no
+ * explicit mention of what the data type is, but the type of each possible
+ * data item (and thus, each possible tag) is fixed.  Usually this is
+ * a relatively simple, fixed amount of data (byte, short, word), but
+ * sometimes it is more complex.
+ *
+ * There is some amount of structure to an AFS volume dump.  Basically,
+ * you get a dump header, followed by a volume header, followed by some
+ * vnodes, followed by a dump end.  Each of these items (header, vnode,
+ * dump end) consists of a tag, a fixed amount of required information,
+ * and 0 or more tagged attributes (except dump-end, which has no attributes).
+ *
+ * Vnodes, in turn, are usually listed in a particular order.  First, we
+ * list all the directory vnodes in the volume, in increasing order by
+ * vnode.  Then, we list all the file vnodes, again in increasing order.
+ * Directory vnodes must have a complete set of attributes and data, but
+ * in an incremental dump, file vnodes may have no attributes if the vnode
+ * has not changed since the reference date.
+ *
+ * The primary purpose of this file is to define the tags and some magic
+ * numbers.  There is also some information that is defined in the Transarc
+ * provided header files.
+ */
+
+
+/** MAGIC NUMBERS **/
+#define DUMPVERSION     1
+#define DUMPBEGINMAGIC  0xb3a11322
+#define DUMPENDMAGIC    0x3a214b6e
+
+
+/** TOP-LEVEL TAGS **/
+#define TAG_DUMPHEADER  1
+#define TAG_VOLHEADER   2
+#define TAG_VNODE       3
+#define TAG_DUMPEND     4
+
+
+/** DUMP HEADER TAGS **/
+#define DHTAG_VOLNAME    'n'
+#define DHTAG_VOLID      'v'
+#define DHTAG_DUMPTIMES  't'
+
+
+/** VOLUME HEADER TAGS **/
+#define VHTAG_VOLID      'i'
+#define VHTAG_VERS       'v'
+#define VHTAG_VOLNAME    'n'
+#define VHTAG_INSERV     's'
+#define VHTAG_BLESSED    'b'
+#define VHTAG_VUNIQ      'u'
+#define VHTAG_TYPE       't'
+#define VHTAG_PARENT     'p'
+#define VHTAG_CLONE      'c'
+#define VHTAG_MAXQUOTA   'q'
+#define VHTAG_MINQUOTA   'm'
+#define VHTAG_DISKUSED   'd'
+#define VHTAG_FILECNT    'f'
+#define VHTAG_ACCOUNT    'a'
+#define VHTAG_OWNER      'o'
+#define VHTAG_CREAT      'C'
+#define VHTAG_ACCESS     'A'
+#define VHTAG_UPDATE     'U'
+#define VHTAG_EXPIRE     'E'
+#define VHTAG_BACKUP     'B'
+#define VHTAG_OFFLINE    'O'
+#define VHTAG_MOTD       'M'
+#define VHTAG_WEEKUSE    'W'
+#define VHTAG_DUDATE     'D'
+#define VHTAG_DAYUSE     'Z'
+
+
+/** VNODE TAGS **/
+#define VTAG_TYPE        't'
+#define VTAG_NLINKS      'l'
+#define VTAG_DVERS       'v'
+#define VTAG_CLIENT_DATE 'm'
+#define VTAG_AUTHOR      'a'
+#define VTAG_OWNER       'o'
+#define VTAG_GROUP       'g'
+#define VTAG_MODE        'b'
+#define VTAG_PARENT      'p'
+#define VTAG_SERVER_DATE 's'
+#define VTAG_ACL         'A'
+#define VTAG_DATA        'f'
+
+
+#define AFS_DIR_EPP 64
+
+typedef struct {
+  afs_uint16 pgcount;
+  afs_uint16 tag;
+  char freecount;
+  char freebitmap[AFS_DIR_EPP/8];
+  char padding[32 - (5 + AFS_DIR_EPP/8)];
+} afs_dir_pagehdr;
+
+typedef struct {
+  char flag;
+  char length;
+  afs_uint16 next;
+  afs_uint32 vnode;
+  afs_uint32 vunique;
+  char name[16];
+  char padding[4];
+} afs_dir_direntry;
+
+typedef union {
+  afs_dir_pagehdr header;
+  afs_dir_direntry entry[AFS_DIR_EPP];
+} afs_dir_page;
+
+#endif /* _DUMPFMT_H_ */
diff --git a/src/tests/dumpscan.h b/src/tests/dumpscan.h
new file mode 100644 (file)
index 0000000..a6b3a24
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* dumpscan.h - Public interface */
+
+#ifndef _DUMPSCAN_H_
+#define _DUMPSCAN_H_
+
+#include "intNN.h"
+#include "xfiles.h"
+
+#include <lock.h>
+#include <afs/afsint.h>
+#include <afs/nfs.h>
+#include <afs/ihandle.h>
+#include <afs/vnode.h>
+#include <afs/cmd.h>
+#include <afs/auth.h>
+#include <afs/bnode.h>
+#include <afs/cellconfig.h>
+#include <afs/kautils.h>
+#include <afs/pterror.h>
+#include <afs/vlserver.h>
+#include <afs/volser.h>
+#include <ubik.h>
+
+/* Random useful types */
+typedef struct tagged_field tagged_field;
+typedef struct tag_parse_info tag_parse_info;
+typedef afs_uint32 (*tag_parser)(XFILE *, unsigned char *, tagged_field *,
+                              afs_uint32, tag_parse_info *, void *, void *);
+
+/* Error codes used within dumpscan.
+ * Any of the routines declared below, or callbacks used by them,
+ * may signal a system error by returning the error number, or
+ * some other error by returning a com_err code.  Note that
+ * ParseTaggedData does _not_ return DSERR_TAG; instead, it returns
+ * 0, assuming the tag will be handled at a higher level.
+ *
+ * In addition, these errors may be reported to the caller of
+ * ParseDumpFile using the error callback.  Such reports will be
+ * issued whether or not error recovery is possible or attempted.
+ *
+ * NB: These errors are now in dumpscan_errs.h
+ */
+
+
+/* Backup system dump header */
+/* Right now, this looks a lot like an old stage header.  Eventually, it
+ * should contain enough fields to fully represent headers from old or
+ * new stage, Transarc, or other backup systems, and the appropriate read
+ * functions should extract as much data as possible from the actual file
+ * to fill this in. */
+typedef struct {
+  afs_uint32 version;
+  afs_uint32 from_date;
+  afs_uint32 to_date;
+  afs_uint32 dump_date;
+  afs_uint32 filenum;
+  unsigned char *server;
+  unsigned char *part;
+  unsigned char *volname;
+  afs_uint32 volid;
+  afs_uint32 dumplen;
+  afs_uint32 level;
+  afs_uint32 magic;
+  afs_uint32 cksum;
+  afs_uint32 flags;
+} backup_system_header;
+
+
+/** AFS dump header **/
+#define F_DUMPHDR_VOLID       0x00000001
+#define F_DUMPHDR_VOLNAME     0x00000002
+#define F_DUMPHDR_FROM        0x00000004
+#define F_DUMPHDR_TO          0x00000008
+typedef struct {
+  u_int64 offset;           /* Where in the file is it? */
+  afs_uint32 field_mask;       /* What fields are present? */
+  afs_uint32 magic;            /* Magic number */
+  afs_uint32 version;          /* Dump format version */
+  afs_uint32 volid;            /* VolID of volume in dump */
+  unsigned char *volname;   /* Name of volume in dump */
+  afs_uint32 from_date;        /* Reference date */
+  afs_uint32 to_date;          /* Date of dump */
+} afs_dump_header;
+
+
+/** AFS volume header **/
+#define F_VOLHDR_VOLID        0x00000001
+#define F_VOLHDR_VOLVERS      0x00000002
+#define F_VOLHDR_VOLNAME      0x00000004
+#define F_VOLHDR_INSERV       0x00000008
+#define F_VOLHDR_BLESSED      0x00000010
+#define F_VOLHDR_VOLUNIQ      0x00000020
+#define F_VOLHDR_VOLTYPE      0x00000040
+#define F_VOLHDR_PARENT       0x00000080
+#define F_VOLHDR_CLONE        0x00000100
+#define F_VOLHDR_MAXQ         0x00000200
+#define F_VOLHDR_MINQ         0x00000400
+#define F_VOLHDR_DISKUSED     0x00000800
+#define F_VOLHDR_NFILES       0x00001000
+#define F_VOLHDR_ACCOUNT      0x00002000
+#define F_VOLHDR_OWNER        0x00004000
+#define F_VOLHDR_CREATE_DATE  0x00008000
+#define F_VOLHDR_ACCESS_DATE  0x00010000
+#define F_VOLHDR_UPDATE_DATE  0x00020000
+#define F_VOLHDR_EXPIRE_DATE  0x00040000
+#define F_VOLHDR_BACKUP_DATE  0x00080000
+#define F_VOLHDR_OFFLINE_MSG  0x00100000
+#define F_VOLHDR_MOTD         0x00200000
+#define F_VOLHDR_WEEKUSE      0x00400000
+#define F_VOLHDR_DAYUSE       0x00800000
+#define F_VOLHDR_DAYUSE_DATE  0x01000000
+typedef struct {
+  u_int64 offset;           /* Where in the file is it? */
+  afs_uint32 field_mask;       /* What fields are present? */
+  afs_uint32 volid;            /* Volume ID */
+  afs_uint32 volvers;          /* ?? */
+  unsigned char *volname;   /* Volume Name */
+  int     flag_inservice;   /* Inservice flag (0 or not) */
+  int     flag_blessed;     /* Blessed to come online (0 or not) */
+  afs_uint32 voluniq;          /* Volume uniquifier */
+  int     voltype;          /* Volume type */
+  afs_uint32 parent_volid;     /* Parent volume ID */
+  afs_uint32 clone_volid;      /* Clone volume ID */
+  afs_uint32 maxquota;         /* Max quota */
+  afs_uint32 minquota;         /* Min quota (obsolete) */
+  afs_uint32 diskused;         /* Disk blocks used */
+  afs_uint32 nfiles;           /* Number of files in volume */
+  afs_uint32 account_no;       /* Account number (unused) */
+  afs_uint32 owner;            /* Volume owner */
+  afs_uint32 create_date;      /* Creation date of this copy */
+  afs_uint32 access_date;      /* Last access */
+  afs_uint32 update_date;      /* Last modification */
+  afs_uint32 expire_date;      /* Expiration (unused) */
+  afs_uint32 backup_date;      /* Last backup clone */
+  unsigned char *offline_msg; /* Offline message */
+  unsigned char *motd_msg;    /* Volume MOTD */
+  afs_uint32 weekuse[7];       /* Weekuse data */
+  afs_uint32 dayuse;           /* # accesses in last day */
+  afs_uint32 dayuse_date;      /* Date for which dayuse is valid */
+} afs_vol_header;
+
+
+/** AFS vnode **/
+#define F_VNODE_TYPE          0x00000001
+#define F_VNODE_NLINKS        0x00000002
+#define F_VNODE_PARENT        0x00000004
+#define F_VNODE_DVERS         0x00000008
+#define F_VNODE_AUTHOR        0x00000010
+#define F_VNODE_OWNER         0x00000020
+#define F_VNODE_GROUP         0x00000040
+#define F_VNODE_MODE          0x00000080
+#define F_VNODE_CDATE         0x00000100
+#define F_VNODE_SDATE         0x00000200
+#define F_VNODE_SIZE          0x00000800
+#define F_VNODE_DATA          0x00001000
+#define F_VNODE_ACL           0x00000400
+typedef struct {
+  u_int64 offset;           /* Where in the file is it? */
+  afs_uint32 field_mask;       /* What fields are present? */
+  afs_uint32 vnode;            /* Vnode number */
+  afs_uint32 vuniq;            /* Uniquifier */
+  int     type;             /* Vnode type */
+  afs_uint16 nlinks;           /* Number of links (should be in 1 dir!) */
+  afs_uint32 parent;           /* Parent vnode */
+  afs_uint32 datavers;         /* Data version */
+  afs_uint32 author;           /* Last writer */
+  afs_uint32 owner;            /* Owner UID */
+  afs_uint32 group;            /* Owning group */
+  afs_uint16 mode;             /* UNIX mode bits */
+  afs_uint32 client_date;      /* Last modified date from client */
+  afs_uint32 server_date;      /* Last modified date on server */
+  afs_uint32 size;             /* Size of data */
+  u_int64 d_offset;         /* Where in the file is the data? */
+  unsigned char acl[SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE];
+} afs_vnode;
+
+
+/** AFS directory entry **/
+typedef struct {
+  int  slot;                /* Directory slot # (info only) */
+  char *name;               /* Name of entry */
+  afs_uint32 vnode;            /* Vnode number */
+  afs_uint32 uniq;             /* Uniquifier */
+} afs_dir_entry;
+
+
+/** Tagged field definitions **/
+#define DKIND_NOOP      0x00  /* No data */
+#define DKIND_BYTE      0x10  /* 1 byte  - decimal */
+#define DKIND_HEX8      0x11  /* 1 byte  - hex */
+#define DKIND_CHAR      0x12  /* 1 byte  - character */
+#define DKIND_FLAG      0x13  /* 1 byte  - true/false */
+#define DKIND_INT16     0x20  /* 2 bytes - decimal */
+#define DKIND_HEX16     0x21  /* 2 bytes - hex */
+#define DKIND_INT32     0x30  /* 4 bytes - decimal */
+#define DKIND_HEX32     0x31  /* 4 bytes - hex */
+#define DKIND_TIME      0x32  /* 4 bytes - time */
+#define DKIND_STRING    0x40  /* ASCIIZ string */
+#define DKIND_SPECIAL   0x50  /* Custom parser */
+#define DKIND_MASK     (~0x0f)
+struct tag_parse_info {
+  void *err_refcon;
+  afs_uint32 (*cb_error)(afs_uint32, int, void *, char *, ...);
+  afs_uint32 flags;
+#define TPFLAG_SKIP   0x0001
+#define TPFLAG_RSKIP  0x0002
+  int shift_offset;
+  u_int64 shift_start;
+};
+struct tagged_field {
+  char tag;        /* Tag character */
+  int  kind;       /* Kind of object */
+  char *label;     /* Label to use (for debugging) */
+  tag_parser func; /* Parser function (for DKIND_SPECIAL) */
+  void *refptr;    /* Reference pointer (for parser's use) */
+  int  refarg;     /* Reference argument (for parser's use) */
+};
+
+
+/** Control structure for parsing volume dumps **/
+typedef struct {
+  /* Callback functions:
+   * Whenever a "complex" object is parsed, we call a callback function.
+   * The callback gets a pointer to the complex object, the file pointer
+   * for the dump we're parsing, and the value of refcon in this structure.
+   * Callbacks should return 0 if all is well, non-0 to abort the dump.
+   * By convention, positive numbers should be errno values, and negative
+   * numbers can be used for other things.  It is OK to _try_ to seek anywhere
+   * in the file.  Beware, though, that the input is not always seekable.
+   * Also, note that the structures passed to these callbacks are going to
+   * go away after the callback returns.  There is no way to prevent this;
+   * make a copy if you want one.
+   */
+  void *refcon;
+  afs_uint32 (*cb_bckhdr)(backup_system_header *, XFILE *, void *); /* Backup   */
+  afs_uint32 (*cb_dumphdr)(afs_dump_header *, XFILE *, void *); /* Dump hdr     */
+  afs_uint32 (*cb_volhdr)(afs_vol_header *, XFILE *, void *);   /* Volume hdr   */
+  afs_uint32 (*cb_vnode_dir)(afs_vnode *, XFILE *, void *);     /* Directory    */
+  afs_uint32 (*cb_vnode_file)(afs_vnode *, XFILE *, void *);    /* File         */
+  afs_uint32 (*cb_vnode_link)(afs_vnode *, XFILE *, void *);    /* Symlink      */
+  afs_uint32 (*cb_vnode_empty)(afs_vnode *, XFILE *, void *);   /* vnode+uniq   */
+  afs_uint32 (*cb_vnode_wierd)(afs_vnode *, XFILE *, void *);   /* Unknown type */
+
+  /* This function is called when there is an error in the dump. */
+  /* (cb_error)(errno, fatal, refcon, msg_fmt, msg_args...) */
+  void *err_refcon; /* If set, use instead of refcon for dir entries */
+  afs_uint32 (*cb_error)(afs_uint32, int, void *, char *, ...);
+
+  /* This function is called for each directory entry, if set */
+  afs_uint32 (*cb_dirent)(afs_vnode *, afs_dir_entry *, XFILE *, void *);
+
+  int flags;            /* Flags and options */
+#define DSFLAG_SEEK     0x0001  /* Input file is seekable */
+
+  int print_flags;      /* Flags to control what is printed */
+#define DSPRINT_BCKHDR  0x0001  /* Print backup system header */
+#define DSPRINT_DUMPHDR 0x0002  /* Print AFS dump header */
+#define DSPRINT_VOLHDR  0x0004  /* Print AFS volume header */
+#define DSPRINT_ITEM    0x0010  /* Print top-level tags */
+#define DSPRINT_VNODE   0x0020  /* Print vnode attributes */
+#define DSPRINT_ACL     0x0040  /* Print directory ACL's */
+#define DSPRINT_DIR     0x0080  /* Print directory contents */
+#define DSPRINT_DEBUG   0x0100  /* Print debugging info */
+#define DSPRINT_PATH    0x0200  /* Print vnode paths */
+
+  int repair_flags;     /* Flags to control what is repaired.
+                         * Most of these _require_ DSFLAG_SEEK */
+#define DSFIX_SKIP      0x0001  /* Try to skip null tags */
+#define DSFIX_RSKIP     0x0002  /* Seek back to fing skipped tags */
+#define DSFIX_VDSYNC    0x0004  /* Resync location after vnode data */
+#define DSFIX_VFSYNC    0x0008  /* Try to resync after bad vnode */
+
+  /** Things below this point for internal use only **/
+  afs_uint32 vol_uniquifier;
+} dump_parser;
+
+
+/** Hash table and control info for pathname manipulation **/
+typedef struct vhash_ent {
+  struct vhash_ent *next;    /* Pointer to next entry */
+  afs_uint32 vnode;             /* VNode number */
+  afs_uint32 parent;            /* Parent VNode number */
+  u_int64 v_offset;          /* Offset to start of vnode */
+  u_int64 d_offset;          /* Offset to data (0 if none) */
+  afs_uint32 d_size;            /* Size of data */
+} vhash_ent;
+typedef struct {
+  afs_uint32 n_vnodes;          /* Number of vnodes in volume */
+  afs_uint32 n_dirs;            /* Number of file vnodes */
+  afs_uint32 n_files;           /* Number of directory vnodes */
+  int hash_size;             /* Hash table size (bits) */
+  vhash_ent **hash_table;    /* Hash table */
+  dump_parser *p;            /* Dump parser to use */
+} path_hashinfo;
+
+
+/** Function prototypes **/
+/** Only the functions declared below are public interfaces **/
+/** Maybe someday, I'll write man pages for these **/
+
+/* primitive.c - I/O primitives */
+extern afs_uint32 ReadByte(XFILE *, unsigned char *);
+extern afs_uint32 ReadInt16(XFILE *, afs_uint16 *);
+extern afs_uint32 ReadInt32(XFILE *, afs_uint32 *);
+extern afs_uint32 ReadString(XFILE *, unsigned char **);
+extern afs_uint32 WriteByte(XFILE *, unsigned char);
+extern afs_uint32 WriteInt16(XFILE *, afs_uint16);
+extern afs_uint32 WriteInt32(XFILE *, afs_uint32);
+extern afs_uint32 WriteString(XFILE *, unsigned char *);
+extern afs_uint32 WriteTagByte(XFILE *, unsigned char, unsigned char);
+extern afs_uint32 WriteTagInt16(XFILE *, unsigned char, afs_uint16);
+extern afs_uint32 WriteTagInt32(XFILE *, unsigned char, afs_uint32);
+extern afs_uint32 WriteTagInt32Pair(XFILE *, unsigned char, afs_uint32, afs_uint32);
+
+/* parsetag.c - Parse tagged data */
+extern afs_uint32 ParseTaggedData(XFILE *, tagged_field *, unsigned char *,
+                           tag_parse_info *, void *, void *);
+
+/* stagehdr.c - Parse and dump Stage dump headers */
+extern afs_uint32 ParseStageHdr(XFILE *, unsigned char *, backup_system_header *);
+extern afs_uint32 DumpStagehdr(XFILE *, backup_system_header *);
+
+/* backuphdr.c - Parse and print backup system headers */
+extern void PrintBackupHdr(backup_system_header *);
+
+/* parsedump.c - Parse all or part of a volume dump */
+extern afs_uint32 ParseDumpFile(XFILE *, dump_parser *);
+extern afs_uint32 ParseDumpHeader(XFILE *, dump_parser *);
+extern afs_uint32 ParseVolumeHeader(XFILE *, dump_parser *);
+extern afs_uint32 ParseVNode(XFILE *, dump_parser *);
+
+
+/* directory.c - Directory parsing and lookup */
+extern afs_uint32 ParseDirectory(XFILE *, dump_parser *, afs_uint32, int);
+extern afs_uint32 DirectoryLookup(XFILE *, dump_parser *, afs_uint32,
+                           char **, afs_uint32 *, afs_uint32 *);
+
+/* dump.c - Dump parts of a volume dump */
+extern afs_uint32 DumpDumpHeader(XFILE *, afs_dump_header *);
+extern afs_uint32 DumpVolumeHeader(XFILE *, afs_vol_header *);
+extern afs_uint32 DumpVNode(XFILE *, afs_vnode *);
+extern afs_uint32 DumpVnodeData(XFILE *, char *, afs_uint32);
+extern afs_uint32 CopyVnodeData(XFILE *, XFILE *, afs_uint32);
+
+/* pathname.c - Follow and construct pathnames */
+extern afs_uint32 Path_PreScan(XFILE *, path_hashinfo *, int);
+extern void Path_FreeHashTable(path_hashinfo *);
+extern afs_uint32 Path_Follow(XFILE *, path_hashinfo *, char *, vhash_ent *);
+extern afs_uint32 Path_Build(XFILE *, path_hashinfo *, afs_uint32, char **, int);
+
+#endif
diff --git a/src/tests/dumpscan_errs.et b/src/tests/dumpscan_errs.et
new file mode 100644 (file)
index 0000000..22c38c3
--- /dev/null
@@ -0,0 +1,37 @@
+# COPYRIGHT NOTICE
+# Copyright (c) 1997 Carnegie Mellon University
+# All Rights Reserved.
+# 
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+# 
+# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+# CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+# 
+# Carnegie Mellon requests users of this software to return to
+# 
+#  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+#  School of Computer Science
+#  Carnegie Mellon University
+#  Pittsburgh PA 15213-3890
+# 
+# any improvements or extensions that they make and grant Carnegie Mellon
+# the rights to redistribute these changes.
+
+# UB - Unified Backups
+# methods/afs/dumpscan/afsdump_errs.et - AFS dump scanner errors
+
+error_table AVds
+  ec DSERR_TAG,            "Unknown tag in AFS volume dump"
+  ec DSERR_MAGIC,          "Bad magic number in AFS volume dump"
+  ec DSERR_BOGUS,          "Bogus value in AFS volume dump"
+  ec DSERR_FMT,            "AFS volume dump format incorrect"
+  ec DSERR_KEEP,           "[AFS dumpscan internal: keep string]"
+  ec DSERR_PANIC,          "[AFS dumpscan internal: panic]"
+  ec DSERR_DONE,           "[AFS dumpscan internal: done]"
+  ec DSERR_MEM,            "[AFS dumpscan internal: out of memory]"
+end
diff --git a/src/tests/dumptool.c b/src/tests/dumptool.c
new file mode 100644 (file)
index 0000000..5f6e73b
--- /dev/null
@@ -0,0 +1,2640 @@
+/*
+ * $Id$
+ *
+ * dumptool - A tool to manage MR-AFS dump files
+ *
+ * The dump file format ought to be documented _somewhere_, and
+ * this seems like a good as a place as any ...
+ *
+ * A AFS dump file is marked off into a series of sections.  Each
+ * section marked by a dump tag.  A tag is a single byte who's value
+ * corresponds with the next section.  The sections are (in order):
+ *
+ * DUMPHEADER (tag 0x01)
+ * VOLUMEHEADER (tag 0x02)
+ * VNODE (tag 0x03)
+ * DUMPEND (tag 0x04)
+ *
+ * Descriptions of the sections follow.  Note that in all cases, data is
+ * stored in the dump in network byte order.
+ *
+ * DUMPHEADER:
+ *
+ * DUMPHEADER contains two parts: the DUMPMAGIC magic number (32 bits)
+ * and the dump header itself.
+ *
+ * The dump header itself consists of a series of tagged values,
+ * each tag marking out members of the DumpHeader structure.  The
+ * routine ReadDumpHeader explains the specifics of these tags.
+ *
+ * VOLUMEHEADER:
+ *
+ * VOLUMEHEADER is a series of tagged values corresponding to the elements
+ * of the VolumeDiskData structure.  See ReadVolumeHeader for more
+ * information
+ *
+ * VNODE:
+ *
+ * The VNODE section is all vnodes contained in the volume (each vnode
+ * itself is marked with the VNODE tag, so it's really a sequence of
+ * VNODE tags, unlike other sections).
+ *
+ * Each vnode consists of three parts: the vnode number (32 bits), the
+ * uniqifier (32 bits), and a tagged list of elements corresponding to
+ * the elements of the VnodeDiskData structure.  See ScanVnodes for
+ * more information.  Note that if file data is associated with a vnode,
+ * it will be contained here.
+ *
+ * DUMPEND:
+ *
+ * The DUMPEND section consists of one part: the DUMPENDMAGIC magic
+ * number (32 bits).
+ * 
+ * Notes:
+ *
+ * The tagged elements are all ASCII letters, as opposed to the section
+ * headers (which are 0x01, 0x02, ...).  Thus, an easy way to tell when
+ * you've reached the end of an element sequence is to check to see if
+ * the next tag is a printable character (this code tests for < 20).
+ *
+ * "vos dump" dumps the large vnode index, then the small vnode index,
+ * so directories will appear first in the VNODE section.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <termios.h>
+#include <fnmatch.h>
+
+#include <lock.h>
+#include <afs/param.h>
+#include <afs/afsint.h>
+#include <afs/nfs.h>
+#include <afs/acl.h>
+#if !defined(PRE_AFS_36) && !defined(RESIDENCY)
+#include <afs/ihandle.h>
+#endif /* !defined(PRE_AFS_36) && !defined(RESIDENCY) */
+#include <afs/vnode.h>
+#include <afs/volume.h>
+
+#ifdef RESIDENCY
+#include <afs/rsdefs.h>
+#include <afs/remioint.h>
+#endif /* RESIDENCY */
+
+#include <afs/dir.h>
+
+/*
+ * Sigh.  Linux blows it again
+ */
+
+#ifdef linux
+#include <pty.h>
+#endif
+
+/*
+ * Stuff that is in private AFS header files, unfortunately
+ */
+
+#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
+
+struct DumpHeader {
+       int32_t version;
+       VolumeId volumeId;
+       char volumeName[VNAMESIZE];
+       int nDumpTimes;             /* Number of pairs */
+       struct {
+               int32_t from, to;
+       } dumpTimes[MAXDUMPTIMES];
+};
+
+/*
+ * Our command-line arguments
+ */
+
+#ifdef RESIDENCY
+struct {
+       int Algorithm;          /* Conversion algorithm */
+       int Size;               /* Directory hierarchy size */
+       int FSType;             /* File system type */
+       int DeviceTag;          /* Device Tag */
+} rscmdlineinfo[RS_MAXRESIDENCIES];
+
+/*
+ * This stuff comes from ufsname.c (which itself takes it from
+ * ufs_interfaces.c)
+ */
+
+/* There is an assumption that all of the prefixes will have exactly one '/' */
+static char *Ufs_Prefixes[] = {"/ufs", "/slowufs", "/cdmf", "/sdmf"};
+#define MAX_ITERATIONS 10
+#define UFS_SUMMARYTREENAME "Summaries"
+#define UFS_STAGINGTREENAME "Staging"
+#define UFS_VOLUMEHEADERTREENAME "VolHeaders"
+#define UFS_VOLUMETREENAME "Volumes"
+#define UFS_ALGORITHMBASE 'A'
+#define UFS_MOUNTPOINTBASE 'a'
+#define UFS_ALGORITHMS 3
+#define UFS_LINK_MAX 64 /* Arbitrary. */
+#define HARD_LINKED_FILE -2
+#define TAGSTONAME(FileName, MountPoint, Sections, Level1, RWVolume, Vnode, Uniquifier, Algorithm) \
+{ \
+    if (Level1) \
+        sprintf(FileName,"%s/%lu/%lu/%c%lu.%lu.%lu.%lu.%lu", MountPoint, \
+                (Sections)[0], (Sections)[1], UFS_ALGORITHMBASE + Algorithm, \
+                (Sections)[2], (Sections)[3], RWVolume, Vnode, Uniquifier); \
+    else \
+        sprintf(FileName,"%s/%lu/%c%lu.%lu.%lu.%lu.%lu", MountPoint, \
+                (Sections)[0], UFS_ALGORITHMBASE + Algorithm, \
+                (Sections)[1], (Sections)[2], RWVolume, Vnode, Uniquifier); \
+}
+#define TAGSTOSTAGINGNAME(FileName, MountPoint, RWVolume, Vnode, Uniquifier) \
+    sprintf(FileName,"%s/%s/%lu.%lu.%lu", MountPoint, \
+           UFS_STAGINGTREENAME, RWVolume, Vnode, Uniquifier)
+#define TAGSTOVOLUMEHEADERNAME(FileName, MountPoint, FileTag1, FileTag2) \
+    sprintf(FileName,"%s/%s/%lu", MountPoint, UFS_VOLUMEHEADERTREENAME, \
+           FileTag1)
+#define TAGSTOVOLUMEINFONAME(FileName, MountPoint, FileTag1, FileTag2) \
+    sprintf(FileName,"%s/%s/%lu/%lu", MountPoint, \
+           UFS_VOLUMETREENAME, FileTag2, FileTag1)
+#define TAGSTOVOLUMENAME(FileName, MountPoint, FileTag1, FileTag2, RWVolume) \
+    sprintf(FileName,"%s/%s/%lu/%lu.%lu", MountPoint, \
+           UFS_VOLUMETREENAME, FileTag2, FileTag1, RWVolume)
+#define TAGSTOSUMMARYNAME(FileName, MountPoint, FileTag1, FileTag2, SummaryRequestor, Residency) \
+    sprintf(FileName,"%s/%s/%lu.%lu.%lu.%lu", MountPoint, \
+           UFS_SUMMARYTREENAME, FileTag1, FileTag2, SummaryRequestor, \
+           Residency)
+#define DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTagNumber, FSType) \
+    sprintf(MountPoint,"%s%c", Ufs_Prefixes[FSType], UFS_MOUNTPOINTBASE + \
+           DeviceTagNumber)
+#define MOUNTPOINTTODEVICETAGNUMBER(MountPoint) \
+    MountPoint[strlen(MountPoint) - 1] - UFS_MOUNTPOINTBASE
+#define DEVICETAGNUMBERTOVOLUMEHEADERTREE(TreeName, MountPoint) \
+    sprintf(TreeName,"%s/%s", MountPoint, UFS_VOLUMEHEADERTREENAME)
+#define UFS_RESIDENCIES_FILE "Residencies"
+
+/* We don't ever want to map to uid/gid -1.  fchown() takes that as a
+   don't change flag.  We know however that volume number range from
+   0x2000000 to 0x20FFFFFF (see VAllocateVolumeId() in vol/vutil.c)
+   so we will use that to insure that -1 never appears. */
+#define RWVolumeToUid(RWVolume) ((RWVolume >> 12) & 0xFFFF)
+#define RWVolumeToGid(RWVolume) ((RWVolume & 0xFFF) | \
+                                (((RWVolume >> 28) & 0xF) << 12))
+#define UidGidToRWVolume(Uid, Gid) ((Gid & 0xFFF) | ((Uid & 0xFFFF) << 12) | \
+                                   ((Gid & 0xF000) << 16))
+
+
+/* These routines generate a file name to correspond to the given tag
+   numbers. */
+
+/* The following entropy array contains the order of bits from highest entropy
+   to lowest in the numbers FileTag1 and FileTag2.  Bit numbers 32 and above
+   correspond to FileTag2.  This ordering was determined by examining all read-
+   write volumes in the psc.edu cell. */
+char UfsEntropy[1][64] = {
+  {1, 2, 3, 4, 33, 5, 6, 7, 44, 45, 46, 36, 8, 34, 42, 35, 
+     9, 40, 38, 32, 43, 10, 39, 37, 11, 41, 12, 13, 14, 0, 
+     15, 16, 61, 17, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 
+     50, 49, 48, 47, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 
+     21, 20, 19, 18, 62, 63},
+};
+
+uint32_t Directories[3][2] = { {256, 0}, {256, 16}, {256, 256}, };
+#endif /* RESIDENCY */
+
+static int verbose = 0;
+static int numNoDirData = 0;
+static int termsize = 0;
+int Testing = 0;
+#ifdef RESIDENCY
+extern resid ServerRequestorId;
+#endif /* RESIDENCY */
+
+/*
+ * We use this structure to hold vnode data in our hash table.
+ * It's indexed by vnode number.
+ */
+
+struct vnodeData {
+       struct VnodeDiskObject *vnode;  /* A pointer to the disk vnode */
+       int vnodeNumber;                /* The vnode number */
+       long dumpdata;                  /* File offset of dump data (if
+                                          available */
+       unsigned char *filedata;        /* A pointer to the actual file
+                                          data itself (if available) */
+       unsigned int datalength;        /* The length of the data */
+};
+
+/*
+ * This contains the current location when we're doing a scan of a
+ * directory.
+ */
+
+struct DirCursor {
+       int hashbucket;                 /* Current hash bucket */
+       int entry;                      /* Entry within hash bucket */
+};
+
+/*
+ * Arrays to hold vnode data
+ */
+
+struct vnodeData **LargeVnodeIndex;
+struct vnodeData **SmallVnodeIndex;
+int numLargeVnodes = 0;
+int numSmallVnodes = 0;
+
+/*
+ * Crap for the libraries
+ */
+
+int ShutdownInProgress = 0;
+
+/*
+ * Our local function prototypes
+ */
+
+static int ReadDumpHeader(FILE *, struct DumpHeader *);
+static int ReadVolumeHeader(FILE *, VolumeDiskData *);
+static int ScanVnodes(FILE *, VolumeDiskData *, int);
+static int DumpVnodeFile(FILE *, struct VnodeDiskObject *, VolumeDiskData *);
+static struct vnodeData *InsertVnode(unsigned int, struct VnodeDiskObject *);
+static struct vnodeData *GetVnode(unsigned int);
+static int CompareVnode(const void *, const void *);
+static void InteractiveRestore(FILE *, VolumeDiskData *);
+static void DirectoryList(int, char **, struct vnodeData *, VolumeDiskData *);
+static void DirListInternal(struct vnodeData *, char *[], int, int, int, int,
+                           int, int, VolumeDiskData *, char *);
+static int CompareDirEntry(const void *, const void *);
+static struct vnodeData *ChangeDirectory(int, char **, struct vnodeData *);
+static void CopyFile(int, char **, struct vnodeData *, FILE *);
+static void CopyVnode(int, char **, FILE *);
+static void DumpAllFiles(int, char **, struct vnodeData *, VolumeDiskData *);
+static void DumpAllResidencies(FILE *, struct vnodeData *, VolumeDiskData *);
+static struct vnodeData *FindFile(struct vnodeData *, char *);
+static void ResetDirCursor(struct DirCursor *, struct vnodeData *);
+static struct DirEntry *ReadNextDir(struct DirCursor *, struct vnodeData *);
+static void MakeArgv(char *, int *, char ***);
+static char *GetToken(char *, char **, char *, char *[]);
+static int ReadInt16(FILE *, uint16_t *);
+static int ReadInt32(FILE *, uint32_t *);
+static int ReadString(FILE *, char *, int);
+static int ReadByteString(FILE *, void *, int);
+
+int
+main(int argc, char *argv[])
+{
+       int c, errflg = 0, dumpvnodes = 0, force = 0, inode = 0;
+       unsigned int magic;
+       struct DumpHeader dheader;
+       VolumeDiskData vol;
+       long offset;
+       int Res, Arg1, Arg2, Arg3, i;
+       char *p;
+       struct winsize win;
+       FILE *f;
+
+#ifdef RESIDENCY
+       for (i = 0; i < RS_MAXRESIDENCIES; i++) {
+               rscmdlineinfo[i].Algorithm = -1;
+               rscmdlineinfo[i].Size = -1;
+               rscmdlineinfo[i].DeviceTag = -1;
+               rscmdlineinfo[i].FSType = - 1;
+       }
+#endif /* RESIDENCY */
+
+       /*
+        * Sigh, this is dumb, but we need the terminal window size
+        * to do intelligent things with "ls" later on.
+        */
+
+       if (isatty(STDOUT_FILENO)) {
+               if ((p = getenv("COLUMNS")) != NULL)
+                       termsize = atoi(p);
+               else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
+                        win.ws_col > 0)
+                       termsize = win.ws_col;
+       }
+
+       while ((c = getopt(argc, argv, "difr:t:v")) != EOF)
+               switch (c) {
+               case 't':
+#ifdef RESIDENCY
+                       if (sscanf(optarg, "%d/%d", &Res, &Arg1) != 2) {
+                               errflg++;
+                               break;
+                       }
+
+                       if (1 << (ffs(Res) - 1) != Res) {
+                               fprintf(stderr, "Invalid residency %d\n", Res);
+                               errflg++;
+                               break;
+                       }
+
+                       if (Arg1 < 0 || Arg1 > 26) {
+                               fprintf(stderr, "Invalid device tag: %d\n",
+                                       Arg1);
+                               errflg++;
+                               break;
+                       }
+                       rscmdlineinfo[ffs(Res) - 1].DeviceTag = Arg1;
+#else /* RESIDENCY */
+                       fprintf(stderr, "-t not supported in non-MRAFS "
+                               "dumptool.\n");
+                       errflg++;
+#endif /* RESIDENCY */
+                       break;
+
+               case 'r':
+#ifdef RESIDENCY
+                       if (sscanf(optarg, "%d/%d/%d/%d", &Res, &Arg1, &Arg2,
+                                  &Arg3) != 4) {
+                               errflg++;
+                               break;
+                       }
+
+                       if (Arg1 < 0 || Arg1 > 3) {
+                               fprintf(stderr, "Invalid fstype: %d\n", Arg1);
+                               errflg++;
+                               break;
+                       }
+
+                       if (Arg2 < 0 || Arg2 > 2) {
+                               fprintf(stderr, "Invalid size: %d\n", Arg2);
+                               errflg++;
+                               break;
+                       }
+
+                       if (Arg3 <= 0 || Arg3 > UFS_ALGORITHMS) {
+                               fprintf(stderr, "Invalid algorithm: %d\n",
+                                       Arg3);
+                               errflg++;
+                               break;
+                       }
+                       rscmdlineinfo[ffs(Res) - 1].FSType = Arg1;
+                       rscmdlineinfo[ffs(Res) - 1].Size = Arg2;
+                       rscmdlineinfo[ffs(Res) - 1].Algorithm = Arg3;
+#else /* RESIDENCY */
+                       fprintf(stderr, "-r not supported in non-MRAFS "
+                               "dumptool.\n");
+                       errflg++;
+#endif /* RESIDENCY */
+                       break;
+               case 'd':
+#ifdef RESIDENCY
+                       dumpvnodes++;
+#else /* RESIDENCY */
+                       fprintf(stderr, "-d not supported in non-MRAFS "
+                               "dumptool.\n");
+                       errflg++;
+#endif /* RESIDENCY */
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               case 'f':
+                       force++;
+                       break;
+               case 'i':
+                       inode++;
+                       break;
+               case '?':
+               default:
+                       errflg++;
+               }
+       
+       if (errflg || optind == argc) {
+               fprintf(stderr, "Usage: %s\n\t[-v] [-f]\n\t"
+#ifdef RESIDENCY
+                       "[-t Residency/Tag]\n\t"
+                       "[-r Residency/Type/Size/Algorithm]\n\t"
+                       "[-d] filename [file_in_dump [file in dump ...]]\n",
+#else /* RESIDENCY */
+                       "filename\n",
+#endif /* RESIDENCY */
+                       argv[0]);
+               exit(1);
+       }
+
+       /*
+        * Try opening the dump file
+        */
+
+       if ((f = fopen(argv[optind], "rb")) == NULL) {
+               fprintf(stderr, "open of dumpfile %s failed: %s\n", argv[optind],
+                       strerror(errno));
+               exit(1);
+       }
+
+       if (ReadDumpHeader(f, &dheader)) {
+               fprintf(stderr, "Failed to read dump header!\n");
+               exit(1);
+       }
+
+       if (verbose)
+               printf("Dump is for volume %lu (%s)\n", dheader.volumeId,
+                       dheader.volumeName);
+
+       if (getc(f) != D_VOLUMEHEADER) {
+               fprintf(stderr, "Volume header is missing from dump, aborting\n");
+               exit(1);
+       }
+
+       if (ReadVolumeHeader(f, &vol)) {
+               fprintf(stderr, "Unable to read volume header\n");
+               exit(1);
+       }
+
+       if (verbose) {
+               printf("Volume information:\n");
+               printf("\tid = %lu\n", vol.id);
+               printf("\tparent id = %lu\n", vol.parentId);
+               printf("\tname = %s\n", vol.name);
+               printf("\tflags =");
+               if (vol.inUse)
+                       printf(" inUse");
+               if (vol.inService)
+                       printf(" inService");
+               if (vol.blessed)
+                       printf(" blessed");
+               if (vol.needsSalvaged)
+                       printf(" needsSalvaged");
+               printf("\n");
+               printf("\tuniquifier = %lu\n", vol.uniquifier);
+               printf("\tCreation date = %s", ctime((time_t *) &vol.creationDate));
+               printf("\tLast access date = %s", ctime((time_t *) &vol.accessDate));
+               printf("\tLast update date = %s", ctime((time_t *) &vol.updateDate));
+               printf("\tVolume owner = %lu\n", vol.owner);
+       }
+
+       if (verbose)
+               printf("Scanning vnodes (this may take a while)\n");
+
+       /*
+        * We need to do two vnode scans; one to get the number of
+        * vnodes, the other to actually build the index.
+        */
+
+       offset = ftell(f);
+
+       if (ScanVnodes(f, &vol, 1)) {
+               fprintf(stderr, "First vnode scan failed, aborting\n");
+               exit(1);
+       }
+
+       fseek(f, offset, SEEK_SET);
+
+       if (ScanVnodes(f, &vol, 0)) {
+               fprintf(stderr, "Second vnode scan failed, aborting\n");
+               exit(1);
+       }
+
+       if (getc(f) != D_DUMPEND || ReadInt32(f, &magic) ||
+           magic != DUMPENDMAGIC) {
+               fprintf(stderr, "Couldn't find dump postamble, ");
+               if (! force) {
+                       fprintf(stderr, "aborting (use -f to override)\n");
+                       exit(1);
+               } else {
+                       fprintf(stderr, "continuing anyway\n");
+                       fprintf(stderr, "WARNING: Dump may not be complete!\n");
+               }
+       }
+
+       /*
+        * If we wanted to simply dump all vnodes, do it now
+        */
+
+#ifdef RESIDENCY
+       if (dumpvnodes) {
+               struct vnodeData *vdata;
+
+               for (i = 0; i < numLargeVnodes; i++) {
+
+                       vdata = LargeVnodeIndex[i];
+
+                       if (vdata->vnode->type == vFidLookup)
+                               if (DumpVnodeFile(stdout, vdata->vnode, &vol)) {
+                                       fprintf(stderr, "DumpVnodeFile failed, "
+                                               "aborting\n");
+                                       exit(1);
+                               }
+               }
+
+               for (i = 0; i < numSmallVnodes; i++) {
+
+                       vdata = SmallVnodeIndex[i];
+
+                       if (vdata->vnode->type == vFidLookup)
+                               if (DumpVnodeFile(stdout, vdata->vnode, &vol)) {
+                                       fprintf(stderr, "DumpVnodeFile failed, "
+                                               "aborting\n");
+                                       exit(1);
+                               }
+               }
+
+       } else
+#endif /* RESIDENCY */
+       if (inode) {
+               /*
+                * Dump out all filenames with their corresponding FID
+                */
+               
+               struct vnodeData *rootvdata;
+
+               if ((rootvdata = GetVnode(1)) == NULL) {
+                       fprintf(stderr, "Can't get vnode data for root "
+                               "vnode!  Aborting\n");
+                       exit(1);
+               }
+
+               DirListInternal(rootvdata, NULL, 0, 0, 1, 0, 1, 0, &vol, "");
+               
+       } else if (argc > optind + 1) {
+#ifdef RESIDENCY
+               /*
+                * Dump out residencies of files given on the command line.
+                */
+
+               struct vnodeData *vdata, *rootvdata;
+
+               if ((rootvdata = GetVnode(1)) == NULL) {
+                       fprintf(stderr, "Can't get vnode data for root "
+                               "vnode!  Aborting\n");
+                       exit(1);
+               }
+
+               for (i = optind + 1; i < argc; i++) {
+
+                       if ((vdata = FindFile(rootvdata, argv[i])) == NULL) {
+                               fprintf(stderr, "Skipping file %s\n",
+                                       argv[i]);
+                               continue;
+                       }
+
+                       if (verbose)
+                               printf("Residency locations for %s:\n",
+                                      argv[i]);
+
+                       while (vdata->vnode->NextVnodeId != 0) {
+
+                               vdata = GetVnode(vdata->vnode->NextVnodeId);
+
+                               if (vdata == NULL) {
+                                       fprintf(stderr, "We had a vnode chain "
+                                               "pointer to a vnode that "
+                                               "doesn't exist, aborting!\n");
+                                       exit(1);
+                               }
+                               if (vdata->vnode->type == vFidLookup)
+                                       DumpVnodeFile(stdout, vdata->vnode,
+                                                     &vol);
+                       }
+               }
+#else /* RESIDENCY */
+               fprintf(stderr, "Extra arguments after dump filename: %s\n",
+                       argv[optind]);
+               exit(1);
+#endif /* RESIDENCY */
+       } else {
+               /*
+                * Perform an interactive restore
+                */
+       
+               InteractiveRestore(f, &vol);
+       }
+
+       exit(0);
+}
+
+/*
+ * Read the dump header, which is at the beginning of every dump
+ */
+
+static int
+ReadDumpHeader(FILE *f, struct DumpHeader *header)
+{
+       unsigned int magic;
+       int tag, i;
+
+       if (getc(f) != D_DUMPHEADER ||
+           ReadInt32(f, &magic) || ReadInt32(f, (unsigned int *)
+                                             &header->version) ||
+           magic != DUMPBEGINMAGIC) {
+               if (verbose)
+                       fprintf(stderr, "Couldn't find dump magic numbers\n");
+               return -1;
+       }
+
+       header->volumeId = 0;
+       header->nDumpTimes = 0;
+
+       while ((tag = getc(f)) > D_MAX && tag != EOF) {
+               unsigned short length;
+               switch (tag) {
+               case 'v':
+                       if (ReadInt32(f, &header->volumeId)) {
+                               if (verbose)
+                                       fprintf(stderr, "Failed to read "
+                                               "volumeId\n");
+                               return -1;
+                       }
+                       break;
+               case 'n':
+                       if (ReadString(f, header->volumeName,
+                                      sizeof(header->volumeName))) {
+                               if (verbose)
+                                       fprintf(stderr, "Failed to read "
+                                               "volume name\n");
+                               return -1;
+                       }
+                       break;
+               case 't':
+                       if (ReadInt16(f, &length)) {
+                               if (verbose)
+                                       fprintf(stderr, "Failed to read "
+                                               "dump time array length\n");
+                               return -1;
+                       }
+                       header->nDumpTimes = (length >> 1);
+                       for (i = 0; i < header->nDumpTimes; i++)
+                               if (ReadInt32(f, (unsigned int *)
+                                             &header->dumpTimes[i].from) ||
+                                   ReadInt32(f, (unsigned int *)
+                                             &header->dumpTimes[i].to)) {
+                                       if (verbose)
+                                               fprintf(stderr, "Failed to "
+                                                       "read dump times\n");
+                                       return -1;
+                               }
+                       break;
+               default:
+                       if (verbose)
+                               fprintf(stderr, "Unknown dump tag \"%c\"\n",
+                                       tag);
+                       return -1;
+               }
+       }
+
+       if (!header->volumeId || !header->nDumpTimes) {
+               if (verbose) 
+                       fprintf(stderr, "We didn't get a volume Id or "
+                               "dump times listing\n");
+               return 1;
+       }
+
+       ungetc(tag, f);
+       return 0;
+}
+
+/*
+ * Read the volume header; this is the information stored in VolumeDiskData.
+ *
+ * I'm not sure we need all of this, but read it in just in case.
+ */
+
+static int
+ReadVolumeHeader(FILE *f, VolumeDiskData *vol)
+{
+       int tag;
+       unsigned int trash;
+       memset((void *) vol, 0, sizeof(*vol));
+
+       while ((tag = getc(f)) > D_MAX && tag != EOF) {
+               switch (tag) {
+               case 'i':
+                       if (ReadInt32(f, &vol->id))
+                               return -1;
+                       break;
+               case 'v':
+                       if (ReadInt32(f, &trash))
+                               return -1;
+                       break;
+               case 'n':
+                       if (ReadString(f, vol->name, sizeof(vol->name)))
+                               return -1;
+                       break;
+               case 's':
+                       vol->inService = getc(f);
+                       break;
+               case 'b':
+                       vol->blessed = getc(f);
+                       break;
+               case 'u':
+                       if (ReadInt32(f, &vol->uniquifier))
+                               return -1;
+                       break;
+               case 't':
+                       vol->type = getc(f);
+                       break;
+               case 'p':
+                       if (ReadInt32(f, &vol->parentId))
+                               return -1;
+                       break;
+               case 'c':
+                       if (ReadInt32(f, &vol->cloneId))
+                               return -1;
+                       break;
+               case 'q':
+                       if (ReadInt32(f, (uint32_t *) &vol->maxquota))
+                               return -1;
+                       break;
+               case 'm':
+                       if (ReadInt32(f, (uint32_t *) &vol->minquota))
+                               return -1;
+                       break;
+               case 'd':
+                       if (ReadInt32(f, (uint32_t *) &vol->diskused))
+                               return -1;
+                       break;
+               case 'f':
+                       if (ReadInt32(f, (uint32_t *) &vol->filecount))
+                               return -1;
+                       break;
+               case 'a':
+                       if (ReadInt32(f, &vol->accountNumber))
+                               return -1;
+                       break;
+               case 'o':
+                       if (ReadInt32(f, &vol->owner))
+                               return -1;
+                       break;
+               case 'C':
+                       if (ReadInt32(f, &vol->creationDate))
+                               return -1;
+                       break;
+               case 'A':
+                       if (ReadInt32(f, &vol->accessDate))
+                               return -1;
+                       break;
+               case 'U':
+                       if (ReadInt32(f, &vol->updateDate))
+                               return -1;
+                       break;
+               case 'E':
+                       if (ReadInt32(f, &vol->expirationDate))
+                               return -1;
+                       break;
+               case 'B':
+                       if (ReadInt32(f, &vol->backupDate))
+                               return -1;
+                       break;
+               case 'O':
+                       if (ReadString(f, vol->offlineMessage,
+                                      sizeof(vol->offlineMessage)))
+                               return -1;
+                       break;
+               case 'M':
+                       if (ReadString(f, (char *) vol->stat_reads, VMSGSIZE))
+                               return -1;
+                       break;
+               case 'W': {
+                       unsigned short length;
+                       int i;
+                       unsigned int data;
+                       if (ReadInt16(f, &length))
+                               return -1;
+                       for (i = 0; i < length; i++) {
+                               if (ReadInt32(f, &data))
+                                       return -1;
+                               if (i < sizeof(vol->weekUse) /
+                                              sizeof(vol->weekUse[0]))
+                                       vol->weekUse[i] = data;
+                       }
+                       break;
+               }
+               case 'D':
+                       if (ReadInt32(f, &vol->dayUseDate))
+                               return -1;
+                       break;
+               case 'Z':
+                       if (ReadInt32(f, (uint32_t *) &vol->dayUse))
+                               return -1;
+                       break;
+#ifdef RESIDENCY
+               case 'R': {
+                       unsigned short length;
+                       int i;
+                       unsigned int data;
+
+                       if (ReadInt16(f, &length))
+                               return -1;
+                       for (i = 0; i < length; i++) {
+                               if (ReadInt32(f, &data))
+                                       return -1;
+                               if (i < sizeof(vol->DesiredInfo.DesiredResidencyWords) /
+                                       sizeof(vol->DesiredInfo.DesiredResidencyWords[0]))
+                                       vol->DesiredInfo.DesiredResidencyWords[i] = data;
+                       }
+                       break;
+               }
+               case 'S': {
+                       unsigned short length;
+                       int i;
+                       unsigned int data;
+
+                       if (ReadInt16(f, &length))
+                               return -1;
+                       for (i = 0; i < length; i++) {
+                               if (ReadInt32(f, &data))
+                                       return -1;
+                               if (i < sizeof(vol->UnDesiredInfo.UnDesiredResidencyWords) /
+                                       sizeof(vol->UnDesiredInfo.UnDesiredResidencyWords[0]))
+                                       vol->UnDesiredInfo.UnDesiredResidencyWords[i] = data;
+                       }
+                       break;
+               }
+#endif
+               default:
+                       if (verbose)
+                               fprintf(stderr, "Unknown dump tag \"%c\"\n",
+                                       tag);
+                       return -1;
+               }
+       }
+
+       ungetc(tag, f);
+       return 0;
+}
+
+/*
+ * Scan all our vnode entries, and build indexing information.
+ */
+
+static int
+ScanVnodes(FILE *f, VolumeDiskData *vol, int sizescan)
+{
+       int vnodeNumber;
+       int tag;
+       int numFileVnodes = 0;
+       int numDirVnodes = 0;
+       unsigned char buf[SIZEOF_LARGEDISKVNODE];
+       struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
+       long offset, oldoffset;
+       struct vnodeData *vdata;
+       unsigned int length;
+
+       tag = getc(f);
+
+       while (tag == D_VNODE) {
+
+               offset = 0;
+               length = 0;
+               vnode->type = -1;
+               vnode->length = -1;
+
+               if (ReadInt32(f, (uint32_t *) &vnodeNumber))
+               {
+                       fprintf(stderr, "failed int32 for 'vnodenum'\n");
+                       return -1;
+               }
+
+               if (ReadInt32(f, &vnode->uniquifier))
+               {
+                       fprintf(stderr, "failed int32 for 'uniquifier'\n");
+                       return -1;
+               }
+               
+               if (verbose > 1 && !sizescan)
+                       printf("Got vnode %d\n", vnodeNumber);
+               
+               while ((tag = getc(f)) > D_MAX && tag != EOF)
+                       switch (tag) {
+                       case 't':
+                               vnode->type = (VnodeType) getc(f);
+                               break;
+                       case 'l':
+                               {
+                                       unsigned short tmp;
+                                       if (ReadInt16(f, &tmp))
+                                       {
+                                               fprintf(stderr, "failed int16 for 'l'\n");
+                                               return -1;
+                                       }
+                                       vnode->linkCount = tmp;
+                               }
+                               break;
+                       case 'v':
+                               if (ReadInt32(f, &vnode->dataVersion))
+                               {
+                                       fprintf(stderr, "failed int32 for 'v'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'm':
+                               if (ReadInt32(f, (uint32_t *) &vnode->unixModifyTime))
+                               {
+                                       fprintf(stderr, "failed int32 for 'm'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 's':
+                               if (ReadInt32(f, (uint32_t *) &vnode->serverModifyTime))
+                               {
+                                       fprintf(stderr, "failed int32 for 's'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'a':
+                               if (ReadInt32(f, &vnode->author))
+                               {
+                                       fprintf(stderr, "failed int32 for 'a'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'o':
+                               if (ReadInt32(f, &vnode->owner))
+                               {
+                                       fprintf(stderr, "failed int32 for 'o'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'g':
+                               if (ReadInt32(f, (uint32_t *) &vnode->group))
+                               {
+                                       fprintf(stderr, "failed int32 for 'g'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'b': {
+                               unsigned short modeBits;
+                               if (ReadInt16(f, &modeBits))
+                                       return -1;
+                               vnode->modeBits = modeBits;
+                               break;
+                       }
+                       case 'p':
+                               if (ReadInt32(f, &vnode->parent))
+                               {
+                                       fprintf(stderr, "failed int32 for 'p'\n");
+                                       return -1;
+                               }
+                               break;
+#ifdef RESIDENCY
+                       case 'N':
+                               if (ReadInt32(f, &vnode->NextVnodeId))
+                               {
+                                       fprintf(stderr, "failed int32 for 'N'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'R':
+                               if (ReadInt32(f, &VLkp_Residencies(vnode)))
+                               {
+                                       fprintf(stderr, "failed int32 for 'R'\n");
+                                       return -1;
+                               }
+                               break;
+#endif
+                       case 'S':
+                               if (ReadInt32(f, &vnode->length))
+                               {
+                                       fprintf(stderr, "failed int32 for 'S'\n");
+                                       return -1;
+                               }
+                               break;
+                       case 'F':
+                               if (ReadInt32(f, (uint32_t *) &vnode->vn_ino_lo))
+                                       return -1;
+                               break;
+                       case 'A':
+                               if (ReadByteString(f,
+                                               (void *)VVnodeDiskACL(vnode),
+                                               VAclDiskSize(vnode)))
+                               {
+                                       fprintf(stderr, "failed readbystring for 'A'\n");
+                                       return -1;
+                               }
+#if 0
+                               acl_NtohACL(VVnodeDiskACL(vnode));
+#endif
+                               break;
+#ifdef RESIDENCY 
+                       case 'h':
+                               if (ReadInt32(f, &vnode->length_hi))
+                               {
+                                       fprintf(stderr, "failed int32 for 'h'\n");
+                                       return -1;
+                               }
+#endif
+                       case 'f':
+                               if (verbose > 1 && ! sizescan)
+                                       printf("We have file data!\n");
+                               if (ReadInt32(f, &length))
+                               {
+                                       fprintf(stderr, "failed int32 for 'f'\n");
+                                       return -1;
+                               }
+                               vnode->length = length;
+                               offset = ftell(f);
+                               fseek(f, length, SEEK_CUR);
+                               break;
+                       default:
+                               if (verbose)
+                               fprintf(stderr, "Unknown dump tag \"%c\"\n",
+                                       tag);
+                       return -1;
+               }
+
+               /*
+                * If we're doing an incremental restore, then vnodes
+                * will be listed in the dump, but won't contain any
+                * vnode information at all (I don't know why they're
+                * included _at all_).  If we get one of these vnodes, then
+                * just skip it (because we can't do anything with it.
+                */
+
+               if (vnode->type == -1)
+                       continue;
+
+#ifdef RESIDENCY
+               if (verbose > 1 && vnode->type == vFidLookup && ! sizescan) {
+                       printf("This is an auxiliary vnode (lookup) for vnode %d, residency %d\n",
+                               VLkp_ParentVnodeId(vnode),
+                               VLkp_Residencies(vnode));
+                       if (DumpVnodeFile(stdout, vnode, vol))
+                               return -1;
+               }
+
+               if (verbose > 1 && vnode->type == vAccessHistory && ! sizescan)
+                       printf("This is an auxiliary vnode (history) for vnode %d\n",
+                               VLkp_ParentVnodeId(vnode));
+#endif
+
+               if (vnode->type == vDirectory)
+                       numDirVnodes++;
+               else
+                       numFileVnodes++;
+
+               /*
+                * We know now all we would ever know about the vnode;
+                * insert it into our hash table (but only if we're not
+                * doing a vnode scan).
+                */
+
+               if (!sizescan) {
+
+                       vdata = InsertVnode(vnodeNumber, vnode);
+
+                       if (vdata == NULL) {
+                               if (verbose)
+                                       fprintf(stderr, "Failed to insert "
+                                               "vnode into hash table");
+                               return -1;
+                       }
+
+                       vdata->dumpdata = offset;
+                       vdata->datalength = length;
+
+                       /*
+                        * Save directory data, since we'll need it later.
+                        */
+
+                       if (vnode->type == vDirectory && length) {
+
+                               vdata->filedata = malloc(length);
+
+                               if (!vdata->filedata) {
+                                       if (verbose)
+                                               fprintf(stderr, "Unable to "
+                                                       "allocate space for "
+                                                       "file data (%d)\n",
+                                                       length);
+                                       return -1;
+                               }
+
+                               oldoffset = ftell(f);
+                               fseek(f, offset, SEEK_SET);
+
+                               if (fread(vdata->filedata, length, 1, f) != 1) {
+                                       if (verbose)
+                                               fprintf(stderr, "Unable to "
+                                                       "read in file data!\n");
+                                       return -1;
+                               }
+
+                               fseek(f, oldoffset, SEEK_SET);
+                       } else if (vnode->type == vDirectory)
+                               /*
+                                * Warn the user we may not have all directory
+                                * vnodes
+                                */
+                               numNoDirData++;
+               }
+       }
+
+       ungetc(tag, f);
+
+       if (!sizescan) {
+
+               numLargeVnodes = numDirVnodes;
+               numSmallVnodes = numFileVnodes;
+
+       } else {
+               LargeVnodeIndex = (struct vnodeData **)
+                                       malloc(numDirVnodes *
+                                               sizeof(struct vnodeData));
+               SmallVnodeIndex = (struct vnodeData **)
+                                       malloc(numFileVnodes *
+                                               sizeof(struct vnodeData));
+               
+               if (LargeVnodeIndex == NULL || SmallVnodeIndex == NULL) {
+                       if (verbose)
+                               fprintf(stderr, "Unable to allocate space "
+                                       "for vnode tables\n");
+                       return -1;
+               }
+       }
+
+       if (verbose)
+               fprintf(stderr,"%s vnode scan completed\n",
+                       sizescan ? "Primary" : "Secondary");
+
+       return 0;
+}
+
+/*
+ * Perform an interactive restore
+ *
+ * Parsing the directory information is a pain, but other than that
+ * we just use the other tools we already have in here.
+ */
+
+static void
+InteractiveRestore(FILE *f, VolumeDiskData *vol)
+{
+       struct vnodeData *vdatacwd;     /* Vnode data for our current dir */
+       char cmdbuf[256];
+       int argc;
+       char **argv;
+
+       /*
+        * Let's see if we can at least get the data for our root directory.
+        * If we can't, there's no way we can do an interactive restore.
+        */
+
+       if ((vdatacwd = GetVnode(1)) == NULL) {
+               fprintf(stderr, "No entry for our root vnode!  Aborting\n");
+               return;
+       }
+
+       if (! vdatacwd->filedata) {
+               fprintf(stderr, "There is no directory data for the root "
+                       "vnode (1.1).  An interactive\nrestore is not "
+                       "possible.\n");
+               return;
+       }
+
+       /*
+        * If you're doing a selective dump correctly, then you should get all
+        * directory vnode data.  But just in case you didn't, let the user
+        * know there may be a problem.
+        */
+
+       if (numNoDirData)
+               fprintf(stderr, "WARNING: %d directory vnodes had no file "
+                       "data.  An interactive restore\nmay not be possible\n",
+                       numNoDirData);
+
+       printf("> ");
+       while (fgets(cmdbuf, 256, stdin)) {
+
+               cmdbuf[strlen(cmdbuf) - 1] = '\0';
+
+               if (strlen(cmdbuf) == 0) {
+                       printf("> ");
+                       continue;
+               }
+
+               MakeArgv(cmdbuf, &argc, &argv);
+
+               if (strcmp(argv[0], "ls") == 0) {
+                       DirectoryList(argc, argv, vdatacwd, vol);
+               } else if (strcmp(argv[0], "cd") == 0) {
+                       struct vnodeData *newvdata;
+
+                       newvdata = ChangeDirectory(argc, argv, vdatacwd);
+
+                       if (newvdata)
+                               vdatacwd = newvdata;
+               } else if (strcmp(argv[0], "file") == 0) {
+                       DumpAllFiles(argc, argv, vdatacwd, vol);
+               } else if (strcmp(argv[0], "cp") == 0) {
+                       CopyFile(argc, argv, vdatacwd, f);
+               } else if (strcmp(argv[0], "vcp") == 0) {
+                       CopyVnode(argc, argv, f);
+               } else if (strcmp(argv[0], "quit") == 0 ||
+                          strcmp(argv[0], "exit") == 0)
+                       break;
+               else if (strcmp(argv[0], "?") == 0 ||
+                        strcmp(argv[0], "help") == 0) {
+                       printf("Valid commands are:\n");
+                       printf("\tls\t\tList current directory\n");
+                       printf("\tcd\t\tChange current directory\n");
+                       printf("\tcp\t\tCopy file from dump\n");
+                       printf("\tvcp\t\tCopy file from dump (via vnode)\n");
+#ifdef RESIDENCY
+                       printf("\tfile\t\tList residency filenames\n");
+#endif /* RESIDENCY */
+                       printf("\tquit | exit\tExit program\n");
+                       printf("\thelp | ?\tBrief help\n");
+               } else 
+                       fprintf(stderr, "Unknown command, \"%s\", enter "
+                               "\"help\" for a list of commands.\n",
+                               argv[0]);
+               
+               printf("> ");
+       }
+
+       return;
+}
+
+/*
+ * Do a listing of all files in a directory.  Sigh, I wish this wasn't
+ * so complicated.
+ *
+ * With the reorganizing, this is just a front-end to DirListInternal()
+ */
+
+static void
+DirectoryList(int argc, char **argv, struct vnodeData *vdata,
+             VolumeDiskData *vol)
+{
+       int errflg = 0, lflag = 0, iflag = 0, Fflag = 0, sflag = 0, Rflag = 0;
+       int c;
+
+       optind = 1;
+
+       while ((c = getopt(argc, argv, "liFRs")) != EOF)
+               switch (c) {
+               case 'l':
+                       lflag++;
+                       break;
+               case 'i':
+                       iflag++;
+                       break;
+               case 'F':
+                       Fflag++;
+                       break;
+               case 'R':
+                       Rflag++;
+               case 's':
+                       sflag++;
+                       break;
+               case '?':
+               default:
+                       errflg++;
+               }
+
+       if (errflg) {
+               fprintf(stderr, "Usage: %s [-liFs] filename [filename ...]\n",
+                       argv[0]);
+               return;
+       }
+
+       DirListInternal(vdata, &(argv[optind]), argc - optind, lflag, iflag,
+                       Fflag, Rflag, 1, vol, NULL);
+
+       return;
+}
+
+/*
+ * Function that does the REAL work in terms of directory listing
+ */
+
+static void
+DirListInternal(struct vnodeData *vdata, char *pathnames[], int numpathnames,
+               int lflag, int iflag, int Fflag, int Rflag, int verbose,
+               VolumeDiskData *vol, char *path)
+{
+       struct DirEntry *ep, **eplist = NULL, **eprecurse = NULL;
+       struct DirCursor cursor;
+       struct vnodeData *lvdata;
+
+       int i, j, numentries = 0, longestname = 0, numcols, col, numrows;
+       int numrecurse = 0;
+               
+       if (! vdata->filedata) {
+               fprintf(stderr, "There is no vnode data for this "
+                       "directory!\n");
+               return;
+       }
+
+       ResetDirCursor(&cursor, vdata);
+
+       /*
+        * Scan through the whole directory
+        */
+
+       while ((ep = ReadNextDir(&cursor, vdata)) != NULL) {
+
+               /*
+                * If we didn't get any filenames on the command line,
+                * get them all.
+                */
+
+               if (numpathnames == 0) {
+                       eplist = realloc(eplist, sizeof(struct DirEntry *) *
+                                        ++numentries);
+                       eplist[numentries - 1] = ep;
+                       if (strlen(ep->name) > longestname)
+                               longestname = strlen(ep->name);
+                       if (Rflag)
+                               if ((lvdata = GetVnode(ntohl(ep->fid.vnode))) &&
+                                   lvdata->vnode->type == vDirectory &&
+                                   !(strcmp(ep->name, ".") == 0 ||
+                                     strcmp(ep->name, "..") == 0)) {
+                                       eprecurse = realloc(eprecurse,
+                                               sizeof(struct DirEntry *) *
+                                               ++numrecurse);
+                                       eprecurse[numrecurse - 1] = ep;
+                               }
+
+               } else {
+                       /*
+                        * Do glob matching via fnmatch()
+                        */
+
+                       for (i = 0; i < numpathnames; i++)
+                               if (fnmatch(pathnames[i], ep->name,
+                                               FNM_PATHNAME) == 0) {
+                                       eplist = realloc(eplist,
+                                                sizeof(struct DirEntry *) *
+                                                ++numentries);
+                                       eplist[numentries - 1] = ep;
+                                       if (strlen(ep->name) > longestname)
+                                               longestname = strlen(ep->name);
+                                       if (Rflag)
+                                               if ((lvdata =
+                                            GetVnode(ntohl(ep->fid.vnode))) &&
+                                                   lvdata->vnode->type ==
+                                                               vDirectory &&
+                                              !(strcmp(ep->name, ".") == 0 ||
+                                                strcmp(ep->name, "..") == 0)) {
+                                                       eprecurse =
+                                                            realloc(eprecurse,
+                                               sizeof(struct DirEntry *) *
+                                                               ++numrecurse);
+                                                eprecurse[numrecurse - 1] = ep;
+                                               }
+                                       break;
+                               }
+               }
+       }
+
+       qsort((void *) eplist, numentries, sizeof(struct DirEntry *),
+             CompareDirEntry);
+
+       if (Rflag && eprecurse)
+               qsort((void *) eprecurse, numrecurse,
+                     sizeof(struct DirEntry *), CompareDirEntry);
+       /*
+        * We don't have to do column printing if we have the -l or the -i
+        * options.  Sigh, column printing is WAY TOO FUCKING COMPLICATED!
+        */
+
+       if (!lflag && !iflag) {
+               char c;
+
+               if (Fflag)
+                       longestname++;
+
+               longestname++;
+
+               numcols = termsize / longestname ? termsize / longestname : 1;
+               numrows = numentries / numcols +
+                       (numentries % numcols ? 1 : 0);
+
+               for (i = 0; i < numrows; i++) {
+                       col = 0;
+                       while (col < numcols && (i + col * numrows) <
+                                                               numentries) {
+                               ep = eplist[i + col++ * numrows];
+                               if (Fflag) {
+                                       if (!(lvdata =
+                                              GetVnode(ntohl(ep->fid.vnode))))
+                                               c = ' ';
+                                       else if (lvdata->vnode->type ==
+                                                  vDirectory)
+                                               c = '/';
+                                       else if (lvdata->vnode->type ==
+                                                  vSymlink)
+                                               c = '@';
+                                       else if (lvdata->vnode->modeBits &
+                                                0111 != 0)
+                                               c = '*';
+                                       else
+                                               c = ' ';
+                               printf("%s%-*c", ep->name,
+                                      longestname - strlen(ep->name), c);
+                               } else
+                                       printf("%-*s", longestname, ep->name);
+                       }
+
+                       printf("\n");
+               }
+       } else if (iflag)
+               for (i = 0; i < numentries; i++)
+                       if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
+                               printf("%d.0.0\t%s\n",
+                                      vol->parentId ? vol->parentId : vol->id,
+                                      eplist[i]->name);
+                       else
+                               if (path)
+                                       printf("%d.%d.%d\t%s/%s\n",
+                                              vol->id,
+                                              ntohl(eplist[i]->fid.vnode),
+                                              ntohl(eplist[i]->fid.vunique),
+                                              path, eplist[i]->name);
+                               else
+                                       printf("%d.%d.%d\t%s\n",
+                                              vol->id,
+                                              ntohl(eplist[i]->fid.vnode),
+                                              ntohl(eplist[i]->fid.vunique),
+                                              eplist[i]->name);
+       else if (lflag) {
+               for (i = 0; i < numentries; i++)
+                       if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
+                               printf("----------   0 0        "
+                                      "0                 0 %s\n",
+                                              eplist[i]->name);
+                       else {
+                               switch (lvdata->vnode->type) {
+                               case vDirectory:
+                                       printf("d");
+                                       break;
+                               case vSymlink:
+                                       printf("l");
+                                       break;
+                               default:
+                                       printf("-");
+                               }
+
+                               for (j = 8; j >= 0; j--) {
+                                       if (lvdata->vnode->modeBits & (1 << j))
+                                               switch (j % 3) {
+                                                       case 2: printf("r");
+                                                               break;
+                                                       case 1: printf("w");
+                                                               break;
+                                                       case 0: printf("x");
+                                               }
+                                       else
+                                               printf("-");
+                               }
+
+                               printf(" %-3d %-8d %-8d %10d %s\n",
+                                      lvdata->vnode->linkCount,
+                                      lvdata->vnode->owner,
+                                      lvdata->vnode->group,
+                                      lvdata->vnode->length,
+                                      eplist[i]->name);
+                       }
+       }
+
+       free(eplist);
+
+       if (Rflag && eprecurse) {
+               char *lpath;
+               lpath = NULL;
+               for (i = 0; i < numrecurse; i++) {
+                       if (verbose)
+                               printf("\n%s:\n", eprecurse[i]->name);
+                       if (path) {
+                               lpath = malloc(strlen(path) +
+                                              strlen(eprecurse[i]->name) + 2);
+                               if (lpath)
+                                       sprintf(lpath, "%s/%s", path,
+                                               eprecurse[i]->name);
+                       }
+                       DirListInternal(
+                                     GetVnode(ntohl(eprecurse[i]->fid.vnode)),
+                                       NULL, 0, lflag, iflag, Fflag, Rflag,
+                                       verbose, vol, lpath);
+                       if (lpath) {
+                               free(lpath);
+                               lpath = NULL;
+                       }
+               }
+       }
+
+       if (eprecurse)
+               free(eprecurse);
+
+       return;
+}
+
+
+/*
+ * Directory name comparison function, used by qsort
+ */
+
+static int
+CompareDirEntry(const void *e1, const void *e2)
+{
+       struct DirEntry **ep1 = (struct DirEntry **) e1;
+       struct DirEntry **ep2 = (struct DirEntry **) e2;
+
+       return strcmp((*ep1)->name, (*ep2)->name);
+}
+
+/*
+ * Change a directory.  Return a pointer to our new vdata structure for
+ * this directory.
+ */
+
+static struct vnodeData *
+ChangeDirectory(int argc, char **argv, struct vnodeData *vdatacwd)
+{
+       struct vnodeData *newvdatacwd;
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage: %s directory\n", argv[0]);
+               return NULL;
+       }
+
+       if ((newvdatacwd = FindFile(vdatacwd, argv[1])) == NULL)
+               return NULL;
+
+       if (newvdatacwd->vnode->type != vDirectory) {
+               fprintf(stderr, "%s: Not a directory\n", argv[1]);
+               return NULL;
+       }
+
+       if (newvdatacwd->filedata == NULL) {
+               fprintf(stderr, "%s: No directory data found.\n", argv[1]);
+               return NULL;
+       }
+
+       return newvdatacwd;
+}
+
+/*
+ * Copy a file from out of the dump file
+ */
+
+#define COPYBUFSIZE 8192
+
+static void
+CopyFile(int argc, char **argv, struct vnodeData *vdatacwd, FILE *f)
+{
+       struct vnodeData *vdata;
+       FILE *out;
+       long cur = 0;
+       int bytes, ret;
+       char buffer[COPYBUFSIZE];
+
+       if (argc != 3) {
+               fprintf(stderr, "Usage: %s dumpfile destfile\n", argv[0]);
+               return;
+       }
+
+       if ((vdata = FindFile(vdatacwd, argv[1])) == NULL)
+               return;
+
+       if (vdata->dumpdata == 0) {
+               fprintf(stderr, "File %s has no data in dump file\n",
+                       argv[1]);
+               return;
+       }
+
+       if ((out = fopen(argv[2], "wb")) == NULL) {
+               fprintf(stderr, "Open of %s failed: %s\n", argv[2],
+                       strerror(errno));
+               return;
+       }
+
+       if (fseek(f, vdata->dumpdata, SEEK_SET)) {
+               fprintf(stderr, "Seek failed: %s\n", strerror(errno));
+               fclose(out);
+               return;
+       }
+
+       while (cur < vdata->datalength) {
+
+               bytes = cur + COPYBUFSIZE < vdata->datalength ?
+                       COPYBUFSIZE : vdata->datalength - cur;
+
+               ret = fread(buffer, sizeof(char), bytes, f);
+               if (ret != bytes) {
+                       if (ret != 0)
+                               fprintf(stderr, "Short read (expected %d, "
+                                       "got %d)\n", bytes, ret);
+                       else
+                               fprintf(stderr, "Error during read: %s\n",
+                                       strerror(errno));
+                       fclose(out);
+                       return;
+               }
+
+               ret = fwrite(buffer, sizeof(char), bytes, out);
+               if (ret != bytes) {
+                       if (ret != 0)
+                               fprintf(stderr, "Short write (expected %d, "
+                                       "got %d)\n", bytes, ret);
+                       else
+                               fprintf(stderr, "Error during write: %s\n",
+                                       strerror(errno));
+                       fclose(out);
+                       return;
+               }
+
+               cur += bytes;
+       }
+
+       fclose(out);
+}
+
+/*
+ * Copy a file from out of the dump file, by using the vnode
+ */
+
+static void
+CopyVnode(int argc, char *argv[], FILE *f)
+{
+       struct vnodeData *vdata;
+       FILE *out;
+       long cur = 0;
+       int bytes, ret;
+       char buffer[COPYBUFSIZE];
+       unsigned int vnode, uniquifier = 0;
+
+       if (argc != 3) {
+               fprintf(stderr, "Usage: %s vnode[.uniqifier] destfile\n",
+                       argv[0]);
+               return;
+       }
+
+       ret = sscanf(argv[1], "%d.%d", &vnode, &uniquifier);
+
+       if (ret < 1) {
+               fprintf(stderr, "Invalid file identifier: %s\n", argv[1]);
+               return;
+       }
+
+       if (!(vdata = GetVnode(vnode))) {
+               fprintf(stderr, "Vnode %d not in dump file\n", vnode);
+               return;
+       }
+
+       if (ret == 2 && vdata->vnode->uniquifier != uniquifier) {
+               fprintf(stderr, "Specified uniquifier %d did not match "
+                       "uniquifier %d found in dump file!\n", uniquifier,
+                       vdata->vnode->uniquifier);
+               return;
+       }
+
+       if (vdata->dumpdata == 0) {
+               fprintf(stderr, "File %s has no data in dump file\n",
+                       argv[1]);
+               return;
+       }
+
+       if ((out = fopen(argv[2], "wb")) == NULL) {
+               fprintf(stderr, "Open of %s failed: %s\n", argv[2],
+                       strerror(errno));
+               return;
+       }
+
+       if (fseek(f, vdata->dumpdata, SEEK_SET)) {
+               fprintf(stderr, "Seek failed: %s\n", strerror(errno));
+               fclose(out);
+               return;
+       }
+
+       while (cur < vdata->datalength) {
+
+               bytes = cur + COPYBUFSIZE < vdata->datalength ?
+                       COPYBUFSIZE : vdata->datalength - cur;
+
+               ret = fread(buffer, sizeof(char), bytes, f);
+               if (ret != bytes) {
+                       if (ret != 0)
+                               fprintf(stderr, "Short read (expected %d, "
+                                       "got %d)\n", bytes, ret);
+                       else
+                               fprintf(stderr, "Error during read: %s\n",
+                                       strerror(errno));
+                       fclose(out);
+                       return;
+               }
+
+               ret = fwrite(buffer, sizeof(char), bytes, out);
+               if (ret != bytes) {
+                       if (ret != 0)
+                               fprintf(stderr, "Short write (expected %d, "
+                                       "got %d)\n", bytes, ret);
+                       else
+                               fprintf(stderr, "Error during write: %s\n",
+                                       strerror(errno));
+                       fclose(out);
+                       return;
+               }
+
+               cur += bytes;
+       }
+
+       fclose(out);
+}
+/*
+ * Dump all residency filenames associated with a file, or all files
+ * within a directory.
+ */
+
+static void
+DumpAllFiles(int argc, char **argv, struct vnodeData *vdatacwd,
+            VolumeDiskData *vol)
+{
+#ifdef RESIDENCY
+       struct vnodeData *vdata, *nvdata;
+       struct DirCursor cursor;
+       struct DirEntry *ep;
+       FILE *f = stdout;
+       int c, i;
+       int dflag = 0, fflag = 0, errflg = 0;
+
+       optind = 1;
+
+       while ((c = getopt(argc, argv, "df:")) != EOF)
+               switch (c) {
+               case 'd':
+                       dflag++;
+                       break;
+               case 'f':
+                       if ((f = fopen(optarg, "a")) == NULL) {
+                               fprintf(stderr, "Cannot open \"%s\": %s\n",
+                                       optarg, strerror(errno));
+                               return;
+                       }
+                       fflag++;
+                       break;
+               case 'h':
+               case '?':
+               default:
+                       errflg++;
+               }
+       
+       if (errflg || argc == optind) {
+               fprintf(stderr, "Usage: %s [-d] [-f filename] file "
+                       "[file ...]\n", argv[0]);
+               if (fflag)
+                       fclose(f);
+               return;
+       }
+
+       for (i = optind; i < argc; i++) {
+
+               if ((vdata = FindFile(vdatacwd, argv[i])) == NULL)
+                       continue;
+
+               if (vdata->vnode->type == vDirectory && ! dflag) {
+                       
+                       ResetDirCursor(&cursor, vdata);
+
+                       while ((ep = ReadNextDir(&cursor, vdata)) != NULL) {
+
+                               if (!(nvdata =
+                                            GetVnode(ntohl(ep->fid.vnode)))) {
+                                       fprintf(stderr, "Cannot find vnode "
+                                               "entry for %s (%d)\n",
+                                               ep->name, ntohl(ep->fid.vnode));
+                                       continue;
+                               }
+
+
+                               if (!fflag) {
+                                       printf("Residency locations for %s:\n",
+                                       ep->name);
+
+                                       if (nvdata->dumpdata)
+                                               printf("Local disk (in dump "
+                                                      "file)\n");
+                               }
+
+                               DumpAllResidencies(f, nvdata, vol);
+                       
+                       }
+
+               } else {
+                       if (!fflag) {
+                               printf("Residency locations for %s:\n",
+                                      argv[i]);
+
+                               if (vdata->dumpdata)
+                                       printf("Local disk (in dump file)\n");
+                       }
+
+                       DumpAllResidencies(f, vdata, vol);
+               }
+       }
+
+       if (fflag)
+               fclose(f);
+#else /* RESIDENCY */
+       fprintf(stderr, "The \"file\" command is not available in the non-"
+               "MRAFS version of dumptool.\n");
+#endif /* RESIDENCY */
+       return;
+}
+
+/*
+ * Take a vnode, traverse the vnode chain, and dump out all files on
+ * all residencies corresponding to that parent vnode.
+ */
+
+#ifdef RESIDENCY
+static void
+DumpAllResidencies(FILE *f, struct vnodeData *vdata, struct VolumeDiskData *vol)
+{
+       unsigned int nextVnodeNum;
+
+       while (nextVnodeNum = vdata->vnode->NextVnodeId) {
+               if ((vdata = GetVnode(nextVnodeNum)) == NULL) {
+                       fprintf(stderr, "We had a pointer to %lu in it's "
+                               "vnode chain, but there\nisn't a record of "
+                               "it!  The dump might be corrupt.\n",
+                               nextVnodeNum);
+                       return;
+               }
+
+               if (vdata->vnode->type == vFidLookup)
+                       DumpVnodeFile(f, vdata->vnode, vol);
+       }
+
+       return;
+}
+#endif
+
+
+/*
+ * Given a directory vnode and a filename, return the vnode corresponding
+ * to the file in that directory.
+ * 
+ * We now handle pathnames with directories in them.
+ */
+
+static struct vnodeData *
+FindFile(struct vnodeData *vdatacwd, char *filename)
+{
+       struct DirHeader *dhp;
+       struct DirEntry *ep;
+       int i, num;
+       struct vnodeData *vdata;
+       char *c, newstr[MAXPATHLEN];
+
+       if (! vdatacwd->filedata) {
+               fprintf(stderr, "There is no vnode data for this "
+                       "directory!\n");
+               return NULL;
+       }
+
+       /*
+        * If we have a "/" in here, look up the vnode data for the
+        * directory (everything before the "/") and use that as our
+        * current directory.  We automagically handle multiple directories
+        * by using FindFile recursively.
+        */
+
+       if ((c = strrchr(filename, '/')) != NULL) {
+
+               strncpy(newstr, filename, c - filename);
+               newstr[c - filename] = '\0';
+
+               if ((vdatacwd = FindFile(vdatacwd, newstr)) == NULL)
+                       return NULL;
+
+               if (vdatacwd->vnode->type != vDirectory) {
+                       fprintf(stderr, "%s: Not a directory\n", newstr);
+                       return NULL;
+               }
+
+               filename = c + 1;
+       }
+
+       dhp = (struct DirHeader *) vdatacwd->filedata;
+
+       i = DirHash(filename);
+
+       num = ntohs(dhp->hashTable[i]);
+
+       while (num) {
+               ep = (struct DirEntry *) (vdatacwd->filedata + (num * 32));
+               if (strcmp(ep->name, filename) == 0)
+                       break;
+               num = ntohs(ep->next);
+       }
+
+       if (! num) {
+               fprintf(stderr, "%s: No such file or directory\n", filename);
+               return NULL;
+       }
+
+       if ((vdata = GetVnode(ntohl(ep->fid.vnode))) == NULL) {
+               fprintf(stderr, "%s: No vnode information for %lu found\n",
+                       filename, ntohl(ep->fid.vnode));
+               return NULL;
+       }
+
+       return vdata;
+}
+
+/*
+ * Reset a structure containing the current directory scan location
+ */
+
+static void
+ResetDirCursor(struct DirCursor *cursor, struct vnodeData *vdata)
+{
+       struct DirHeader *dhp;
+
+       cursor->hashbucket = 0;
+
+       dhp = (struct DirHeader *) vdata->filedata;
+
+       cursor->entry = ntohs(dhp->hashTable[0]);
+}
+
+/*
+ * Given a cursor and a directory entry, return the next entry in the
+ * directory.
+ */
+
+static struct DirEntry *
+ReadNextDir(struct DirCursor *cursor, struct vnodeData *vdata)
+{
+       struct DirHeader *dhp;
+       struct DirEntry *ep;
+
+       dhp = (struct DirHeader *) vdata->filedata;
+
+       if (cursor->entry) {
+               ep = (struct DirEntry *) (vdata->filedata +
+                                                       (cursor->entry * 32));
+               cursor->entry = ntohs(ep->next);
+               return ep;
+       } else {
+               while (++(cursor->hashbucket) < NHASHENT) {
+                       cursor->entry =
+                               ntohs(dhp->hashTable[cursor->hashbucket]);
+                       if (cursor->entry) {
+                               ep = (struct DirEntry *) (vdata->filedata +
+                                                       (cursor->entry * 32));
+                               cursor->entry = ntohs(ep->next);
+                               return ep;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Given a string, split it up into components a la Unix argc/argv.
+ *
+ * This code is most stolen from ftp.
+ */
+
+static void
+MakeArgv(char *string, int *argc, char ***argv)
+{
+       static char *largv[64];
+       char **la = largv;
+       char *s = string;
+       static char argbuf[256];
+       char *ap = argbuf;
+
+       *argc = 0;
+       *argv = largv;
+
+       while (*la++ = GetToken(s, &s, ap, &ap))
+               (*argc)++;
+}
+
+/*
+ * Return a pointer to the next token, and update the current string
+ * position.
+ */
+
+static char *
+GetToken(char *string, char **nexttoken, char argbuf[], char *nextargbuf[])
+{
+       char *sp = string;
+       char *ap = argbuf;
+       int got_one = 0;
+
+S0:
+       switch (*sp) {
+
+       case '\0':
+               goto OUTTOKEN;
+       
+       case ' ':
+       case '\t':
+               sp++; goto S0;
+       
+       default:
+               goto S1;
+       }
+
+S1:
+       switch (*sp) {
+
+       case ' ':
+       case '\t':
+       case '\0':
+               goto OUTTOKEN;  /* End of our token */
+
+       case '\\':
+               sp++; goto S2;  /* Get next character */
+
+       case '"':
+               sp++; goto S3;  /* Get quoted string */
+       
+       default:
+               *ap++ = *sp++;  /* Add a character to our token */
+               got_one = 1;
+               goto S1;
+       }
+
+S2:
+       switch (*sp) {
+
+       case '\0':
+               goto OUTTOKEN;
+
+       default:
+               *ap++ = *sp++;
+               got_one = 1;
+               goto S1;
+       }
+
+S3:
+       switch (*sp) {
+
+       case '\0':
+               goto OUTTOKEN;
+       
+       case '"':
+               sp++; goto S1;
+       
+       default:
+               *ap++ = *sp++;
+               got_one = 1;
+               goto S3;
+       }
+
+OUTTOKEN:
+       if (got_one)
+               *ap++ = '\0';
+       *nextargbuf = ap;               /* Update storage pointer */
+       *nexttoken = sp;                /* Update token pointer */
+
+       return got_one ? argbuf : NULL;
+}
+
+/*
+ * Insert vnodes into our hash table.
+ */
+
+static struct vnodeData *
+InsertVnode(unsigned int vnodeNumber, struct VnodeDiskObject *vnode)
+{
+       struct VnodeDiskObject *nvnode;
+       struct vnodeData *vdata;
+       static int curSmallVnodeIndex = 0;
+       static int curLargeVnodeIndex = 0;
+       struct vnodeData ***vnodeIndex;
+       int *curIndex;
+
+       nvnode = (struct VnodeDiskObject *) malloc(sizeof(struct VnodeDiskObject));
+
+       if (!nvnode) {
+               if (verbose)
+                       fprintf(stderr, "Unable to allocate space for vnode\n");
+               return NULL;
+       }
+
+       memcpy((void *) nvnode, (void *) vnode, sizeof(struct VnodeDiskObject));
+
+       if (vnodeNumber & 1) {
+               vnodeIndex = &LargeVnodeIndex;
+               curIndex = &curLargeVnodeIndex;
+       } else {
+               vnodeIndex = &SmallVnodeIndex;
+               curIndex = &curSmallVnodeIndex;
+       }
+
+       vdata = (struct vnodeData *) malloc(sizeof(struct vnodeData));
+
+       vdata->vnode = nvnode;
+       vdata->vnodeNumber = vnodeNumber;
+       vdata->dumpdata = 0;
+       vdata->filedata = 0;
+       vdata->datalength = 0;
+
+       (*vnodeIndex)[(*curIndex)++] = vdata;
+
+       return vdata;
+}
+
+/*
+ * Routine to retrieve a vnode from the hash table.
+ */
+
+static struct vnodeData *
+GetVnode(unsigned int vnodeNumber)
+{
+       struct vnodeData vnode, *vnodep, **tmp;
+
+       vnode.vnodeNumber = vnodeNumber;
+       vnodep = &vnode;
+
+       tmp = (struct vnodeData **)
+               bsearch((void *) &vnodep,
+                       vnodeNumber & 1 ? LargeVnodeIndex : SmallVnodeIndex,
+                       vnodeNumber & 1 ? numLargeVnodes : numSmallVnodes,
+                       sizeof(struct vnodeData *), CompareVnode);
+
+       return tmp ? *tmp : NULL;
+}
+
+/*
+ * Our comparator function for bsearch
+ */
+
+static int
+CompareVnode(const void *node1, const void *node2)
+{
+       struct vnodeData **vnode1 = (struct vnodeData **) node1;
+       struct vnodeData **vnode2 = (struct vnodeData **) node2;
+
+       if ((*vnode1)->vnodeNumber == (*vnode2)->vnodeNumber)
+               return 0;
+       else if ((*vnode1)->vnodeNumber > (*vnode2)->vnodeNumber)
+               return 1;
+       else
+               return -1;
+}
+
+#ifdef RESIDENCY
+/*
+ * Dump out the filename corresponding to a particular vnode.
+ *
+ * This routine has the following dependancies:
+ *
+ * - Only will work on UFS filesystems at this point
+ * - Has to talk to the rsserver.
+ * - Can only determine UFS algorithm type when run on the same machine
+ *   as the residency (unless you manually specify algorithm information)
+ */
+
+static int
+DumpVnodeFile(FILE *f, struct VnodeDiskObject *vnode, VolumeDiskData *vol)
+{
+       static int rscache = 0;
+       static rsaccessinfoList rsnlist = {0, 0};
+       char MountPoint[MAXPATHLEN + 1];
+       char FileName[MAXPATHLEN + 1];
+       unsigned int Size, Level[4];
+       unsigned int DeviceTag, Algorithm;
+       FileSystems *FSInfo;
+       int i, found, FSType, rsindex;
+
+       /*
+        * Maybe we found out something about this residency via the
+        * command-line; check that first.
+        */
+
+       rsindex = ffs(VLkp_Residencies(vnode)) - 1;
+
+       /*
+        * We need to get information from the rsserver (so we can
+        * find out the device tag for a given residency).  If we
+        * haven't cached that, talk to the rsserver to get it.
+        * If we have info about this already, then don't talk to
+        * the rsserver (this lets us still do disaster recovery if
+        * MR-AFS is completely hosed).
+        */
+
+       if (! rscache && rscmdlineinfo[rsindex].DeviceTag == -1) {
+               int code;
+
+               code = ServerInitResidencyConnection();
+
+               if (code) {
+                       fprintf(stderr, "ServerInitResidencyConnection failed "
+                               "with code %d\n", code);
+                       return -1;
+               }
+
+               code = rs_GetResidencySummary(ServerRequestorId, &rsnlist);
+
+               if (code) {
+                       fprintf(stderr, "rs_GetResidencySummary failed "
+                               "with code %d\n", code);
+                       return -1;
+               }
+
+               rscache = 1;
+       }
+
+       /*
+        * For a given residency (as specified in the vnode),
+        * find out it's device tag number, either via the rsserver
+        * or via the command line.
+        */
+
+       if (rscmdlineinfo[rsindex].DeviceTag != -1) {
+               DeviceTag = rscmdlineinfo[rsindex].DeviceTag;
+               found = 1;
+       } else
+               for (i = 0, found = 0; (i < rsnlist.rsaccessinfoList_len) &&
+                                                               (!found); i++) {
+                       if (rsnlist.rsaccessinfoList_val[i].id.residency ==
+                           VLkp_Residencies(vnode)) {
+                               found = 1;
+                               DeviceTag =
+                            rsnlist.rsaccessinfoList_val[i].devicetagnumber;
+                               break;
+                       }
+               }
+
+       if (! found) {
+               if (verbose)
+                       fprintf(stderr, "Unable to find residency %d in "
+                               "rsserver database, aborting\n",
+                               VLkp_Residencies(vnode));
+               return -1;
+       }
+
+       /*
+        * Okay, now we've got the DeviceTag ... which we can use to
+        * lookup the on-disk configuration information (which we
+        * assume is locally stored).  We also need the DeviceTag to
+        * print out which partition we're using (but that comes later).
+        *
+        * We lookup the on-disk configuration information by calling
+        * Ufs_GetFSInfo() to get the configuration information on the
+        * filesystems specified by the given DeviceTag.
+        *
+        * Before we call Ufs_GetFSInfo, check the command-line cache;
+        * if we got something via the command-line, don't go to disk.
+        */
+
+       if (rscmdlineinfo[rsindex].FSType == -1 &&
+           Ufs_GetFSInfo(&FSInfo, DeviceTag)) {
+               if (verbose)
+                       fprintf(stderr, "Ufs_GetFSInfo failed for DeviceTag "
+                               "%d, Residency %d\n", DeviceTag,
+                               VLkp_Residencies(vnode));
+               return -1;
+       }
+
+       /*
+        * The FSInfo structure has the last two things we need: the
+        * FSType (ufs, slowufs, etc etc), and the usage algorithm (which
+        * ends up being how many directories are being used on the
+        * residency filesystem).
+        *
+        * With these last two parameters, use routines stolen from
+        * ufsname to generate the filename.
+        *
+        * (Actually, I lied - we also need the "Size" parameter, which
+        * we can also get from FSInfo);
+        */
+
+       if (rscmdlineinfo[rsindex].FSType != -1) {
+               FSType = rscmdlineinfo[rsindex].FSType;
+               Algorithm = rscmdlineinfo[rsindex].Algorithm;
+               Size = rscmdlineinfo[rsindex].Size;
+       } else {
+               FSType = FSInfo->FileSystems_u.UfsInterface.FSType;
+               Algorithm = FSInfo->FileSystems_u.UfsInterface.Algorithm;
+               if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 0)
+                       Size = 0;
+               else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 16)
+                       Size = 1;
+               else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 256)
+                       Size = 2;
+               else {
+                       if (verbose)
+                               fprintf(stderr, "Unknown directory size %d, "
+                                       "aborting\n",
+                                       FSInfo->FileSystems_u.UfsInterface.Directories[1]);
+                       return -1;
+               }
+       }
+
+       /*
+        * First, generate our mount point from the DeviceTag and
+        * FSType.
+        */
+
+       DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTag, FSType);
+
+       /*
+        * Then, generate the "level" (directory bitmasks) from the
+        * file tags, size, and algorithm
+        */
+
+       UfsTagsToLevel(VLkp_FileTag1(vnode), VLkp_FileTag2(vnode), Algorithm,
+                      Size, Level, VLkp_ParentVnodeId(vnode),
+                      VLkp_ParentUniquifierId(vnode));
+
+       /*
+        * Finally, take the above information and generate the
+        * corresponding filename (this macro ends up being a
+        * sprintf() call)
+        */
+
+       TAGSTONAME(FileName, MountPoint, Level, Directories[Size][1],
+                  vol->parentId, VLkp_ParentVnodeId(vnode),
+                  VLkp_ParentUniquifierId(vnode), Algorithm);
+
+       fprintf(f, "%s\n", FileName);
+
+       return 0;
+}
+#endif
+
+/*
+ * Read a 16 bit integer in network order
+ */
+
+static int
+ReadInt16(FILE *f, unsigned short *s)
+{
+       unsigned short in;
+
+       if (fread((void *)&in, sizeof(in), 1, f) != 1) {
+               if (verbose)
+                       fprintf(stderr, "ReadInt16 failed!\n");
+               return -1;
+       }
+
+       *s = ntohs(in);
+
+       return 0;
+}
+
+
+/*
+ * Read a 32 bit integer in network order
+ */
+
+static int
+ReadInt32(FILE *f, unsigned int *i)
+{
+       unsigned int in;
+
+       if (fread((void *)&in, sizeof(in), 1, f) != 1) {
+               if (verbose)
+                       fprintf(stderr, "ReadInt32 failed!\n");
+               return -1;
+       }
+
+       *i = ntohl((unsigned long) in);
+
+       return 0;
+}
+
+/*
+ * Read a string from a dump file
+ */
+
+static int
+ReadString(FILE *f, char *string, int maxlen)
+{
+       int c;
+
+       while (maxlen--) {
+               if ((*string++ = getc(f)) == 0)
+                       break;
+       }
+
+       /*
+        * I'm not sure what the _hell_ this is supposed to do ...
+        * but it was in the original dump code
+        */
+
+       if (string[-1]) {
+               while ((c = getc(f)) && c != EOF);
+               string[-1] = 0;
+       }
+
+       return 0;
+}
+
+static int
+ReadByteString(FILE *f, void *s, int size)
+{
+       unsigned char *c = (unsigned char *) s;
+
+       while (size--)
+               *c++ = getc(f);
+       
+       return 0;
+}
+
+/*
+ * The directory hashing algorithm used by AFS
+ */
+
+DirHash (string)
+    register char *string; {
+    /* Hash a string to a number between 0 and NHASHENT. */
+    register unsigned char tc;
+    register int hval;
+    register int tval;
+    hval = 0;
+    while(tc=(*string++)) {
+        hval *= 173;
+        hval  += tc;
+    }
+    tval = hval & (NHASHENT-1);
+#ifdef AFS_CRAY_ENV     /* actually, any > 32 bit environment */
+    if (tval == 0) return tval;
+    else if (hval & 0x80000000) tval = NHASHENT-tval;
+#else /* AFS_CRAY_ENV */
+    if (tval == 0) return tval;
+    else if (hval < 0) tval = NHASHENT-tval;
+#endif /* AFS_CRAY_ENV */
+    return tval;
+}
+
+#ifdef RESIDENCY
+/*
+ * Sigh, we need this for the AFS libraries
+ */
+
+int
+LogErrors(int level, char *a, char *b, char *c, char *d, char *e, char *f,
+         char *g, char *h, char *i, char *j, char *k)
+{
+       if (level <= 0) {
+               fprintf(stderr, a, b, c, d, e, f, g, h, i, j, k);
+       }
+       return 0;
+}
+
+/*
+ * These are routines taken from AFS libraries and programs.  Most of
+ * them are from ufsname.c, but a few are from the dir library (the dir
+ * library has a bunch of hidden dependancies, so it's not suitable to
+ * include it outright).
+ */
+
+UfsEntropiesToTags(HighEntropy,LowEntropy,Algorithm,FileTag1,FileTag2)
+    uint32_t HighEntropy;
+    uint32_t LowEntropy;
+    uint32_t Algorithm;
+    uint32_t *FileTag1;
+    uint32_t *FileTag2;
+{
+    int i;
+
+    if ((Algorithm > UFS_ALGORITHMS) || (Algorithm <= 0))
+        return -1;
+    *FileTag1 = 0;
+    *FileTag2 = 0;
+    for (i=0;i<32;++i) {
+        if (UfsEntropy[Algorithm-1][i] < 32)
+           *FileTag1 |= ((HighEntropy & (1 << i)) == 0) ?
+               0 : 1 << UfsEntropy[Algorithm-1][i];
+       else
+           *FileTag2 |= ((HighEntropy & (1 << i)) == 0) ?
+               0 : 1 << (UfsEntropy[Algorithm-1][i] - 32);
+    }
+    for (i=32;i<64;++i) {
+        if (UfsEntropy[Algorithm-1][i] < 32)
+           *FileTag1 |=((LowEntropy & (1 << (i - 32))) == 0) ?
+               0 : 1 << UfsEntropy[Algorithm-1][i];
+       else
+           *FileTag2 |=((LowEntropy & (1 << (i - 32))) == 0) ?
+               0 : 1 << (UfsEntropy[Algorithm-1][i] - 32);
+    }
+    return 0;
+}
+
+uint32_t UfsTagsToHighEntropy(FileTag1,FileTag2,Algorithm)
+    uint32_t FileTag1;
+    uint32_t FileTag2;
+    uint32_t Algorithm;
+{
+    int i;
+    uint32_t Value;
+
+    Value = 0;
+    for (i=0;i<32;++i) {
+        if (UfsEntropy[Algorithm-1][i] < 32)
+           Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm-1][i]))
+                     == 0) ? 0: 1 << i;
+       else
+           Value |= ((FileTag2 & (1 << (UfsEntropy[Algorithm-1][i] - 
+                                        32))) == 0) ? 0: 1 << i;
+    }
+    return Value;
+}
+
+uint32_t UfsTagsToLowEntropy(FileTag1,FileTag2,Algorithm)
+    uint32_t FileTag1;
+    uint32_t FileTag2;
+    uint32_t Algorithm;
+{
+    int i;
+    uint32_t Value;
+
+    Value = 0;
+    for (i=32;i<64;++i) {
+        if (UfsEntropy[Algorithm-1][i] < 32)
+           Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm-1][i])) 
+                     == 0) ? 0: 1 << (i - 32);
+       else
+           Value |= ((FileTag2 & (1 << (UfsEntropy[Algorithm-1][i] -
+                                        32))) == 0) ? 0: 1 << (i - 32) ;
+    }
+    return Value;
+}
+
+UfsTagsToLevel(FileTag1, FileTag2, Algorithm, Size, Sections, vnode, Uniquifier)
+    uint32_t FileTag1;
+    uint32_t FileTag2;
+    uint32_t Algorithm;
+    uint32_t Size;
+    uint32_t Sections[4];
+    uint32_t vnode;
+    uint32_t Uniquifier;
+{
+    uint32_t HighEntropy;
+    uint32_t LowEntropy;
+
+    switch (Algorithm) {
+        case 1:
+            LowEntropy = UfsTagsToLowEntropy(
+                                FileTag1,
+                                FileTag2,
+                                Algorithm);
+            HighEntropy = UfsTagsToHighEntropy(
+                                FileTag1,
+                                FileTag2,
+                                Algorithm);
+            Sections[0] = HighEntropy % Directories[Size][0];
+            HighEntropy /= Directories[Size][0];
+            if (Directories[Size][1]) {
+                Sections[1] = HighEntropy % Directories[Size][1];
+                HighEntropy /= Directories[Size][1];
+                Sections[2] = HighEntropy;
+                Sections[3] = LowEntropy;
+            } else {
+                Sections[1] = HighEntropy;
+                Sections[2] = LowEntropy;
+            }
+            break;
+        case 2:
+            Sections[0] = FileTag1 & 0xff;
+            if (Directories[Size][1]) {
+                Sections[1] = Uniquifier & 0xff;
+                if (Directories[Size][1] == 16) Sections[1] &= 0xf;
+                Sections[2] = FileTag1;
+                Sections[3] = FileTag2;
+            } else {
+                Sections[1] = FileTag1;
+                Sections[2] = FileTag2;
+            }
+            break;
+        case 3:
+            Sections[0] = FileTag1 & 0xff;
+            if (Directories[Size][1]) {
+                Sections[1] = (vnode >> 1) & 0xff;
+                if (Directories[Size][1] == 16) Sections[1] &= 0xf;
+                Sections[2] = FileTag1;
+                Sections[3] = FileTag2;
+            } else {
+                Sections[1] = FileTag1;
+                Sections[2] = FileTag2;
+            }
+            break;
+        default:
+            fprintf(stderr,"UfsTagsToLevel: bad algorithm %lu!\n", Algorithm);
+            return -1;
+    }
+    return 0;
+}
+
+#include <afs/afscbdummies.h>
+#endif /* RESIDENCY */
diff --git a/src/tests/int64.c b/src/tests/int64.c
new file mode 100644 (file)
index 0000000..4d4a68f
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* int64.c - Support for 64-bit integers */
+
+#include <stdio.h>
+#include <string.h>
+#include "intNN.h"
+
+char *hexify_int64(u_int64 *X, char *buf)
+{
+  static char mybuf[17];
+
+#ifdef NATIVE_INT64
+  char c, *p;
+  u_int64 x = *X;
+
+  if (!buf) buf = mybuf;
+  p = buf + 16;
+  *p-- = 0;
+  while (x && p >= buf) {
+    c = x & 0xf;
+    c += ((c < 10) ? '0' : 'a' - 10);
+    *p-- = c;
+    x >>= 4;
+  }
+  while (p >= buf) *p-- = '0';
+
+#else
+  if (!buf) buf = mybuf;
+  sprintf(buf, "%08lx%08lx", X->hi, X->lo);
+#endif
+
+  return buf;
+}
+
+
+#ifdef NATIVE_INT64
+char *decimate_int64(u_int64 *X, char *buf)
+{
+  static char mybuf[21];
+  char *p;
+  u_int64 x = *X;
+
+  if (!buf) buf = mybuf;
+  p = buf + 21;
+  *--p = 0;
+  while (x && p > buf) {
+    *--p = ((x % 10) + '0');
+    x /= 10;
+  }
+  if (!*p) *--p = '0';
+  return p;
+}
+
+#else
+static char bitvals[64][21] = {
+/*                1 */ "00000000000000000001",
+/*                2 */ "00000000000000000002",
+/*                4 */ "00000000000000000004",
+/*                8 */ "00000000000000000008",
+/*               10 */ "00000000000000000016",
+/*               20 */ "00000000000000000032",
+/*               40 */ "00000000000000000064",
+/*               80 */ "00000000000000000128",
+/*              100 */ "00000000000000000256",
+/*              200 */ "00000000000000000512",
+/*              400 */ "00000000000000001024",
+/*              800 */ "00000000000000002048",
+/*             1000 */ "00000000000000004096",
+/*             2000 */ "00000000000000008192",
+/*             4000 */ "00000000000000016384",
+/*             8000 */ "00000000000000032768",
+/*            10000 */ "00000000000000065536",
+/*            20000 */ "00000000000000131072",
+/*            40000 */ "00000000000000262144",
+/*            80000 */ "00000000000000524288",
+/*           100000 */ "00000000000001048576",
+/*           200000 */ "00000000000002097152",
+/*           400000 */ "00000000000004194304",
+/*           800000 */ "00000000000008388608",
+/*          1000000 */ "00000000000016777216",
+/*          2000000 */ "00000000000033554432",
+/*          4000000 */ "00000000000067108864",
+/*          8000000 */ "00000000000134217728",
+/*         10000000 */ "00000000000268435456",
+/*         20000000 */ "00000000000536870912",
+/*         40000000 */ "00000000001073741824",
+/*         80000000 */ "00000000002147483648",
+/*        100000000 */ "00000000004294967296",
+/*        200000000 */ "00000000008589934592",
+/*        400000000 */ "00000000017179869184",
+/*        800000000 */ "00000000034359738368",
+/*       1000000000 */ "00000000068719476736",
+/*       2000000000 */ "00000000137438953472",
+/*       4000000000 */ "00000000274877906944",
+/*       8000000000 */ "00000000549755813888",
+/*      10000000000 */ "00000001099511627776",
+/*      20000000000 */ "00000002199023255552",
+/*      40000000000 */ "00000004398046511104",
+/*      80000000000 */ "00000008796093022208",
+/*     100000000000 */ "00000017592186044416",
+/*     200000000000 */ "00000035184372088832",
+/*     400000000000 */ "00000070368744177664",
+/*     800000000000 */ "00000140737488355328",
+/*    1000000000000 */ "00000281474976710656",
+/*    2000000000000 */ "00000562949953421312",
+/*    4000000000000 */ "00001125899906842624",
+/*    8000000000000 */ "00002251799813685248",
+/*   10000000000000 */ "00004503599627370496",
+/*   20000000000000 */ "00009007199254740992",
+/*   40000000000000 */ "00018014398509481984",
+/*   80000000000000 */ "00036028797018963968",
+/*  100000000000000 */ "00072057594037927936",
+/*  200000000000000 */ "00144115188075855872",
+/*  400000000000000 */ "00288230376151711744",
+/*  800000000000000 */ "00576460752303423488",
+/* 1000000000000000 */ "01152921504606846976",
+/* 2000000000000000 */ "02305843009213693952",
+/* 4000000000000000 */ "04611686018427387904",
+/* 8000000000000000 */ "09223372036854775808" };
+
+
+static void prep_table(void)
+{
+  int bit, digit;
+
+  if (bitvals[0][0] < '0') return;
+  for (bit = 0; bit < 64; bit++)
+    for (digit = 0; digit < 20; digit++)
+      bitvals[bit][digit] -= '0';
+}
+
+
+static void add_bit(int bit, char *answer)
+{
+  int digit;
+
+  for (digit = 19; digit >= 0; digit--) {
+    answer[digit] += bitvals[bit][digit];
+    if (!digit) break;
+    while(answer[digit] > 9) {
+      answer[digit] -= 10;
+      answer[digit-1]++;
+    }
+  }
+}
+
+
+static void decimate(unsigned long hi, unsigned long lo, char *answer)
+{
+  unsigned long mask;
+  int bit, digit;
+
+  memset(answer, 0, 21);
+  for (bit = 0, mask = 1; bit < 32; bit++, mask <<= 1)
+    if (lo&mask) add_bit(bit, answer);
+  for (bit = 0, mask = 1; bit < 32; bit++, mask <<= 1)
+    if (hi&mask) add_bit(bit + 32, answer);
+
+  for (digit = 0; digit < 20; digit++)
+    answer[digit] += '0';
+}
+
+char *decimate_int64(u_int64 *X, char *buf)
+{
+  static char mybuf[21];
+  char *p;
+
+  prep_table();
+  if (!buf) buf = mybuf;
+  decimate(X->hi, X->lo, buf);
+  for (p = buf; *p == '0'; p++);
+  return (*p) ? p : p-1;
+}
+
+#endif /* NATIVE_INT64 */
+
+
+void shift_int64(u_int64 *X, int bits)
+{
+#ifdef NATIVE_INT64
+  if (bits < 0) *X >>= (-bits);
+  else          *X <<= bits;
+#else
+  if (bits < 0) {
+    bits = -bits;
+    if (bits >= 32) {
+      X->lo = ((X->hi & 0xffffffffL) >> (bits - 32));
+      X->hi = 0;
+    } else {
+      X->lo = ((X->lo & 0xffffffffL) >> bits)
+            | ((X->hi & ((1 << (32 - bits)) - 1)) << (32 - bits));
+      X->hi = ((X->hi & 0xffffffffL) >> bits);
+    }
+  } else {
+    if (bits >= 32) {
+      X->hi = ((X->lo & 0xffffffffL) << (bits - 32));
+      X->lo = 0;
+    } else {
+      X->hi = ((X->hi & 0xffffffffL) << bits)
+            | ((X->lo & (((1 << bits) - 1) << (32 - bits))) >> (32 - bits));
+      X->lo = ((X->lo & 0xffffffffL) << bits);
+    }
+  }
+#endif
+}
+
+
+#ifdef TEST_INT64
+
+/** the rest of this is for testing the int64 suite **/
+
+#ifdef NATIVE_INT64
+
+#define xize(x) #x
+#define stringize(x) xize(x)
+#define INT64_NAME stringize(unsigned NATIVE_INT64)
+
+
+#endif /* NATIVE_INT64 */
+
+
+void verify_int64_size () {
+#ifdef NATIVE_INT64
+  signed char testchar = -1;
+  unsigned int testint = (unsigned char)testchar;
+
+  printf("We think '%s' is a native 64-bit type\n", INT64_NAME);
+
+  if (testint != 0xff) {
+    printf("testint = 0x%x; should be 0xff\n", testint);
+    fprintf(stderr, "Hmm...  char's are not 8 bits.  That sucks!\n");
+    exit(-1);
+  }
+  printf("Looks like a char is 8 bits...\n");
+
+  if (sizeof(unsigned NATIVE_INT64) != 8) {
+    printf("sizeof(%s) = %d; should be 8\n", INT64_NAME, sizeof(unsigned NATIVE_INT64));
+    fprintf(stderr, "Hey!  You said a %s was 64-bits wide!\n", INT64_NAME);
+    exit(-1);
+  }
+  printf("Yippee!  We have a native 64-bit type (%s)\n\n", INT64_NAME);
+
+#else /* !NATIVE_INT64 */
+
+  printf("Using fake 64-bit integers...\n\n");
+#endif /* NATIVE_INT64 */
+}
+
+
+void test_int64_constructs(void)
+{
+  u_int64 x, y;
+  afs_uint32 hi, lo;
+  int failures = 0, pass;
+  char buf[17];
+
+  printf("Constructor/accessor tests:\n");
+  printf("Setting x := %s\n", INT64_TEST_STR);
+  mk64(x, INT64_TEST_HI, INT64_TEST_LO);
+
+#ifdef NATIVE_INT64
+  pass = (x == INT64_TEST_CONST);
+  hexify_int64(&x, buf);
+  printf("NATIVE mk64: x       = 0x%16s                %s\n",
+         buf, pass ? "PASSED" : "FAILED");
+  if (!pass) failures++;
+#else
+  pass = (x.hi == INT64_TEST_HI && x.lo == INT64_TEST_LO);
+  printf("FAKE mk64:   x.hi    = 0x%08lx  x.lo    = 0x%08lx  %s\n",
+         x.hi, x.lo, pass ? "PASSED" : "FAILED");
+  if (!pass) failures++;
+#endif
+
+  pass = (hi64(x) == INT64_TEST_HI && lo64(x) == INT64_TEST_LO);
+  printf("hi64/lo64:   hi64(x) = 0x%08lx  lo64(x) = 0x%08lx  %s\n",
+         hi64(x), lo64(x), pass ? "PASSED" : "FAILED");
+  if (!pass) failures++;
+
+  ex64(x, hi, lo);
+  pass = (hi == INT64_TEST_HI && lo == INT64_TEST_LO);
+  printf("ex64:        hi      = 0x%08lx  lo      = 0x%08lx  %s\n",
+         hi, lo, pass ? "PASSED" : "FAILED");
+  if (!pass) failures++;
+
+  cp64(y, x);
+  pass = (hi64(y) == INT64_TEST_HI && lo64(y) == INT64_TEST_LO);
+  printf("cp64:        hi64(y) = 0x%08lx  lo64(y) = 0x%08lx  %s\n",
+         hi64(y), lo64(y), pass ? "PASSED" : "FAILED");
+  if (!pass) failures++;
+
+  if (failures) printf("%d/4 tests FAILED\n\n", failures);
+  else          printf("All 4 tests PASSED\n\n");
+}
+
+
+void test_int64_compares()
+{
+#define NCOMPARE 9
+  u_int64 control, test[NCOMPARE];
+  char cbuf[17], tbuf[17];
+  int i, r, result[NCOMPARE];
+  int pass, failures, FAILURES = 0;
+
+  printf("Comparison tests:\n");
+
+  mk64(control, 0x12345678, 0xabcdabcd);
+  mk64(test[0], 0x12340000, 0xabcd0000);  result[0] = +1;
+  mk64(test[1], 0x12340000, 0xabcdabcd);  result[1] = +1;
+  mk64(test[2], 0x12340000, 0xabcdffff);  result[2] = +1;
+  mk64(test[3], 0x12345678, 0xabcd0000);  result[3] = +1;
+  mk64(test[4], 0x12345678, 0xabcdabcd);  result[4] =  0;
+  mk64(test[5], 0x12345678, 0xabcdffff);  result[5] = -1;
+  mk64(test[6], 0x1234ffff, 0xabcd0000);  result[6] = -1;
+  mk64(test[7], 0x1234ffff, 0xabcdabcd);  result[7] = -1;
+  mk64(test[8], 0x1234ffff, 0xabcdffff);  result[8] = -1;
+
+  for (i = 0; i < NCOMPARE; i++) {
+    failures = 0;
+    hexify_int64(&control, cbuf);
+    hexify_int64(&test[i], tbuf);
+
+    r = eq64(control, test[i]);
+    pass = (r == (result[i] == 0));   if (!pass) failures++;
+    printf("0x%s == 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED");
+
+    r = ne64(control, test[i]);
+    pass = (r == (result[i] != 0));   if (!pass) failures++;
+    printf("0x%s != 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED");
+
+    r = lt64(control, test[i]);
+    pass = (r == (result[i] <  0));   if (!pass) failures++;
+    printf("0x%s <  0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED");
+
+    r = le64(control, test[i]);
+    pass = (r == (result[i] <= 0));   if (!pass) failures++;
+    printf("0x%s <= 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED");
+
+    r = gt64(control, test[i]);
+    pass = (r == (result[i] >  0));   if (!pass) failures++;
+    printf("0x%s >  0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED");
+
+    r = ge64(control, test[i]);
+    pass = (r == (result[i] >= 0));   if (!pass) failures++;
+    printf("0x%s >= 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED");
+
+    r = zero64(test[i]);
+    pass = !r;                        if (!pass) failures++;
+    printf("0x%s is nonzero            %s\n",
+           tbuf, pass ? "PASSED" : "FAILED");
+
+    if (failures) printf("%d/7 tests on this pair FAILED\n\n", failures);
+    else          printf("All 7 tests on this pair PASSED\n\n");
+  }
+
+  mk64(control, 0, 0);
+  pass = zero64(control);  if (!pass) FAILURES++;
+  printf("0x0000000000000000 is zero               %s\n",
+         pass ? "PASSED" : "FAILED");
+  
+  if (FAILURES)
+    printf("%d/%d comparison tests FAILED\n\n", FAILURES, 7 * NCOMPARE + 1);
+  else
+    printf("All %d comparison tests PASSED\n\n", 7 * NCOMPARE + 1);
+}
+
+
+void test_int64_arith()
+{
+  printf("No arithmetic tests yet!!!\n");
+}
+
+
+void main() {
+  verify_int64_size();
+  test_int64_constructs();
+  test_int64_compares();
+  test_int64_arith();
+  exit(0);
+}
+#endif
diff --git a/src/tests/intNN.h b/src/tests/intNN.h
new file mode 100644 (file)
index 0000000..9646052
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifndef _INTNN_H_
+#define _INTNN_H_
+
+/* intNN.h - Sized integer types */
+#include <afs/stds.h>
+#if 0
+typedef short afs_int16;
+typedef unsigned short afs_uint16;
+
+typedef long afs_int32;
+typedef unsigned long afs_uint32;
+#endif
+
+
+/* Support for 64-bit integers.
+ * Presently, only unsigned 64-bit numbers are supported.
+ */
+#define INT64_TEST_STR "0x12345678fedcba98"
+#define INT64_TEST_HI  0x12345678
+#define INT64_TEST_LO  0xfedcba98
+
+
+#ifdef NATIVE_INT64
+typedef unsigned NATIVE_INT64 u_int64;
+
+/* construct/extract/assign */
+#define mk64(X,H,L) ((X) = ( ((u_int64)(H) << 32) \
+                           | ((u_int64)(L) & 0xffffffff)))
+#define hi64(Y)     ((afs_uint32)(((Y) >> 32) & 0xffffffff))
+#define lo64(Y)     ((afs_uint32)((Y) & 0xffffffff))
+#define ex64(Y,H,L) ((H) = hi64(Y), (L) = lo64(Y))
+#define cp64(X,Y)   ((X) = (Y))
+#define get64(X)    (X)
+#define set64(X,V)  ((X) = (V))
+
+/* Comparison */
+#define eq64(X,Y)   ((X) == (Y))
+#define ne64(X,Y)   ((X) != (Y))
+#define lt64(X,Y)   ((X) <  (Y))
+#define le64(X,Y)   ((X) <= (Y))
+#define gt64(X,Y)   ((X) >  (Y))
+#define ge64(X,Y)   ((X) >= (Y))
+#define zero64(X)   (!(X))
+
+/* Arithmetic */
+#define add64_32(X,A,B) ((X) = (A) + (u_int64)(B))
+#define add64_64(X,A,B) ((X) = (A) + (B))
+#define sub64_32(X,A,B) ((X) = (A) - (u_int64)(B))
+#define sub64_64(X,A,B) ((X) = (A) - (B))
+
+/* Byte-order */
+#ifdef WORDS_BIGENDIAN
+#define hton64(X,Y) cp64(X,Y)
+#define ntoh64(X,Y) cp64(X,Y)
+#else
+#define hton64(X,Y) mk64(X,htonl(lo64(Y)),htonl(hi64(Y)))
+#define ntoh64(X,Y) mk64(X,ntohl(lo64(Y)),ntohl(hi64(Y)))
+#endif
+
+#else /* !NATIVE_INT64 */
+/** We have to provide our own 64-bit integers **/
+typedef struct { afs_uint32 hi, lo; } u_int64;
+
+/* construct/extract/assign */
+#define mk64(X,H,L) ((X).hi = (H), (X).lo = (L))
+#define ex64(Y,H,L) ((H) = (Y).hi, (L) = (Y).lo)
+#define hi64(Y)     ((Y).hi)
+#define lo64(Y)     ((Y).lo)
+#define cp64(X,Y)   ((X).hi = (Y).hi, (X).lo = (Y).lo)
+#define get64(X)    ((X).lo)
+#define set64(X,V)  ((X).hi = 0, (X).lo = (V))
+
+/* Comparison */
+#define eq64(A,B) ((A).hi == (B).hi && (A).lo == (B).lo)
+#define ne64(A,B) ((A).hi != (B).hi || (A).lo != (B).lo)
+#define lt64(A,B) ((A).hi <  (B).hi || ((A).hi == (B).hi && (A).lo <  (B).lo))
+#define le64(A,B) ((A).hi <  (B).hi || ((A).hi == (B).hi && (A).lo <= (B).lo))
+#define gt64(A,B) ((A).hi >  (B).hi || ((A).hi == (B).hi && (A).lo >  (B).lo))
+#define ge64(A,B) ((A).hi >  (B).hi || ((A).hi == (B).hi && (A).lo >= (B).lo))
+#define zero64(X) ((X).hi == 0 && (X).lo == 0)
+
+/* Arithmetic */
+#define add64_32(X,A,B) (                                              \
+  (X).lo = (A).lo + (B),                                               \
+  (X).hi = (A).hi +                                                    \
+   (((((A).lo & 0x80000000) ^  ((B) & 0x80000000)) && !((X).lo & 0x80000000)) \
+  || (((A).lo & 0x80000000) && ((B) & 0x80000000)))                     \
+  )
+#define add64_64(X,A,B) (add64_32(X,A,(B).lo), (X).hi += (B).hi)
+
+#define sub64_32(X,A,B) ((X).lo = (A).lo - (B), \
+                         (X).hi = (A).hi - ((A).lo < (B)))
+#define sub64_64(X,A,B) (sub64_32(X,A,(B).lo), (X).hi -= (B).hi)
+
+/* Byte-order */
+#define hton64(X,Y) mk64(X,htonl(hi64(Y)),htonl(lo64(Y)))
+#define ntoh64(X,Y) mk64(X,ntohl(hi64(Y)),ntohl(lo64(Y)))
+
+#endif /* NATIVE_INT64 */
+
+
+/* The following are too complex to be macros: */
+
+/* char *hexify_int64(u_int64 a, char *buf)
+ * Produces an ASCII representation of a in hexadecimal, and returns
+ * a pointer to the resulting string.  If buf is non-NULL, it is taken
+ * to be a pointer to the buffer to be used, which must be at least 17
+ * bytes long.  This function is thread-safe iff buf is provided.
+ */
+extern char *hexify_int64(u_int64 *, char *);
+
+/* char *decimate_int64(u_int64 a, char *buf)
+ * Produces an ASCII representation of a in decimal, and returns
+ * a pointer to the resulting string.  If buf is non-NULL, it is taken
+ * to be a pointer to the buffer to be used, which must be at least 21
+ * bytes long.  This function is thread-safe iff buf is provided.
+ */
+extern char *decimate_int64(u_int64 *, char *);
+
+/* void shift_int64(u_int64 a, int bits)
+ * Shifts the 64-bit integer in a by the specified number of bits.
+ * If bits is positive, the shift is to the left; if negative, the
+ * shift is to the right.
+ */
+extern void shift_int64(u_int64 *, int);
+
+#endif /* _INTNN_H_ */
diff --git a/src/tests/internal.h b/src/tests/internal.h
new file mode 100644 (file)
index 0000000..04233db
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* internal.h - Routines for internal use only */
+
+#include "xfiles.h"
+#include "dumpscan.h"
+
+
+/* parsevol.c - Routines to parse volume headers */
+extern afs_uint32 parse_volhdr(XFILE *, unsigned char *, tagged_field *, afs_uint32,
+                            tag_parse_info *, void *, void *);
+
+/* parsevnode.c - Routines to parse vnodes and their fields */
+extern afs_uint32 parse_vnode(XFILE *, unsigned char *, tagged_field *, afs_uint32,
+                           tag_parse_info *, void *, void *);
+
+/* directory.c - Routines for parsing AFS directories */
+extern afs_uint32 parse_directory(XFILE *, dump_parser *, afs_vnode *,
+                               afs_uint32, int);
+
+/* backuphdr.c - Generic support for backup system headers */
+extern afs_uint32 try_backuphdr(XFILE *X, unsigned char *tag, tagged_field *field,
+                             afs_uint32 value, tag_parse_info *pi,
+                             void *g_refcon, void *l_refcon);
+
+/* util.c - Random utilities */
+extern afs_uint32 handle_return(int, XFILE *, unsigned char, dump_parser *);
+extern void prep_pi(dump_parser *, tag_parse_info *);
+extern afs_uint32 match_next_vnode(XFILE *, dump_parser *, u_int64 *, afs_uint32);
diff --git a/src/tests/null-search.c b/src/tests/null-search.c
new file mode 100644 (file)
index 0000000..39afb04
--- /dev/null
@@ -0,0 +1,150 @@
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "dumpscan.h"
+
+char *argv0;
+static char *input_path = 0;
+static int quiet = 0, showpaths = 0, searchcount = 1;
+static int error_count = 0, bad_count = 0;
+static path_hashinfo phi;
+static dump_parser dp;
+
+/* Print a usage message and exit */
+static void usage(int status, char *msg)
+{
+  if (msg) fprintf(stderr, "%s: %s\n", argv0, msg);
+  fprintf(stderr, "Usage: %s [options] [file]\n", argv0);
+  fprintf(stderr, "  -h     Print this help message\n");
+  fprintf(stderr, "  -p     Print paths of bad vnodes\n");
+  fprintf(stderr, "  -q     Quiet mode (don't print errors)\n");
+  exit(status);
+}
+
+
+/* Parse the command-line options */
+static void parse_options(int argc, char **argv)
+{
+  int c;
+
+  if (argv0 = strrchr(argv[0], '/')) argv0++;
+  else argv0 = argv[0];
+
+  /* Parse the options */
+  while ((c = getopt(argc, argv, "n:hpq")) != EOF) {
+    switch (c) {
+      case 'n': searchcount  = atoi(optarg); continue;
+      case 'p': showpaths    = 1;            continue;
+      case 'q': quiet        = 1;            continue;
+      case 'h': usage(0, 0);
+      default:  usage(1, "Invalid option!");
+    }
+  }
+
+  if (argc - optind > 1) usage(1, "Too many arguments!");
+  input_path = (argc == optind) ? "-" : argv[optind];
+}
+
+
+/* A callback to count and print errors */
+static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...)
+{
+  va_list alist;
+
+  error_count++;
+  if (!quiet) {
+    va_start(alist, msg);
+    com_err_va(argv0, code, msg, alist);
+    va_end(alist);
+  }
+}
+
+
+/* A callback to process file vnodes */
+static afs_uint32 my_file_cb(afs_vnode *v, XFILE *X, void *refcon)
+{
+  static char buf[1024];
+  afs_uint32 size, nulls, cnulls, maxcnulls, n, r;
+  char *name = 0;
+  int i;
+
+  nulls = cnulls = maxcnulls = 0;
+  size = v->size;
+  if ((r = xfseek(X, &v->d_offset))) return r;
+  while (size) {
+    n = (size > 1024) ? 1024 : size;
+    if (r = xfread(X, buf, n)) return r;
+    for (i = 0; i < n; i++) {
+      if (buf[i]) {
+        if (cnulls > maxcnulls) maxcnulls = cnulls;
+        cnulls = 0;
+      } else {
+        nulls++;
+        cnulls++;
+      }
+    }
+    size -= n;
+  }
+  if (maxcnulls >= searchcount) {
+    bad_count++;
+    if (showpaths) Path_Build(X, &phi, v->vnode, &name, 0);
+    if (name) {
+      printf("*** BAD %d (%s) - %d nulls, %d consecutive\n",
+             v->vnode, name, nulls, maxcnulls);
+      free(name);
+    } else {
+      printf("*** BAD %d - %d nulls, %d consecutive\n",
+             v->vnode, nulls, maxcnulls);
+    }
+  }
+  return r;
+}
+
+
+int main(int argc, char **argv)
+{
+  XFILE input_file;
+  afs_uint32 r;
+
+  parse_options(argc, argv);
+  initialize_acfg_error_table();
+  initialize_AVds_error_table();
+  initialize_rxk_error_table();
+  initialize_u_error_table();
+  initialize_vl_error_table();
+  initialize_vols_error_table();
+  initialize_xFil_error_table();
+  r = xfopen(&input_file, O_RDONLY, input_path);
+  if (r) {
+    com_err(argv0, r, "opening %s", input_path);
+    exit(2);
+  }
+
+  memset(&dp, 0, sizeof(dp));
+  dp.cb_error      = my_error_cb;
+  if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK;
+  if (showpaths) {
+    u_int64 where;
+
+    memset(&phi, 0, sizeof(phi));
+    phi.p = &dp;
+
+    if ((r = xftell(&input_file, &where))
+    ||  (r = Path_PreScan(&input_file, &phi, 0))
+    ||  (r = xfseek(&input_file, &where))) {
+      com_err(argv0, r, "- path initialization failed");
+      xfclose(&input_file);
+      exit(2);
+    }
+  }
+
+  dp.cb_vnode_file = my_file_cb;
+  r = ParseDumpFile(&input_file, &dp);
+  xfclose(&input_file);
+
+  if (error_count) printf("*** %d errors\n", error_count);
+  if (bad_count)   printf("*** %d bad files\n", bad_count);
+  if (r && !quiet) printf("*** FAILED: %s\n", error_message(r));
+}
diff --git a/src/tests/parsedump.c b/src/tests/parsedump.c
new file mode 100644 (file)
index 0000000..edf6903
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* parsedump.c - Parse a volume dump file */
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "dumpfmt.h"
+#include "internal.h"
+#include "stagehdr.h"
+
+static afs_uint32 parse_dumphdr  (XFILE *, unsigned char *, tagged_field *,
+                               afs_uint32, tag_parse_info *, void *, void *);
+static afs_uint32 parse_dumpend  (XFILE *, unsigned char *, tagged_field *,
+                               afs_uint32, tag_parse_info *, void *, void *);
+static afs_uint32 store_dumphdr  (XFILE *, unsigned char *, tagged_field *,
+                               afs_uint32, tag_parse_info *, void *, void *);
+static afs_uint32 parse_dumptimes(XFILE *, unsigned char *, tagged_field *,
+                               afs_uint32, tag_parse_info *, void *, void *);
+
+/** Field list for top-level objects **/
+static tagged_field top_fields[] = {
+  { TAG_DUMPHEADER,  DKIND_SPECIAL, "* DUMP HEADER",   parse_dumphdr, 0, 0 },
+  { TAG_VOLHEADER,   DKIND_SPECIAL, "* VOLUME HEADER", parse_volhdr,  0, 0 },
+  { TAG_VNODE,       DKIND_SPECIAL, "* VNODE ",        parse_vnode,   0, 0 },
+  { TAG_DUMPEND,     DKIND_INT32,   "* DUMP END",      parse_dumpend, 0, 0 },
+  { STAGE_VERSMIN,   DKIND_SPECIAL, "* STAGE HEADER",  try_backuphdr, 0, 0 },
+  { 0,0,0,0,0,0 }};
+
+
+/** Field list for dump headers **/
+static tagged_field dumphdr_fields[] = {
+  { DHTAG_VOLNAME,   DKIND_STRING,  " Volume name:  ", store_dumphdr,   0, 0 },
+  { DHTAG_VOLID,     DKIND_INT32,   " Volume ID:    ", store_dumphdr,   0, 0 },
+  { DHTAG_DUMPTIMES, DKIND_SPECIAL, " Dump Range:   ", parse_dumptimes, 0, 0 },
+  { 0,0,0,0,0,0 }};
+
+
+/* Parse a dump header, including its tagged attributes, and call the
+ * dump-header callback, if one is defined.
+ */
+static afs_uint32 parse_dumphdr(XFILE *X, unsigned char *tag, tagged_field *field,
+                             afs_uint32 value, tag_parse_info *pi,
+                             void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_dump_header hdr;
+  u_int64 where;
+  afs_uint32 r;
+
+  memset(&hdr, 0, sizeof(hdr));
+  if (r = xftell(X, &where)) return r;
+  sub64_32(hdr.offset, where, 1);
+
+  if (r = ReadInt32(X, &hdr.magic)) return r;
+  if (r = ReadInt32(X, &hdr.version)) return r;
+
+  if (hdr.magic != DUMPBEGINMAGIC) {
+    if (p->cb_error)
+      (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon,
+                    "Invalid magic number (0x%08x) in dump header",
+                    hdr.magic);
+    return DSERR_MAGIC;
+  }
+  if (hdr.version != DUMPVERSION) {
+    if (p->cb_error)
+      (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon,
+                    "Unknown dump format version (%d) in dump header",
+                    hdr.version);
+    return DSERR_MAGIC;
+  }
+
+  if (p->print_flags & DSPRINT_DUMPHDR)
+    printf("%s [%s = 0x%s]\n", field->label,
+      decimate_int64(&hdr.offset, 0), hexify_int64(&hdr.offset, 0));
+  if (p->print_flags & DSPRINT_DUMPHDR) {
+    printf(" Magic number: 0x%08x\n", hdr.magic);
+    printf(" Version:      %d\n", hdr.version);
+  }
+  r = ParseTaggedData(X, dumphdr_fields, tag, pi, g_refcon, (void *)&hdr);
+
+  if (!r && p->cb_dumphdr) {
+    r = xftell(X, &where);
+    if (!r) r = (p->cb_dumphdr)(&hdr, X, p->refcon);
+    if (p->flags & DSFLAG_SEEK) {
+      if (!r) r = xfseek(X, &where);
+      else xfseek(X, &where);
+    }
+  }
+  if (hdr.field_mask & F_DUMPHDR_VOLNAME)
+    free(hdr.volname);
+  return r;
+}
+
+
+/* Store tagged attributes into a dump header */
+static afs_uint32 store_dumphdr(XFILE *X, unsigned char *tag, tagged_field *field,
+                             afs_uint32 value, tag_parse_info *pi,
+                             void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_dump_header *hdr = (afs_dump_header *)l_refcon;
+
+  switch (field->tag) {
+  case DHTAG_VOLID:
+    hdr->field_mask |= F_DUMPHDR_VOLID;
+    hdr->volid = value;
+    if (p->print_flags & DSPRINT_DUMPHDR)
+      printf("%s%d\n", field->label, hdr->volid);
+    return 0;
+
+  case DHTAG_VOLNAME:
+    if (tag && tag[0]) {
+      hdr->field_mask |= F_DUMPHDR_VOLNAME;
+      hdr->volname = tag;
+      if (p->print_flags & DSPRINT_DUMPHDR)
+        printf("%s%s\n", field->label, hdr->volname);
+      return DSERR_KEEP;
+    } else return 0;
+
+  default:
+    if (p->print_flags & DSPRINT_DUMPHDR)
+      printf("%s<<< UNKNOWN FIELD >>>\n", field->label);
+    return 0;
+  }
+}
+
+
+/* Parse and store the dump time range from a dump header */
+static afs_uint32 parse_dumptimes(XFILE *X, unsigned char *tag,
+                               tagged_field *field, afs_uint32 value,
+                               tag_parse_info *pi,
+                               void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_dump_header *hdr = (afs_dump_header *)l_refcon;
+  afs_uint16 count;
+  afs_uint32 r;
+
+  if (r = ReadInt16(X, &count)) return r;
+  if (count != 2) {
+    if (p->cb_error)
+      (p->cb_error)(DSERR_FMT, 1, p->err_refcon,
+                    "Incorrect array count (%d) in dump times", count);
+    return DSERR_FMT;
+  }
+  if (r = ReadInt32(X, &hdr->from_date)) return r;
+  if (r = ReadInt32(X, &hdr->to_date)) return r;
+  hdr->field_mask |= (F_DUMPHDR_FROM | F_DUMPHDR_TO);
+  if (p->print_flags & DSPRINT_DUMPHDR)
+    printf("%s%d => %d\n", field->label, hdr->from_date, hdr->to_date);
+
+  return ReadByte(X, tag);
+}
+
+
+/* Parse a dump_end record */
+static afs_uint32 parse_dumpend(XFILE *X, unsigned char *tag, tagged_field *field,
+                             afs_uint32 value, tag_parse_info *pi,
+                             void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_uint32 r;
+
+  if (value != DUMPENDMAGIC) {
+    if (p->cb_error)
+      (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon,
+                    "Invalid magic number (0x%08x) in dump trailer",
+                    value);
+    return DSERR_MAGIC;
+  }
+  if (p->print_flags & (DSPRINT_DUMPHDR | DSPRINT_ITEM))
+    printf("%s\n", field->label);
+  return DSERR_DONE;
+}
+
+
+
+afs_uint32 ParseDumpFile(XFILE *X, dump_parser *p)
+{
+  tag_parse_info pi;
+  unsigned char tag;
+  afs_uint32 r;
+
+  prep_pi(p, &pi);
+  r = ParseTaggedData(X, top_fields, &tag, &pi, (void *)p, 0);
+  return handle_return(r, X, tag, p);
+}
+
+
+afs_uint32 ParseDumpHeader(XFILE *X, dump_parser *p)
+{
+  tag_parse_info pi;
+  unsigned char tag;
+  afs_uint32 r;
+
+  prep_pi(p, &pi);
+  if (r = ReadByte(X, &tag)) return handle_return(r, X, tag, p);
+  if (tag != TAG_DUMPHEADER) return handle_return(0, X, tag, p);
+  r = parse_dumphdr(X, &tag, &top_fields[0], 0, &pi, (void *)p, 0);
+  if (!r && tag >= 1 && tag <= 4) r = DSERR_DONE;
+  return handle_return(r, X, tag, p);
+}
+
+
+afs_uint32 ParseVolumeHeader(XFILE *X, dump_parser *p)
+{
+  tag_parse_info pi;
+  unsigned char tag;
+  afs_uint32 r;
+
+  prep_pi(p, &pi);
+  if (r = ReadByte(X, &tag)) return handle_return(r, X, tag, p);
+  if (tag != TAG_VOLHEADER) return handle_return(0, X, tag, p);
+  r = parse_volhdr(X, &tag, &top_fields[1], 0, &pi, (void *)p, 0);
+  if (!r && tag >= 1 && tag <= 4) r = DSERR_DONE;
+  return handle_return(r, X, tag, p);
+}
+
+
+afs_uint32 ParseVNode(XFILE *X, dump_parser *p)
+{
+  tag_parse_info pi;
+  unsigned char tag;
+  afs_uint32 r;
+
+  prep_pi(p, &pi);
+  if (r = ReadByte(X, &tag)) return handle_return(r, X, tag, p);
+  if (tag != TAG_VNODE) return handle_return(0, X, tag, p);
+  r = parse_vnode(X, &tag, &top_fields[2], 0, &pi, (void *)p, 0);
+  if (!r && tag >= 1 && tag <= 4) r = DSERR_DONE;
+  return handle_return(r, X, tag, p);
+}
diff --git a/src/tests/parsetag.c b/src/tests/parsetag.c
new file mode 100644 (file)
index 0000000..037ea38
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* parsetag.c - Parse a tagged data stream */
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+
+/* If a parser function is defined, it will be called after the data value
+ * (if any) is read.  The parser is called as follows:
+ *
+ *   parser(input_file, &tag, &field_rec, value, g_refcon, l_refcon);
+ *
+ * - input_file is the FILE * for the input stream
+ * - field_rec is a pointer to the field record for the field just read
+ * - g_refcon and l_refcon are as passed in to ParseTaggedData
+ * - For integer types, value is the integer value
+ * - For DKIND_STRING, tag is a pointer to the string just read
+ * - For DKIND_SPEACH, tag is a pointer to the place to put the next tag.
+ *
+ * If the field type is DKIND_SPECIAL, the parser is expected to read its
+ * own data from the input stream, and return when ParseTaggedData is supposed
+ * to take over, with the next tag to process in *tag.  At no other time
+ * should the parser read, write, or reposition the input stream.
+ *
+ * The parser routine should return 0 on success, non-0 on failure.  If the
+ * data type is DKIND_STRING, the parser may return DSERR_KEEP to indicate
+ * that the memory allocated for the value should not be freed.
+ */
+
+/* Parse a file containing tagged data and attributes **/
+afs_uint32 ParseTaggedData(XFILE *X, tagged_field *fields, unsigned char *tag,
+                    tag_parse_info *pi, void *g_refcon, void *l_refcon)
+{
+  int i = -1;
+  afs_uint32 r, val;
+  afs_uint16 val16;
+  unsigned char val8;
+  unsigned char *strval;
+
+  for (;;) {
+    if (i < 0 || (fields[i].kind & DKIND_MASK) != DKIND_SPECIAL) {
+      /* Need to read in a tag */
+      if (r = ReadByte(X, tag)) return r;
+    }
+
+    /* Simple error recovery - if we encounter a 0, it can never be
+     * a valid tag.  If TPFLAG_SKIP is set, we can skip over any
+     * such null bytes, and process whatever tag we find beyond.
+     * In addition, if TPFLAG_RSKIP is set, then the next time
+     * we encounter a 0, try skipping backwards.  That seems to
+     * work much of the time.
+     */
+    if (!*tag && pi->shift_offset && (pi->flags & TPFLAG_RSKIP)) {
+      u_int64 where, tmp64a, tmp64b;
+      char buf1[21], buf2[21], buf3[21];
+      char *p1, *p2, *p3;
+
+      if (r = xftell(X, &tmp64a)) return r;
+      sub64_32(where, tmp64a, pi->shift_offset + 1);
+      if (r = xfseek(X, &where)) return r;
+      if (pi->cb_error){
+        (pi->cb_error)(DSERR_FMT, 0, pi->err_refcon,
+                       "Inserted %d bytes before offset %d",
+                       pi->shift_offset, decimate_int64(&where, 0));
+        add64_32(tmp64a, pi->shift_start, pi->shift_offset);
+        p1 = decimate_int64(&tmp64a, buf1);
+        sub64_64(tmp64b, where, tmp64a);
+        p2 = decimate_int64(&tmp64b, buf2);
+        p3 = decimate_int64(&pi->shift_start, buf3);
+        (pi->cb_error)(DSERR_FMT, 0, pi->err_refcon,
+                       ">>> SHIFT start=%s length=%s target=%s",
+                       p1, p2, p3);
+      }
+      pi->shift_offset = 0;
+      if (r = ReadByte(X, tag)) return r;
+    }
+    if (!*tag && (pi->flags & TPFLAG_SKIP)) {
+      int count = 0;
+      u_int64 where, tmp64a;
+
+      if (r = xftell(X, &where)) return r;
+      
+      while (!*tag) {
+        if (r = ReadByte(X, tag)) return r;
+        count++;
+      }
+      pi->shift_offset += count;
+      cp64(pi->shift_start, where);
+      if (pi->cb_error) {
+        sub64_32(tmp64a, where, 1);
+        (pi->cb_error)(DSERR_FMT, 0, pi->err_refcon,
+                       "Skipped %d bytes at offset %s",
+                       count, decimate_int64(&tmp64a, 0));
+      }
+    }
+
+    for (i = 0; fields[i].tag && fields[i].tag != *tag; i++);
+    if (!fields[i].tag) return 0;
+
+    switch (fields[i].kind & DKIND_MASK) {
+    case DKIND_NOOP:
+      if (fields[i].func) {
+        r = (fields[i].func)(X, 0, fields+i, 0, pi, g_refcon, l_refcon);
+        if (r) return r;
+      }
+      break;
+
+    case DKIND_BYTE:
+      if (r = ReadByte(X, &val8)) return r;
+      if (fields[i].func) {
+        r = (fields[i].func)(X, 0, fields+i, val8, pi, g_refcon, l_refcon);
+        if (r) return r;
+      }
+      break;
+
+    case DKIND_INT16:
+      if (r = ReadInt16(X, &val16)) return r;
+      if (fields[i].func) {
+        r = (fields[i].func)(X, 0, fields+i, val16, pi, g_refcon, l_refcon);
+        if (r) return r;
+      }
+      break;
+
+    case DKIND_INT32:
+      if (r = ReadInt32(X, &val)) return r;
+      if (fields[i].func) {
+        r = (fields[i].func)(X, 0, fields+i, val, pi, g_refcon, l_refcon);
+        if (r) return r;
+      }
+      break;
+
+    case DKIND_STRING: 
+      if (r = ReadString(X, &strval)) return r;
+      if (fields[i].func) {
+        r = (fields[i].func)(X, strval, fields+i, 0, pi, g_refcon, l_refcon);
+        if (r != DSERR_KEEP) free(strval);
+        if (r && r != DSERR_KEEP) return r;
+      } else free(strval);
+      break;
+
+    case DKIND_SPECIAL:
+      if (fields[i].func) {
+        r = (fields[i].func)(X, tag, fields+i, 0, pi, g_refcon, l_refcon);
+        if (r) return r;
+      } else i = -1;
+    }
+  }
+}
diff --git a/src/tests/parsevnode.c b/src/tests/parsevnode.c
new file mode 100644 (file)
index 0000000..09f86ce
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* parsevnode.c - Parse a VNode */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "dumpfmt.h"
+#include "internal.h"
+
+#include <afs/acl.h>
+#include <afs/prs_fs.h>
+
+static afs_uint32 LastGoodVNode = 0;
+static afs_uint32 store_vnode(XFILE *, unsigned char *, tagged_field *, afs_uint32,
+                           tag_parse_info *, void *, void *);
+static afs_uint32 parse_acl  (XFILE *, unsigned char *, tagged_field *, afs_uint32,
+                           tag_parse_info *, void *, void *);
+static afs_uint32 parse_vdata(XFILE *, unsigned char *, tagged_field *, afs_uint32,
+                           tag_parse_info *, void *, void *);
+
+/** Field list for vnodes **/
+static tagged_field vnode_fields[] = {
+  { VTAG_TYPE,        DKIND_BYTE,    " VNode type:   ", store_vnode, 0, 0 },
+  { VTAG_NLINKS,      DKIND_INT16,   " Link count:   ", store_vnode, 0, 0 },
+  { VTAG_DVERS,       DKIND_INT32,   " Version:      ", store_vnode, 0, 0 },
+  { VTAG_CLIENT_DATE, DKIND_TIME,    " Server Date:  ", store_vnode, 0, 0 },
+  { VTAG_AUTHOR,      DKIND_INT32,   " Author:       ", store_vnode, 0, 0 },
+  { VTAG_OWNER,       DKIND_INT32,   " Owner:        ", store_vnode, 0, 0 },
+  { VTAG_GROUP,       DKIND_INT32,   " Group:        ", store_vnode, 0, 0 },
+  { VTAG_MODE,        DKIND_INT16,   " UNIX mode:    ", store_vnode, 0, 0 },
+  { VTAG_PARENT,      DKIND_INT32,   " Parent:       ", store_vnode, 0, 0 },
+  { VTAG_SERVER_DATE, DKIND_TIME,    " Client Date:  ", store_vnode, 0, 0 },
+  { VTAG_ACL,         DKIND_SPECIAL, " xxxxxxxx ACL: ", parse_acl,   0, 0 },
+  { VTAG_DATA,        DKIND_SPECIAL, " Contents:     ", parse_vdata, 0, 0 },
+  { 0,0,0,0,0,0 }};
+
+
+static afs_uint32 resync_vnode(XFILE *X, dump_parser *p, afs_vnode *v,
+                            int start, int limit)
+{
+  u_int64 where, expected_where;
+  afs_uint32 r;
+  int i;
+
+  if (r = xftell(X, &expected_where)) return r;
+  cp64(where, expected_where);
+
+  r = match_next_vnode(X, p, &where, v->vnode);
+  if (r && r != DSERR_FMT) return r;
+  if (r) for (i = -start; i < limit; i++) {
+    add64_32(where, expected_where, i);
+    r = match_next_vnode(X, p, &where, v->vnode);
+    if (!r) break;
+    if (r != DSERR_FMT) return r;
+  }
+  if (r) {
+    if (p->cb_error)
+      (p->cb_error)(r, 1, p->err_refcon,
+                    "Unable to resync after vnode %d [%s = 0x%s]",
+                    v->vnode, decimate_int64(&expected_where, 0),
+                    hexify_int64(&expected_where, 0));
+    return r;
+  }
+  if (ne64(where, expected_where) && p->cb_error) {
+    (p->cb_error)(DSERR_FMT, 0, p->err_refcon,
+                  "Vnode after %d not in expected location",
+                  v->vnode);
+    (p->cb_error)(DSERR_FMT, 0, p->err_refcon, "Expected location: %s = 0x%s",
+                  decimate_int64(&expected_where, 0),
+                  hexify_int64(&expected_where, 0));
+    (p->cb_error)(DSERR_FMT, 0, p->err_refcon, "Actual location: %s = 0x%s",
+                  decimate_int64(&where, 0), hexify_int64(&where, 0));
+  }
+  return xfseek(X, &where);
+}
+
+
+/* Parse a VNode, including any tagged attributes and data, and call the
+ * appropriate callback, if one is defined.
+ */
+afs_uint32 parse_vnode(XFILE *X, unsigned char *tag, tagged_field *field,
+                    afs_uint32 value, tag_parse_info *pi,
+                    void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_uint32 (*cb)(afs_vnode *, XFILE *, void *);
+  u_int64 where, offset2k;
+  afs_vnode v;
+  afs_uint32 r;
+
+
+  if (r = xftell(X, &where)) return r;
+  memset(&v, 0, sizeof(v));
+  sub64_32(v.offset, where, 1);
+  if (r = ReadInt32(X, &v.vnode)) return r;
+  if (r = ReadInt32(X, &v.vuniq)) return r;
+
+  mk64(offset2k, 0, 2048);
+  if (!LastGoodVNode
+  || ((p->flags & DSFLAG_SEEK) && v.vnode == 1
+       && lt64(v.offset, offset2k)))
+    LastGoodVNode = -1;
+
+  if (p->print_flags & DSPRINT_ITEM) {
+    printf("%s %d/%d [%s = 0x%s]\n", field->label, v.vnode, v.vuniq,
+           decimate_int64(&where, 0), hexify_int64(&where, 0));
+  }
+
+  r = ParseTaggedData(X, vnode_fields, tag, pi, g_refcon, (void *)&v);
+
+  /* Try to resync, if requested */
+  if (!r && (p->repair_flags & DSFIX_VFSYNC)) {
+    afs_uint32 drop;
+    u_int64 xwhere;
+
+    if (r = xftell(X, &where)) return r;
+    sub64_32(xwhere, where, 1);
+
+    /* Are we at the start of a valid vnode (or dump end)? */
+    r = match_next_vnode(X, p, &xwhere, v.vnode);
+    if (r && r != DSERR_FMT) return r;
+    if (r) { /* Nope. */
+      /* Was _this_ a valid vnode?  If so, we can keep it and search for
+       * the next one.  Otherwise, we throw it out, and start the search
+       * at the starting point of this vnode.
+       */
+      drop = r = match_next_vnode(X, p, &v.offset, LastGoodVNode);
+      if (r && r != DSERR_FMT) return r;
+      if (!r) {
+        add64_32(where, v.offset, 1);
+        if (r = xfseek(X, &v.offset)) return r;
+      } else {
+        if (r = xfseek(X, &xwhere)) return r;
+      }
+      if (r = resync_vnode(X, p, &v, 0, 1024)) return r;
+      if (r = ReadByte(X, tag)) return r;
+      if (drop) {
+        if (p->cb_error)
+          (p->cb_error)(DSERR_FMT, 0, p->err_refcon,
+                        "Dropping vnode %d", v.vnode);
+        return 0;
+      }
+    } else {
+      if (r = xfseek(X, &where)) return r;
+    }
+  }
+  LastGoodVNode = v.vnode;
+
+  if (!r) {
+    if (v.field_mask & F_VNODE_TYPE)
+      switch (v.type) {
+      case vFile:      cb = p->cb_vnode_file;  break;
+      case vDirectory: cb = p->cb_vnode_dir;   break;
+      case vSymlink:   cb = p->cb_vnode_link;  break;
+      default:         cb = p->cb_vnode_wierd; break;
+      }
+    else               cb = p->cb_vnode_empty;
+    if (cb) {
+      u_int64 where;
+
+      if (r = xftell(X, &where)) return r;
+      r = (cb)(&v, X, p->refcon);
+      if (p->flags & DSFLAG_SEEK) {
+        if (!r) r = xfseek(X, &where);
+        else xfseek(X, &where);
+      }
+    }
+  }
+  return r;
+}
+
+
+/* Store data in a vnode */
+static afs_uint32 store_vnode(XFILE *X, unsigned char *tag, tagged_field *field,
+                           afs_uint32 value, tag_parse_info *pi,
+                           void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_vnode *v = (afs_vnode *)l_refcon;
+  time_t when;
+  afs_uint32 r = 0;
+
+  switch (field->tag) {
+  case VTAG_TYPE:
+    v->field_mask |= F_VNODE_TYPE;
+    v->type = value;
+    if (p->print_flags & DSPRINT_VNODE) {
+      switch (value) {
+      case vFile:
+        printf("%sFile (%d)\n", field->label, value);
+        break;
+      case vDirectory:
+        printf("%sDirectory (%d)\n", field->label, value);
+        break;
+      case vSymlink:
+        printf("%sSymbolic Link (%d)\n", field->label, value);
+        break;
+      default:
+        printf("%s??? (%d)\n", field->label, value);
+      }
+      return r;
+    }
+    break;
+
+  case VTAG_NLINKS:
+    v->field_mask |= F_VNODE_NLINKS;
+    v->nlinks = value;
+    break;
+
+  case VTAG_DVERS:
+    v->field_mask |= F_VNODE_DVERS;
+    v->datavers = value;
+    break;
+
+  case VTAG_CLIENT_DATE:
+    v->field_mask |= F_VNODE_CDATE;
+    v->client_date = value;
+    break;
+
+  case VTAG_SERVER_DATE:
+    v->field_mask |= F_VNODE_SDATE;
+    v->server_date = value;
+    break;
+
+  case VTAG_AUTHOR:
+    v->field_mask |= F_VNODE_AUTHOR;
+    v->author = value;
+    break;
+
+  case VTAG_OWNER:
+    v->field_mask |= F_VNODE_OWNER;
+    v->owner = value;
+    break;
+
+  case VTAG_GROUP:
+    v->field_mask |= F_VNODE_GROUP;
+    v->group = value;
+    break;
+
+  case VTAG_MODE:
+    v->field_mask |= F_VNODE_MODE;
+    v->mode = value;
+    break;
+
+  case VTAG_PARENT:
+    v->field_mask |= F_VNODE_PARENT;
+    v->parent = value;
+    break;
+  }
+
+  if (p->print_flags & DSPRINT_VNODE)
+    switch (field->kind) {
+    case DKIND_BYTE:
+    case DKIND_INT16:
+    case DKIND_INT32:  printf("%s%d\n",     field->label, value); break;
+    case DKIND_HEX8:   printf("%s0x%02x\n", field->label, value); break;
+    case DKIND_HEX16:  printf("%s0x%04x\n", field->label, value); break;
+    case DKIND_HEX32:  printf("%s0x%08x\n", field->label, value); break;
+    case DKIND_CHAR:   printf("%s%c\n",     field->label, value); break;
+    case DKIND_STRING: printf("%s%s\n",     field->label, tag);   break;
+    case DKIND_FLAG:
+      printf("%s%s\n", field->label, value ? "true" : "false");
+      break;
+    case DKIND_TIME:
+      when = value;
+      printf("%s%s", field->label, ctime(&when));
+      break;
+  }
+  return r;
+}
+
+
+static char *rights2str(afs_uint32 rights)
+{
+  static char str[16];
+  char *p = str;
+
+  if (rights & PRSFS_READ)       *p++ = 'r';
+  if (rights & PRSFS_LOOKUP)     *p++ = 'l';
+  if (rights & PRSFS_INSERT)     *p++ = 'i';
+  if (rights & PRSFS_DELETE)     *p++ = 'd';
+  if (rights & PRSFS_WRITE)      *p++ = 'w';
+  if (rights & PRSFS_LOCK)       *p++ = 'k';
+  if (rights & PRSFS_ADMINISTER) *p++ = 'a';
+  if (rights & PRSFS_USR0)       *p++ = 'A';
+  if (rights & PRSFS_USR1)       *p++ = 'B';
+  if (rights & PRSFS_USR2)       *p++ = 'C';
+  if (rights & PRSFS_USR3)       *p++ = 'D';
+  if (rights & PRSFS_USR4)       *p++ = 'E';
+  if (rights & PRSFS_USR5)       *p++ = 'F';
+  if (rights & PRSFS_USR6)       *p++ = 'G';
+  if (rights & PRSFS_USR7)       *p++ = 'H';
+
+  *p = 0;
+  if (!str[0]) strcpy(str, "none");
+  return str;
+}
+
+
+/* Parse and store the ACL data from a directory vnode */
+static afs_uint32 parse_acl(XFILE *X, unsigned char *tag, tagged_field *field,
+                         afs_uint32 value, tag_parse_info *pi,
+                         void *g_refcon, void *l_refcon)
+{
+  struct acl_accessList *acl;
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_vnode *v = (afs_vnode *)l_refcon;
+  afs_uint32 r, i, n;
+
+  if (r = xfread(X, v->acl, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE))
+    return r;
+
+  v->field_mask |= F_VNODE_ACL;
+  if (p->print_flags & DSPRINT_ACL) {
+    acl = (struct acl_accessList *)(v->acl);
+    n = ntohl(acl->positive);
+    if (n) {
+      printf("Positive ACL: %d entries\n", n);
+      for (i = 0; i < n; i++)
+        printf("              %9d  %s\n",
+               ntohl(acl->entries[i].id),
+               rights2str(acl->entries[i].rights));
+    }
+    n = ntohl(acl->negative);
+    if (n) {
+      printf("Positive ACL: %d entries\n", n);
+      for (i = ntohl(acl->positive); i < ntohl(acl->total); i++)
+        printf("              %9d  %s\n",
+               ntohl(acl->entries[i].id),
+               rights2str(acl->entries[i].rights));
+    }
+  }
+  return ReadByte(X, tag);
+}
+
+
+/* Parse or skip over the vnode data */
+static afs_uint32 parse_vdata(XFILE *X, unsigned char *tag, tagged_field *field,
+                           afs_uint32 value, tag_parse_info *pi,
+                           void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_vnode *v = (afs_vnode *)l_refcon;
+  static char *symlink_buf = 0;
+  static int symlink_size = 0;
+  afs_uint32 r;
+
+  if (r = ReadInt32(X, &v->size)) return r;
+  v->field_mask |= F_VNODE_SIZE;
+
+  if (v->size) {
+    v->field_mask |= F_VNODE_DATA;
+    if (r = xftell(X, &v->d_offset)) return r;
+    if (p->print_flags & DSPRINT_VNODE)
+      printf("%s%d (0x%08x) bytes at %s (0x%s)\n", field->label,
+             v->size, v->size, decimate_int64(&v->d_offset, 0),
+             hexify_int64(&v->d_offset, 0));
+    
+    switch (v->type) {
+    case vSymlink:
+      if (v->size > symlink_size) {
+        if (symlink_buf) symlink_buf = (char *)realloc(symlink_buf, v->size + 1);
+        else symlink_buf = (char *)malloc(v->size + 1);
+        symlink_size = symlink_buf ? v->size : 0;
+      }
+      if (symlink_buf) {
+        if (r = xfread(X, symlink_buf, v->size)) return r;
+        symlink_buf[v->size] = 0;
+        if (p->print_flags & DSPRINT_VNODE)
+          printf("Target:       %s\n", symlink_buf);
+      } else {
+        /* Call the callback here, because it's non-fatal */
+        if (p->cb_error)
+          (p->cb_error)(ENOMEM, 0, p->err_refcon,
+                        "Out of memory reading symlink");
+        if (r = xfskip(X, v->size)) return r;
+      }
+      break;
+
+    case vDirectory:
+      if (p->cb_dirent || (p->print_flags & DSPRINT_DIR)) {
+        if (r = parse_directory(X, p, v, v->size, 0)) return r;
+        break;
+      }
+
+    default:
+      if (r = xfskip(X, v->size)) return r;
+    }
+  } else if (p->print_flags & DSPRINT_VNODE) {
+    printf("%sEmpty\n", field->label);
+  }
+  if (p->repair_flags & DSFIX_VDSYNC) {
+    r = resync_vnode(X, p, v, 10, 15);
+    if (r) return r;
+  }
+  return ReadByte(X, tag);
+}
diff --git a/src/tests/parsevol.c b/src/tests/parsevol.c
new file mode 100644 (file)
index 0000000..86143dd
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* parsevol.c - Parse a volume header */
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "dumpfmt.h"
+
+static afs_uint32 store_volhdr   (XFILE *, unsigned char *, tagged_field *,
+                               afs_uint32, tag_parse_info *, void *, void *);
+static afs_uint32 parse_weekuse  (XFILE *, unsigned char *, tagged_field *,
+                               afs_uint32, tag_parse_info *, void *, void *);
+
+/** Field list for volume headers **/
+static tagged_field volhdr_fields[] = {
+  { VHTAG_VOLID,     DKIND_INT32,   " Volume ID:   ", store_volhdr,  0, 0 },
+  { VHTAG_VERS,      DKIND_INT32,   " Version:     ", store_volhdr,  0, 0 },
+  { VHTAG_VOLNAME,   DKIND_STRING,  " Volume name: ", store_volhdr,  0, 0 },
+  { VHTAG_INSERV,    DKIND_FLAG,    " In service?  ", store_volhdr,  0, 0 },
+  { VHTAG_BLESSED,   DKIND_FLAG,    " Blessed?     ", store_volhdr,  0, 0 },
+  { VHTAG_VUNIQ,     DKIND_INT32,   " Uniquifier:  ", store_volhdr,  0, 0 },
+  { VHTAG_TYPE,      DKIND_BYTE,    " Type:        ", store_volhdr,  0, 0 },
+  { VHTAG_PARENT,    DKIND_INT32,   " Parent ID:   ", store_volhdr,  0, 0 },
+  { VHTAG_CLONE,     DKIND_INT32,   " Clone ID:    ", store_volhdr,  0, 0 },
+  { VHTAG_MAXQUOTA,  DKIND_INT32,   " Max quota:   ", store_volhdr,  0, 0 },
+  { VHTAG_MINQUOTA,  DKIND_INT32,   " Min quota:   ", store_volhdr,  0, 0 },
+  { VHTAG_DISKUSED,  DKIND_INT32,   " Disk used:   ", store_volhdr,  0, 0 },
+  { VHTAG_FILECNT,   DKIND_INT32,   " File count:  ", store_volhdr,  0, 0 },
+  { VHTAG_ACCOUNT,   DKIND_INT32,   " Account:     ", store_volhdr,  0, 0 },
+  { VHTAG_OWNER,     DKIND_INT32,   " Owner:       ", store_volhdr,  0, 0 },
+  { VHTAG_CREAT,     DKIND_TIME,    " Created:     ", store_volhdr,  0, 0 },
+  { VHTAG_ACCESS,    DKIND_TIME,    " Accessed:    ", store_volhdr,  0, 0 },
+  { VHTAG_UPDATE,    DKIND_TIME,    " Updated:     ", store_volhdr,  0, 0 },
+  { VHTAG_EXPIRE,    DKIND_TIME,    " Expires:     ", store_volhdr,  0, 0 },
+  { VHTAG_BACKUP,    DKIND_TIME,    " Backed up:   ", store_volhdr,  0, 0 },
+  { VHTAG_OFFLINE,   DKIND_STRING,  " Offine Msg:  ", store_volhdr,  0, 0 },
+  { VHTAG_MOTD,      DKIND_STRING,  " MOTD:        ", store_volhdr,  0, 0 },
+  { VHTAG_WEEKUSE,   DKIND_SPECIAL, " Weekuse:     ", parse_weekuse, 0, 0 },
+  { VHTAG_DUDATE,    DKIND_TIME,    " Dayuse Date: ", store_volhdr,  0, 0 },
+  { VHTAG_DAYUSE,    DKIND_INT32,   " Daily usage: ", store_volhdr,  0, 0 },
+  { 0,0,0,0,0,0 }};
+
+
+/* Parse a volume header, including any tagged attributes, and call the
+ * volume-header callback, if one is defined.
+ */
+afs_uint32 parse_volhdr(XFILE *X, unsigned char *tag, tagged_field *field,
+                     afs_uint32 value, tag_parse_info *pi,
+                     void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_vol_header hdr;
+  u_int64 where;
+  afs_uint32 r;
+
+  memset(&hdr, 0, sizeof(hdr));
+  if (r = xftell(X, &where)) return r;
+  sub64_32(hdr.offset, where, 1);
+  if (p->print_flags & DSPRINT_VOLHDR)
+    printf("%s [%s = 0x%s]\n", field->label,
+           decimate_int64(&hdr.offset, 0), hexify_int64(&hdr.offset, 0));
+
+  r = ParseTaggedData(X, volhdr_fields, tag, pi, g_refcon, (void *)&hdr);
+
+  if (!r && p->cb_volhdr) {
+    if (r = xftell(X, &where)) return r;
+    r = (p->cb_volhdr)(&hdr, X, p->refcon);
+    if (p->flags & DSFLAG_SEEK) {
+      if (!r) r = xfseek(X, &where);
+      else xfseek(X, &where);
+    }
+  }
+  if (hdr.field_mask & F_VOLHDR_VOLUNIQ)
+    p->vol_uniquifier = hdr.voluniq;
+  if (hdr.field_mask & F_VOLHDR_VOLNAME)
+    free(hdr.volname);
+  if (hdr.field_mask & F_VOLHDR_OFFLINE_MSG)
+    free(hdr.offline_msg);
+  if (hdr.field_mask & F_VOLHDR_MOTD)
+    free(hdr.motd_msg);
+  return r;
+}
+
+
+/* Store data in a volume header */
+static afs_uint32 store_volhdr(XFILE *X, unsigned char *tag, tagged_field *field,
+                            afs_uint32 value, tag_parse_info *pi,
+                            void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_vol_header *hdr = (afs_vol_header *)l_refcon;
+  time_t when;
+  afs_uint32 r = 0;
+
+  switch (field->tag) {
+  case VHTAG_VOLID:
+    hdr->field_mask |= F_VOLHDR_VOLID;
+    hdr->volid = value;
+    break;
+
+  case VHTAG_VERS:
+    hdr->field_mask |= F_VOLHDR_VOLVERS;
+    hdr->volvers = value;
+    break;
+
+  case VHTAG_VOLNAME:
+    if (tag && tag[0]) {
+      hdr->field_mask |= F_VOLHDR_VOLNAME;
+      hdr->volname = tag;
+      r = DSERR_KEEP;
+    }
+    break;
+
+  case VHTAG_INSERV:
+    hdr->field_mask |= F_VOLHDR_INSERV;
+    hdr->flag_inservice = value;
+    break;
+
+  case VHTAG_BLESSED:
+    hdr->field_mask |= F_VOLHDR_BLESSED;
+    hdr->flag_blessed = value;
+    break;
+
+  case VHTAG_VUNIQ:
+    hdr->field_mask |= F_VOLHDR_VOLUNIQ;
+    hdr->voluniq = value;
+    break;
+
+  case VHTAG_TYPE:
+    hdr->field_mask |= F_VOLHDR_VOLTYPE;
+    hdr->voltype = value;
+    break;
+
+  case VHTAG_PARENT:
+    hdr->field_mask |= F_VOLHDR_PARENT;
+    hdr->parent_volid = value;
+    break;
+
+  case VHTAG_CLONE:
+    hdr->field_mask |= F_VOLHDR_CLONE;
+    hdr->clone_volid = value;
+    break;
+
+  case VHTAG_MAXQUOTA:
+    hdr->field_mask |= F_VOLHDR_MAXQ;
+    hdr->maxquota = value;
+    break;
+
+  case VHTAG_MINQUOTA:
+    hdr->field_mask |= F_VOLHDR_MINQ;
+    hdr->minquota = value;
+    break;
+
+  case VHTAG_DISKUSED:
+    hdr->field_mask |= F_VOLHDR_DISKUSED;
+    hdr->diskused = value;
+    break;
+
+  case VHTAG_FILECNT:
+    hdr->field_mask |= F_VOLHDR_NFILES;
+    hdr->nfiles = value;
+    break;
+
+  case VHTAG_ACCOUNT:
+    hdr->field_mask |= F_VOLHDR_ACCOUNT;
+    hdr->account_no = value;
+    break;
+
+  case VHTAG_OWNER:
+    hdr->field_mask |= F_VOLHDR_OWNER;
+    hdr->owner = value;
+    break;
+
+  case VHTAG_CREAT:
+    hdr->field_mask |= F_VOLHDR_CREATE_DATE;
+    hdr->create_date = value;
+    break;
+
+  case VHTAG_ACCESS:
+    hdr->field_mask |= F_VOLHDR_ACCESS_DATE;
+    hdr->access_date = value;
+    break;
+
+  case VHTAG_UPDATE:
+    hdr->field_mask |= F_VOLHDR_UPDATE_DATE;
+    hdr->update_date = value;
+    break;
+
+  case VHTAG_EXPIRE:
+    hdr->field_mask |= F_VOLHDR_EXPIRE_DATE;
+    hdr->expire_date = value;
+    break;
+
+  case VHTAG_BACKUP:
+    hdr->field_mask |= F_VOLHDR_BACKUP_DATE;
+    hdr->backup_date = value;
+    break;
+
+  case VHTAG_OFFLINE:
+    if (tag && tag[0]) {
+      hdr->field_mask |= F_VOLHDR_OFFLINE_MSG;
+      hdr->offline_msg = tag;
+      r = DSERR_KEEP;
+    }
+    break;
+
+  case VHTAG_MOTD:
+    if (tag && tag[0]) {
+      hdr->field_mask |= F_VOLHDR_MOTD;
+      hdr->motd_msg = tag;
+      r = DSERR_KEEP;
+    }
+    break;
+
+  case VHTAG_DUDATE:
+    hdr->field_mask |= F_VOLHDR_DAYUSE_DATE;
+    hdr->dayuse_date = value;
+    break;
+
+  case VHTAG_DAYUSE:
+    hdr->field_mask |= F_VOLHDR_DAYUSE;
+    hdr->dayuse = value;
+    break;
+  }
+
+  if (p->print_flags & DSPRINT_VOLHDR)
+    switch (field->kind) {
+    case DKIND_BYTE:
+    case DKIND_INT16:
+    case DKIND_INT32:  printf("%s%d\n",     field->label, value); break;
+    case DKIND_HEX8:   printf("%s0x%02x\n", field->label, value); break;
+    case DKIND_HEX16:  printf("%s0x%04x\n", field->label, value); break;
+    case DKIND_HEX32:  printf("%s0x%08x\n", field->label, value); break;
+    case DKIND_CHAR:   printf("%s%c\n",     field->label, value); break;
+    case DKIND_STRING: printf("%s%s\n",     field->label, tag);   break;
+    case DKIND_FLAG:
+      printf("%s%s\n", field->label, value ? "true" : "false");
+      break;
+    case DKIND_TIME:
+      when = value;
+      printf("%s%s", field->label, ctime(&when));
+      break;
+  }
+  return r;
+}
+
+
+/* Parse and store the week use data from a volume header */
+static afs_uint32 parse_weekuse(XFILE *X, unsigned char *tag, tagged_field *field,
+                             afs_uint32 value, tag_parse_info *pi,
+                             void *g_refcon, void *l_refcon)
+{
+  dump_parser *p = (dump_parser *)g_refcon;
+  afs_vol_header *hdr = (afs_vol_header *)l_refcon;
+  afs_uint16 count;
+  afs_uint32 r;
+  unsigned int i;
+
+  if (r = ReadInt16(X, &count)) return r;
+  if (count != 7) {
+    if (p->cb_error)
+      (p->cb_error)(DSERR_FMT, 1, p->err_refcon,
+                    "Incorrect array count (%d) in weekuse data", count);
+    return DSERR_FMT;
+  }
+  for (i = 0; i < count; i++)
+    if (r = ReadInt32(X, hdr->weekuse + i)) return r;
+  hdr->field_mask |= F_VOLHDR_WEEKUSE;
+  if (p->print_flags & DSPRINT_VOLHDR) {
+    printf("%s%10d %10d %10d %10d\n", field->label,
+           hdr->weekuse[0], hdr->weekuse[1], hdr->weekuse[2], hdr->weekuse[3]);
+    printf("%s%10d %10d %10d\n", field->label,
+           hdr->weekuse[4], hdr->weekuse[5], hdr->weekuse[6]);
+  }
+  return ReadByte(X, tag);
+}
diff --git a/src/tests/pathname.c b/src/tests/pathname.c
new file mode 100644 (file)
index 0000000..ee3af28
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* pathname.c - Pathname lookup and traversal */
+
+#include <errno.h>
+#include <string.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+
+/* Hash function for a vnode */
+#define BUCKET_SIZE 32
+#define vnode_hash(phi,vnode) ((vnode) & ((1 << (phi)->hash_size) - 1))
+
+
+static vhash_ent *get_vhash_ent(path_hashinfo *phi, afs_uint32 vnode, int make)
+{
+  int key = vnode_hash(phi, vnode);
+  vhash_ent *vhe;
+
+  for (vhe = phi->hash_table[key];
+       vhe && vhe->vnode != vnode;
+       vhe = vhe->next);
+  if (make && !vhe) {
+    vhe = (vhash_ent *)malloc(sizeof(vhash_ent));
+    if (vhe) {
+      memset(vhe, 0, sizeof(vhash_ent));
+      vhe->vnode = vnode;
+      vhe->next = phi->hash_table[key];
+      phi->hash_table[key] = vhe;
+    }
+  }
+  return vhe;
+}
+
+
+static afs_uint32 volhdr_cb(afs_vol_header *hdr, XFILE *X, void *refcon)
+{
+  path_hashinfo *phi = (path_hashinfo *)refcon;
+  int nfiles, hsize;
+
+  if (hdr->field_mask & F_VOLHDR_NFILES) {
+    nfiles = phi->n_vnodes = hdr->nfiles;
+    for (phi->hash_size = 1;
+         nfiles > BUCKET_SIZE;
+         phi->hash_size++, nfiles >>= 1);
+    hsize = (1 << phi->hash_size);
+    phi->hash_table = (vhash_ent **)malloc(hsize * sizeof(vhash_ent *));
+    if (!phi->hash_table) return ENOMEM;
+    memset(phi->hash_table, 0, hsize * sizeof(vhash_ent *));
+    return 0;
+  } else {
+    if (phi->p->cb_error)
+      (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                         "File count missing from volume header");
+    return DSERR_FMT;
+  }
+}
+
+
+static afs_uint32 vnode_keep(afs_vnode *v, XFILE *X, void *refcon)
+{
+  path_hashinfo *phi = (path_hashinfo *)refcon;
+  vhash_ent *vhe;
+
+  if (!phi->hash_table) {
+    if (phi->p->cb_error)
+      (phi->p->cb_error)(DSERR_FMT, 1, phi->p->refcon,
+                         "No volume header in dump???");
+    return DSERR_FMT;
+  }
+  vhe = get_vhash_ent(phi, v->vnode, 1);
+  if (!vhe) return ENOMEM;
+  cp64(vhe->v_offset, v->offset);
+  if (v->field_mask & F_VNODE_PARENT)
+    vhe->parent = v->parent;
+  if (v->field_mask & F_VNODE_DATA) {
+    cp64(vhe->d_offset, v->d_offset);
+    vhe->d_size   = v->size;
+  }
+  if ((v->field_mask & F_VNODE_TYPE) && v->type == vDirectory)
+    phi->n_dirs++;
+  else
+    phi->n_files++;
+  return 0;
+}
+
+
+static afs_uint32 vnode_stop(afs_vnode *v, XFILE *X, void *refcon)
+{
+  path_hashinfo *phi = (path_hashinfo *)refcon;
+  int r;
+
+  /* If the file is seekable, try to position so we can pick up later... */
+  if (phi->p->flags && DSFLAG_SEEK)
+    if (r = xfseek(X, &v->offset)) return r;
+  return DSERR_DONE;
+}
+
+
+static afs_uint32 dirent_cb(afs_vnode *v, afs_dir_entry *de,
+                         XFILE *X, void *refcon)
+{
+  path_hashinfo *phi = (path_hashinfo *)refcon;
+  vhash_ent *vhe;
+
+  if (!phi->hash_table) {
+    if (phi->p->cb_error)
+      (phi->p->cb_error)(DSERR_FMT, 1, phi->p->refcon,
+                         "No volume header in dump???");
+    return DSERR_FMT;
+  }
+  if (!strcmp(de->name, ".") || !strcmp(de->name, "..")) return 0;
+  vhe = get_vhash_ent(phi, de->vnode, 1);
+  if (!vhe) return ENOMEM;
+  vhe->parent = v->vnode;
+  return 0;
+}
+
+
+/* Prescan the vnodes in a dump file, collecting information that will
+ * be useful in generating and following pathnames.  
+ */
+afs_uint32 Path_PreScan(XFILE *X, path_hashinfo *phi, int full)
+{
+  dump_parser my_p, *p = phi->p;
+  int r;
+
+  memset(phi, 0, sizeof(path_hashinfo));
+  phi->p = p;
+  memset(&my_p, 0, sizeof(my_p));
+  my_p.refcon       = (void *)phi;
+  my_p.cb_volhdr    = volhdr_cb;
+  my_p.cb_vnode_dir = vnode_keep;
+  if (full) {
+    my_p.cb_vnode_file  = vnode_keep;
+    my_p.cb_vnode_link  = vnode_keep;
+    my_p.cb_vnode_empty = vnode_keep;
+    my_p.cb_vnode_wierd = vnode_keep;
+  } else {
+    my_p.cb_vnode_file  = vnode_stop;
+    my_p.cb_vnode_link  = vnode_stop;
+    my_p.cb_vnode_empty = vnode_stop;
+    my_p.cb_vnode_wierd = vnode_stop;
+  }
+  my_p.err_refcon   = p->err_refcon;
+  my_p.cb_error     = p->cb_error;
+  my_p.cb_dirent    = dirent_cb;
+  my_p.flags        = p->flags;
+  my_p.print_flags  = p->print_flags;
+  my_p.repair_flags = p->repair_flags;
+
+  return ParseDumpFile(X, &my_p);
+}
+
+
+/* Free the hash table in a path_hashinfo */
+void Path_FreeHashTable(path_hashinfo *phi)
+{
+  int i, size;
+  vhash_ent *vhe, *next_vhe;
+
+  if (phi->hash_table) {
+    size = (1 << phi->hash_size);
+    for (i = 0; i < size; i++)
+      for (vhe = phi->hash_table[i]; vhe; vhe = next_vhe) {
+        next_vhe = vhe->next;
+        free(vhe);
+      }
+    free(phi->hash_table);
+  }
+}
+
+
+/* Follow a pathname to the vnode it represents */
+afs_uint32 Path_Follow(XFILE *X, path_hashinfo *phi,
+                    char *path, vhash_ent *his_vhe)
+{
+  vhash_ent *vhe;
+  char *name;
+  afs_uint32 r, vnum = 1;
+
+  if (*path == '/') path++;
+  name = strtok(path, "/");
+
+  for (name = strtok(path, "/"); name; name = strtok(0, "/")) {
+    if (!(vnum & 1)) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(ENOTDIR, 1, phi->p->err_refcon,
+                           "Not a directory vnode");
+      return ENOTDIR;
+    }
+    vhe = get_vhash_ent(phi, vnum, 0);
+    if (!vhe) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                           "Vnode %d not found in hash table", vnum);
+      return DSERR_FMT;
+    }
+    if (zero64(vhe->d_offset)) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                           "Directory vnode %d is incomplete", vnum);
+      return DSERR_FMT;
+    }
+    if (r = xfseek(X, &vhe->d_offset)) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(r, 1, phi->p->err_refcon,
+                           "Unable to seek to directory %d", vnum);
+      return r;
+    }
+    vnum = 0;
+    r = DirectoryLookup(X, phi->p, vhe->d_size, &name, &vnum, 0);
+    if (r) return r;
+    if (!vnum) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(ENOENT, 1, phi->p->err_refcon,
+                           "No such vnode");
+      return ENOENT;
+    }
+  }
+  vhe = get_vhash_ent(phi, vnum, 0);
+  if (!vhe) {
+    if (phi->p->cb_error)
+      (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                         "Vnode %d not found in hash table", vnum);
+    return DSERR_FMT;
+  }
+  if (his_vhe) *his_vhe = *vhe;
+  return 0;
+}
+
+
+afs_uint32 Path_Build(XFILE *X, path_hashinfo *phi, afs_uint32 vnode,
+                   char **his_path, int fast)
+{
+  vhash_ent *vhe;
+  char *name, *path = 0, fastbuf[12];
+  char *x, *y;
+  afs_uint32 parent, r;
+  int nl, pl = 0;
+
+  if (vnode == 1) {
+    *his_path = (char *)malloc(2);
+    if (!his_path) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(ENOMEM, 1, phi->p->err_refcon,
+                           "No memory for pathname of vnode 1");
+      return ENOMEM;
+    }
+    strcpy(*his_path, "/");
+    return 0;
+  }
+
+  *his_path = 0;
+  vhe = get_vhash_ent(phi, vnode, 0);
+  if (!vhe) {
+    if (phi->p->cb_error)
+      (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                         "Vnode %d not found in hash table", vnode);
+    return DSERR_FMT;
+  }
+  while (vnode != 1) {
+    /* Find the parent */
+    if (!vhe->parent) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                           "Vnode %d has no parent?", vnode);
+      if (path) free(path);
+      return DSERR_FMT;
+    }
+    parent = vhe->parent;
+    vhe = get_vhash_ent(phi, parent, 0);
+    if (phi->p->print_flags & DSPRINT_DEBUG)
+      fprintf(stderr, "Searching for vnode %d in parent %d\n", vnode, parent);
+    if (!vhe) {
+      if (phi->p->cb_error)
+        (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                           "Vnode %d not found in hash table", parent);
+      if (path) free(path);
+      return DSERR_FMT;
+    }
+
+    if (fast) {
+      /* Make up a path component from the vnode number */
+      sprintf(fastbuf, "%d", vnode);
+      name = fastbuf;
+    } else {
+      /* Do a reverse-lookup in the parent directory */
+      if (zero64(vhe->d_offset)) {
+        if (phi->p->cb_error)
+          (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                             "Directory vnode %d is incomplete", parent);
+        if (path) free(path);
+        return DSERR_FMT;
+      }
+      if (r = xfseek(X, &vhe->d_offset)) {
+        if (phi->p->cb_error)
+          (phi->p->cb_error)(errno, 1, phi->p->err_refcon,
+                             "Unable to seek to directory %d", parent);
+        if (path) free(path);
+        return r;
+      }
+
+      name = 0;
+      r = DirectoryLookup(X, phi->p, vhe->d_size, &name, &vnode, 0);
+      if (r) return r;
+      if (!name) {
+        if (phi->p->cb_error)
+          (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon,
+                             "No entry for vnode %d in directory %d",
+                             vnode, parent);
+        if (path) free(path);
+        return ENOENT;
+      }
+    }
+
+    nl = strlen(name);
+    if (path) {
+      path = (char *)realloc(path, nl + pl + 2);
+      if (!path) {
+        if (phi->p->cb_error)
+          (phi->p->cb_error)(ENOMEM, 1, phi->p->err_refcon,
+                             "No memory for pathname of vnode 1");
+        return ENOMEM;
+      }
+      x = path + pl;
+      y = x + nl + 1;
+      while (x >= path) *y-- = *x--;
+      path[0] = '/';
+      for (x = name, y = path + 1; *x;) *y++ = *x++;
+      pl += nl + 1;
+    } else {
+      path = (char *)malloc(nl + 2);
+      if (!path) {
+        if (phi->p->cb_error)
+          (phi->p->cb_error)(ENOMEM, 1, phi->p->err_refcon,
+                             "No memory for pathname of vnode 1");
+        return ENOMEM;
+      }
+      path[0] = '/';
+      strcpy(path + 1, name);
+      pl = nl + 1;
+    }
+    if (!fast) free(name);
+    vnode = parent;
+  }
+  *his_path = path;
+  return 0;
+}
diff --git a/src/tests/primitive.c b/src/tests/primitive.c
new file mode 100644 (file)
index 0000000..95dcac2
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* primitive.c - Routines for reading and writing low-level things */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumpscan.h"
+
+#define BUFSIZE 256
+
+
+afs_uint32 ReadByte(XFILE *X, unsigned char *val)
+{
+  return xfread(X, val, 1);
+}
+
+afs_uint32 ReadInt16(XFILE *X, afs_uint16 *val)
+{
+  afs_uint32 r;
+
+  if (r = xfread(X, val, 2)) return r;
+  *val = ntohs(*val);
+  return 0;
+}
+
+afs_uint32 ReadInt32(XFILE *X, afs_uint32 *val)
+{
+  afs_uint32 r;
+
+  if (r = xfread(X, val, 4)) return r;
+  *val = ntohl(*val);
+  return 0;
+}
+
+/* Read in a NUL-terminated string.  This method is kind of messy, but
+ * has the advantage that it reads the data stream only once, doesn't
+ * read anything extra, and never has to seek on the data stream.
+ */
+afs_uint32 ReadString(XFILE *X, unsigned char **val)
+{
+  static unsigned char buf[BUFSIZE];
+  unsigned char *result = 0;
+  afs_uint32 r;
+  int i, l = 0;
+
+  *val = 0;
+  for (;;) {
+    for (i = 0; i < BUFSIZE; i++) {
+      r = ReadByte(X, buf + i);
+      if (r) {
+        if (result) free(result);
+        return r;
+      }
+      if (!buf[i]) break;
+    }
+    /* iff we found a null, i < BUFSIZE and buf[i] holds the NUL */
+    if (result) result = (unsigned char *)realloc(result, l + i + 1);
+    else result = (unsigned char *)malloc(i + 1);
+    if (!result) return ENOMEM;
+    memcpy(result + l, buf, i);
+    result[l+i] = 0;
+    l += i;
+    if (i < BUFSIZE) break;
+  }
+  *val = result;
+  return 0;
+}
+
+
+afs_uint32 WriteByte(XFILE *X, unsigned char val)
+{
+  return xfwrite(X, &val, 1);
+}
+
+afs_uint32 WriteInt16(XFILE *X, afs_uint16 val)
+{
+  val = htons(val);
+  return xfwrite(X, &val, 2);
+}
+
+afs_uint32 WriteInt32(XFILE *X, afs_uint32 val)
+{
+  val = htonl(val);
+  return xfwrite(X, &val, 4);
+}
+
+afs_uint32 WriteString(XFILE *X, unsigned char *str)
+{
+  int len = strlen((char *)str) + 1;
+  return xfwrite(X, str, len);
+}
+
+afs_uint32 WriteTagByte(XFILE *X, unsigned char tag, unsigned char val)
+{
+  char buffer[2];
+  buffer[0] = tag;
+  buffer[1] = val;
+  return xfwrite(X, buffer, 2);
+}
+
+afs_uint32 WriteTagInt16(XFILE *X, unsigned char tag, afs_uint16 val)
+{
+  char buffer[3];
+  buffer[0] = tag;
+  buffer[1] = (val & 0xff00) >> 8;
+  buffer[2] = val & 0xff;
+  return xfwrite(X, buffer, 3);
+}
+
+afs_uint32 WriteTagInt32(XFILE *X, unsigned char tag, afs_uint32 val)
+{
+  char buffer[5];
+  buffer[0] = tag;
+  buffer[1] = (val & 0xff000000) >> 24;
+  buffer[2] = (val & 0xff0000) >> 16;
+  buffer[3] = (val & 0xff00) >> 8;
+  buffer[4] = val & 0xff;
+  return xfwrite(X, buffer, 5);
+}
+
+afs_uint32 WriteTagInt32Pair(XFILE *X, unsigned char tag,
+                             afs_uint32 val1, afs_uint32 val2)
+{
+  char buffer[9];
+  buffer[0] = tag;
+  buffer[1] = (val1 & 0xff000000) >> 24;
+  buffer[2] = (val1 & 0xff0000) >> 16;
+  buffer[3] = (val1 & 0xff00) >> 8;
+  buffer[4] = val1 & 0xff;
+  buffer[5] = (val2 & 0xff000000) >> 24;
+  buffer[6] = (val2 & 0xff0000) >> 16;
+  buffer[7] = (val2 & 0xff00) >> 8;
+  buffer[8] = val2 & 0xff;
+  return xfwrite(X, buffer, 9);
+}
diff --git a/src/tests/repair.c b/src/tests/repair.c
new file mode 100644 (file)
index 0000000..ece84b1
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* repair.c - Routines to generate a repaired dump */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "dumpfmt.h"
+
+#include <afs/acl.h>
+#include <afs/dir.h>
+#include <afs/prs_fs.h>
+
+XFILE repair_output;
+int repair_verbose;
+#define RV repair_verbose
+
+
+/* Try to dump a dump header.  Generate missing fields, if neccessary */
+afs_uint32 repair_dumphdr_cb(afs_dump_header *hdr, XFILE *X, void *refcon)
+{
+  afs_uint32 r, field_mask = hdr->field_mask;
+  char volname[22];
+
+  if (!(field_mask & F_DUMPHDR_VOLID)) {
+    if (RV) fprintf(stderr, ">>> DUMP HEADER missing volume ID\n");
+    return DSERR_FMT;
+  }
+  if (!(field_mask & F_DUMPHDR_VOLNAME)) {
+    if (RV) {
+      fprintf(stderr, ">>> DUMP HEADER missing volume name\n");
+      fprintf(stderr, ">>> Will use RESTORED.%d\n", hdr->volid);
+    }
+    sprintf(volname, "RESTORED.%d", hdr->volid);
+    hdr->volname = (unsigned char *)malloc(strlen(volname) + 1);
+    if (!hdr->volname) return ENOMEM;
+    strcpy(hdr->volname, volname);
+    hdr->field_mask |= F_DUMPHDR_VOLNAME;
+  }
+  if (!(field_mask & F_DUMPHDR_FROM)) {
+    if (RV) fprintf(stderr, ">>> DUMP HEADER missing from time (using 0)\n");
+    hdr->from_date = 0;
+    hdr->field_mask |= F_DUMPHDR_FROM;
+  }
+  if (!(field_mask & F_DUMPHDR_TO)) {
+    hdr->to_date = time(0);
+    if (RV) fprintf(stderr, ">>> DUMP HEADER missing from time (using %d)\n",
+      hdr->to_date);
+    hdr->field_mask |= F_DUMPHDR_TO;
+  }
+
+  return DumpDumpHeader(&repair_output, hdr);
+}
+
+
+/* Try to dump a volume header.  Generate missing fields, if necessary */
+afs_uint32 repair_volhdr_cb(afs_vol_header *hdr, XFILE *X, void *refcon)
+{
+  afs_uint32 r, field_mask = hdr->field_mask;
+  char volname[22];
+
+  if (!(field_mask & F_VOLHDR_VOLID)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing volume ID\n");
+    return DSERR_FMT;
+  }
+  if (!(field_mask & F_VOLHDR_VOLVERS)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing version (using 1)\n");
+    hdr->volvers = 1;
+    hdr->field_mask |= F_VOLHDR_VOLVERS;
+  } else if (hdr->volvers != 1) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER bogus version %d (using 1)\n",
+            hdr->volvers);
+    hdr->volvers = 1;
+  }
+  if (!(field_mask & F_VOLHDR_VOLNAME)) {
+    if (RV) {
+      fprintf(stderr, ">>> VOL HEADER missing volume name\n");
+      fprintf(stderr, ">>> Will use RESTORED.%d\n", hdr->volid);
+    }
+    sprintf(volname, "RESTORED.%d", hdr->volid);
+    hdr->volname = (unsigned char *)malloc(strlen(volname) + 1);
+    if (!hdr->volname) return ENOMEM;
+    strcpy(hdr->volname, volname);
+    hdr->field_mask |= F_VOLHDR_VOLNAME;
+  }
+  if (!(field_mask & F_VOLHDR_INSERV)) {
+    if (RV)
+      fprintf(stderr, ">>> VOL HEADER missing in-service flag (using 1)\n");
+    hdr->flag_inservice = 1;
+    hdr->field_mask |= F_VOLHDR_INSERV;
+  }
+  if (!(field_mask & F_VOLHDR_BLESSED)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing blessed flag (using 1)\n");
+    hdr->flag_blessed = 1;
+    hdr->field_mask |= F_VOLHDR_BLESSED;
+  }
+  if (!(field_mask & F_VOLHDR_VOLUNIQ)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing uniquifier (using 1)\n");
+    hdr->voluniq = 1;
+    hdr->field_mask |= F_VOLHDR_VOLUNIQ;
+  }
+  if (!(field_mask & F_VOLHDR_VOLTYPE)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing type (using 0: RW)\n");
+    hdr->voltype = 0;
+    hdr->field_mask |= F_VOLHDR_VOLTYPE;
+  } else if (hdr->voltype < 0 || hdr->voltype > 2) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER bogus type %d (using 0: RW)\n",
+            hdr->voltype);
+    hdr->voltype = 0;
+  }
+  if (!(field_mask & F_VOLHDR_PARENT)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER parent (using %d)\n", hdr->volid);
+    hdr->parent_volid = hdr->volid;
+    hdr->field_mask |= F_VOLHDR_PARENT;
+  }
+  if (!(field_mask & F_VOLHDR_MAXQ)) {
+    if (field_mask & F_VOLHDR_DISKUSED) hdr->maxquota = hdr->diskused;
+    else hdr->maxquota = 1;
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing max quota (using %d)\n",
+            hdr->maxquota);
+    hdr->field_mask |= F_VOLHDR_MAXQ;
+  }
+  if (!(field_mask & F_VOLHDR_DISKUSED)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing disk used (using 2048)\n");
+    hdr->diskused = 2048;
+    hdr->field_mask |= F_VOLHDR_DISKUSED;
+  }
+  if (!(field_mask & F_VOLHDR_NFILES)) {
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing file count (using 1)\n");
+    hdr->nfiles = 1;
+    hdr->field_mask |= F_VOLHDR_NFILES;
+  }
+  if (!(field_mask & F_VOLHDR_CREATE_DATE)) {
+    hdr->create_date = 0;
+    if ((field_mask & F_VOLHDR_ACCESS_DATE)
+    &&  (!hdr->create_date || hdr->access_date < hdr->create_date))
+      hdr->create_date = hdr->access_date;
+    if ((field_mask & F_VOLHDR_UPDATE_DATE)
+    &&  (!hdr->create_date || hdr->update_date < hdr->create_date))
+      hdr->create_date = hdr->update_date;
+    if ((field_mask & F_VOLHDR_BACKUP_DATE)
+    &&  (!hdr->create_date || hdr->backup_date < hdr->create_date))
+      hdr->create_date = hdr->backup_date;
+
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing create date (using %d)\n",
+            hdr->create_date);
+    hdr->field_mask |= F_VOLHDR_CREATE_DATE;
+  }
+  if (!(field_mask & F_VOLHDR_ACCESS_DATE)) {
+    hdr->access_date = 0;
+    if ((field_mask & F_VOLHDR_CREATE_DATE)
+    &&  (!hdr->access_date || hdr->create_date > hdr->access_date))
+      hdr->access_date = hdr->create_date;
+    if ((field_mask & F_VOLHDR_UPDATE_DATE)
+    &&  (!hdr->access_date || hdr->update_date > hdr->access_date))
+      hdr->access_date = hdr->update_date;
+    if ((field_mask & F_VOLHDR_BACKUP_DATE)
+    &&  (!hdr->access_date || hdr->backup_date > hdr->access_date))
+      hdr->access_date = hdr->backup_date;
+
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing access date (using %d)\n",
+            hdr->access_date);
+    hdr->field_mask |= F_VOLHDR_ACCESS_DATE;
+  }
+  if (!(field_mask & F_VOLHDR_UPDATE_DATE)) {
+    hdr->update_date = 0;
+    if ((field_mask & F_VOLHDR_CREATE_DATE)
+    &&  (!hdr->update_date || hdr->create_date > hdr->update_date))
+      hdr->update_date = hdr->create_date;
+    if ((field_mask & F_VOLHDR_ACCESS_DATE) && !hdr->update_date)
+      hdr->update_date = hdr->access_date;
+    if ((field_mask & F_VOLHDR_BACKUP_DATE) && !hdr->update_date)
+      hdr->update_date = hdr->backup_date;
+
+    if (RV) fprintf(stderr, ">>> VOL HEADER missing update date (using %d)\n",
+            hdr->update_date);
+    hdr->field_mask |= F_VOLHDR_UPDATE_DATE;
+  }
+
+  return DumpVolumeHeader(&repair_output, hdr);
+}
+
+
+/* Try to dump a vnode.  Generate missing fields, if necessary */
+afs_uint32 repair_vnode_cb(afs_vnode *v, XFILE *X, void *refcon)
+{
+  afs_uint32 r, field_mask = v->field_mask;
+
+  if ((v->vnode & 1) && !field_mask) {
+    if (RV) fprintf(stderr, ">>> VNODE %d is directory but has no fields?\n");
+    v->type = vDirectory;
+    v->field_mask |= F_VNODE_TYPE;
+    field_mask = F_VNODE_TYPE; /* Messy! */
+  }
+  if (field_mask && !(field_mask & F_VNODE_TYPE)) {
+    v->type = (v->vnode & 1) ? vDirectory : vFile;
+    if (RV) fprintf(stderr, ">>> VNODE %d missing type (using %d)\n",
+            v->vnode, v->type);
+    v->field_mask |= F_VNODE_TYPE;
+  }
+  if (field_mask && !(field_mask & F_VNODE_NLINKS)) {
+    if (RV)
+      fprintf(stderr, ">>> VNODE %d missing link count (using 1)\n", v->vnode);
+    v->nlinks = 1;
+    v->field_mask |= F_VNODE_NLINKS;
+  }
+  if (field_mask && !(field_mask & F_VNODE_PARENT)) {
+    if (RV)
+      fprintf(stderr, ">>> VNODE %d missing parent (using 1)\n", v->vnode);
+    v->parent = 1;
+    v->field_mask |= F_VNODE_PARENT;
+  }
+  if (field_mask && !(field_mask & F_VNODE_DVERS)) {
+    if (RV) fprintf(stderr, ">>> VNODE %d missing data version (using 1)\n",
+              v->vnode);
+    v->datavers = 1;
+    v->field_mask |= F_VNODE_DVERS;
+  }
+  if (field_mask && !(field_mask & F_VNODE_AUTHOR)) {
+    if (field_mask & F_VNODE_OWNER) v->author = v->owner;
+    else v->author = 0;
+    if (RV) fprintf(stderr, ">>> VNODE %d missing author (using %d)\n",
+            v->vnode, v->author);
+    v->field_mask |= F_VNODE_AUTHOR;
+  }
+  if (field_mask && !(field_mask & F_VNODE_OWNER)) {
+    if (field_mask & F_VNODE_AUTHOR) v->owner = v->author;
+    else v->owner = 0;
+    if (RV) fprintf(stderr, ">>> VNODE %d missing owner (using %d)\n",
+            v->vnode, v->owner);
+    v->field_mask |= F_VNODE_OWNER;
+  }
+  if (field_mask && !(field_mask & F_VNODE_MODE)) {
+    v->mode = (v->vnode & 1) ? 0755 : 0644;
+    if (RV) fprintf(stderr, ">>> VNODE missing mode (using %d)\n", v->mode);
+    v->field_mask |= F_VNODE_MODE;
+  }
+  if (field_mask && !(field_mask & F_VNODE_CDATE)) {
+    if (field_mask & F_VNODE_SDATE) v->client_date = v->server_date;
+    else v->client_date = 0;
+
+    if (RV) fprintf(stderr, ">>> VNODE %d missing client date (using %d)\n",
+            v->vnode, v->client_date);
+    v->field_mask |= F_VNODE_CDATE;
+  }
+  if (field_mask && !(field_mask & F_VNODE_SDATE)) {
+    if (field_mask & F_VNODE_CDATE) v->server_date = v->client_date;
+    else v->server_date = 0;
+
+    if (RV) fprintf(stderr, ">>> VNODE %d missing server date (using %d)\n",
+            v->vnode, v->server_date);
+    v->field_mask |= F_VNODE_SDATE;
+  }
+  if (field_mask && !(field_mask & F_VNODE_SIZE)) {
+    if (RV) fprintf(stderr, ">>> VNODE %d has no data size (using 0)\n");
+    v->size = 0;
+    v->field_mask |= F_VNODE_SIZE;
+  }
+  if ((field_mask & F_VNODE_DATA) && !v->size) {
+    if (RV)
+      fprintf(stderr, ">>> VNODE %d has data, but size == 0 (ignoring)\n",
+            v->vnode);
+    v->field_mask &=~ F_VNODE_DATA;
+  }
+  if (field_mask && v->type == vDirectory && !(field_mask & F_VNODE_ACL)) {
+    struct acl_accessList *acl = (struct acl_accessList *)v->acl;
+    if (RV) {
+      fprintf(stderr, ">>> VNODE %d is directory but has no ACL\n");
+      fprintf(stderr, ">>> Will generate default ACL\n");
+    }
+    memset(v->acl, 0, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE);
+    acl->size = htonl(SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE);
+    acl->version = htonl(ACL_ACLVERSION);
+    acl->total = htonl(v->owner ? 0 : 1);
+    acl->positive = acl->total;
+    acl->negative = 0;
+    if (v->owner) {
+      acl->entries[0].id = htonl(v->owner);
+      acl->entries[0].rights = htonl((PRSFS_READ   | PRSFS_WRITE
+                                    | PRSFS_INSERT | PRSFS_LOOKUP
+                                    | PRSFS_DELETE | PRSFS_LOCK
+                                    | PRSFS_ADMINISTER));
+    }
+    v->field_mask |= F_VNODE_ACL;
+  }
+
+  r = DumpVNode(&repair_output, v);
+  if (r) return r;
+
+  if (v->size) {
+    if (r = xfseek(X, &v->d_offset)) return r;
+    r = CopyVNodeData(&repair_output, X, v->size);
+  } else if (v->type == vDirectory) {
+    afs_dir_page page;
+    struct DirHeader *dhp = (struct DirHeader *)&page;
+    int i;
+
+    if (RV) {
+      fprintf(stderr, ">>> VNODE %d is directory but has no contents\n");
+      fprintf(stderr, ">>> Will generate deafult directory entries\n");
+    }
+    memset(&page, 0, sizeof(page));
+
+    /* Page and Directory Headers */
+    page.header.tag = htons(1234);
+    page.header.freecount = (EPP - DHE - 3);
+    page.header.freebitmap[0] = 0xff;
+    page.header.freebitmap[1] = 0x7f;
+    dhp->alloMap[0] = EPP - DHE - 3;
+    for (i = 1; i < MAXPAGES; i++) dhp->alloMap[i] = EPP;
+
+    /* Entry for . */
+    page.entry[DHE + 1].flag    = FFIRST;
+    page.entry[DHE + 1].length  = 1;
+    page.entry[DHE + 1].vnode   = v->vnode;
+    page.entry[DHE + 1].vunique = v->vuniq;
+    strcpy(page.entry[DHE + 1].name, ".");
+    dhp->hashTable[0x2e] = DHE + 1;
+
+    /* Entry for .. */
+    page.entry[DHE + 2].flag    = FFIRST;
+    page.entry[DHE + 2].length  = 1;
+    page.entry[DHE + 2].vnode   = v->parent;
+    page.entry[DHE + 2].vunique = 1;         /* Can't have everything! */
+    strcpy(page.entry[DHE + 2].name, "..");
+    dhp->hashTable[0x44] = DHE + 2;
+
+    r = DumpVNodeData(&repair_output, (char *)&page, 2048);
+  } else if (field_mask) {
+    /* We wrote out attributes, so we should also write the 0-length data */
+    r = DumpVNodeData(&repair_output, "", 0);
+  }
+
+  return r;
+}
diff --git a/src/tests/stagehdr.c b/src/tests/stagehdr.c
new file mode 100644 (file)
index 0000000..be63426
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* stagehdr.c - Parse and dump stage backup headers */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "xf_errs.h"
+#include "stagehdr.h"
+
+static afs_uint32 hdr_checksum(char *buf, int size)
+{
+  afs_uint32 sum = 0, n = size / sizeof(afs_uint32), *words = (afs_uint32 *)buf;
+
+  while (--n)
+    sum += ntohl(*words++);
+  return sum;
+}
+
+
+/* Parse a stage backup header.
+ * If tag is non-NULL, *tag should contain the first byte (already read),
+ * and will be filled in with the first byte after the header, if one exists.
+ * On success, returns 0 and leaves us positioned after the header
+ * On failure, returns an error and position is undefined
+ * Iff there is no header, returns DSERR_MAGIC and leaves us
+ * positioned where we started.
+ */
+afs_uint32 ParseStageHdr(XFILE *X, unsigned char *tag, backup_system_header *hdr)
+{
+  char buf[STAGE_HDRLEN];
+  struct stage_header *bckhdr = (struct stage_header *)buf;
+  u_int64 where;
+  afs_uint32 r;
+
+  if (r = xftell(X, &where)) return r;
+  if (hdr) memset(hdr, 0, sizeof(*hdr));
+  if (tag) {
+    if (*tag != STAGE_VERSMIN) return DSERR_MAGIC;
+    buf[0] = *tag;
+    r = xfread(X, buf + 1, STAGE_HDRLEN - 1);
+  } else {
+    r = xfread(X, buf, STAGE_HDRLEN);
+  }
+
+  if (r == ERROR_XFILE_EOF) {
+    r = xfseek(X, &where);
+    return r ? r : DSERR_MAGIC;
+  } else if (r) return r;
+
+  if (bckhdr->c_vers < STAGE_VERSMIN
+  ||  ntohl(bckhdr->c_magic) != STAGE_MAGIC
+  ||  hdr_checksum(buf, STAGE_HDRLEN) != STAGE_CHECKSUM) {
+    r = xfseek(X, &where);
+    return r ? r : DSERR_MAGIC;
+  }
+
+  if (hdr) {
+    hdr->version   = bckhdr->c_vers;
+    hdr->from_date = ntohl(bckhdr->c_fdate);
+    hdr->to_date   = ntohl(bckhdr->c_tdate);
+    hdr->dump_date = ntohl(bckhdr->c_time);
+    hdr->filenum   = ntohl(bckhdr->c_filenum);
+    hdr->volid     = ntohl(bckhdr->c_id);
+    hdr->dumplen   = ntohl(bckhdr->c_length);
+    hdr->level     = ntohl(bckhdr->c_level);
+    hdr->magic     = ntohl(bckhdr->c_magic);
+    hdr->cksum     = ntohl(bckhdr->c_checksum);
+    hdr->flags     = ntohl(bckhdr->c_flags);
+    hdr->server    = malloc(strlen(bckhdr->c_host) + 1);
+    hdr->part      = malloc(strlen(bckhdr->c_disk) + 1);
+    hdr->volname   = malloc(strlen(bckhdr->c_name) + 1);
+
+    if (!hdr->server || !hdr->part || !hdr->volname) {
+      if (hdr->server)  free(hdr->server);
+      if (hdr->part)    free(hdr->part);
+      if (hdr->volname) free(hdr->volname);
+      return ENOMEM;
+    }
+    strcpy(hdr->server,  bckhdr->c_host);
+    strcpy(hdr->part,    bckhdr->c_disk);
+    strcpy(hdr->volname, bckhdr->c_name);
+  }
+
+  if (tag) return ReadByte(X, tag);
+  else return 0;
+}
+
+
+/* Dump a stage backup header */
+afs_uint32 DumpStageHdr(XFILE *OX, backup_system_header *hdr)
+{
+  char buf[STAGE_HDRLEN];
+  struct stage_header *bckhdr = (struct stage_header *)buf;
+  afs_uint32 checksum;
+  afs_uint32 r;
+
+  memset(buf, 0, STAGE_HDRLEN);
+  bckhdr->c_vers     = hdr->version;
+  bckhdr->c_fdate    = htonl(hdr->from_date);
+  bckhdr->c_tdate    = htonl(hdr->to_date);
+  bckhdr->c_filenum  = htonl(hdr->filenum);
+  bckhdr->c_time     = htonl(hdr->dump_date);
+  bckhdr->c_id       = htonl(hdr->volid);
+  bckhdr->c_length   = htonl(hdr->dumplen);
+  bckhdr->c_level    = htonl(hdr->level);
+  bckhdr->c_magic    = htonl(STAGE_MAGIC);
+  bckhdr->c_flags    = htonl(hdr->flags);
+
+  strcpy(bckhdr->c_host, hdr->server);
+  strcpy(bckhdr->c_disk, hdr->part);
+  strcpy(bckhdr->c_name, hdr->volname);
+
+  /* Now, compute the checksum */
+  checksum = hdr_checksum(buf, STAGE_HDRLEN);
+  bckhdr->c_checksum = htonl(STAGE_CHECKSUM - checksum);
+
+  if (r = xfwrite(OX, buf, STAGE_HDRLEN)) return r;
+  return 0;
+}
diff --git a/src/tests/stagehdr.h b/src/tests/stagehdr.h
new file mode 100644 (file)
index 0000000..985fe9e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* stagehdr.h - (old) Stage backup header format */
+
+#ifndef _STAGEHDR_H_
+#define _STAGEHDR_H_
+
+#include "intNN.h"
+
+/* Stage-related constants */
+#define STAGE_MAGIC    0x00adf8bc       /* magic number for stage header */
+#define STAGE_CHECKSUM 84446            /* checksum (same as 4.2bsd dump) */
+#define STAGE_VERSMIN  20               /* minimum version */
+#define STAGE_NAMLEN   64               /* length of host/part/vol names */
+#define STAGE_HDRLEN   1024             /* size of the header */
+
+struct stage_header {
+  unsigned char c_vers;               /* header version (starts at 20) */
+  unsigned char c_notused[3];
+  afs_uint32       c_fdate;              /* dump "from" date */
+  afs_uint32       c_tdate;              /* dump "to" date */
+  afs_uint32       c_filenum;            /* tape file number */
+  afs_uint32       c_time;               /* time dump was done */
+  char          c_host[STAGE_NAMLEN]; /* hostname volume came from */
+  char          c_disk[STAGE_NAMLEN]; /* partition volume came from */
+  char          c_name[STAGE_NAMLEN]; /* volume name */
+  afs_uint32       c_id;                 /* volume ID */
+  afs_uint32       c_length;             /* length of the dump */
+  afs_uint32       c_level;              /* dump level */
+  afs_uint32       c_magic;              /* magic number */
+  afs_uint32       c_checksum;           /* checksum of backup header */
+  afs_uint32       c_flags;              /* feature flags */
+};
+
+#endif /* _STAGEHDR_H_ */
diff --git a/src/tests/util.c b/src/tests/util.c
new file mode 100644 (file)
index 0000000..7c9ac4d
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* util.c - Useful utilities */
+
+#include <errno.h>
+
+#include "xf_errs.h"
+#include "dumpscan.h"
+#include "dumpscan_errs.h"
+#include "dumpfmt.h"
+
+
+/* Take care of errno, ERROR_XFILE_EOF, and ENOMEM return codes.
+ * Call whatever callbacks are necessary, and return the code to
+ * actually use.  If you don't want '0' to result in a DSERR_TAG,
+ * then you must translate it to DSERR_DONE before calling this.
+ */
+/*** THIS FUNCTION INTENDED FOR INTERNAL USE ONLY ***/
+int handle_return(int r, XFILE *X, unsigned char tag, dump_parser *p)
+{
+  u_int64 where, xwhere;
+
+  switch (r) {
+  case 0:
+    if (p->cb_error) {
+      xftell(X, &where);
+      sub64_32(xwhere, where, 1);
+      (p->cb_error)(DSERR_TAG, 1, p->err_refcon,
+                    (tag > 0x20 && tag < 0x7f)
+                    ? "Unexpected tag '%c' at %s = 0x%s"
+                    : "Unexpected tag 0x%02x at %s = 0x%s",
+                    tag, decimate_int64(&xwhere, 0), hexify_int64(&xwhere, 0));
+    }
+    return DSERR_TAG;
+    
+  case ERROR_XFILE_EOF:
+    if (p->cb_error) {
+      xftell(X, &where);
+      (p->cb_error)(ERROR_XFILE_EOF, 1, p->err_refcon,
+                    "Unexpected EOF at %s = 0x%s",
+                    decimate_int64(&where, 0), hexify_int64(&where, 0));
+    }
+    return ERROR_XFILE_EOF;
+    
+  case ENOMEM:
+    if (p->cb_error) {
+      xftell(X, &where);
+      (p->cb_error)(ENOMEM, 1, p->err_refcon,
+                    "Out of memory at %s = 0x%s",
+                    decimate_int64(&where, 0), hexify_int64(&where, 0));
+    }
+    return ENOMEM;
+    
+  case DSERR_DONE:
+    return 0;
+
+  default:
+    /* For other negative valuees, the callback was already done */
+    if (r > 0 && p->cb_error)
+      (p->cb_error)(r, 1, p->err_refcon,
+                    "System error %d reading dump file", r);
+    return r;
+  }
+}
+
+
+/* Prepare a tag_parse_info for use by the dump parser. *
+/*** THIS FUNCTION INTENDED FOR INTERNAL USE ONLY ***/
+void prep_pi(dump_parser *p, tag_parse_info *pi)
+{
+  memset(pi, 0, sizeof(tag_parse_info));
+  pi->err_refcon = p->err_refcon;
+  pi->cb_error = p->cb_error;
+
+  if (p->repair_flags & DSFIX_SKIP)
+    pi->flags |= TPFLAG_SKIP;
+  if ((p->flags & DSFLAG_SEEK) && (p->repair_flags & DSFIX_RSKIP))
+    pi->flags |= TPFLAG_RSKIP;
+}
+
+
+/* Does the designated location match a vnode?
+ * Returns 0 if yes, DSERR_FMT if no, something else on error
+ */
+/*** THIS FUNCTION INTENDED FOR INTERNAL USE ONLY ***/
+int match_next_vnode(XFILE *X, dump_parser *p, u_int64 *where, afs_uint32 vnode)
+{
+  afs_uint32 r, x, y, z;
+  unsigned char tag;
+
+  if (r = xfseek(X, where)) return r;
+  if (r = ReadByte(X, &tag)) return r;
+  switch (tag) {
+  case 3:  /* A vnode? */
+    if (r = ReadInt32(X, &x)) return r;
+    if (r = ReadInt32(X, &y)) return r;
+    if (r = ReadByte(X, &tag)) return r;
+    if ( !((vnode & 1) && !(x & 1) && x < vnode)
+    &&   !((vnode & 1) == (x & 1) && x > vnode))
+      return DSERR_FMT;
+    if (x > vnode && x - vnode > 10000) return DSERR_FMT;
+    if (y < 0 || y > p->vol_uniquifier)  return DSERR_FMT;
+
+    /* Now, what follows the vnode/uniquifier? */
+    switch (tag) {
+    case 3:   /* Another vnode? - Only if this is a non-directory */
+      if (x & 1) return DSERR_FMT;
+      if (r = ReadInt32(X, &z)) return r;
+      if ( !((x & 1) && !(z & 1) && z < x)
+      &&   !((x & 1) == (z & 1) && z > x))
+        return DSERR_FMT;
+      return 0;
+
+    case 4:   /* Dump end - Only if this is a non-directory */
+      if (x & 1) return DSERR_FMT;
+      if (r = ReadInt32(X, &z)) return r;
+      if (z != DUMPENDMAGIC) return DSERR_FMT;
+      return 0;
+
+    case 't': /* Vnode type byte */
+      if (r = ReadByte(X, &tag)) return r;
+      if ((tag == vFile || tag == vSymlink) && !(x & 1)) return 0;
+      if (tag == vDirectory && (x & 1)) return 0;
+      return DSERR_FMT;
+
+    default:
+      return DSERR_FMT;
+    }
+
+  case 4:  /* A dump end? */
+    if (r = ReadInt32(X, &x)) return r;
+    if (x != DUMPENDMAGIC) return DSERR_FMT;
+    return 0;
+
+  default:
+    return DSERR_FMT;
+  }
+}
diff --git a/src/tests/xf_errs.et b/src/tests/xf_errs.et
new file mode 100644 (file)
index 0000000..03cca8f
--- /dev/null
@@ -0,0 +1,37 @@
+# CMUCS AFStools
+# dumpscan - routines for scanning and manipulating AFS volume dumps
+#
+# Copyright (c) 1998 Carnegie Mellon University
+# All Rights Reserved.
+# 
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+#
+# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+# CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+#
+# Carnegie Mellon requests users of this software to return to
+#
+#  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+#  School of Computer Science
+#  Carnegie Mellon University
+#  Pittsburgh PA 15213-3890
+#
+# any improvements or extensions that they make and grant Carnegie Mellon
+# the rights to redistribute these changes.
+
+# xf_errs.et - Error table for xfiles
+
+error_table xFil
+  ec ERROR_XFILE_EOF,            "EOF while reading XFILE"
+  ec ERROR_XFILE_WRONLY,         "XFILE may not be opened write-only"
+  ec ERROR_XFILE_RDONLY,         "XFILE is read-only"
+  ec ERROR_XFILE_NOSEEK,         "XFILE is not seekable"
+  ec ERROR_XFILE_ISPASS,         "XFILE passthru already set"
+  ec ERROR_XFILE_NOPASS,         "XFILE passthru not set"
+  ec ERROR_XFILE_TYPE,           "unknown XFILE type"
+end
diff --git a/src/tests/xf_files.c b/src/tests/xf_files.c
new file mode 100644 (file)
index 0000000..877e1ae
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* xf_files.c - XFILE routines for accessing UNIX files */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "xfiles.h"
+#include "xf_errs.h"
+
+#define O_MODE_MASK (O_RDONLY | O_WRONLY | O_RDWR)
+
+
+/* do_read for stdio xfiles */
+static afs_uint32 xf_FILE_do_read(XFILE *X, void *buf, afs_uint32 count)
+{
+  FILE *F = X->refcon;
+
+  /* XXX: handle short and interrupted reads */
+  if (fread(buf, count, 1, F) != 1)
+    return ferror(F) ? errno : ERROR_XFILE_EOF;
+  return 0;
+}
+
+
+/* do_write for stdio xfiles */
+static afs_uint32 xf_FILE_do_write(XFILE *X, void *buf, afs_uint32 count)
+{
+  FILE *F = X->refcon;
+
+  /* XXX: handle interrupted writes */
+  if (fwrite(buf, count, 1, F) != 1)
+    return errno;
+  return 0;
+}
+
+
+/* do_tell for stdio xfiles */
+static afs_uint32 xf_FILE_do_tell(XFILE *X, u_int64 *offset)
+{
+  FILE *F = X->refcon;
+  off_t where;
+
+  where = ftell(F);
+  if (where == -1) return errno;
+  set64(*offset, where);
+  return 0;
+}
+
+
+/* do_seek for stdio xfiles */
+static afs_uint32 xf_FILE_do_seek(XFILE *X, u_int64 *offset)
+{
+  FILE *F = X->refcon;
+  off_t where = get64(*offset);
+
+  if (fseek(F, where, SEEK_SET) == -1) return errno;
+  return 0;
+}
+
+
+/* do_skip for stdio xfiles */
+static afs_uint32 xf_FILE_do_skip(XFILE *X, afs_uint32 count)
+{
+  FILE *F = X->refcon;
+
+  if (fseek(F, count, SEEK_CUR) == -1) return errno;
+  return 0;
+}
+
+
+/* do_close for stdio xfiles */
+static afs_uint32 xf_FILE_do_close(XFILE *X)
+{
+  FILE *F = X->refcon;
+
+  X->refcon = 0;
+  if (fclose(F)) return errno;
+  return 0;
+}
+
+
+/* Prepare a stdio XFILE */
+static void prepare(XFILE *X, FILE *F, int xflag)
+{
+  struct stat st;
+
+  memset(X, 0, sizeof(*X));
+  X->do_read  = xf_FILE_do_read;
+  X->do_write = xf_FILE_do_write;
+  X->do_tell  = xf_FILE_do_tell;
+  X->do_close = xf_FILE_do_close;
+  X->refcon = F;
+  if (xflag == O_RDWR) X->is_writable = 1;
+
+  if (!fstat(fileno(F), &st)
+  && ((st.st_mode & S_IFMT) == S_IFREG
+  ||  (st.st_mode & S_IFMT) == S_IFBLK)) {
+    X->is_seekable = 1;
+    X->do_seek = xf_FILE_do_seek;
+    X->do_skip = xf_FILE_do_skip;
+  }
+}
+
+
+/* Open an XFILE by path */
+afs_uint32 xfopen_path(XFILE *X, int flag, char *path, int mode)
+{
+  FILE *F = 0;
+  int fd = -1, xflag;
+  afs_uint32 code;
+
+  xflag = flag & O_MODE_MASK;
+  if (xflag == O_WRONLY) return ERROR_XFILE_WRONLY;
+
+  if ((fd = open(path, flag, mode)) < 0) return errno;
+  if (!(F = fdopen(fd, (xflag == O_RDONLY) ? "r" : "r+"))) {
+    code = errno;
+    close(fd);
+    return code;
+  }
+
+  prepare(X, F, xflag);
+  return 0;
+}
+
+
+/* Open an XFILE by FILE * */
+afs_uint32 xfopen_FILE(XFILE *X, int flag, FILE *F)
+{
+  flag &= O_MODE_MASK;
+  if (flag == O_WRONLY) return ERROR_XFILE_WRONLY;
+  prepare(X, F, flag);
+  return 0;
+}
+
+
+/* Open an XFILE by file descriptor */
+afs_uint32 xfopen_fd(XFILE *X, int flag, int fd)
+{
+  FILE *F;
+
+  flag &= O_MODE_MASK;
+  if (flag == O_WRONLY) return ERROR_XFILE_WRONLY;
+  if (!(F = fdopen(fd, (flag == O_RDONLY) ? "r" : "r+"))) return errno;
+  prepare(X, F, flag);
+  return 0;
+}
+
+
+/* open-by-name support for filenames */
+afs_uint32 xfon_path(XFILE *X, int flag, char *name)
+{
+  return xfopen_path(X, flag, name, 0644);
+}
+
+
+/* open-by-name support for file descriptors */
+afs_uint32 xfon_fd(XFILE *X, int flag, char *name)
+{
+  int fd = atoi(name);
+  return xfopen_fd(X, flag, fd);
+}
+
+
+/* open-by-name support for standard I/O */
+afs_uint32 xfon_stdio(XFILE *X, int flag)
+{
+  flag &= O_MODE_MASK;
+  if (flag == O_WRONLY) flag = O_RDWR;
+  return xfopen_FILE(X, flag, (flag == O_RDONLY) ? stdin : stdout);
+}
diff --git a/src/tests/xf_printf.c b/src/tests/xf_printf.c
new file mode 100644 (file)
index 0000000..d1060ea
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * CMUCS AFStools
+ * dumpscan - routines for scanning and manipulating AFS volume dumps
+ *
+ * Copyright (c) 1998 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdarg.h>
+
+#include "xfiles.h"
+#include "xf_errs.h"
+
+#define SPBUFLEN 40
+static char spbuf[SPBUFLEN + 1] = "";
+
+
+#define MAXPREC 100
+
+/* Generate an ASCII representation of an integer <val>, as follows:
+ * <base> indicates the base to be used (2-36)
+ * <uc> is nonzero if letter digits should be uppercase
+ * <prec> is the minimum number of digits
+ * The resulting number is stored in <buf>, which must be long enough
+ * to receive it.  The minimum length is <prec> or ceil(log{base}(val)),
+ * whichever is larger, plus room for a trailing NUL.
+ */
+static void mkint(char *buf, unsigned long val, int base, int uc, int prec)
+{
+  int len = 0, dig, i;
+
+  while (val) {
+    dig = val % base;
+    val = (val - dig) / base;
+    if (dig < 10)  dig = dig + '0';
+    else if (uc) dig = dig + 'A' - 10;
+    else         dig = dig + 'a' - 10;
+    buf[len++] = dig;
+  }
+  while (len < prec) buf[len++] = '0';
+  for (i = 0; i < (len + 1) / 2; i++) {
+    dig = buf[i];
+    buf[i] = buf[len - i - 1];
+    buf[len - i - 1] = dig;
+  }
+  buf[len] = 0;
+}
+
+
+/* Write spaces faster than one at a time */
+static afs_ui