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