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