reindent-20030715
[openafs.git] / src / tests / dumptool.c
1 /*
2  * $Id$
3  *
4  * dumptool - A tool to manage MR-AFS dump files
5  *
6  * The dump file format ought to be documented _somewhere_, and
7  * this seems like a good as a place as any ...
8  *
9  * A AFS dump file is marked off into a series of sections.  Each
10  * section marked by a dump tag.  A tag is a single byte who's value
11  * corresponds with the next section.  The sections are (in order):
12  *
13  * DUMPHEADER (tag 0x01)
14  * VOLUMEHEADER (tag 0x02)
15  * VNODE (tag 0x03)
16  * DUMPEND (tag 0x04)
17  *
18  * Descriptions of the sections follow.  Note that in all cases, data is
19  * stored in the dump in network byte order.
20  *
21  * DUMPHEADER:
22  *
23  * DUMPHEADER contains two parts: the DUMPMAGIC magic number (32 bits)
24  * and the dump header itself.
25  *
26  * The dump header itself consists of a series of tagged values,
27  * each tag marking out members of the DumpHeader structure.  The
28  * routine ReadDumpHeader explains the specifics of these tags.
29  *
30  * VOLUMEHEADER:
31  *
32  * VOLUMEHEADER is a series of tagged values corresponding to the elements
33  * of the VolumeDiskData structure.  See ReadVolumeHeader for more
34  * information
35  *
36  * VNODE:
37  *
38  * The VNODE section is all vnodes contained in the volume (each vnode
39  * itself is marked with the VNODE tag, so it's really a sequence of
40  * VNODE tags, unlike other sections).
41  *
42  * Each vnode consists of three parts: the vnode number (32 bits), the
43  * uniqifier (32 bits), and a tagged list of elements corresponding to
44  * the elements of the VnodeDiskData structure.  See ScanVnodes for
45  * more information.  Note that if file data is associated with a vnode,
46  * it will be contained here.
47  *
48  * DUMPEND:
49  *
50  * The DUMPEND section consists of one part: the DUMPENDMAGIC magic
51  * number (32 bits).
52  * 
53  * Notes:
54  *
55  * The tagged elements are all ASCII letters, as opposed to the section
56  * headers (which are 0x01, 0x02, ...).  Thus, an easy way to tell when
57  * you've reached the end of an element sequence is to check to see if
58  * the next tag is a printable character (this code tests for < 20).
59  *
60  * "vos dump" dumps the large vnode index, then the small vnode index,
61  * so directories will appear first in the VNODE section.
62  */
63
64 #include <stdio.h>
65 #include <sys/types.h>
66 #include <sys/param.h>
67 #include <netinet/in.h>
68 #include <unistd.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <errno.h>
72 #include <termios.h>
73 #include <fnmatch.h>
74
75 #include <lock.h>
76 #include <afs/param.h>
77 #include <afs/afsint.h>
78 #include <afs/nfs.h>
79 #include <afs/acl.h>
80 #if !defined(PRE_AFS_36) && !defined(RESIDENCY)
81 #include <afs/ihandle.h>
82 #endif /* !defined(PRE_AFS_36) && !defined(RESIDENCY) */
83 #include <afs/vnode.h>
84 #include <afs/volume.h>
85
86 #ifdef RESIDENCY
87 #include <afs/rsdefs.h>
88 #include <afs/remioint.h>
89 #endif /* RESIDENCY */
90
91 #include <afs/dir.h>
92
93 /*
94  * Sigh.  Linux blows it again
95  */
96
97 #ifdef linux
98 #include <pty.h>
99 #endif
100
101 /*
102  * Stuff that is in private AFS header files, unfortunately
103  */
104
105 #define DUMPVERSION     1
106 #define DUMPENDMAGIC    0x3A214B6E
107 #define DUMPBEGINMAGIC  0xB3A11322
108 #define D_DUMPHEADER    1
109 #define D_VOLUMEHEADER  2
110 #define D_VNODE         3
111 #define D_DUMPEND       4
112 #define D_MAX           20
113
114 #define MAXDUMPTIMES    50
115
116 struct DumpHeader {
117     int32_t version;
118     VolumeId volumeId;
119     char volumeName[VNAMESIZE];
120     int nDumpTimes;             /* Number of pairs */
121     struct {
122         int32_t from, to;
123     } dumpTimes[MAXDUMPTIMES];
124 };
125
126 /*
127  * Our command-line arguments
128  */
129
130 #ifdef RESIDENCY
131 struct {
132     int Algorithm;              /* Conversion algorithm */
133     int Size;                   /* Directory hierarchy size */
134     int FSType;                 /* File system type */
135     int DeviceTag;              /* Device Tag */
136 } rscmdlineinfo[RS_MAXRESIDENCIES];
137
138 /*
139  * This stuff comes from ufsname.c (which itself takes it from
140  * ufs_interfaces.c)
141  */
142
143 /* There is an assumption that all of the prefixes will have exactly one '/' */
144 static char *Ufs_Prefixes[] = { "/ufs", "/slowufs", "/cdmf", "/sdmf" };
145
146 #define MAX_ITERATIONS 10
147 #define UFS_SUMMARYTREENAME "Summaries"
148 #define UFS_STAGINGTREENAME "Staging"
149 #define UFS_VOLUMEHEADERTREENAME "VolHeaders"
150 #define UFS_VOLUMETREENAME "Volumes"
151 #define UFS_ALGORITHMBASE 'A'
152 #define UFS_MOUNTPOINTBASE 'a'
153 #define UFS_ALGORITHMS 3
154 #define UFS_LINK_MAX 64         /* Arbitrary. */
155 #define HARD_LINKED_FILE -2
156 #define TAGSTONAME(FileName, MountPoint, Sections, Level1, RWVolume, Vnode, Uniquifier, Algorithm) \
157 { \
158     if (Level1) \
159         sprintf(FileName,"%s/%lu/%lu/%c%lu.%lu.%lu.%lu.%lu", MountPoint, \
160                 (Sections)[0], (Sections)[1], UFS_ALGORITHMBASE + Algorithm, \
161                 (Sections)[2], (Sections)[3], RWVolume, Vnode, Uniquifier); \
162     else \
163         sprintf(FileName,"%s/%lu/%c%lu.%lu.%lu.%lu.%lu", MountPoint, \
164                 (Sections)[0], UFS_ALGORITHMBASE + Algorithm, \
165                 (Sections)[1], (Sections)[2], RWVolume, Vnode, Uniquifier); \
166 }
167 #define TAGSTOSTAGINGNAME(FileName, MountPoint, RWVolume, Vnode, Uniquifier) \
168     sprintf(FileName,"%s/%s/%lu.%lu.%lu", MountPoint, \
169             UFS_STAGINGTREENAME, RWVolume, Vnode, Uniquifier)
170 #define TAGSTOVOLUMEHEADERNAME(FileName, MountPoint, FileTag1, FileTag2) \
171     sprintf(FileName,"%s/%s/%lu", MountPoint, UFS_VOLUMEHEADERTREENAME, \
172             FileTag1)
173 #define TAGSTOVOLUMEINFONAME(FileName, MountPoint, FileTag1, FileTag2) \
174     sprintf(FileName,"%s/%s/%lu/%lu", MountPoint, \
175             UFS_VOLUMETREENAME, FileTag2, FileTag1)
176 #define TAGSTOVOLUMENAME(FileName, MountPoint, FileTag1, FileTag2, RWVolume) \
177     sprintf(FileName,"%s/%s/%lu/%lu.%lu", MountPoint, \
178             UFS_VOLUMETREENAME, FileTag2, FileTag1, RWVolume)
179 #define TAGSTOSUMMARYNAME(FileName, MountPoint, FileTag1, FileTag2, SummaryRequestor, Residency) \
180     sprintf(FileName,"%s/%s/%lu.%lu.%lu.%lu", MountPoint, \
181             UFS_SUMMARYTREENAME, FileTag1, FileTag2, SummaryRequestor, \
182             Residency)
183 #define DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTagNumber, FSType) \
184     sprintf(MountPoint,"%s%c", Ufs_Prefixes[FSType], UFS_MOUNTPOINTBASE + \
185             DeviceTagNumber)
186 #define MOUNTPOINTTODEVICETAGNUMBER(MountPoint) \
187     MountPoint[strlen(MountPoint) - 1] - UFS_MOUNTPOINTBASE
188 #define DEVICETAGNUMBERTOVOLUMEHEADERTREE(TreeName, MountPoint) \
189     sprintf(TreeName,"%s/%s", MountPoint, UFS_VOLUMEHEADERTREENAME)
190 #define UFS_RESIDENCIES_FILE "Residencies"
191
192 /* We don't ever want to map to uid/gid -1.  fchown() takes that as a
193    don't change flag.  We know however that volume number range from
194    0x2000000 to 0x20FFFFFF (see VAllocateVolumeId() in vol/vutil.c)
195    so we will use that to insure that -1 never appears. */
196 #define RWVolumeToUid(RWVolume) ((RWVolume >> 12) & 0xFFFF)
197 #define RWVolumeToGid(RWVolume) ((RWVolume & 0xFFF) | \
198                                  (((RWVolume >> 28) & 0xF) << 12))
199 #define UidGidToRWVolume(Uid, Gid) ((Gid & 0xFFF) | ((Uid & 0xFFFF) << 12) | \
200                                     ((Gid & 0xF000) << 16))
201
202
203 /* These routines generate a file name to correspond to the given tag
204    numbers. */
205
206 /* The following entropy array contains the order of bits from highest entropy
207    to lowest in the numbers FileTag1 and FileTag2.  Bit numbers 32 and above
208    correspond to FileTag2.  This ordering was determined by examining all read-
209    write volumes in the psc.edu cell. */
210 char UfsEntropy[1][64] = {
211     {1, 2, 3, 4, 33, 5, 6, 7, 44, 45, 46, 36, 8, 34, 42, 35,
212      9, 40, 38, 32, 43, 10, 39, 37, 11, 41, 12, 13, 14, 0,
213      15, 16, 61, 17, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51,
214      50, 49, 48, 47, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
215      21, 20, 19, 18, 62, 63},
216 };
217
218 uint32_t Directories[3][2] = { {256, 0}, {256, 16}, {256, 256}, };
219 #endif /* RESIDENCY */
220
221 static int verbose = 0;
222 static int numNoDirData = 0;
223 static int termsize = 0;
224 int Testing = 0;
225 #ifdef RESIDENCY
226 extern resid ServerRequestorId;
227 #endif /* RESIDENCY */
228
229 /*
230  * We use this structure to hold vnode data in our hash table.
231  * It's indexed by vnode number.
232  */
233
234 struct vnodeData {
235     struct VnodeDiskObject *vnode;      /* A pointer to the disk vnode */
236     int vnodeNumber;            /* The vnode number */
237     long dumpdata;              /* File offset of dump data (if
238                                  * available */
239     unsigned char *filedata;    /* A pointer to the actual file
240                                  * data itself (if available) */
241     unsigned int datalength;    /* The length of the data */
242 };
243
244 /*
245  * This contains the current location when we're doing a scan of a
246  * directory.
247  */
248
249 struct DirCursor {
250     int hashbucket;             /* Current hash bucket */
251     int entry;                  /* Entry within hash bucket */
252 };
253
254 /*
255  * Arrays to hold vnode data
256  */
257
258 struct vnodeData **LargeVnodeIndex;
259 struct vnodeData **SmallVnodeIndex;
260 int numLargeVnodes = 0;
261 int numSmallVnodes = 0;
262
263 /*
264  * Crap for the libraries
265  */
266
267 int ShutdownInProgress = 0;
268
269 /*
270  * Our local function prototypes
271  */
272
273 static int ReadDumpHeader(FILE *, struct DumpHeader *);
274 static int ReadVolumeHeader(FILE *, VolumeDiskData *);
275 static int ScanVnodes(FILE *, VolumeDiskData *, int);
276 static int DumpVnodeFile(FILE *, struct VnodeDiskObject *, VolumeDiskData *);
277 static struct vnodeData *InsertVnode(unsigned int, struct VnodeDiskObject *);
278 static struct vnodeData *GetVnode(unsigned int);
279 static int CompareVnode(const void *, const void *);
280 static void InteractiveRestore(FILE *, VolumeDiskData *);
281 static void DirectoryList(int, char **, struct vnodeData *, VolumeDiskData *);
282 static void DirListInternal(struct vnodeData *, char *[], int, int, int, int,
283                             int, int, VolumeDiskData *, char *);
284 static int CompareDirEntry(const void *, const void *);
285 static struct vnodeData *ChangeDirectory(int, char **, struct vnodeData *);
286 static void CopyFile(int, char **, struct vnodeData *, FILE *);
287 static void CopyVnode(int, char **, FILE *);
288 static void DumpAllFiles(int, char **, struct vnodeData *, VolumeDiskData *);
289 static void DumpAllResidencies(FILE *, struct vnodeData *, VolumeDiskData *);
290 static struct vnodeData *FindFile(struct vnodeData *, char *);
291 static void ResetDirCursor(struct DirCursor *, struct vnodeData *);
292 static struct DirEntry *ReadNextDir(struct DirCursor *, struct vnodeData *);
293 static void MakeArgv(char *, int *, char ***);
294 static char *GetToken(char *, char **, char *, char *[]);
295 static int ReadInt16(FILE *, uint16_t *);
296 static int ReadInt32(FILE *, uint32_t *);
297 static int ReadString(FILE *, char *, int);
298 static int ReadByteString(FILE *, void *, int);
299
300 int
301 main(int argc, char *argv[])
302 {
303     int c, errflg = 0, dumpvnodes = 0, force = 0, inode = 0;
304     unsigned int magic;
305     struct DumpHeader dheader;
306     VolumeDiskData vol;
307     long offset;
308     int Res, Arg1, Arg2, Arg3, i;
309     char *p;
310     struct winsize win;
311     FILE *f;
312
313 #ifdef RESIDENCY
314     for (i = 0; i < RS_MAXRESIDENCIES; i++) {
315         rscmdlineinfo[i].Algorithm = -1;
316         rscmdlineinfo[i].Size = -1;
317         rscmdlineinfo[i].DeviceTag = -1;
318         rscmdlineinfo[i].FSType = -1;
319     }
320 #endif /* RESIDENCY */
321
322     /*
323      * Sigh, this is dumb, but we need the terminal window size
324      * to do intelligent things with "ls" later on.
325      */
326
327     if (isatty(STDOUT_FILENO)) {
328         if ((p = getenv("COLUMNS")) != NULL)
329             termsize = atoi(p);
330         else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0
331                  && win.ws_col > 0)
332             termsize = win.ws_col;
333     }
334
335     while ((c = getopt(argc, argv, "difr:t:v")) != EOF)
336         switch (c) {
337         case 't':
338 #ifdef RESIDENCY
339             if (sscanf(optarg, "%d/%d", &Res, &Arg1) != 2) {
340                 errflg++;
341                 break;
342             }
343
344             if (1 << (ffs(Res) - 1) != Res) {
345                 fprintf(stderr, "Invalid residency %d\n", Res);
346                 errflg++;
347                 break;
348             }
349
350             if (Arg1 < 0 || Arg1 > 26) {
351                 fprintf(stderr, "Invalid device tag: %d\n", Arg1);
352                 errflg++;
353                 break;
354             }
355             rscmdlineinfo[ffs(Res) - 1].DeviceTag = Arg1;
356 #else /* RESIDENCY */
357             fprintf(stderr, "-t not supported in non-MRAFS " "dumptool.\n");
358             errflg++;
359 #endif /* RESIDENCY */
360             break;
361
362         case 'r':
363 #ifdef RESIDENCY
364             if (sscanf(optarg, "%d/%d/%d/%d", &Res, &Arg1, &Arg2, &Arg3) != 4) {
365                 errflg++;
366                 break;
367             }
368
369             if (Arg1 < 0 || Arg1 > 3) {
370                 fprintf(stderr, "Invalid fstype: %d\n", Arg1);
371                 errflg++;
372                 break;
373             }
374
375             if (Arg2 < 0 || Arg2 > 2) {
376                 fprintf(stderr, "Invalid size: %d\n", Arg2);
377                 errflg++;
378                 break;
379             }
380
381             if (Arg3 <= 0 || Arg3 > UFS_ALGORITHMS) {
382                 fprintf(stderr, "Invalid algorithm: %d\n", Arg3);
383                 errflg++;
384                 break;
385             }
386             rscmdlineinfo[ffs(Res) - 1].FSType = Arg1;
387             rscmdlineinfo[ffs(Res) - 1].Size = Arg2;
388             rscmdlineinfo[ffs(Res) - 1].Algorithm = Arg3;
389 #else /* RESIDENCY */
390             fprintf(stderr, "-r not supported in non-MRAFS " "dumptool.\n");
391             errflg++;
392 #endif /* RESIDENCY */
393             break;
394         case 'd':
395 #ifdef RESIDENCY
396             dumpvnodes++;
397 #else /* RESIDENCY */
398             fprintf(stderr, "-d not supported in non-MRAFS " "dumptool.\n");
399             errflg++;
400 #endif /* RESIDENCY */
401             break;
402         case 'v':
403             verbose++;
404             break;
405         case 'f':
406             force++;
407             break;
408         case 'i':
409             inode++;
410             break;
411         case '?':
412         default:
413             errflg++;
414         }
415
416     if (errflg || optind == argc) {
417         fprintf(stderr, "Usage: %s\n\t[-v] [-f]\n\t"
418 #ifdef RESIDENCY
419                 "[-t Residency/Tag]\n\t"
420                 "[-r Residency/Type/Size/Algorithm]\n\t"
421                 "[-d] filename [file_in_dump [file in dump ...]]\n",
422 #else /* RESIDENCY */
423                 "filename\n",
424 #endif /* RESIDENCY */
425                 argv[0]);
426         exit(1);
427     }
428
429     /*
430      * Try opening the dump file
431      */
432
433     if ((f = fopen(argv[optind], "rb")) == NULL) {
434         fprintf(stderr, "open of dumpfile %s failed: %s\n", argv[optind],
435                 strerror(errno));
436         exit(1);
437     }
438
439     if (ReadDumpHeader(f, &dheader)) {
440         fprintf(stderr, "Failed to read dump header!\n");
441         exit(1);
442     }
443
444     if (verbose)
445         printf("Dump is for volume %lu (%s)\n", dheader.volumeId,
446                dheader.volumeName);
447
448     if (getc(f) != D_VOLUMEHEADER) {
449         fprintf(stderr, "Volume header is missing from dump, aborting\n");
450         exit(1);
451     }
452
453     if (ReadVolumeHeader(f, &vol)) {
454         fprintf(stderr, "Unable to read volume header\n");
455         exit(1);
456     }
457
458     if (verbose) {
459         printf("Volume information:\n");
460         printf("\tid = %lu\n", vol.id);
461         printf("\tparent id = %lu\n", vol.parentId);
462         printf("\tname = %s\n", vol.name);
463         printf("\tflags =");
464         if (vol.inUse)
465             printf(" inUse");
466         if (vol.inService)
467             printf(" inService");
468         if (vol.blessed)
469             printf(" blessed");
470         if (vol.needsSalvaged)
471             printf(" needsSalvaged");
472         printf("\n");
473         printf("\tuniquifier = %lu\n", vol.uniquifier);
474         printf("\tCreation date = %s", ctime((time_t *) & vol.creationDate));
475         printf("\tLast access date = %s", ctime((time_t *) & vol.accessDate));
476         printf("\tLast update date = %s", ctime((time_t *) & vol.updateDate));
477         printf("\tVolume owner = %lu\n", vol.owner);
478     }
479
480     if (verbose)
481         printf("Scanning vnodes (this may take a while)\n");
482
483     /*
484      * We need to do two vnode scans; one to get the number of
485      * vnodes, the other to actually build the index.
486      */
487
488     offset = ftell(f);
489
490     if (ScanVnodes(f, &vol, 1)) {
491         fprintf(stderr, "First vnode scan failed, aborting\n");
492         exit(1);
493     }
494
495     fseek(f, offset, SEEK_SET);
496
497     if (ScanVnodes(f, &vol, 0)) {
498         fprintf(stderr, "Second vnode scan failed, aborting\n");
499         exit(1);
500     }
501
502     if (getc(f) != D_DUMPEND || ReadInt32(f, &magic) || magic != DUMPENDMAGIC) {
503         fprintf(stderr, "Couldn't find dump postamble, ");
504         if (!force) {
505             fprintf(stderr, "aborting (use -f to override)\n");
506             exit(1);
507         } else {
508             fprintf(stderr, "continuing anyway\n");
509             fprintf(stderr, "WARNING: Dump may not be complete!\n");
510         }
511     }
512
513     /*
514      * If we wanted to simply dump all vnodes, do it now
515      */
516
517 #ifdef RESIDENCY
518     if (dumpvnodes) {
519         struct vnodeData *vdata;
520
521         for (i = 0; i < numLargeVnodes; i++) {
522
523             vdata = LargeVnodeIndex[i];
524
525             if (vdata->vnode->type == vFidLookup)
526                 if (DumpVnodeFile(stdout, vdata->vnode, &vol)) {
527                     fprintf(stderr, "DumpVnodeFile failed, " "aborting\n");
528                     exit(1);
529                 }
530         }
531
532         for (i = 0; i < numSmallVnodes; i++) {
533
534             vdata = SmallVnodeIndex[i];
535
536             if (vdata->vnode->type == vFidLookup)
537                 if (DumpVnodeFile(stdout, vdata->vnode, &vol)) {
538                     fprintf(stderr, "DumpVnodeFile failed, " "aborting\n");
539                     exit(1);
540                 }
541         }
542
543     } else
544 #endif /* RESIDENCY */
545     if (inode) {
546         /*
547          * Dump out all filenames with their corresponding FID
548          */
549
550         struct vnodeData *rootvdata;
551
552         if ((rootvdata = GetVnode(1)) == NULL) {
553             fprintf(stderr,
554                     "Can't get vnode data for root " "vnode!  Aborting\n");
555             exit(1);
556         }
557
558         DirListInternal(rootvdata, NULL, 0, 0, 1, 0, 1, 0, &vol, "");
559
560     } else if (argc > optind + 1) {
561 #ifdef RESIDENCY
562         /*
563          * Dump out residencies of files given on the command line.
564          */
565
566         struct vnodeData *vdata, *rootvdata;
567
568         if ((rootvdata = GetVnode(1)) == NULL) {
569             fprintf(stderr,
570                     "Can't get vnode data for root " "vnode!  Aborting\n");
571             exit(1);
572         }
573
574         for (i = optind + 1; i < argc; i++) {
575
576             if ((vdata = FindFile(rootvdata, argv[i])) == NULL) {
577                 fprintf(stderr, "Skipping file %s\n", argv[i]);
578                 continue;
579             }
580
581             if (verbose)
582                 printf("Residency locations for %s:\n", argv[i]);
583
584             while (vdata->vnode->NextVnodeId != 0) {
585
586                 vdata = GetVnode(vdata->vnode->NextVnodeId);
587
588                 if (vdata == NULL) {
589                     fprintf(stderr,
590                             "We had a vnode chain " "pointer to a vnode that "
591                             "doesn't exist, aborting!\n");
592                     exit(1);
593                 }
594                 if (vdata->vnode->type == vFidLookup)
595                     DumpVnodeFile(stdout, vdata->vnode, &vol);
596             }
597         }
598 #else /* RESIDENCY */
599         fprintf(stderr, "Extra arguments after dump filename: %s\n",
600                 argv[optind]);
601         exit(1);
602 #endif /* RESIDENCY */
603     } else {
604         /*
605          * Perform an interactive restore
606          */
607
608         InteractiveRestore(f, &vol);
609     }
610
611     exit(0);
612 }
613
614 /*
615  * Read the dump header, which is at the beginning of every dump
616  */
617
618 static int
619 ReadDumpHeader(FILE * f, struct DumpHeader *header)
620 {
621     unsigned int magic;
622     int tag, i;
623
624     if (getc(f) != D_DUMPHEADER || ReadInt32(f, &magic)
625         || ReadInt32(f, (unsigned int *)
626                      &header->version) || magic != DUMPBEGINMAGIC) {
627         if (verbose)
628             fprintf(stderr, "Couldn't find dump magic numbers\n");
629         return -1;
630     }
631
632     header->volumeId = 0;
633     header->nDumpTimes = 0;
634
635     while ((tag = getc(f)) > D_MAX && tag != EOF) {
636         unsigned short length;
637         switch (tag) {
638         case 'v':
639             if (ReadInt32(f, &header->volumeId)) {
640                 if (verbose)
641                     fprintf(stderr, "Failed to read " "volumeId\n");
642                 return -1;
643             }
644             break;
645         case 'n':
646             if (ReadString(f, header->volumeName, sizeof(header->volumeName))) {
647                 if (verbose)
648                     fprintf(stderr, "Failed to read " "volume name\n");
649                 return -1;
650             }
651             break;
652         case 't':
653             if (ReadInt16(f, &length)) {
654                 if (verbose)
655                     fprintf(stderr,
656                             "Failed to read " "dump time array length\n");
657                 return -1;
658             }
659             header->nDumpTimes = (length >> 1);
660             for (i = 0; i < header->nDumpTimes; i++)
661                 if (ReadInt32(f, (unsigned int *)
662                               &header->dumpTimes[i].from)
663                     || ReadInt32(f, (unsigned int *)
664                                  &header->dumpTimes[i].to)) {
665                     if (verbose)
666                         fprintf(stderr, "Failed to " "read dump times\n");
667                     return -1;
668                 }
669             break;
670         default:
671             if (verbose)
672                 fprintf(stderr, "Unknown dump tag \"%c\"\n", tag);
673             return -1;
674         }
675     }
676
677     if (!header->volumeId || !header->nDumpTimes) {
678         if (verbose)
679             fprintf(stderr,
680                     "We didn't get a volume Id or " "dump times listing\n");
681         return 1;
682     }
683
684     ungetc(tag, f);
685     return 0;
686 }
687
688 /*
689  * Read the volume header; this is the information stored in VolumeDiskData.
690  *
691  * I'm not sure we need all of this, but read it in just in case.
692  */
693
694 static int
695 ReadVolumeHeader(FILE * f, VolumeDiskData * vol)
696 {
697     int tag;
698     unsigned int trash;
699     memset((void *)vol, 0, sizeof(*vol));
700
701     while ((tag = getc(f)) > D_MAX && tag != EOF) {
702         switch (tag) {
703         case 'i':
704             if (ReadInt32(f, &vol->id))
705                 return -1;
706             break;
707         case 'v':
708             if (ReadInt32(f, &trash))
709                 return -1;
710             break;
711         case 'n':
712             if (ReadString(f, vol->name, sizeof(vol->name)))
713                 return -1;
714             break;
715         case 's':
716             vol->inService = getc(f);
717             break;
718         case 'b':
719             vol->blessed = getc(f);
720             break;
721         case 'u':
722             if (ReadInt32(f, &vol->uniquifier))
723                 return -1;
724             break;
725         case 't':
726             vol->type = getc(f);
727             break;
728         case 'p':
729             if (ReadInt32(f, &vol->parentId))
730                 return -1;
731             break;
732         case 'c':
733             if (ReadInt32(f, &vol->cloneId))
734                 return -1;
735             break;
736         case 'q':
737             if (ReadInt32(f, (uint32_t *) & vol->maxquota))
738                 return -1;
739             break;
740         case 'm':
741             if (ReadInt32(f, (uint32_t *) & vol->minquota))
742                 return -1;
743             break;
744         case 'd':
745             if (ReadInt32(f, (uint32_t *) & vol->diskused))
746                 return -1;
747             break;
748         case 'f':
749             if (ReadInt32(f, (uint32_t *) & vol->filecount))
750                 return -1;
751             break;
752         case 'a':
753             if (ReadInt32(f, &vol->accountNumber))
754                 return -1;
755             break;
756         case 'o':
757             if (ReadInt32(f, &vol->owner))
758                 return -1;
759             break;
760         case 'C':
761             if (ReadInt32(f, &vol->creationDate))
762                 return -1;
763             break;
764         case 'A':
765             if (ReadInt32(f, &vol->accessDate))
766                 return -1;
767             break;
768         case 'U':
769             if (ReadInt32(f, &vol->updateDate))
770                 return -1;
771             break;
772         case 'E':
773             if (ReadInt32(f, &vol->expirationDate))
774                 return -1;
775             break;
776         case 'B':
777             if (ReadInt32(f, &vol->backupDate))
778                 return -1;
779             break;
780         case 'O':
781             if (ReadString
782                 (f, vol->offlineMessage, sizeof(vol->offlineMessage)))
783                 return -1;
784             break;
785         case 'M':
786             if (ReadString(f, (char *)vol->stat_reads, VMSGSIZE))
787                 return -1;
788             break;
789         case 'W':{
790                 unsigned short length;
791                 int i;
792                 unsigned int data;
793                 if (ReadInt16(f, &length))
794                     return -1;
795                 for (i = 0; i < length; i++) {
796                     if (ReadInt32(f, &data))
797                         return -1;
798                     if (i < sizeof(vol->weekUse) / sizeof(vol->weekUse[0]))
799                         vol->weekUse[i] = data;
800                 }
801                 break;
802             }
803         case 'D':
804             if (ReadInt32(f, &vol->dayUseDate))
805                 return -1;
806             break;
807         case 'Z':
808             if (ReadInt32(f, (uint32_t *) & vol->dayUse))
809                 return -1;
810             break;
811 #ifdef RESIDENCY
812         case 'R':{
813                 unsigned short length;
814                 int i;
815                 unsigned int data;
816
817                 if (ReadInt16(f, &length))
818                     return -1;
819                 for (i = 0; i < length; i++) {
820                     if (ReadInt32(f, &data))
821                         return -1;
822                     if (i <
823                         sizeof(vol->DesiredInfo.DesiredResidencyWords) /
824                         sizeof(vol->DesiredInfo.DesiredResidencyWords[0]))
825                         vol->DesiredInfo.DesiredResidencyWords[i] = data;
826                 }
827                 break;
828             }
829         case 'S':{
830                 unsigned short length;
831                 int i;
832                 unsigned int data;
833
834                 if (ReadInt16(f, &length))
835                     return -1;
836                 for (i = 0; i < length; i++) {
837                     if (ReadInt32(f, &data))
838                         return -1;
839                     if (i <
840                         sizeof(vol->UnDesiredInfo.UnDesiredResidencyWords) /
841                         sizeof(vol->UnDesiredInfo.UnDesiredResidencyWords[0]))
842                         vol->UnDesiredInfo.UnDesiredResidencyWords[i] = data;
843                 }
844                 break;
845             }
846 #endif
847         default:
848             if (verbose)
849                 fprintf(stderr, "Unknown dump tag \"%c\"\n", tag);
850             return -1;
851         }
852     }
853
854     ungetc(tag, f);
855     return 0;
856 }
857
858 /*
859  * Scan all our vnode entries, and build indexing information.
860  */
861
862 static int
863 ScanVnodes(FILE * f, VolumeDiskData * vol, int sizescan)
864 {
865     int vnodeNumber;
866     int tag;
867     int numFileVnodes = 0;
868     int numDirVnodes = 0;
869     unsigned char buf[SIZEOF_LARGEDISKVNODE];
870     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
871     long offset, oldoffset;
872     struct vnodeData *vdata;
873     unsigned int length;
874
875     tag = getc(f);
876
877     while (tag == D_VNODE) {
878
879         offset = 0;
880         length = 0;
881         vnode->type = -1;
882         vnode->length = -1;
883
884         if (ReadInt32(f, (uint32_t *) & vnodeNumber)) {
885             fprintf(stderr, "failed int32 for 'vnodenum'\n");
886             return -1;
887         }
888
889         if (ReadInt32(f, &vnode->uniquifier)) {
890             fprintf(stderr, "failed int32 for 'uniquifier'\n");
891             return -1;
892         }
893
894         if (verbose > 1 && !sizescan)
895             printf("Got vnode %d\n", vnodeNumber);
896
897         while ((tag = getc(f)) > D_MAX && tag != EOF)
898             switch (tag) {
899             case 't':
900                 vnode->type = (VnodeType) getc(f);
901                 break;
902             case 'l':
903                 {
904                     unsigned short tmp;
905                     if (ReadInt16(f, &tmp)) {
906                         fprintf(stderr, "failed int16 for 'l'\n");
907                         return -1;
908                     }
909                     vnode->linkCount = tmp;
910                 }
911                 break;
912             case 'v':
913                 if (ReadInt32(f, &vnode->dataVersion)) {
914                     fprintf(stderr, "failed int32 for 'v'\n");
915                     return -1;
916                 }
917                 break;
918             case 'm':
919                 if (ReadInt32(f, (uint32_t *) & vnode->unixModifyTime)) {
920                     fprintf(stderr, "failed int32 for 'm'\n");
921                     return -1;
922                 }
923                 break;
924             case 's':
925                 if (ReadInt32(f, (uint32_t *) & vnode->serverModifyTime)) {
926                     fprintf(stderr, "failed int32 for 's'\n");
927                     return -1;
928                 }
929                 break;
930             case 'a':
931                 if (ReadInt32(f, &vnode->author)) {
932                     fprintf(stderr, "failed int32 for 'a'\n");
933                     return -1;
934                 }
935                 break;
936             case 'o':
937                 if (ReadInt32(f, &vnode->owner)) {
938                     fprintf(stderr, "failed int32 for 'o'\n");
939                     return -1;
940                 }
941                 break;
942             case 'g':
943                 if (ReadInt32(f, (uint32_t *) & vnode->group)) {
944                     fprintf(stderr, "failed int32 for 'g'\n");
945                     return -1;
946                 }
947                 break;
948             case 'b':{
949                     unsigned short modeBits;
950                     if (ReadInt16(f, &modeBits))
951                         return -1;
952                     vnode->modeBits = modeBits;
953                     break;
954                 }
955             case 'p':
956                 if (ReadInt32(f, &vnode->parent)) {
957                     fprintf(stderr, "failed int32 for 'p'\n");
958                     return -1;
959                 }
960                 break;
961 #ifdef RESIDENCY
962             case 'N':
963                 if (ReadInt32(f, &vnode->NextVnodeId)) {
964                     fprintf(stderr, "failed int32 for 'N'\n");
965                     return -1;
966                 }
967                 break;
968             case 'R':
969                 if (ReadInt32(f, &VLkp_Residencies(vnode))) {
970                     fprintf(stderr, "failed int32 for 'R'\n");
971                     return -1;
972                 }
973                 break;
974 #endif
975             case 'S':
976                 if (ReadInt32(f, &vnode->length)) {
977                     fprintf(stderr, "failed int32 for 'S'\n");
978                     return -1;
979                 }
980                 break;
981             case 'F':
982                 if (ReadInt32(f, (uint32_t *) & vnode->vn_ino_lo))
983                     return -1;
984                 break;
985             case 'A':
986                 if (ReadByteString
987                     (f, (void *)VVnodeDiskACL(vnode), VAclDiskSize(vnode))) {
988                     fprintf(stderr, "failed readbystring for 'A'\n");
989                     return -1;
990                 }
991 #if 0
992                 acl_NtohACL(VVnodeDiskACL(vnode));
993 #endif
994                 break;
995 #ifdef RESIDENCY
996             case 'h':
997                 if (ReadInt32(f, &vnode->length_hi)) {
998                     fprintf(stderr, "failed int32 for 'h'\n");
999                     return -1;
1000                 }
1001 #endif
1002             case 'f':
1003                 if (verbose > 1 && !sizescan)
1004                     printf("We have file data!\n");
1005                 if (ReadInt32(f, &length)) {
1006                     fprintf(stderr, "failed int32 for 'f'\n");
1007                     return -1;
1008                 }
1009                 vnode->length = length;
1010                 offset = ftell(f);
1011                 fseek(f, length, SEEK_CUR);
1012                 break;
1013             default:
1014                 if (verbose)
1015                     fprintf(stderr, "Unknown dump tag \"%c\"\n", tag);
1016                 return -1;
1017             }
1018
1019         /*
1020          * If we're doing an incremental restore, then vnodes
1021          * will be listed in the dump, but won't contain any
1022          * vnode information at all (I don't know why they're
1023          * included _at all_).  If we get one of these vnodes, then
1024          * just skip it (because we can't do anything with it.
1025          */
1026
1027         if (vnode->type == -1)
1028             continue;
1029
1030 #ifdef RESIDENCY
1031         if (verbose > 1 && vnode->type == vFidLookup && !sizescan) {
1032             printf
1033                 ("This is an auxiliary vnode (lookup) for vnode %d, residency %d\n",
1034                  VLkp_ParentVnodeId(vnode), VLkp_Residencies(vnode));
1035             if (DumpVnodeFile(stdout, vnode, vol))
1036                 return -1;
1037         }
1038
1039         if (verbose > 1 && vnode->type == vAccessHistory && !sizescan)
1040             printf("This is an auxiliary vnode (history) for vnode %d\n",
1041                    VLkp_ParentVnodeId(vnode));
1042 #endif
1043
1044         if (vnode->type == vDirectory)
1045             numDirVnodes++;
1046         else
1047             numFileVnodes++;
1048
1049         /*
1050          * We know now all we would ever know about the vnode;
1051          * insert it into our hash table (but only if we're not
1052          * doing a vnode scan).
1053          */
1054
1055         if (!sizescan) {
1056
1057             vdata = InsertVnode(vnodeNumber, vnode);
1058
1059             if (vdata == NULL) {
1060                 if (verbose)
1061                     fprintf(stderr,
1062                             "Failed to insert " "vnode into hash table");
1063                 return -1;
1064             }
1065
1066             vdata->dumpdata = offset;
1067             vdata->datalength = length;
1068
1069             /*
1070              * Save directory data, since we'll need it later.
1071              */
1072
1073             if (vnode->type == vDirectory && length) {
1074
1075                 vdata->filedata = malloc(length);
1076
1077                 if (!vdata->filedata) {
1078                     if (verbose)
1079                         fprintf(stderr,
1080                                 "Unable to " "allocate space for "
1081                                 "file data (%d)\n", length);
1082                     return -1;
1083                 }
1084
1085                 oldoffset = ftell(f);
1086                 fseek(f, offset, SEEK_SET);
1087
1088                 if (fread(vdata->filedata, length, 1, f) != 1) {
1089                     if (verbose)
1090                         fprintf(stderr, "Unable to " "read in file data!\n");
1091                     return -1;
1092                 }
1093
1094                 fseek(f, oldoffset, SEEK_SET);
1095             } else if (vnode->type == vDirectory)
1096                 /*
1097                  * Warn the user we may not have all directory
1098                  * vnodes
1099                  */
1100                 numNoDirData++;
1101         }
1102     }
1103
1104     ungetc(tag, f);
1105
1106     if (!sizescan) {
1107
1108         numLargeVnodes = numDirVnodes;
1109         numSmallVnodes = numFileVnodes;
1110
1111     } else {
1112         LargeVnodeIndex = (struct vnodeData **)
1113             malloc(numDirVnodes * sizeof(struct vnodeData));
1114         SmallVnodeIndex = (struct vnodeData **)
1115             malloc(numFileVnodes * sizeof(struct vnodeData));
1116
1117         if (LargeVnodeIndex == NULL || SmallVnodeIndex == NULL) {
1118             if (verbose)
1119                 fprintf(stderr,
1120                         "Unable to allocate space " "for vnode tables\n");
1121             return -1;
1122         }
1123     }
1124
1125     if (verbose)
1126         fprintf(stderr, "%s vnode scan completed\n",
1127                 sizescan ? "Primary" : "Secondary");
1128
1129     return 0;
1130 }
1131
1132 /*
1133  * Perform an interactive restore
1134  *
1135  * Parsing the directory information is a pain, but other than that
1136  * we just use the other tools we already have in here.
1137  */
1138
1139 static void
1140 InteractiveRestore(FILE * f, VolumeDiskData * vol)
1141 {
1142     struct vnodeData *vdatacwd; /* Vnode data for our current dir */
1143     char cmdbuf[256];
1144     int argc;
1145     char **argv;
1146
1147     /*
1148      * Let's see if we can at least get the data for our root directory.
1149      * If we can't, there's no way we can do an interactive restore.
1150      */
1151
1152     if ((vdatacwd = GetVnode(1)) == NULL) {
1153         fprintf(stderr, "No entry for our root vnode!  Aborting\n");
1154         return;
1155     }
1156
1157     if (!vdatacwd->filedata) {
1158         fprintf(stderr,
1159                 "There is no directory data for the root "
1160                 "vnode (1.1).  An interactive\nrestore is not "
1161                 "possible.\n");
1162         return;
1163     }
1164
1165     /*
1166      * If you're doing a selective dump correctly, then you should get all
1167      * directory vnode data.  But just in case you didn't, let the user
1168      * know there may be a problem.
1169      */
1170
1171     if (numNoDirData)
1172         fprintf(stderr,
1173                 "WARNING: %d directory vnodes had no file "
1174                 "data.  An interactive restore\nmay not be possible\n",
1175                 numNoDirData);
1176
1177     printf("> ");
1178     while (fgets(cmdbuf, 256, stdin)) {
1179
1180         cmdbuf[strlen(cmdbuf) - 1] = '\0';
1181
1182         if (strlen(cmdbuf) == 0) {
1183             printf("> ");
1184             continue;
1185         }
1186
1187         MakeArgv(cmdbuf, &argc, &argv);
1188
1189         if (strcmp(argv[0], "ls") == 0) {
1190             DirectoryList(argc, argv, vdatacwd, vol);
1191         } else if (strcmp(argv[0], "cd") == 0) {
1192             struct vnodeData *newvdata;
1193
1194             newvdata = ChangeDirectory(argc, argv, vdatacwd);
1195
1196             if (newvdata)
1197                 vdatacwd = newvdata;
1198         } else if (strcmp(argv[0], "file") == 0) {
1199             DumpAllFiles(argc, argv, vdatacwd, vol);
1200         } else if (strcmp(argv[0], "cp") == 0) {
1201             CopyFile(argc, argv, vdatacwd, f);
1202         } else if (strcmp(argv[0], "vcp") == 0) {
1203             CopyVnode(argc, argv, f);
1204         } else if (strcmp(argv[0], "quit") == 0
1205                    || strcmp(argv[0], "exit") == 0)
1206             break;
1207         else if (strcmp(argv[0], "?") == 0 || strcmp(argv[0], "help") == 0) {
1208             printf("Valid commands are:\n");
1209             printf("\tls\t\tList current directory\n");
1210             printf("\tcd\t\tChange current directory\n");
1211             printf("\tcp\t\tCopy file from dump\n");
1212             printf("\tvcp\t\tCopy file from dump (via vnode)\n");
1213 #ifdef RESIDENCY
1214             printf("\tfile\t\tList residency filenames\n");
1215 #endif /* RESIDENCY */
1216             printf("\tquit | exit\tExit program\n");
1217             printf("\thelp | ?\tBrief help\n");
1218         } else
1219             fprintf(stderr,
1220                     "Unknown command, \"%s\", enter "
1221                     "\"help\" for a list of commands.\n", argv[0]);
1222
1223         printf("> ");
1224     }
1225
1226     return;
1227 }
1228
1229 /*
1230  * Do a listing of all files in a directory.  Sigh, I wish this wasn't
1231  * so complicated.
1232  *
1233  * With the reorganizing, this is just a front-end to DirListInternal()
1234  */
1235
1236 static void
1237 DirectoryList(int argc, char **argv, struct vnodeData *vdata,
1238               VolumeDiskData * vol)
1239 {
1240     int errflg = 0, lflag = 0, iflag = 0, Fflag = 0, sflag = 0, Rflag = 0;
1241     int c;
1242
1243     optind = 1;
1244
1245     while ((c = getopt(argc, argv, "liFRs")) != EOF)
1246         switch (c) {
1247         case 'l':
1248             lflag++;
1249             break;
1250         case 'i':
1251             iflag++;
1252             break;
1253         case 'F':
1254             Fflag++;
1255             break;
1256         case 'R':
1257             Rflag++;
1258         case 's':
1259             sflag++;
1260             break;
1261         case '?':
1262         default:
1263             errflg++;
1264         }
1265
1266     if (errflg) {
1267         fprintf(stderr, "Usage: %s [-liFs] filename [filename ...]\n",
1268                 argv[0]);
1269         return;
1270     }
1271
1272     DirListInternal(vdata, &(argv[optind]), argc - optind, lflag, iflag,
1273                     Fflag, Rflag, 1, vol, NULL);
1274
1275     return;
1276 }
1277
1278 /*
1279  * Function that does the REAL work in terms of directory listing
1280  */
1281
1282 static void
1283 DirListInternal(struct vnodeData *vdata, char *pathnames[], int numpathnames,
1284                 int lflag, int iflag, int Fflag, int Rflag, int verbose,
1285                 VolumeDiskData * vol, char *path)
1286 {
1287     struct DirEntry *ep, **eplist = NULL, **eprecurse = NULL;
1288     struct DirCursor cursor;
1289     struct vnodeData *lvdata;
1290
1291     int i, j, numentries = 0, longestname = 0, numcols, col, numrows;
1292     int numrecurse = 0;
1293
1294     if (!vdata->filedata) {
1295         fprintf(stderr, "There is no vnode data for this " "directory!\n");
1296         return;
1297     }
1298
1299     ResetDirCursor(&cursor, vdata);
1300
1301     /*
1302      * Scan through the whole directory
1303      */
1304
1305     while ((ep = ReadNextDir(&cursor, vdata)) != NULL) {
1306
1307         /*
1308          * If we didn't get any filenames on the command line,
1309          * get them all.
1310          */
1311
1312         if (numpathnames == 0) {
1313             eplist =
1314                 realloc(eplist, sizeof(struct DirEntry *) * ++numentries);
1315             eplist[numentries - 1] = ep;
1316             if (strlen(ep->name) > longestname)
1317                 longestname = strlen(ep->name);
1318             if (Rflag)
1319                 if ((lvdata = GetVnode(ntohl(ep->fid.vnode)))
1320                     && lvdata->vnode->type == vDirectory
1321                     && !(strcmp(ep->name, ".") == 0
1322                          || strcmp(ep->name, "..") == 0)) {
1323                     eprecurse =
1324                         realloc(eprecurse,
1325                                 sizeof(struct DirEntry *) * ++numrecurse);
1326                     eprecurse[numrecurse - 1] = ep;
1327                 }
1328
1329         } else {
1330             /*
1331              * Do glob matching via fnmatch()
1332              */
1333
1334             for (i = 0; i < numpathnames; i++)
1335                 if (fnmatch(pathnames[i], ep->name, FNM_PATHNAME) == 0) {
1336                     eplist =
1337                         realloc(eplist,
1338                                 sizeof(struct DirEntry *) * ++numentries);
1339                     eplist[numentries - 1] = ep;
1340                     if (strlen(ep->name) > longestname)
1341                         longestname = strlen(ep->name);
1342                     if (Rflag)
1343                         if ((lvdata = GetVnode(ntohl(ep->fid.vnode)))
1344                             && lvdata->vnode->type == vDirectory
1345                             && !(strcmp(ep->name, ".") == 0
1346                                  || strcmp(ep->name, "..") == 0)) {
1347                             eprecurse =
1348                                 realloc(eprecurse,
1349                                         sizeof(struct DirEntry *) *
1350                                         ++numrecurse);
1351                             eprecurse[numrecurse - 1] = ep;
1352                         }
1353                     break;
1354                 }
1355         }
1356     }
1357
1358     qsort((void *)eplist, numentries, sizeof(struct DirEntry *),
1359           CompareDirEntry);
1360
1361     if (Rflag && eprecurse)
1362         qsort((void *)eprecurse, numrecurse, sizeof(struct DirEntry *),
1363               CompareDirEntry);
1364     /*
1365      * We don't have to do column printing if we have the -l or the -i
1366      * options.  Sigh, column printing is WAY TOO FUCKING COMPLICATED!
1367      */
1368
1369     if (!lflag && !iflag) {
1370         char c;
1371
1372         if (Fflag)
1373             longestname++;
1374
1375         longestname++;
1376
1377         numcols = termsize / longestname ? termsize / longestname : 1;
1378         numrows = numentries / numcols + (numentries % numcols ? 1 : 0);
1379
1380         for (i = 0; i < numrows; i++) {
1381             col = 0;
1382             while (col < numcols && (i + col * numrows) < numentries) {
1383                 ep = eplist[i + col++ * numrows];
1384                 if (Fflag) {
1385                     if (!(lvdata = GetVnode(ntohl(ep->fid.vnode))))
1386                         c = ' ';
1387                     else if (lvdata->vnode->type == vDirectory)
1388                         c = '/';
1389                     else if (lvdata->vnode->type == vSymlink)
1390                         c = '@';
1391                     else if (lvdata->vnode->modeBits & 0111 != 0)
1392                         c = '*';
1393                     else
1394                         c = ' ';
1395                     printf("%s%-*c", ep->name, longestname - strlen(ep->name),
1396                            c);
1397                 } else
1398                     printf("%-*s", longestname, ep->name);
1399             }
1400
1401             printf("\n");
1402         }
1403     } else if (iflag)
1404         for (i = 0; i < numentries; i++)
1405             if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
1406                 printf("%d.0.0\t%s\n",
1407                        vol->parentId ? vol->parentId : vol->id,
1408                        eplist[i]->name);
1409             else if (path)
1410                 printf("%d.%d.%d\t%s/%s\n", vol->id,
1411                        ntohl(eplist[i]->fid.vnode),
1412                        ntohl(eplist[i]->fid.vunique), path, eplist[i]->name);
1413             else
1414                 printf("%d.%d.%d\t%s\n", vol->id, ntohl(eplist[i]->fid.vnode),
1415                        ntohl(eplist[i]->fid.vunique), eplist[i]->name);
1416     else if (lflag) {
1417         for (i = 0; i < numentries; i++)
1418             if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
1419                 printf("----------   0 0        " "0                 0 %s\n",
1420                        eplist[i]->name);
1421             else {
1422                 switch (lvdata->vnode->type) {
1423                 case vDirectory:
1424                     printf("d");
1425                     break;
1426                 case vSymlink:
1427                     printf("l");
1428                     break;
1429                 default:
1430                     printf("-");
1431                 }
1432
1433                 for (j = 8; j >= 0; j--) {
1434                     if (lvdata->vnode->modeBits & (1 << j))
1435                         switch (j % 3) {
1436                         case 2:
1437                             printf("r");
1438                             break;
1439                         case 1:
1440                             printf("w");
1441                             break;
1442                         case 0:
1443                             printf("x");
1444                     } else
1445                         printf("-");
1446                 }
1447
1448                 printf(" %-3d %-8d %-8d %10d %s\n", lvdata->vnode->linkCount,
1449                        lvdata->vnode->owner, lvdata->vnode->group,
1450                        lvdata->vnode->length, eplist[i]->name);
1451             }
1452     }
1453
1454     free(eplist);
1455
1456     if (Rflag && eprecurse) {
1457         char *lpath;
1458         lpath = NULL;
1459         for (i = 0; i < numrecurse; i++) {
1460             if (verbose)
1461                 printf("\n%s:\n", eprecurse[i]->name);
1462             if (path) {
1463                 lpath = malloc(strlen(path) + strlen(eprecurse[i]->name) + 2);
1464                 if (lpath)
1465                     sprintf(lpath, "%s/%s", path, eprecurse[i]->name);
1466             }
1467             DirListInternal(GetVnode(ntohl(eprecurse[i]->fid.vnode)), NULL, 0,
1468                             lflag, iflag, Fflag, Rflag, verbose, vol, lpath);
1469             if (lpath) {
1470                 free(lpath);
1471                 lpath = NULL;
1472             }
1473         }
1474     }
1475
1476     if (eprecurse)
1477         free(eprecurse);
1478
1479     return;
1480 }
1481
1482
1483 /*
1484  * Directory name comparison function, used by qsort
1485  */
1486
1487 static int
1488 CompareDirEntry(const void *e1, const void *e2)
1489 {
1490     struct DirEntry **ep1 = (struct DirEntry **)e1;
1491     struct DirEntry **ep2 = (struct DirEntry **)e2;
1492
1493     return strcmp((*ep1)->name, (*ep2)->name);
1494 }
1495
1496 /*
1497  * Change a directory.  Return a pointer to our new vdata structure for
1498  * this directory.
1499  */
1500
1501 static struct vnodeData *
1502 ChangeDirectory(int argc, char **argv, struct vnodeData *vdatacwd)
1503 {
1504     struct vnodeData *newvdatacwd;
1505
1506     if (argc != 2) {
1507         fprintf(stderr, "Usage: %s directory\n", argv[0]);
1508         return NULL;
1509     }
1510
1511     if ((newvdatacwd = FindFile(vdatacwd, argv[1])) == NULL)
1512         return NULL;
1513
1514     if (newvdatacwd->vnode->type != vDirectory) {
1515         fprintf(stderr, "%s: Not a directory\n", argv[1]);
1516         return NULL;
1517     }
1518
1519     if (newvdatacwd->filedata == NULL) {
1520         fprintf(stderr, "%s: No directory data found.\n", argv[1]);
1521         return NULL;
1522     }
1523
1524     return newvdatacwd;
1525 }
1526
1527 /*
1528  * Copy a file from out of the dump file
1529  */
1530
1531 #define COPYBUFSIZE 8192
1532
1533 static void
1534 CopyFile(int argc, char **argv, struct vnodeData *vdatacwd, FILE * f)
1535 {
1536     struct vnodeData *vdata;
1537     FILE *out;
1538     long cur = 0;
1539     int bytes, ret;
1540     char buffer[COPYBUFSIZE];
1541
1542     if (argc != 3) {
1543         fprintf(stderr, "Usage: %s dumpfile destfile\n", argv[0]);
1544         return;
1545     }
1546
1547     if ((vdata = FindFile(vdatacwd, argv[1])) == NULL)
1548         return;
1549
1550     if (vdata->dumpdata == 0) {
1551         fprintf(stderr, "File %s has no data in dump file\n", argv[1]);
1552         return;
1553     }
1554
1555     if ((out = fopen(argv[2], "wb")) == NULL) {
1556         fprintf(stderr, "Open of %s failed: %s\n", argv[2], strerror(errno));
1557         return;
1558     }
1559
1560     if (fseek(f, vdata->dumpdata, SEEK_SET)) {
1561         fprintf(stderr, "Seek failed: %s\n", strerror(errno));
1562         fclose(out);
1563         return;
1564     }
1565
1566     while (cur < vdata->datalength) {
1567
1568         bytes =
1569             cur + COPYBUFSIZE <
1570             vdata->datalength ? COPYBUFSIZE : vdata->datalength - cur;
1571
1572         ret = fread(buffer, sizeof(char), bytes, f);
1573         if (ret != bytes) {
1574             if (ret != 0)
1575                 fprintf(stderr, "Short read (expected %d, " "got %d)\n",
1576                         bytes, ret);
1577             else
1578                 fprintf(stderr, "Error during read: %s\n", strerror(errno));
1579             fclose(out);
1580             return;
1581         }
1582
1583         ret = fwrite(buffer, sizeof(char), bytes, out);
1584         if (ret != bytes) {
1585             if (ret != 0)
1586                 fprintf(stderr, "Short write (expected %d, " "got %d)\n",
1587                         bytes, ret);
1588             else
1589                 fprintf(stderr, "Error during write: %s\n", strerror(errno));
1590             fclose(out);
1591             return;
1592         }
1593
1594         cur += bytes;
1595     }
1596
1597     fclose(out);
1598 }
1599
1600 /*
1601  * Copy a file from out of the dump file, by using the vnode
1602  */
1603
1604 static void
1605 CopyVnode(int argc, char *argv[], FILE * f)
1606 {
1607     struct vnodeData *vdata;
1608     FILE *out;
1609     long cur = 0;
1610     int bytes, ret;
1611     char buffer[COPYBUFSIZE];
1612     unsigned int vnode, uniquifier = 0;
1613
1614     if (argc != 3) {
1615         fprintf(stderr, "Usage: %s vnode[.uniqifier] destfile\n", argv[0]);
1616         return;
1617     }
1618
1619     ret = sscanf(argv[1], "%d.%d", &vnode, &uniquifier);
1620
1621     if (ret < 1) {
1622         fprintf(stderr, "Invalid file identifier: %s\n", argv[1]);
1623         return;
1624     }
1625
1626     if (!(vdata = GetVnode(vnode))) {
1627         fprintf(stderr, "Vnode %d not in dump file\n", vnode);
1628         return;
1629     }
1630
1631     if (ret == 2 && vdata->vnode->uniquifier != uniquifier) {
1632         fprintf(stderr,
1633                 "Specified uniquifier %d did not match "
1634                 "uniquifier %d found in dump file!\n", uniquifier,
1635                 vdata->vnode->uniquifier);
1636         return;
1637     }
1638
1639     if (vdata->dumpdata == 0) {
1640         fprintf(stderr, "File %s has no data in dump file\n", argv[1]);
1641         return;
1642     }
1643
1644     if ((out = fopen(argv[2], "wb")) == NULL) {
1645         fprintf(stderr, "Open of %s failed: %s\n", argv[2], strerror(errno));
1646         return;
1647     }
1648
1649     if (fseek(f, vdata->dumpdata, SEEK_SET)) {
1650         fprintf(stderr, "Seek failed: %s\n", strerror(errno));
1651         fclose(out);
1652         return;
1653     }
1654
1655     while (cur < vdata->datalength) {
1656
1657         bytes =
1658             cur + COPYBUFSIZE <
1659             vdata->datalength ? COPYBUFSIZE : vdata->datalength - cur;
1660
1661         ret = fread(buffer, sizeof(char), bytes, f);
1662         if (ret != bytes) {
1663             if (ret != 0)
1664                 fprintf(stderr, "Short read (expected %d, " "got %d)\n",
1665                         bytes, ret);
1666             else
1667                 fprintf(stderr, "Error during read: %s\n", strerror(errno));
1668             fclose(out);
1669             return;
1670         }
1671
1672         ret = fwrite(buffer, sizeof(char), bytes, out);
1673         if (ret != bytes) {
1674             if (ret != 0)
1675                 fprintf(stderr, "Short write (expected %d, " "got %d)\n",
1676                         bytes, ret);
1677             else
1678                 fprintf(stderr, "Error during write: %s\n", strerror(errno));
1679             fclose(out);
1680             return;
1681         }
1682
1683         cur += bytes;
1684     }
1685
1686     fclose(out);
1687 }
1688
1689 /*
1690  * Dump all residency filenames associated with a file, or all files
1691  * within a directory.
1692  */
1693
1694 static void
1695 DumpAllFiles(int argc, char **argv, struct vnodeData *vdatacwd,
1696              VolumeDiskData * vol)
1697 {
1698 #ifdef RESIDENCY
1699     struct vnodeData *vdata, *nvdata;
1700     struct DirCursor cursor;
1701     struct DirEntry *ep;
1702     FILE *f = stdout;
1703     int c, i;
1704     int dflag = 0, fflag = 0, errflg = 0;
1705
1706     optind = 1;
1707
1708     while ((c = getopt(argc, argv, "df:")) != EOF)
1709         switch (c) {
1710         case 'd':
1711             dflag++;
1712             break;
1713         case 'f':
1714             if ((f = fopen(optarg, "a")) == NULL) {
1715                 fprintf(stderr, "Cannot open \"%s\": %s\n", optarg,
1716                         strerror(errno));
1717                 return;
1718             }
1719             fflag++;
1720             break;
1721         case 'h':
1722         case '?':
1723         default:
1724             errflg++;
1725         }
1726
1727     if (errflg || argc == optind) {
1728         fprintf(stderr, "Usage: %s [-d] [-f filename] file " "[file ...]\n",
1729                 argv[0]);
1730         if (fflag)
1731             fclose(f);
1732         return;
1733     }
1734
1735     for (i = optind; i < argc; i++) {
1736
1737         if ((vdata = FindFile(vdatacwd, argv[i])) == NULL)
1738             continue;
1739
1740         if (vdata->vnode->type == vDirectory && !dflag) {
1741
1742             ResetDirCursor(&cursor, vdata);
1743
1744             while ((ep = ReadNextDir(&cursor, vdata)) != NULL) {
1745
1746                 if (!(nvdata = GetVnode(ntohl(ep->fid.vnode)))) {
1747                     fprintf(stderr,
1748                             "Cannot find vnode " "entry for %s (%d)\n",
1749                             ep->name, ntohl(ep->fid.vnode));
1750                     continue;
1751                 }
1752
1753
1754                 if (!fflag) {
1755                     printf("Residency locations for %s:\n", ep->name);
1756
1757                     if (nvdata->dumpdata)
1758                         printf("Local disk (in dump " "file)\n");
1759                 }
1760
1761                 DumpAllResidencies(f, nvdata, vol);
1762
1763             }
1764
1765         } else {
1766             if (!fflag) {
1767                 printf("Residency locations for %s:\n", argv[i]);
1768
1769                 if (vdata->dumpdata)
1770                     printf("Local disk (in dump file)\n");
1771             }
1772
1773             DumpAllResidencies(f, vdata, vol);
1774         }
1775     }
1776
1777     if (fflag)
1778         fclose(f);
1779 #else /* RESIDENCY */
1780     fprintf(stderr,
1781             "The \"file\" command is not available in the non-"
1782             "MRAFS version of dumptool.\n");
1783 #endif /* RESIDENCY */
1784     return;
1785 }
1786
1787 /*
1788  * Take a vnode, traverse the vnode chain, and dump out all files on
1789  * all residencies corresponding to that parent vnode.
1790  */
1791
1792 #ifdef RESIDENCY
1793 static void
1794 DumpAllResidencies(FILE * f, struct vnodeData *vdata,
1795                    struct VolumeDiskData *vol)
1796 {
1797     unsigned int nextVnodeNum;
1798
1799     while (nextVnodeNum = vdata->vnode->NextVnodeId) {
1800         if ((vdata = GetVnode(nextVnodeNum)) == NULL) {
1801             fprintf(stderr,
1802                     "We had a pointer to %lu in it's "
1803                     "vnode chain, but there\nisn't a record of "
1804                     "it!  The dump might be corrupt.\n", nextVnodeNum);
1805             return;
1806         }
1807
1808         if (vdata->vnode->type == vFidLookup)
1809             DumpVnodeFile(f, vdata->vnode, vol);
1810     }
1811
1812     return;
1813 }
1814 #endif
1815
1816
1817 /*
1818  * Given a directory vnode and a filename, return the vnode corresponding
1819  * to the file in that directory.
1820  * 
1821  * We now handle pathnames with directories in them.
1822  */
1823
1824 static struct vnodeData *
1825 FindFile(struct vnodeData *vdatacwd, char *filename)
1826 {
1827     struct DirHeader *dhp;
1828     struct DirEntry *ep;
1829     int i, num;
1830     struct vnodeData *vdata;
1831     char *c, newstr[MAXPATHLEN];
1832
1833     if (!vdatacwd->filedata) {
1834         fprintf(stderr, "There is no vnode data for this " "directory!\n");
1835         return NULL;
1836     }
1837
1838     /*
1839      * If we have a "/" in here, look up the vnode data for the
1840      * directory (everything before the "/") and use that as our
1841      * current directory.  We automagically handle multiple directories
1842      * by using FindFile recursively.
1843      */
1844
1845     if ((c = strrchr(filename, '/')) != NULL) {
1846
1847         strncpy(newstr, filename, c - filename);
1848         newstr[c - filename] = '\0';
1849
1850         if ((vdatacwd = FindFile(vdatacwd, newstr)) == NULL)
1851             return NULL;
1852
1853         if (vdatacwd->vnode->type != vDirectory) {
1854             fprintf(stderr, "%s: Not a directory\n", newstr);
1855             return NULL;
1856         }
1857
1858         filename = c + 1;
1859     }
1860
1861     dhp = (struct DirHeader *)vdatacwd->filedata;
1862
1863     i = DirHash(filename);
1864
1865     num = ntohs(dhp->hashTable[i]);
1866
1867     while (num) {
1868         ep = (struct DirEntry *)(vdatacwd->filedata + (num * 32));
1869         if (strcmp(ep->name, filename) == 0)
1870             break;
1871         num = ntohs(ep->next);
1872     }
1873
1874     if (!num) {
1875         fprintf(stderr, "%s: No such file or directory\n", filename);
1876         return NULL;
1877     }
1878
1879     if ((vdata = GetVnode(ntohl(ep->fid.vnode))) == NULL) {
1880         fprintf(stderr, "%s: No vnode information for %lu found\n", filename,
1881                 ntohl(ep->fid.vnode));
1882         return NULL;
1883     }
1884
1885     return vdata;
1886 }
1887
1888 /*
1889  * Reset a structure containing the current directory scan location
1890  */
1891
1892 static void
1893 ResetDirCursor(struct DirCursor *cursor, struct vnodeData *vdata)
1894 {
1895     struct DirHeader *dhp;
1896
1897     cursor->hashbucket = 0;
1898
1899     dhp = (struct DirHeader *)vdata->filedata;
1900
1901     cursor->entry = ntohs(dhp->hashTable[0]);
1902 }
1903
1904 /*
1905  * Given a cursor and a directory entry, return the next entry in the
1906  * directory.
1907  */
1908
1909 static struct DirEntry *
1910 ReadNextDir(struct DirCursor *cursor, struct vnodeData *vdata)
1911 {
1912     struct DirHeader *dhp;
1913     struct DirEntry *ep;
1914
1915     dhp = (struct DirHeader *)vdata->filedata;
1916
1917     if (cursor->entry) {
1918         ep = (struct DirEntry *)(vdata->filedata + (cursor->entry * 32));
1919         cursor->entry = ntohs(ep->next);
1920         return ep;
1921     } else {
1922         while (++(cursor->hashbucket) < NHASHENT) {
1923             cursor->entry = ntohs(dhp->hashTable[cursor->hashbucket]);
1924             if (cursor->entry) {
1925                 ep = (struct DirEntry *)(vdata->filedata +
1926                                          (cursor->entry * 32));
1927                 cursor->entry = ntohs(ep->next);
1928                 return ep;
1929             }
1930         }
1931     }
1932
1933     return NULL;
1934 }
1935
1936 /*
1937  * Given a string, split it up into components a la Unix argc/argv.
1938  *
1939  * This code is most stolen from ftp.
1940  */
1941
1942 static void
1943 MakeArgv(char *string, int *argc, char ***argv)
1944 {
1945     static char *largv[64];
1946     char **la = largv;
1947     char *s = string;
1948     static char argbuf[256];
1949     char *ap = argbuf;
1950
1951     *argc = 0;
1952     *argv = largv;
1953
1954     while (*la++ = GetToken(s, &s, ap, &ap))
1955         (*argc)++;
1956 }
1957
1958 /*
1959  * Return a pointer to the next token, and update the current string
1960  * position.
1961  */
1962
1963 static char *
1964 GetToken(char *string, char **nexttoken, char argbuf[], char *nextargbuf[])
1965 {
1966     char *sp = string;
1967     char *ap = argbuf;
1968     int got_one = 0;
1969
1970   S0:
1971     switch (*sp) {
1972
1973     case '\0':
1974         goto OUTTOKEN;
1975
1976     case ' ':
1977     case '\t':
1978         sp++;
1979         goto S0;
1980
1981     default:
1982         goto S1;
1983     }
1984
1985   S1:
1986     switch (*sp) {
1987
1988     case ' ':
1989     case '\t':
1990     case '\0':
1991         goto OUTTOKEN;          /* End of our token */
1992
1993     case '\\':
1994         sp++;
1995         goto S2;                /* Get next character */
1996
1997     case '"':
1998         sp++;
1999         goto S3;                /* Get quoted string */
2000
2001     default:
2002         *ap++ = *sp++;          /* Add a character to our token */
2003         got_one = 1;
2004         goto S1;
2005     }
2006
2007   S2:
2008     switch (*sp) {
2009
2010     case '\0':
2011         goto OUTTOKEN;
2012
2013     default:
2014         *ap++ = *sp++;
2015         got_one = 1;
2016         goto S1;
2017     }
2018
2019   S3:
2020     switch (*sp) {
2021
2022     case '\0':
2023         goto OUTTOKEN;
2024
2025     case '"':
2026         sp++;
2027         goto S1;
2028
2029     default:
2030         *ap++ = *sp++;
2031         got_one = 1;
2032         goto S3;
2033     }
2034
2035   OUTTOKEN:
2036     if (got_one)
2037         *ap++ = '\0';
2038     *nextargbuf = ap;           /* Update storage pointer */
2039     *nexttoken = sp;            /* Update token pointer */
2040
2041     return got_one ? argbuf : NULL;
2042 }
2043
2044 /*
2045  * Insert vnodes into our hash table.
2046  */
2047
2048 static struct vnodeData *
2049 InsertVnode(unsigned int vnodeNumber, struct VnodeDiskObject *vnode)
2050 {
2051     struct VnodeDiskObject *nvnode;
2052     struct vnodeData *vdata;
2053     static int curSmallVnodeIndex = 0;
2054     static int curLargeVnodeIndex = 0;
2055     struct vnodeData ***vnodeIndex;
2056     int *curIndex;
2057
2058     nvnode = (struct VnodeDiskObject *)malloc(sizeof(struct VnodeDiskObject));
2059
2060     if (!nvnode) {
2061         if (verbose)
2062             fprintf(stderr, "Unable to allocate space for vnode\n");
2063         return NULL;
2064     }
2065
2066     memcpy((void *)nvnode, (void *)vnode, sizeof(struct VnodeDiskObject));
2067
2068     if (vnodeNumber & 1) {
2069         vnodeIndex = &LargeVnodeIndex;
2070         curIndex = &curLargeVnodeIndex;
2071     } else {
2072         vnodeIndex = &SmallVnodeIndex;
2073         curIndex = &curSmallVnodeIndex;
2074     }
2075
2076     vdata = (struct vnodeData *)malloc(sizeof(struct vnodeData));
2077
2078     vdata->vnode = nvnode;
2079     vdata->vnodeNumber = vnodeNumber;
2080     vdata->dumpdata = 0;
2081     vdata->filedata = 0;
2082     vdata->datalength = 0;
2083
2084     (*vnodeIndex)[(*curIndex)++] = vdata;
2085
2086     return vdata;
2087 }
2088
2089 /*
2090  * Routine to retrieve a vnode from the hash table.
2091  */
2092
2093 static struct vnodeData *
2094 GetVnode(unsigned int vnodeNumber)
2095 {
2096     struct vnodeData vnode, *vnodep, **tmp;
2097
2098     vnode.vnodeNumber = vnodeNumber;
2099     vnodep = &vnode;
2100
2101     tmp = (struct vnodeData **)
2102         bsearch((void *)&vnodep,
2103                 vnodeNumber & 1 ? LargeVnodeIndex : SmallVnodeIndex,
2104                 vnodeNumber & 1 ? numLargeVnodes : numSmallVnodes,
2105                 sizeof(struct vnodeData *), CompareVnode);
2106
2107     return tmp ? *tmp : NULL;
2108 }
2109
2110 /*
2111  * Our comparator function for bsearch
2112  */
2113
2114 static int
2115 CompareVnode(const void *node1, const void *node2)
2116 {
2117     struct vnodeData **vnode1 = (struct vnodeData **)node1;
2118     struct vnodeData **vnode2 = (struct vnodeData **)node2;
2119
2120     if ((*vnode1)->vnodeNumber == (*vnode2)->vnodeNumber)
2121         return 0;
2122     else if ((*vnode1)->vnodeNumber > (*vnode2)->vnodeNumber)
2123         return 1;
2124     else
2125         return -1;
2126 }
2127
2128 #ifdef RESIDENCY
2129 /*
2130  * Dump out the filename corresponding to a particular vnode.
2131  *
2132  * This routine has the following dependancies:
2133  *
2134  * - Only will work on UFS filesystems at this point
2135  * - Has to talk to the rsserver.
2136  * - Can only determine UFS algorithm type when run on the same machine
2137  *   as the residency (unless you manually specify algorithm information)
2138  */
2139
2140 static int
2141 DumpVnodeFile(FILE * f, struct VnodeDiskObject *vnode, VolumeDiskData * vol)
2142 {
2143     static int rscache = 0;
2144     static rsaccessinfoList rsnlist = { 0, 0 };
2145     char MountPoint[MAXPATHLEN + 1];
2146     char FileName[MAXPATHLEN + 1];
2147     unsigned int Size, Level[4];
2148     unsigned int DeviceTag, Algorithm;
2149     FileSystems *FSInfo;
2150     int i, found, FSType, rsindex;
2151
2152     /*
2153      * Maybe we found out something about this residency via the
2154      * command-line; check that first.
2155      */
2156
2157     rsindex = ffs(VLkp_Residencies(vnode)) - 1;
2158
2159     /*
2160      * We need to get information from the rsserver (so we can
2161      * find out the device tag for a given residency).  If we
2162      * haven't cached that, talk to the rsserver to get it.
2163      * If we have info about this already, then don't talk to
2164      * the rsserver (this lets us still do disaster recovery if
2165      * MR-AFS is completely hosed).
2166      */
2167
2168     if (!rscache && rscmdlineinfo[rsindex].DeviceTag == -1) {
2169         int code;
2170
2171         code = ServerInitResidencyConnection();
2172
2173         if (code) {
2174             fprintf(stderr,
2175                     "ServerInitResidencyConnection failed " "with code %d\n",
2176                     code);
2177             return -1;
2178         }
2179
2180         code = rs_GetResidencySummary(ServerRequestorId, &rsnlist);
2181
2182         if (code) {
2183             fprintf(stderr, "rs_GetResidencySummary failed " "with code %d\n",
2184                     code);
2185             return -1;
2186         }
2187
2188         rscache = 1;
2189     }
2190
2191     /*
2192      * For a given residency (as specified in the vnode),
2193      * find out it's device tag number, either via the rsserver
2194      * or via the command line.
2195      */
2196
2197     if (rscmdlineinfo[rsindex].DeviceTag != -1) {
2198         DeviceTag = rscmdlineinfo[rsindex].DeviceTag;
2199         found = 1;
2200     } else
2201         for (i = 0, found = 0; (i < rsnlist.rsaccessinfoList_len) && (!found);
2202              i++) {
2203             if (rsnlist.rsaccessinfoList_val[i].id.residency ==
2204                 VLkp_Residencies(vnode)) {
2205                 found = 1;
2206                 DeviceTag = rsnlist.rsaccessinfoList_val[i].devicetagnumber;
2207                 break;
2208             }
2209         }
2210
2211     if (!found) {
2212         if (verbose)
2213             fprintf(stderr,
2214                     "Unable to find residency %d in "
2215                     "rsserver database, aborting\n", VLkp_Residencies(vnode));
2216         return -1;
2217     }
2218
2219     /*
2220      * Okay, now we've got the DeviceTag ... which we can use to
2221      * lookup the on-disk configuration information (which we
2222      * assume is locally stored).  We also need the DeviceTag to
2223      * print out which partition we're using (but that comes later).
2224      *
2225      * We lookup the on-disk configuration information by calling
2226      * Ufs_GetFSInfo() to get the configuration information on the
2227      * filesystems specified by the given DeviceTag.
2228      *
2229      * Before we call Ufs_GetFSInfo, check the command-line cache;
2230      * if we got something via the command-line, don't go to disk.
2231      */
2232
2233     if (rscmdlineinfo[rsindex].FSType == -1
2234         && Ufs_GetFSInfo(&FSInfo, DeviceTag)) {
2235         if (verbose)
2236             fprintf(stderr,
2237                     "Ufs_GetFSInfo failed for DeviceTag "
2238                     "%d, Residency %d\n", DeviceTag, VLkp_Residencies(vnode));
2239         return -1;
2240     }
2241
2242     /*
2243      * The FSInfo structure has the last two things we need: the
2244      * FSType (ufs, slowufs, etc etc), and the usage algorithm (which
2245      * ends up being how many directories are being used on the
2246      * residency filesystem).
2247      *
2248      * With these last two parameters, use routines stolen from
2249      * ufsname to generate the filename.
2250      *
2251      * (Actually, I lied - we also need the "Size" parameter, which
2252      * we can also get from FSInfo);
2253      */
2254
2255     if (rscmdlineinfo[rsindex].FSType != -1) {
2256         FSType = rscmdlineinfo[rsindex].FSType;
2257         Algorithm = rscmdlineinfo[rsindex].Algorithm;
2258         Size = rscmdlineinfo[rsindex].Size;
2259     } else {
2260         FSType = FSInfo->FileSystems_u.UfsInterface.FSType;
2261         Algorithm = FSInfo->FileSystems_u.UfsInterface.Algorithm;
2262         if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 0)
2263             Size = 0;
2264         else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 16)
2265             Size = 1;
2266         else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 256)
2267             Size = 2;
2268         else {
2269             if (verbose)
2270                 fprintf(stderr, "Unknown directory size %d, " "aborting\n",
2271                         FSInfo->FileSystems_u.UfsInterface.Directories[1]);
2272             return -1;
2273         }
2274     }
2275
2276     /*
2277      * First, generate our mount point from the DeviceTag and
2278      * FSType.
2279      */
2280
2281     DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTag, FSType);
2282
2283     /*
2284      * Then, generate the "level" (directory bitmasks) from the
2285      * file tags, size, and algorithm
2286      */
2287
2288     UfsTagsToLevel(VLkp_FileTag1(vnode), VLkp_FileTag2(vnode), Algorithm,
2289                    Size, Level, VLkp_ParentVnodeId(vnode),
2290                    VLkp_ParentUniquifierId(vnode));
2291
2292     /*
2293      * Finally, take the above information and generate the
2294      * corresponding filename (this macro ends up being a
2295      * sprintf() call)
2296      */
2297
2298     TAGSTONAME(FileName, MountPoint, Level, Directories[Size][1],
2299                vol->parentId, VLkp_ParentVnodeId(vnode),
2300                VLkp_ParentUniquifierId(vnode), Algorithm);
2301
2302     fprintf(f, "%s\n", FileName);
2303
2304     return 0;
2305 }
2306 #endif
2307
2308 /*
2309  * Read a 16 bit integer in network order
2310  */
2311
2312 static int
2313 ReadInt16(FILE * f, unsigned short *s)
2314 {
2315     unsigned short in;
2316
2317     if (fread((void *)&in, sizeof(in), 1, f) != 1) {
2318         if (verbose)
2319             fprintf(stderr, "ReadInt16 failed!\n");
2320         return -1;
2321     }
2322
2323     *s = ntohs(in);
2324
2325     return 0;
2326 }
2327
2328
2329 /*
2330  * Read a 32 bit integer in network order
2331  */
2332
2333 static int
2334 ReadInt32(FILE * f, unsigned int *i)
2335 {
2336     unsigned int in;
2337
2338     if (fread((void *)&in, sizeof(in), 1, f) != 1) {
2339         if (verbose)
2340             fprintf(stderr, "ReadInt32 failed!\n");
2341         return -1;
2342     }
2343
2344     *i = ntohl((unsigned long)in);
2345
2346     return 0;
2347 }
2348
2349 /*
2350  * Read a string from a dump file
2351  */
2352
2353 static int
2354 ReadString(FILE * f, char *string, int maxlen)
2355 {
2356     int c;
2357
2358     while (maxlen--) {
2359         if ((*string++ = getc(f)) == 0)
2360             break;
2361     }
2362
2363     /*
2364      * I'm not sure what the _hell_ this is supposed to do ...
2365      * but it was in the original dump code
2366      */
2367
2368     if (string[-1]) {
2369         while ((c = getc(f)) && c != EOF);
2370         string[-1] = 0;
2371     }
2372
2373     return 0;
2374 }
2375
2376 static int
2377 ReadByteString(FILE * f, void *s, int size)
2378 {
2379     unsigned char *c = (unsigned char *)s;
2380
2381     while (size--)
2382         *c++ = getc(f);
2383
2384     return 0;
2385 }
2386
2387 /*
2388  * The directory hashing algorithm used by AFS
2389  */
2390
2391 DirHash(string)
2392      register char *string;
2393 {
2394     /* Hash a string to a number between 0 and NHASHENT. */
2395     register unsigned char tc;
2396     register int hval;
2397     register int tval;
2398     hval = 0;
2399     while (tc = (*string++)) {
2400         hval *= 173;
2401         hval += tc;
2402     }
2403     tval = hval & (NHASHENT - 1);
2404 #ifdef AFS_CRAY_ENV             /* actually, any > 32 bit environment */
2405     if (tval == 0)
2406         return tval;
2407     else if (hval & 0x80000000)
2408         tval = NHASHENT - tval;
2409 #else /* AFS_CRAY_ENV */
2410     if (tval == 0)
2411         return tval;
2412     else if (hval < 0)
2413         tval = NHASHENT - tval;
2414 #endif /* AFS_CRAY_ENV */
2415     return tval;
2416 }
2417
2418 #ifdef RESIDENCY
2419 /*
2420  * Sigh, we need this for the AFS libraries
2421  */
2422
2423 int
2424 LogErrors(int level, char *a, char *b, char *c, char *d, char *e, char *f,
2425           char *g, char *h, char *i, char *j, char *k)
2426 {
2427     if (level <= 0) {
2428         fprintf(stderr, a, b, c, d, e, f, g, h, i, j, k);
2429     }
2430     return 0;
2431 }
2432
2433 /*
2434  * These are routines taken from AFS libraries and programs.  Most of
2435  * them are from ufsname.c, but a few are from the dir library (the dir
2436  * library has a bunch of hidden dependancies, so it's not suitable to
2437  * include it outright).
2438  */
2439
2440 UfsEntropiesToTags(HighEntropy, LowEntropy, Algorithm, FileTag1, FileTag2)
2441      uint32_t HighEntropy;
2442      uint32_t LowEntropy;
2443      uint32_t Algorithm;
2444      uint32_t *FileTag1;
2445      uint32_t *FileTag2;
2446 {
2447     int i;
2448
2449     if ((Algorithm > UFS_ALGORITHMS) || (Algorithm <= 0))
2450         return -1;
2451     *FileTag1 = 0;
2452     *FileTag2 = 0;
2453     for (i = 0; i < 32; ++i) {
2454         if (UfsEntropy[Algorithm - 1][i] < 32)
2455             *FileTag1 |=
2456                 ((HighEntropy & (1 << i)) ==
2457                  0) ? 0 : 1 << UfsEntropy[Algorithm - 1][i];
2458         else
2459             *FileTag2 |=
2460                 ((HighEntropy & (1 << i)) ==
2461                  0) ? 0 : 1 << (UfsEntropy[Algorithm - 1][i] - 32);
2462     }
2463     for (i = 32; i < 64; ++i) {
2464         if (UfsEntropy[Algorithm - 1][i] < 32)
2465             *FileTag1 |=
2466                 ((LowEntropy & (1 << (i - 32))) ==
2467                  0) ? 0 : 1 << UfsEntropy[Algorithm - 1][i];
2468         else
2469             *FileTag2 |=
2470                 ((LowEntropy & (1 << (i - 32))) ==
2471                  0) ? 0 : 1 << (UfsEntropy[Algorithm - 1][i] - 32);
2472     }
2473     return 0;
2474 }
2475
2476 uint32_t
2477 UfsTagsToHighEntropy(FileTag1, FileTag2, Algorithm)
2478      uint32_t FileTag1;
2479      uint32_t FileTag2;
2480      uint32_t Algorithm;
2481 {
2482     int i;
2483     uint32_t Value;
2484
2485     Value = 0;
2486     for (i = 0; i < 32; ++i) {
2487         if (UfsEntropy[Algorithm - 1][i] < 32)
2488             Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm - 1][i]))
2489                       == 0) ? 0 : 1 << i;
2490         else
2491             Value |=
2492                 ((FileTag2 & (1 << (UfsEntropy[Algorithm - 1][i] - 32))) ==
2493                  0) ? 0 : 1 << i;
2494     }
2495     return Value;
2496 }
2497
2498 uint32_t
2499 UfsTagsToLowEntropy(FileTag1, FileTag2, Algorithm)
2500      uint32_t FileTag1;
2501      uint32_t FileTag2;
2502      uint32_t Algorithm;
2503 {
2504     int i;
2505     uint32_t Value;
2506
2507     Value = 0;
2508     for (i = 32; i < 64; ++i) {
2509         if (UfsEntropy[Algorithm - 1][i] < 32)
2510             Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm - 1][i]))
2511                       == 0) ? 0 : 1 << (i - 32);
2512         else
2513             Value |=
2514                 ((FileTag2 & (1 << (UfsEntropy[Algorithm - 1][i] - 32))) ==
2515                  0) ? 0 : 1 << (i - 32);
2516     }
2517     return Value;
2518 }
2519
2520 UfsTagsToLevel(FileTag1, FileTag2, Algorithm, Size, Sections, vnode,
2521                Uniquifier)
2522      uint32_t FileTag1;
2523      uint32_t FileTag2;
2524      uint32_t Algorithm;
2525      uint32_t Size;
2526      uint32_t Sections[4];
2527      uint32_t vnode;
2528      uint32_t Uniquifier;
2529 {
2530     uint32_t HighEntropy;
2531     uint32_t LowEntropy;
2532
2533     switch (Algorithm) {
2534     case 1:
2535         LowEntropy = UfsTagsToLowEntropy(FileTag1, FileTag2, Algorithm);
2536         HighEntropy = UfsTagsToHighEntropy(FileTag1, FileTag2, Algorithm);
2537         Sections[0] = HighEntropy % Directories[Size][0];
2538         HighEntropy /= Directories[Size][0];
2539         if (Directories[Size][1]) {
2540             Sections[1] = HighEntropy % Directories[Size][1];
2541             HighEntropy /= Directories[Size][1];
2542             Sections[2] = HighEntropy;
2543             Sections[3] = LowEntropy;
2544         } else {
2545             Sections[1] = HighEntropy;
2546             Sections[2] = LowEntropy;
2547         }
2548         break;
2549     case 2:
2550         Sections[0] = FileTag1 & 0xff;
2551         if (Directories[Size][1]) {
2552             Sections[1] = Uniquifier & 0xff;
2553             if (Directories[Size][1] == 16)
2554                 Sections[1] &= 0xf;
2555             Sections[2] = FileTag1;
2556             Sections[3] = FileTag2;
2557         } else {
2558             Sections[1] = FileTag1;
2559             Sections[2] = FileTag2;
2560         }
2561         break;
2562     case 3:
2563         Sections[0] = FileTag1 & 0xff;
2564         if (Directories[Size][1]) {
2565             Sections[1] = (vnode >> 1) & 0xff;
2566             if (Directories[Size][1] == 16)
2567                 Sections[1] &= 0xf;
2568             Sections[2] = FileTag1;
2569             Sections[3] = FileTag2;
2570         } else {
2571             Sections[1] = FileTag1;
2572             Sections[2] = FileTag2;
2573         }
2574         break;
2575     default:
2576         fprintf(stderr, "UfsTagsToLevel: bad algorithm %lu!\n", Algorithm);
2577         return -1;
2578     }
2579     return 0;
2580 }
2581
2582 #include <afs/afscbdummies.h>
2583 #endif /* RESIDENCY */