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