upstream dumpscan changes
authorDerrick Brashear <shadow@dementia.org>
Wed, 28 Apr 2010 06:02:05 +0000 (02:02 -0400)
committerDerrick Brashear <shadow@dementia.org>
Wed, 28 Apr 2010 14:18:12 +0000 (07:18 -0700)
pull in code changes from upstream for dumpscan

Change-Id: Ib1bcba7a420d19c1bf27b2a5405aaee786e6cbd6
Reviewed-on: http://gerrit.openafs.org/1873
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: Derrick Brashear <shadow@dementia.org>

src/tests/directory.c
src/tests/dumpfmt.h
src/tests/dumpscan.h

index eed269f..7db1e46 100644 (file)
@@ -2,7 +2,7 @@
  * CMUCS AFStools
  * dumpscan - routines for scanning and manipulating AFS volume dumps
  *
- * Copyright (c) 1998 Carnegie Mellon University
+ * Copyright (c) 1998, 2001, 2004 Carnegie Mellon University
  * All Rights Reserved.
  * 
  * Permission to use, copy, modify and distribute this software and its
  * the rights to redistribute these changes.
  */
 
-/* directory.c - Parse an AFS directory */
+/* directory.c - AFS directory parsing and generation */
 /* See the end of this file for a description of the directory format */
 
 #include <errno.h>
 #include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
 #include <netinet/in.h>
 
 #include "dumpscan.h"
 
 #include <afs/dir.h>
 
+struct dir_state {
+  unsigned char **dirpages;
+  int npages;
+
+  afs_dir_header *dh;        /* Directory header */
+  afs_dir_page *page;        /* Current page */
+  int pageno;                /* Current page # */
+  int entno;                 /* Current (next avail) entry # */
+  int used;                  /* # entries used in this page */
+};
+
 static afs_dir_page page;
 
-#define allocbit(x) (page.header.freebitmap[(x)>>3] & (1 << ((x) & 7)))
+#define bmbyte(bm,x) bm[(x)>>3]
+#define bmbit(x) (1 << ((x) & 7))
+
+#define allocbit(x) (bmbyte(page.header.freebitmap,x) & bmbit(x))
+#define setallocbit(bm,x) (bmbyte(bm,x) |= bmbit(x))
+
 #define DPHE (DHE + 1)
+
+/* Hash function used in AFS directories.  */
+static int namehash(char *name, int buckets, int seed)
+{   
+  int hval = seed, tval;
+
+  while (*name) hval = (hval * 173) + *name++;
+  tval = hval & (buckets - 1);
+  return tval ? hval < 0 ? buckets - tval : tval : 0;
+}
+
 #if 0
-static void
-fixup(char *name, int l)
+static void fixup(char *name, int l)
 {
-    name += 16;
-    l -= 15;
+  name += 16;
+  l -= 15;
 
-    while (l-- > 0) {
-       name[0] = name[4];
-       name++;
-    }
+  while (l-- > 0) {
+    name[0] = name[4];
+    name++;
+  }
 }
 #endif
-afs_uint32
-parse_directory(XFILE * X, dump_parser * p, afs_vnode * v, afs_uint32 size,
-               int toeof)
+
+afs_uint32 parse_directory(XFILE *X, dump_parser *p, afs_vnode *v,
+                        afs_uint32 size, int toeof)
 {
-    afs_dir_entry de;
-    int pgno, i, l, n;
-    afs_int32 r;
-    u_int64 where;
-
-    if (p->print_flags & DSPRINT_DIR) {
-       printf("  VNode      Uniqifier   Name\n");
-       printf("  ========== ==========  ==============================\n");
+  afs_dir_entry de;
+  int pgno, i, l, n;
+  afs_int32 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;
     }
-    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;
-           }
+    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 (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;
+  }
+  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 ParseDirectory(XFILE *X, dump_parser *p, afs_uint32 size, int toeof)
 {
-    afs_uint32 r;
+  afs_uint32 r;
 
-    r = parse_directory(X, p, 0, size, toeof);
-    return r;
+  r = parse_directory(X, p, 0, size, toeof);
+  return r;
 }
 
 
 typedef struct {
-    char **name;
-    afs_uint32 *vnode;
-    afs_uint32 *vuniq;
+  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)
+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;
+  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);
     }
-    return DSERR_DONE;
+    if (s->vuniq) s->vuniq[0] = de->uniq;
+  }
+  return DSERR_DONE;
 }
 
 
@@ -189,29 +202,131 @@ dirlookup_cb(afs_vnode * v, afs_dir_entry * de, XFILE * X, void *refcon)
  * 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)
+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);
+}
+
+
+static int allocpage(struct dir_state *ds, int reserve)
 {
-    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);
+  unsigned char **dirpages;
+  int i;
+
+  dirpages = malloc((ds->npages + 1) * sizeof(unsigned char *));
+  if (!dirpages) return ENOMEM;
+  if (ds->dirpages) {
+    memcpy(dirpages, ds->dirpages, ds->npages * sizeof(unsigned char *));
+    free(ds->dirpages);
+  }
+  ds->dirpages = dirpages;
+
+  ds->dirpages[ds->npages] = malloc(AFS_PAGESIZE);
+  if (!ds->dirpages[ds->npages]) return ENOMEM;
+  ds->pageno = ds->npages++;
+
+  ds->page = (afs_dir_page *)(ds->dirpages[ds->pageno]);
+  memset(ds->page, 0, AFS_PAGESIZE);
+  ds->page->header.tag = htons(AFS_DIR_MAGIC);
+  ds->entno = ds->used = reserve;
+  for (i = 0; i < reserve; i++)
+    setallocbit(ds->page->header.freebitmap, i);
+  return 0;
+}
+
+
+afs_uint32 Dir_Init(struct dir_state **dsp)
+{
+  afs_uint32 r;
+
+  *dsp = malloc(sizeof(struct dir_state));
+  if (!*dsp) return ENOMEM;
+  memset(*dsp, 0, sizeof(struct dir_state));
+  if ((r = allocpage(*dsp, DPHE))) return r;
+  (*dsp)->dh = (afs_dir_header *)((*dsp)->page);
+  return 0;
+}
+
+
+afs_uint32 Dir_AddEntry(struct dir_state *ds, char *name,
+                        afs_uint32 vnode, afs_uint32 unique)
+{
+  afs_uint32 r;
+  int l = strlen(name) + 1;
+  int ne = l > 16 ? 1 + ((l - 16) / 32) + !!((l - 16) % 32) : 1;
+  int hash = namehash(name, NHASHENT, 0);
+
+  if (ne > EPP - 1) return ENAMETOOLONG;
+  if (ds->entno + ne > EPP) {
+    if (ds->pageno < 128) ds->dh->allomap[ds->pageno] = ds->used;
+    if ((r = allocpage(ds, 1))) return r;
+  }
+  ds->page->entry[ds->entno].flag    = FFIRST;
+  ds->page->entry[ds->entno].next    = ds->dh->hash[hash];
+  ds->page->entry[ds->entno].vnode   = htonl(vnode);
+  ds->page->entry[ds->entno].vunique = htonl(unique);
+  strcpy(ds->page->entry[ds->entno].name, name);
+  ds->dh->hash[hash] = htons((ds->pageno * EPP) + ds->entno);
+  while (ne--) {
+    setallocbit(ds->page->header.freebitmap, ds->entno);
+    ds->used++;
+    ds->entno++;
+  }
+  return 0;
+}
+
+
+afs_uint32 Dir_Finalize(struct dir_state *ds)
+{
+  int pages = ds->pageno + 1;
+  
+  if (ds->pageno < 128) ds->dh->allomap[ds->pageno] = ds->used;
+  ds->dh->pagehdr.pgcount = htons(pages);
+  return 0;
+}
+
+
+afs_uint32 Dir_EmitData(struct dir_state *ds, XFILE *X, int dotag)
+{
+  afs_uint32 r, size;
+  int i;
+
+  size = ds->npages * AFS_PAGESIZE;
+  if (dotag && (r = WriteTagInt32(X, VTAG_DATA, size))) return r;
+  for (i = 0; i < ds->npages; i++) {
+    if ((r = xfwrite(X, ds->dirpages[i], AFS_PAGESIZE))) return r;
+  }
+  return 0;
+}
+
+
+afs_uint32 Dir_Free(struct dir_state *ds)
+{
+  int i;
+
+  for (i = 0; i < ds->npages; i++)
+    free(ds->dirpages[i]);
+  free(ds->dirpages);
+  free(ds);
+  return 0;
 }
 
 
index 184d98c..74d49de 100644 (file)
@@ -2,7 +2,7 @@
  * CMUCS AFStools
  * dumpscan - routines for scanning and manipulating AFS volume dumps
  *
- * Copyright (c) 1998 Carnegie Mellon University
+ * Copyright (c) 1998, 2001 Carnegie Mellon University
  * All Rights Reserved.
  * 
  * Permission to use, copy, modify and distribute this software and its
 #define VTAG_DATA        'f'
 
 
-#define AFS_DIR_EPP 64
+#define AFS_DIR_MAGIC    1234
+#define AFS_DIR_EPP        64
+#define AFS_DIR_MAXPAGES  128
+#define AFS_DIR_NHASH     128
 
 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_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_pagehdr pagehdr;
+  char allomap[AFS_DIR_MAXPAGES];
+  afs_uint16 hash[AFS_DIR_NHASH];
+} afs_dir_header;
+
+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_pagehdr header;
+  afs_dir_direntry entry[AFS_DIR_EPP];
 } afs_dir_page;
 
 #endif /* _DUMPFMT_H_ */
index b3e077b..ba1c4dd 100644 (file)
@@ -2,7 +2,7 @@
  * CMUCS AFStools
  * dumpscan - routines for scanning and manipulating AFS volume dumps
  *
- * Copyright (c) 1998 Carnegie Mellon University
+ * Copyright (c) 1998, 2001, 2003 Carnegie Mellon University
  * All Rights Reserved.
  * 
  * Permission to use, copy, modify and distribute this software and its
 #include <afs/nfs.h>
 #include <afs/ihandle.h>
 #include <afs/vnode.h>
-#include <afs/cmd.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 *);
+typedef afs_uint32 (*tag_parser)(XFILE *, unsigned char *, tagged_field *,
+                              afs_uint32, tag_parse_info *, void *, void *);
+typedef struct dir_state dir_state;
 
 /* Error codes used within dumpscan.
  * Any of the routines declared below, or callbacks used by them,
@@ -77,20 +69,20 @@ typedef afs_uint32(*tag_parser) (XFILE *, unsigned char *, tagged_field *,
  * 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;
+  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;
+  u_int64 dumplen;
+  afs_uint32 level;
+  afs_uint32 magic;
+  afs_uint32 cksum;
+  afs_uint32 flags;
 } backup_system_header;
 
 
@@ -100,14 +92,14 @@ typedef struct {
 #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 */
+  u_int64 offset;              /* Where in the input stream 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;
 
 
@@ -138,33 +130,33 @@ typedef struct {
 #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 */
+  u_int64 offset;              /* Where in the input stream 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;
 
 
@@ -179,146 +171,154 @@ typedef struct {
 #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
+#define F_VNODE_SIZE          0x00000800 /* Set if size is present */
+#define F_VNODE_DATA          0x00001000 /* Set if size nonzero and data present */
+#define F_VNODE_PARTIAL       0x00002000 /* Partial vnode continuation (no header) */
+#define F_VNODE_LINK_TARGET   0x00004000 /* Symlink target present */
 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];
+  u_int64 offset;              /* Where in the input stream 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 input stream is the data? */
+  char *link_target;           /* Target of symbolic link */
+  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 */
+  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_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_OCT16     0x28  /* 2 bytes - octal */
+#define DKIND_INT32     0x30  /* 4 bytes - decimal */
+#define DKIND_HEX32     0x31  /* 4 bytes - hex */
+#define DKIND_TIME      0x32  /* 4 bytes - time */
+#define DKIND_OCT32     0x38  /* 4 bytes - octal */
+#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;
+  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;
+  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) */
+  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 */
+  /* 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 Header   */
+  afs_uint32 (*cb_dumphdr)(afs_dump_header *, XFILE *, void *);     /* Dump Header     */
+  afs_uint32 (*cb_volhdr)(afs_vol_header *, XFILE *, void *);       /* Volume Header   */
+  afs_uint32 (*cb_vnode_dir)(afs_vnode *, XFILE *, void *);         /* Directory Vnode */
+  afs_uint32 (*cb_vnode_file)(afs_vnode *, XFILE *, void *);        /* File Vnode      */
+  afs_uint32 (*cb_vnode_link)(afs_vnode *, XFILE *, void *);        /* Symlink Vnode   */
+  afs_uint32 (*cb_vnode_empty)(afs_vnode *, XFILE *, void *);       /* vnode+uniq only */
+  afs_uint32 (*cb_vnode_wierd)(afs_vnode *, XFILE *, void *);       /* Unknown type    */
+  afs_uint32 (*cb_file_data)(afs_vnode *, XFILE *, void *);         /* File Data       */
+  afs_uint32 (*cb_dir_data)(afs_vnode *, XFILE *, void *);          /* Directory Data  */
+  afs_uint32 (*cb_link_data)(afs_vnode *, XFILE *, void *);         /* Symlink Data    */
+
+  /* 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;
+  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 */
+  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 */
+  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;
 
 
@@ -338,17 +338,17 @@ 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);
+extern afs_uint32 WriteTagInt32Pair(XFILE *, unsigned char, afs_uint32, afs_uint32);
+extern afs_uint32 WriteTagString(XFILE *, unsigned char, unsigned char *);
 
 /* parsetag.c - Parse tagged data */
 extern afs_uint32 ParseTaggedData(XFILE *, tagged_field *, unsigned char *,
-                                 tag_parse_info *, void *, void *);
+                           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 *);
+extern afs_uint32 ParseStageHdr(XFILE *, unsigned char *, backup_system_header *);
+extern afs_uint32 ParseStageV20Hdr(XFILE *, unsigned char *, backup_system_header *);
+extern afs_uint32 DumpStageV20Hdr(XFILE *, backup_system_header *);
 
 /* backuphdr.c - Parse and print backup system headers */
 extern void PrintBackupHdr(backup_system_header *);
@@ -360,10 +360,16 @@ extern afs_uint32 ParseVolumeHeader(XFILE *, dump_parser *);
 extern afs_uint32 ParseVNode(XFILE *, dump_parser *);
 
 
-/* directory.c - Directory parsing and lookup */
+/* directory.c - Directory parsing, lookup, and generation */
 extern afs_uint32 ParseDirectory(XFILE *, dump_parser *, afs_uint32, int);
-extern afs_uint32 DirectoryLookup(XFILE *, dump_parser *, afs_uint32, char **,
-                                 afs_uint32 *, afs_uint32 *);
+extern afs_uint32 DirectoryLookup(XFILE *, dump_parser *, afs_uint32,
+                           char **, afs_uint32 *, afs_uint32 *);
+extern afs_uint32 Dir_Init(dir_state **);
+extern afs_uint32 Dir_AddEntry(dir_state *, char *, afs_uint32, afs_uint32);
+extern afs_uint32 Dir_Finalize(dir_state *);
+extern afs_uint32 Dir_EmitData(dir_state *, XFILE *, int);
+extern afs_uint32 Dir_Free(dir_state *ds);
+
 
 /* dump.c - Dump parts of a volume dump */
 extern afs_uint32 DumpDumpHeader(XFILE *, afs_dump_header *);
@@ -371,12 +377,13 @@ 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);
+extern afs_uint32 DumpVNodeData(XFILE *OX, char *buf, afs_uint32 size);
+extern afs_uint32 DumpDumpEnd(XFILE *OX);
 
 /* 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);
+extern afs_uint32 Path_Build(XFILE *, path_hashinfo *, afs_uint32, char **, int);
 
 #endif