[OpenAFS] amanda-afs list

Andrej Filipcic andrej.filipcic@ijs.si
Mon, 29 May 2006 09:24:04 +0200


--Boundary-00=_VGqeE/RZiwo4tQ3
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


Hi, 

I have troubles with amanda-afs indexer. There is a program "list", which 
dumps a contents of an afs volume dump. For most of volumes it works ok, but 
for some large volumes (10-20GB), it loops inside vnode linked list at 
t->next and never returns.

(line 114)
struct vnodeData *get(Node **List)
{
   Node *L = *List;
   Node *t, *t1;
   struct vnodeData *vdata;

   if(L)
   {
      t = L;
      t1 = NULL;
      while(t->next)
      {
         t1 = t;
         t = t->next;
      }

Could someone take a look at where the problem might be? I am using 
openafs-1.4.1. There is no problems with vos dump of the volume and vos 
restore.

Thanks,
Andrej

-- 
_____________________________________________________________
   doc. dr. Andrej Filipcic,   E-mail: Andrej.Filipcic@ijs.si
   Department of Experimental High Energy Physics - F9
   Jozef Stefan Institute, Jamova 39, P.o.Box 3000
   SI-1001 Ljubljana, Slovenia
   Tel.: +386-1-477-3674    Fax: +386-1-477-3166
-------------------------------------------------------------

--Boundary-00=_VGqeE/RZiwo4tQ3
Content-Type: text/x-csrc;
  charset="us-ascii";
  name="list.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="list.c"

#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>
#include <afs/ihandle.h>
#include <afs/vnode.h>
#include <afs/volume.h>

#include <afs/dir.h>

#include "config.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
 */

static int verbose = 0;
static int numNoDirData = 0;
static int termsize = 0;
int Testing = 0;

/*
 * 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 */
      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
 */


typedef struct Node_val Node;

struct Node_val {
      struct vnodeData *data;
      Node *next;
};


void insert(Node **List, struct vnodeData *vdata)
{
   Node *t;
   
   t = (Node *) malloc(sizeof(Node));
   t->data = vdata;
   t->next = *List;
   *List = t;

}

struct vnodeData *get(Node **List)
{
   Node *L = *List;
   Node *t, *t1;
   struct vnodeData *vdata;
   
   if(L)
   {
      t = L;
      t1 = NULL;
      while(t->next)
      {
	 t1 = t;
	 t = t->next;
      }
      if(t1)
      {
	 t1->next = NULL;
      }
      else
      {
	 *List = NULL;
      }
      vdata = t->data;
      free(t);
      return vdata;
   }
   return NULL;
}


Node *SmList = NULL;
Node *LgList = NULL;

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 *GetVnode(unsigned int);
static int CompareVnode(const void *, const void *);
static void DirListInternal(struct vnodeData *, VolumeDiskData *, char *);
static int CompareDirEntry(const void *, const void *);
static void ResetDirCursor(struct DirCursor *, struct vnodeData *);
static struct DirEntry *ReadNextDir(struct DirCursor *, struct vnodeData *);
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;
   char tag;
   struct winsize win;
   FILE *f;
   struct vnodeData *rootvdata;

   /*
    * Sigh, this is dumb, but we need the terminal window size
    * to do intelligent things with "ls" later on.
    */

   while ((c = getopt(argc, argv, "hdifr:t:v")) != EOF)
      switch (c) {
	 case 'h':
	    fprintf(stderr, "%s\nRun this utility as follows:\n\t%s\n", AMANDA_AFS_VERSION, argv[0]);
	    fprintf(stderr, "It will take its standard input, and, provided it is a vos dump, will print the contents.\n");
	    return 0;
	    break;

	 case 't':
	    fprintf(stderr, "-t not supported in non-MRAFS "
		    "dumptool.\n");
	    errflg++;
	    break;

	 case 'r':
	    fprintf(stderr, "-r not supported in non-MRAFS "
		    "dumptool.\n");
	    errflg++;
	    break;
	 case 'd':
	    fprintf(stderr, "-d not supported in non-MRAFS "
		    "dumptool.\n");
	    errflg++;
	    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"
	"filename\n",
	argv[0]);
	exit(1);
	}
*/
   /*
    * Try opening the dump file
    */
   f = stdin;
   printf("/\n");
      
   while(!feof(f))
   {
      if ((tag = getc(f)) == D_DUMPHEADER)
      {
	 ungetc(tag, f);
      }
      else
      {
	 break;
      }
      
      if (ReadDumpHeader(f, &dheader)) {
	 fprintf(stderr, "Failed to read dump header!\n");
	 exit(1);
      }
      
      if (verbose)
	 fprintf(stderr, "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.
       */
      
      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
       */
      
      if ((rootvdata = GetVnode(1)) == NULL) {
	 fprintf(stderr, "Can't get vnode data for root "
		 "vnode!  Aborting\n");
	 exit(1);
      }

      printf("/%s/\n", vol.name);

      p = (char *) malloc((strlen(vol.name)+2)*sizeof(char));
      sprintf(p, "/%s", vol.name);
      
      DirListInternal(rootvdata, &vol, p);

      if(p)
	 free(p);
      
      /* Now, to avoid assorted memory leaks, destroy the {Small,Large}VnodeIndex */
      /* in two for loops cycle through the Indeces and destroy their entries. */
      for(i = 0; i<numLargeVnodes; i++)
      {
	 /* destroy LargeVnodeIndex[i] */
	 /* LargeVnodeIndex[i] is vnodeData, vnodeData has vnode. */
	 free(LargeVnodeIndex[i]->vnode);
	 LargeVnodeIndex[i]->vnode = NULL;
	 free(LargeVnodeIndex[i]->filedata);
	 LargeVnodeIndex[i]->filedata = NULL;
	 free(LargeVnodeIndex[i]);
	 LargeVnodeIndex[i] = NULL;
      }
      for(i = 0; i<numSmallVnodes; i++)
      {
	 /* destroy SmallVnodeIndex[i] */
	 /* SmallVnodeIndex[i] is vnodeData, vnodeData has vnode. */
	 free(SmallVnodeIndex[i]->vnode);
	 SmallVnodeIndex[i]->vnode = NULL;
	 free(SmallVnodeIndex[i]);
	 SmallVnodeIndex[i] = NULL;
      }
      /* then destroy the indices themselves */
      if(SmallVnodeIndex)
	 free(SmallVnodeIndex);
      if(LargeVnodeIndex)
	 free(LargeVnodeIndex);
      
   }
}
/*
 * 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, (uint32_t *)&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, (uint32_t *)&vol->id))
	       return -1;
	    break;
	 case 'v':
	    if (ReadInt32(f, (uint32_t *)&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, (uint32_t *)&vol->uniquifier))
	       return -1;
	    break;
	 case 't':
	    vol->type = getc(f);
	    break;
	 case 'p':
	    if (ReadInt32(f, (uint32_t *)&vol->parentId))
	       return -1;
	    break;
	 case 'c':
	    if (ReadInt32(f, (uint32_t *)&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, (uint32_t *)&vol->accountNumber))
	       return -1;
	    break;
	 case 'o':
	    if (ReadInt32(f, (uint32_t *)&vol->owner))
	       return -1;
	    break;
	 case 'C':
	    if (ReadInt32(f, (uint32_t *)&vol->creationDate))
	       return -1;
	    break;
	 case 'A':
	    if (ReadInt32(f, (uint32_t *)&vol->accessDate))
	       return -1;
	    break;
	 case 'U':
	    if (ReadInt32(f, (uint32_t *)&vol->updateDate))
	       return -1;
	    break;
	 case 'E':
	    if (ReadInt32(f, (uint32_t *)&vol->expirationDate))
	       return -1;
	    break;
	 case 'B':
	    if (ReadInt32(f, (uint32_t *)&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, (uint32_t *)&data))
		  return -1;
	       if (i < sizeof(vol->weekUse) /
		   sizeof(vol->weekUse[0]))
		  vol->weekUse[i] = data;
	    }
	    break;
	 }
	 case 'D':
	    if (ReadInt32(f, (uint32_t *)&vol->dayUseDate))
	       return -1;
	    break;
	 case 'Z':
	    if (ReadInt32(f, (uint32_t *) &vol->dayUse))
	       return -1;
	    break;
	 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;
   struct VnodeDiskObject *nvnode;
   long offset, oldoffset;
   struct vnodeData *vdata;
   unsigned int length;
   char *iobuf;
   int i;

   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, (uint32_t *)&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, (uint32_t *)&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, (uint32_t *)&vnode->author))
	       {
		  fprintf(stderr, "failed int32 for 'a'\n");
		  return -1;
	       }
	       break;
	    case 'o':
	       if (ReadInt32(f, (uint32_t *)&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, (uint32_t *)&vnode->parent))
	       {
		  fprintf(stderr, "failed int32 for 'p'\n");
		  return -1;
	       }
	       break;
	    case 'S':
	       if (ReadInt32(f, (uint32_t *)&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;
	    case 'f':
	       if (verbose > 1 && ! sizescan)
		  printf("We have file data!\n");
	       if (ReadInt32(f, (uint32_t *)&length))
	       {
		  fprintf(stderr, "failed int32 for 'f'\n");
		  return -1;
	       }
	       vnode->length = length;
/* stuff */

	       if (vnode->type == -1)
		  break;
			      
	       if (vnode->type == vDirectory)
		  numDirVnodes++;
	       else
		  numFileVnodes++;

	       nvnode = (struct VnodeDiskObject *) malloc(sizeof(struct VnodeDiskObject));
			      
	       if (!nvnode) {
		  if (verbose)
		     fprintf(stderr, "Unable to allocate space for vnode\n");
		  return -1;
	       }
	       memcpy((void *) nvnode, (void *) vnode, sizeof(struct VnodeDiskObject));
			      
	       vdata = (struct vnodeData *) malloc(sizeof(struct vnodeData));
			      
	       vdata->vnode = nvnode;
	       vdata->vnodeNumber = vnodeNumber;
	       vdata->filedata = NULL;
	       vdata->datalength = length;
			      
	       if (vnodeNumber & 1) {
		  insert(&LgList, vdata);
	       } else {
		  if(vdata == NULL) printf("inserting NULL!\n");
		  insert(&SmList, vdata);
	       }
			      
	       if (vdata == NULL) {
		  if (verbose)
		     fprintf(stderr, "Failed to insert "
			     "vnode into hash table");
		  return -1;
	       }
	       
				/*
				 * Save directory data, since we'll need it later.
				 */
	       
	       if (vnode->type == vDirectory && length) {
				 
		  vdata->filedata = (char *) malloc(length*sizeof(char));
				 
		  if (!vdata->filedata) {
		     if (verbose)
			fprintf(stderr, "Unable to "
				"allocate space for "
				"file data (%d)\n",
				length);
		     return -1;
		  }
				 
		  if (fread(vdata->filedata, length, 1, f) != 1) {
		     if (verbose)
			fprintf(stderr, "Unable to "
				"read in file data!\n");
		     return -1;
		  }
				 
	       } else if (vnode->type == vDirectory) {
				/*
				 * Warn the user we may not have all directory
				 * vnodes
				 */
		  numNoDirData++;
	       } else if (length) {
		  if(fileseek(f, length, SEEK_CUR) == -1)
		  {
		     fprintf(stderr, "Error: could not seek the input stream!\n");
		     break;
		  }
	       }

/* end stuff */
			
	       break;
	    default:
	       if (verbose)
		  fprintf(stderr, "Unknown dump tag \"%c\"\n",
			  tag);
	       return -1;
	 }
   }
	
	
   ungetc(tag, f);
	
   if (!sizescan) {
      numLargeVnodes = numDirVnodes;
      numSmallVnodes = numFileVnodes;

      /* if there are directories, allocate structure for them, if not, set structure to NULL */
      LargeVnodeIndex = numLargeVnodes ? (struct vnodeData **) malloc(numLargeVnodes*sizeof(struct vnodeData)) : NULL;
      /* if there are files, allocate structure for them, if not, set structure to NULL */
      SmallVnodeIndex = numSmallVnodes ? (struct vnodeData **) malloc(numSmallVnodes*sizeof(struct vnodeData)) : NULL;

      /* check, whether memory was allocated */
      if ((numLargeVnodes && LargeVnodeIndex == NULL)|| (numSmallVnodes && SmallVnodeIndex == NULL)) {
	 if (verbose)
	    fprintf(stderr, "Unable to allocate space "
		    "for vnode tables\n");
	 return -1;
      }
      /* copy from the {Lg,Sm}Lists into the {Large,Small}VnodeIndeces */
	   
      for(i = 0; i<numLargeVnodes; i++)
      {
	 LargeVnodeIndex[i] = get(&LgList);
      }
      for(i = 0; i<numSmallVnodes; i++)
      {
	      
	 SmallVnodeIndex[i] = get(&SmList);
	 if (SmallVnodeIndex[i] == NULL) printf("D'oh!\n");
      }
   }
	
   if (verbose)
      fprintf(stderr,"%s vnode scan completed\n",
	      sizescan ? "Primary" : "Secondary");

   return 0;
}

/*
 * Function that does the REAL work in terms of directory listing
 */

static void
DirListInternal(struct vnodeData *vdata, VolumeDiskData *vol, char *path)
{
   struct DirEntry *ep, **eplist = NULL, **eprecurse = NULL;
   struct DirCursor cursor;
   struct vnodeData *lvdata;
   
   int i, j, numentries = 0;
   char s;
   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.
       */
      
      eplist = realloc(eplist, sizeof(struct DirEntry *) * ++numentries);
      eplist[numentries - 1] = ep;
      
      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;
      }
   }
   
/*   qsort((void *) eplist, numentries, sizeof(struct DirEntry *), CompareDirEntry);
   
   if (eprecurse)
   {
      qsort((void *) eprecurse, numrecurse, sizeof(struct DirEntry *), CompareDirEntry);
   } 
*/
   for (i = 0; i < numentries; i++)
   {
      if(strcmp(eplist[i]->name, ".") != 0 && strcmp(eplist[i]->name, "..") != 0)
      {
	 if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
	 {
	    printf("%s\n", eplist[i]->name);
	 }
	 else
	 {
	    if(lvdata->vnode->type == vDirectory)
	       if (path)
		  printf("%s/%s/\n", path, eplist[i]->name);
	       else
		  printf("%s/\n", eplist[i]->name);
	    else
	       if (path)
		  printf("%s/%s\n", path, eplist[i]->name);
	       else
		  printf("%s\n", eplist[i]->name);
	 }
      }
   }
   free(eplist);
   
   if (eprecurse)
   {
      char *lpath;
      lpath = NULL;
      for (i = 0; i < numrecurse; i++)
      {

/*	 if (verbose)
	 {
	    printf("\n%s:\n", eprecurse[i]->name);
	 }
*/
	 if (path)
	 {
	    lpath = (char *) malloc((strlen(path) + strlen(eprecurse[i]->name) + 2)*sizeof(char));
	    if (lpath)
	    {
	       sprintf(lpath, "%s/%s", path, eprecurse[i]->name);
	    }
	 }
	 DirListInternal(GetVnode(ntohl(eprecurse[i]->fid.vnode)), 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);
}

/*
 * 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;
}

/*
 * 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;
}

/*
 * 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;
}

--Boundary-00=_VGqeE/RZiwo4tQ3--