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