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