There is a utility (restorevol) in OpenAFS to unpack a vos dump image
into a file tree, but I wanted something to just give me a listing of
what files/directories were in the volume, so I hacked up the attached,
which adds "tocvol" to the OpenAFS tree to do a table of contents of a
vos dump image.
Why did I want this? I'm working on integrating AFS backups into
Amanda, and I wanted a table of contents of the volume dump so
I can have file level backup indexing...
Anyway, it's mostly restorevol with most of its guts replaced with
a (wimpy, horribly inefficient, but simple) vnode graph.
*** src/volser/Makefile.in.orig Thu May 9 11:43:41 2002
--- src/volser/Makefile.in Thu May 9 11:42:21 2002
***************
*** 8,14 ****
srcdir=@srcdir@
include @TOP_OBJDIR@/src/config/Makefile.config
-
CFLAGS=-I. -I${srcdir} ${DBG} ${OPTMZ} -I${TOP_OBJDIR}/src/config -I${TOP_INCDIR} ${XCFLAGS}
LDFLAGS=${DBG} ${OPTMZ} ${XLDFLAGS}
--- 8,13 ----
***************
*** 48,54 ****
SOBJS=volmain.o volprocs.o physio.o common.o voltrans.o volerr.o \
volint.cs.o dumpstuff.o volint.ss.o volint.xdr.o
! all: volserver vos restorevol \
${TOP_INCDIR}/afs/volser.h \
${TOP_INCDIR}/afs/volint.h \
${TOP_LIBDIR}/libvolser.a
--- 47,53 ----
SOBJS=volmain.o volprocs.o physio.o common.o voltrans.o volerr.o \
volint.cs.o dumpstuff.o volint.ss.o volint.xdr.o
! all: volserver vos restorevol tocvol \
${TOP_INCDIR}/afs/volser.h \
${TOP_INCDIR}/afs/volint.h \
${TOP_LIBDIR}/libvolser.a
***************
*** 57,62 ****
--- 56,65 ----
${CC} ${CFLAGS} -o restorevol ${srcdir}/restorevol.c \
${TOP_LIBDIR}/libcmd.a ${TOP_LIBDIR}/util.a
+ tocvol: tocvol.c
+ ${CC} ${CFLAGS} -o tocvol ${srcdir}/tocvol.c \
+ ${TOP_LIBDIR}/libcmd.a ${TOP_LIBDIR}/util.a
+
vos: vos.o ${VSOBJS} libvolser.a ${LIBS}
${CC} ${LDFLAGS} -o vos vos.o $(VSOBJS) libvolser.a ${LIBS} ${XLIBS}
***************
*** 111,116 ****
--- 114,120 ----
#
install: \
${DESTDIR}${sbindir}/restorevol \
+ ${DESTDIR}${sbindir}/tocvol \
${DESTDIR}${includedir}/afs/volser.h \
${DESTDIR}${includedir}/afs/volint.h \
${DESTDIR}${sbindir}/vos \
***************
*** 126,131 ****
--- 130,138 ----
${INSTALL} $? $@
${DEST}/etc/restorevol: restorevol
+ ${INSTALL} $? $@
+
+ ${DEST}/etc/tocvol: tocvol
${INSTALL} $? $@
${DEST}/etc/vos ${DEST}/root.server/usr/afs/bin/vos: vos
*** src/volser/tocvol.c.orig Thu May 9 13:54:52 2002
--- src/volser/tocvol.c Thu May 9 13:58:11 2002
***************
*** 0 ****
--- 1,715 ----
+ /*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License. For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+ /*
+ * Read a vos dump and print a table of contents
+ *
+ * tocvol [-file <dump file>]
+ * [-dir <restore dir>]
+ * [-extension <name extension>]
+ * [-mountpoint <mount point root>]
+ * [-umask <mode mask>]
+ * Based on restorevol, but with an internal index instead of symlinks.
+ *
+ */
+
+ #include <afsconfig.h>
+ #include <afs/param.h>
+
+ RCSID("$Header: /cvs/openafs/src/volser/restorevol.c,v 1.6 2001/08/08 00:04:26 shadow Exp $");
+
+ #include <afs/afsint.h>
+ #include <afs/nfs.h>
+ #include <lock.h>
+ #include <afs/ihandle.h>
+ #include <afs/vnode.h>
+ #include <afs/volume.h>
+ #include "volint.h"
+ #include "dump.h"
+ #include <afs/cmd.h>
+
+ #include <sys/param.h>
+ #include <sys/types.h>
+ #include <sys/uio.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include <netinet/in.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <dirent.h>
+
+ struct vmapnode {
+ char *name;
+ int parent;
+ char printed;
+ char isset;
+ } *vnodemap = 0;
+ int vnodemaplen = 0;
+ int vnodemapmax = 0;
+
+ growvnodemap(int n) {
+ int i;
+ if (n > vnodemapmax) {
+ vnodemapmax = 2 * n;
+ /* fprintf(stderr,"growing map to %d\n", vnodemapmax); */
+ /* fflush(stderr); */
+ vnodemap = realloc(vnodemap, sizeof(struct vmapnode) * vnodemapmax );
+ for ( i = vnodemaplen+1; i < vnodemapmax; i++ )
+ vnodemap[i].isset = 0;
+ }
+ if (n > vnodemaplen)
+ vnodemaplen = n;
+ }
+
+ void
+ setvnode(int n, char *name, int parent) {
+ /* fprintf(stderr, "setting map for %d to %s parent %d\n", n, name, parent); */
+ /* fflush(stderr); */
+ growvnodemap(n);
+ vnodemap[n].name = (char *)strdup(name);
+ vnodemap[n].parent = parent;
+ vnodemap[n].isset = 1;
+ }
+
+ int
+ getvnodename(int n, char *p, int max) {
+ int len, nlen;
+ /* printf("getvnodename(%d)\n", n); */
+ if( vnodemap[n].isset ) {
+ nlen = strlen(vnodemap[n].name);
+ if (n != 1 && vnodemap[n].parent != n ) {
+ len = getvnodename(vnodemap[n].parent, p, max);
+ } else {
+ /* root vnode */
+ p[0] = '.';
+ p[1] = 0;
+ return 1;
+ }
+ if (len != 0 && len + nlen + 1 < max) {
+ strcat(p+len, "/");
+ strcat(p+len, vnodemap[n].name);
+ return len + nlen + 1;
+ }
+ } else {
+ /* printf("notset\n") */;
+ }
+ return 0;
+ }
+
+ int printvnode(int n) {
+ char buf[1024];
+ int len;
+ /* printf ("printvnode %d\n", n); */
+ if (!vnodemap[n].printed && vnodemap[n].isset) {
+ len = getvnodename(n, buf, 1024);
+ if( len != 0 ) {
+ printf("%s\n", buf);
+ vnodemap[n].printed = 1;
+ }
+ }
+ }
+
+ int inc_dump = 0;
+
+ FILE *dumpfile;
+
+ afs_int32 readvalue(size)
+ {
+ afs_int32 value, s;
+ int code;
+ char *ptr;
+
+ value = 0;
+ ptr = (char *) &value;
+
+ s = sizeof(value) - size;
+ if (size < 0) {
+ fprintf (stderr, "Too much data in afs_int32\n");
+ return 0;
+ }
+
+ code = fread (&ptr[s], 1, size, dumpfile);
+ if (code != size) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
+
+ return (value);
+ }
+
+ char readchar()
+ {
+ char value;
+ int code;
+ char *ptr;
+
+ value = '\0';
+ code = fread (&value, 1, 1, dumpfile);
+ if (code != 1) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
+
+ return (value);
+ }
+
+ #define BUFSIZE 16384
+ char buf[BUFSIZE];
+
+ char readdata(buffer, size)
+ char *buffer;
+ afs_int32 size;
+ {
+ int code;
+ afs_int32 s;
+
+ if (!buffer) {
+ while (size > 0) {
+ s = ((size > BUFSIZE) ? BUFSIZE : size);
+ code = fread(buf, 1, s, dumpfile);
+ if (code != s) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
+ size -= s;
+ }
+ }
+ else {
+ code = fread (buffer, 1, size, dumpfile);
+ if (code != size) {
+ if (code < 0)
+ fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
+ else
+ fprintf (stderr, "Read %d bytes out of %d\n", code, size);
+ }
+ if ((code >= 0) && (code < BUFSIZE))
+ buffer[size] = 0; /* Add null char at end */
+ }
+ }
+
+ afs_int32 ReadDumpHeader(dh)
+ struct DumpHeader *dh; /* Defined in dump.h */
+ {
+ int code, i, done;
+ char tag, c;
+ afs_int32 magic;
+
+ /* memset(&dh, 0, sizeof(dh)); */
+
+ magic = ntohl(readvalue(4));
+ dh->version = ntohl(readvalue(4));
+
+ done = 0;
+ while (!done) {
+ tag = readchar();
+ switch (tag) {
+ case 'v':
+ dh->volumeId = ntohl(readvalue(4));
+ break;
+
+ case 'n':
+ for (i=0, c='a'; c!='\0'; i++) {
+ dh->volumeName[i] = c = readchar();
+ }
+ dh->volumeName[i] = c;
+ break;
+
+ case 't':
+ dh->nDumpTimes = ntohl(readvalue(2)) >> 1;
+ for (i=0; i<dh->nDumpTimes; i++) {
+ dh->dumpTimes[i].from = ntohl(readvalue(4));
+ dh->dumpTimes[i].to = ntohl(readvalue(4));
+ }
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ return((afs_int32)tag);
+ }
+
+ struct volumeHeader
+ {
+ afs_int32 volumeId;
+ char volumeName[100];
+ afs_int32 volType;
+ afs_int32 uniquifier;
+ afs_int32 parentVol;
+ afs_int32 cloneId;
+ afs_int32 maxQuota;
+ afs_int32 minQuota;
+ afs_int32 diskUsed;
+ afs_int32 fileCount;
+ afs_int32 accountNumber;
+ afs_int32 owner;
+ afs_int32 creationDate;
+ afs_int32 accessDate;
+ afs_int32 updateDate;
+ afs_int32 expirationDate;
+ afs_int32 backupDate;
+ afs_int32 dayUseDate;
+ afs_int32 dayUse;
+ afs_int32 weekCount;
+ afs_int32 weekUse[100]; /* weekCount of these */
+ char motd[1024];
+ int inService;
+ int blessed;
+ char message[1024];
+ };
+
+ afs_int32 ReadVolumeHeader(count)
+ afs_int32 count;
+ {
+ struct volumeHeader vh;
+ int code, i, done, entries;
+ char tag, c;
+
+ /* memset(&vh, 0, sizeof(vh)); */
+
+ done = 0;
+ while (!done) {
+ tag = readchar();
+ switch (tag) {
+ case 'i':
+ vh.volumeId = ntohl(readvalue(4));
+ break;
+
+ case 'v':
+ ntohl(readvalue(4)); /* version stamp - ignore */
+ break;
+
+ case 'n':
+ for (i=0, c='a'; c!='\0'; i++) {
+ vh.volumeName[i] = c = readchar();
+ }
+ vh.volumeName[i] = c;
+ break;
+
+ case 's':
+ vh.inService = ntohl(readvalue(1));
+ break;
+
+ case 'b':
+ vh.blessed = ntohl(readvalue(1));
+ break;
+
+ case 'u':
+ vh.uniquifier = ntohl(readvalue(4));
+ break;
+
+ case 't':
+ vh.volType = ntohl(readvalue(1));
+ break;
+
+ case 'p':
+ vh.parentVol = ntohl(readvalue(4));
+ break;
+
+ case 'c':
+ vh.cloneId = ntohl(readvalue(4));
+ break;
+
+ case 'q':
+ vh.maxQuota = ntohl(readvalue(4));
+ break;
+
+ case 'm':
+ vh.minQuota = ntohl(readvalue(4));
+ break;
+
+ case 'd':
+ vh.diskUsed = ntohl(readvalue(4));
+ break;
+
+ case 'f':
+ vh.fileCount = ntohl(readvalue(4));
+ break;
+
+ case 'a':
+ vh.accountNumber = ntohl(readvalue(4));
+ break;
+
+ case 'o':
+ vh.owner = ntohl(readvalue(4));
+ break;
+
+ case 'C':
+ vh.creationDate = ntohl(readvalue(4));
+ break;
+
+ case 'A':
+ vh.accessDate = ntohl(readvalue(4));
+ break;
+
+ case 'U':
+ vh.updateDate = ntohl(readvalue(4));
+ break;
+
+ case 'E':
+ vh.expirationDate = ntohl(readvalue(4));
+ break;
+
+ case 'B':
+ vh.backupDate = ntohl(readvalue(4));
+ break;
+
+ case 'O':
+ for (i=0, c='a'; c!='\0'; i++) {
+ vh.message[i] = c = readchar();
+ }
+ vh.volumeName[i] = c;
+ break;
+
+ case 'W':
+ vh.weekCount = ntohl(readvalue(2));
+ for (i=0; i<vh.weekCount; i++) {
+ vh.weekUse[i] = ntohl(readvalue(4));
+ }
+ break;
+
+ case 'M':
+ for (i=0, c='a'; c!='\0'; i++) {
+ vh.motd[i] = c = readchar();
+ }
+ break;
+
+ case 'D':
+ vh.dayUseDate = ntohl(readvalue(4));
+ break;
+
+ case 'Z':
+ vh.dayUse = ntohl(readvalue(4));
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ return((afs_int32)tag);
+ }
+
+ struct vNode
+ {
+ afs_int32 vnode;
+ afs_int32 uniquifier;
+ afs_int32 type;
+ afs_int32 linkCount;
+ afs_int32 dataVersion;
+ afs_int32 unixModTime;
+ afs_int32 servModTime;
+ afs_int32 author;
+ afs_int32 owner;
+ afs_int32 group;
+ afs_int32 modebits;
+ afs_int32 parent;
+ char acl[192];
+ #ifdef notdef
+ struct acl_accessList
+ {
+ int size; /*size of this access list in bytes, including MySize itself*/
+ int version; /*to deal with upward compatibility ; <= ACL_ACLVERSION*/
+ int total;
+ int positive; /* number of positive entries */
+ int negative; /* number of minus entries */
+ struct acl_accessEntry
+ {
+ int id; /*internally-used ID of user or group*/
+ int rights; /*mask*/
+ } entries[100];
+ } acl;
+ #endif
+ afs_int32 dataSize;
+ };
+
+ #define MAXNAMELEN 256
+
+ afs_int32 ReadVNode(count, tocflag)
+ afs_int32 count;
+ int tocflag;
+ {
+ struct vNode vn;
+ int code, i, done, entries;
+ char tag, c;
+ char dirname[MAXNAMELEN], linkname[MAXNAMELEN], lname[MAXNAMELEN];
+ char parentdir[MAXNAMELEN], vflink[MAXNAMELEN];
+ char filename[MAXNAMELEN], fname[MAXNAMELEN];
+ int len;
+ afs_int32 vnode;
+ afs_int32 mode=0;
+
+ /* memset(&vn, 0, sizeof(vn)); */
+ vn.dataSize = 0;
+ vn.vnode = 0;
+ vn.parent = 0;
+ vn.type = 0;
+
+ vn.vnode = ntohl(readvalue(4));
+ vn.uniquifier = ntohl(readvalue(4));
+
+ done = 0;
+ while (!done) {
+ tag = readchar();
+ switch (tag) {
+ case 't':
+ vn.type = ntohl(readvalue(1));
+ break;
+
+ case 'l':
+ vn.linkCount = ntohl(readvalue(2));
+ break;
+
+ case 'v':
+ vn.dataVersion = ntohl(readvalue(4));
+ break;
+
+ case 'm':
+ vn.unixModTime = ntohl(readvalue(4));
+ break;
+
+ case 's':
+ vn.servModTime = ntohl(readvalue(4));
+ break;
+
+ case 'a':
+ vn.author = ntohl(readvalue(4));
+ break;
+
+ case 'o':
+ vn.owner = ntohl(readvalue(4));
+ break;
+
+ case 'g':
+ vn.group = ntohl(readvalue(4));
+ break;
+
+ case 'b':
+ vn.modebits = ntohl(readvalue(2));
+ break;
+
+ case 'p':
+ vn.parent = ntohl(readvalue(4));
+ break;
+
+ case 'A':
+ readdata(vn.acl, 192); /* Skip ACL data */
+ break;
+
+ case 'f':
+ vn.dataSize = ntohl(readvalue(4));
+
+ /* parentdir is the name of this dir's vnode-file-link
+ * or this file's parent vnode-file-link.
+ * "./AFSDir-<#>". It's a symbolic link to its real dir.
+ * The parent dir and symbolic link to it must exist.
+ */
+ vnode = ((vn.type == 2) ? vn.vnode : vn.parent);
+
+ if (vn.type == 2) {
+ /*ITSADIR*/
+ /* We read the directory entries. If the entry is a
+ * directory, the subdir is created and the root dir
+ * will contain a link to it. If its a file, we only
+ * create a symlink in the dir to the file name.
+ */
+ char *buffer;
+ unsigned short j;
+ afs_int32 this_vn;
+ char *this_name;
+
+ struct DirEntry
+ {
+ char flag;
+ char length;
+ unsigned short next;
+ struct MKFid
+ {
+ afs_int32 vnode;
+ afs_int32 vunique;
+ } fid;
+ char name[20];
+ };
+
+ struct Pageheader
+ {
+ unsigned short pgcount;
+ unsigned short tag;
+ char freecount;
+ char freebitmap[8];
+ char padding[19];
+ };
+
+ struct DirHeader
+ {
+ struct Pageheader header;
+ char alloMap[128];
+ unsigned short hashTable[128];
+ };
+
+ struct Page0
+ {
+ struct DirHeader header;
+ struct DirEntry entry[1];
+ } *page0;
+
+
+ buffer = (char *)0;
+ buffer = (char *)malloc(vn.dataSize);
+
+ readdata(buffer, vn.dataSize);
+ page0 = (struct Page0 *)buffer;
+
+ /* Step through each bucket in the hash table, i,
+ * and follow each element in the hash chain, j.
+ * This gives us each entry of the dir.
+ */
+ for (i=0; i<128; i++) {
+ for (j=ntohs(page0->header.hashTable[i]); j;
+ j=ntohs(page0->entry[j].next)) {
+ j -= 13;
+ this_vn = ntohl(page0->entry[j].fid.vnode);
+ this_name = page0->entry[j].name;
+
+ if ((strcmp(this_name,"." ) == 0) ||
+ (strcmp(this_name,"..") == 0))
+ continue; /* Skip these */
+
+ setvnode(this_vn,this_name, vnode);
+ printvnode(this_vn);
+ }
+ }
+ free(buffer);
+ } /*ITSADIR*/
+
+ else if (vn.type == 1) {
+ /*ITSAFILE*/
+ /* A file vnode. So create it into the desired directory. A
+ * link should exist in the directory naming the file.
+ */
+ int fid;
+ int lfile;
+ afs_int32 size, s;
+
+ size = vn.dataSize;
+ while (size > 0) {
+ s = ((size > BUFSIZE) ? BUFSIZE : size);
+ code = fread(buf, 1, s, dumpfile);
+ if (code > 0) {
+ size -= code;
+ }
+ if (code != s) {
+ if (code < 0)
+ fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
+ else
+ fprintf (stderr, "Read %d bytes out of %d\n",
+ (vn.dataSize - size), vn.dataSize);
+ break;
+ }
+ }
+ if (size != 0) {
+ fprintf(stderr, " File %s (%s) is incomplete\n", filename, fname);
+ }
+
+ } /*ITSAFILE*/
+
+ else if (vn.type == 3) {
+ /*ITSASYMLINK*/
+ /* A symlink vnode. So read it into the desired directory. This could
+ * also be a mount point. If the volume is being restored to AFS, this
+ * will become a mountpoint. If not, it becomes a symlink to no-where.
+ */
+ int fid;
+ afs_int32 size, s;
+
+ /* Check if its vnode-file-link exists and create pathname
+ * of the symbolic link. If it doesn't exist,
+ * then the link will be an orphaned link.
+ */
+ /* Read the link in */
+ readdata(buf, vn.dataSize);
+ } /*ITSASYMLINK*/
+ else {
+ fprintf (stderr, "Unknown Vnode block\n");
+ }
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ }
+ if (vn.type == 0)
+ inc_dump = 1;
+
+ return((afs_int32)tag);
+ }
+
+ WorkerBee(as, arock)
+ struct cmd_syndesc *as;
+ char *arock;
+ {
+ int code=0, c, len;
+ afs_int32 type, count, vcount;
+ DIR *dirP, *dirQ;
+ struct dirent *dirE, *dirF;
+ char fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN], mname[MAXNAMELEN];
+ char thisdir[MAXPATHLEN], *t;
+ struct DumpHeader dh; /* Defined in dump.h */
+ #if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */
+ extern char *getwd();
+ #define getcwd(x,y) getwd(x)
+ #endif
+
+ if (as->parms[0].items) { /* -file <dumpfile> */
+ dumpfile = fopen(as->parms[0].items->data, "r");
+ if (!dumpfile) {
+ fprintf(stderr, "Cannot open '%s'. Code = %d\n",
+ as->parms[0].items->data, errno);
+ goto cleanup;
+ }
+ } else {
+ dumpfile = (FILE *)stdin; /* use stdin */
+ }
+
+ /* Read the dump header. From it we get the volume name */
+ type = ntohl(readvalue(1));
+ if (type != 1) {
+ fprintf(stderr, "Expected DumpHeader\n");
+ code = -1;
+ goto cleanup;
+ }
+ type = ReadDumpHeader(&dh);
+
+ fprintf(stderr, "Table of Contents of volume dump of '%s':\n",
+ dh.volumeName);
+
+ for (count=1; type==2; count++) {
+ type = ReadVolumeHeader(count);
+ for (vcount=1; type==3; vcount++)
+ type = ReadVNode(vcount, as->parms[5].items);
+ }
+ for (count=1; count < vnodemaplen ; count++ ) {
+ printvnode(count);
+ }
+
+ cleanup:
+ return(code);
+ }
+
+ main(argc, argv)
+ int argc;
+ char **argv;
+ {
+ struct cmd_syndesc *ts;
+ struct cmd_item *ti;
+
+ setlinebuf(stdout);
+ setvnode(1,"/",1);
+ printvnode(1);
+
+ ts=cmd_CreateSyntax((char *)0, WorkerBee, (char *) 0, "vldb check");
+ cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
+
+ return cmd_Dispatch(argc, argv);
+ }