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