6f6e44fef391dbb76cb951fe2fda511bb36b1c44
[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         LargeVnodeIndex = (struct vnodeData **)
1153             malloc(numDirVnodes * sizeof(struct vnodeData));
1154         SmallVnodeIndex = (struct vnodeData **)
1155             malloc(numFileVnodes * sizeof(struct vnodeData));
1156
1157         if (LargeVnodeIndex == NULL || SmallVnodeIndex == NULL) {
1158             if (verbose)
1159                 fprintf(stderr,
1160                         "Unable to allocate space " "for vnode tables\n");
1161             return -1;
1162         }
1163     }
1164
1165     if (verbose)
1166         fprintf(stderr, "%s vnode scan completed\n",
1167                 sizescan ? "Primary" : "Secondary");
1168
1169     return 0;
1170 }
1171
1172 /*
1173  * Perform an interactive restore
1174  *
1175  * Parsing the directory information is a pain, but other than that
1176  * we just use the other tools we already have in here.
1177  */
1178 #define CMDBUFSIZE      (AFSPATHMAX * 2)
1179 static void
1180 InteractiveRestore(FILE * f, VolumeDiskData * vol)
1181 {
1182     struct vnodeData *vdatacwd; /* Vnode data for our current dir */
1183     char cmdbuf[CMDBUFSIZE];
1184     int argc;
1185     char **argv;
1186
1187     /*
1188      * Let's see if we can at least get the data for our root directory.
1189      * If we can't, there's no way we can do an interactive restore.
1190      */
1191
1192     if ((vdatacwd = GetVnode(1)) == NULL) {
1193         fprintf(stderr, "No entry for our root vnode!  Aborting\n");
1194         return;
1195     }
1196
1197     if (!vdatacwd->filedata) {
1198         fprintf(stderr,
1199                 "There is no directory data for the root "
1200                 "vnode (1.1).  An interactive\nrestore is not "
1201                 "possible.\n");
1202         return;
1203     }
1204
1205     /*
1206      * If you're doing a selective dump correctly, then you should get all
1207      * directory vnode data.  But just in case you didn't, let the user
1208      * know there may be a problem.
1209      */
1210
1211     if (numNoDirData)
1212         fprintf(stderr,
1213                 "WARNING: %d directory vnodes had no file "
1214                 "data.  An interactive restore\nmay not be possible\n",
1215                 numNoDirData);
1216
1217     printf("> ");
1218     while (fgets(cmdbuf, CMDBUFSIZE, stdin)) {
1219
1220         cmdbuf[strlen(cmdbuf) - 1] = '\0';
1221
1222         if (strlen(cmdbuf) == 0) {
1223             printf("> ");
1224             continue;
1225         }
1226
1227         MakeArgv(cmdbuf, &argc, &argv);
1228
1229         if (strcmp(argv[0], "ls") == 0) {
1230             DirectoryList(argc, argv, vdatacwd, vol);
1231         } else if (strcmp(argv[0], "cd") == 0) {
1232             struct vnodeData *newvdata;
1233
1234             newvdata = ChangeDirectory(argc, argv, vdatacwd);
1235
1236             if (newvdata)
1237                 vdatacwd = newvdata;
1238         } else if (strcmp(argv[0], "file") == 0) {
1239             DumpAllFiles(argc, argv, vdatacwd, vol);
1240         } else if (strcmp(argv[0], "cp") == 0) {
1241             CopyFile(argc, argv, vdatacwd, f);
1242         } else if (strcmp(argv[0], "vcp") == 0) {
1243             CopyVnode(argc, argv, f);
1244         } else if (strcmp(argv[0], "quit") == 0
1245                    || strcmp(argv[0], "exit") == 0)
1246             break;
1247         else if (strcmp(argv[0], "?") == 0 || strcmp(argv[0], "help") == 0) {
1248             printf("Valid commands are:\n");
1249             printf("\tls\t\tList current directory\n");
1250             printf("\tcd\t\tChange current directory\n");
1251             printf("\tcp\t\tCopy file from dump\n");
1252             printf("\tvcp\t\tCopy file from dump (via vnode)\n");
1253 #ifdef RESIDENCY
1254             printf("\tfile\t\tList residency filenames\n");
1255 #endif /* RESIDENCY */
1256             printf("\tquit | exit\tExit program\n");
1257             printf("\thelp | ?\tBrief help\n");
1258         } else
1259             fprintf(stderr,
1260                     "Unknown command, \"%s\", enter "
1261                     "\"help\" for a list of commands.\n", argv[0]);
1262
1263         printf("> ");
1264     }
1265
1266     return;
1267 }
1268
1269 /*
1270  * Do a listing of all files in a directory.  Sigh, I wish this wasn't
1271  * so complicated.
1272  *
1273  * With the reorganizing, this is just a front-end to DirListInternal()
1274  */
1275
1276 static void
1277 DirectoryList(int argc, char **argv, struct vnodeData *vdata,
1278               VolumeDiskData * vol)
1279 {
1280     int errflg = 0, lflag = 0, iflag = 0, Fflag = 0, sflag = 0, Rflag = 0;
1281     int c;
1282
1283     optind = 1;
1284
1285     while ((c = getopt(argc, argv, "liFRs")) != EOF)
1286         switch (c) {
1287         case 'l':
1288             lflag++;
1289             break;
1290         case 'i':
1291             iflag++;
1292             break;
1293         case 'F':
1294             Fflag++;
1295             break;
1296         case 'R':
1297             Rflag++;
1298         case 's':
1299             sflag++;
1300             break;
1301         case '?':
1302         default:
1303             errflg++;
1304         }
1305
1306     if (errflg) {
1307         fprintf(stderr, "Usage: %s [-liFs] filename [filename ...]\n",
1308                 argv[0]);
1309         return;
1310     }
1311
1312     DirListInternal(vdata, &(argv[optind]), argc - optind, lflag, iflag,
1313                     Fflag, Rflag, 1, vol, NULL);
1314
1315     return;
1316 }
1317
1318 /*
1319  * Function that does the REAL work in terms of directory listing
1320  */
1321
1322 static void
1323 DirListInternal(struct vnodeData *vdata, char *pathnames[], int numpathnames,
1324                 int lflag, int iflag, int Fflag, int Rflag, int verbose,
1325                 VolumeDiskData * vol, char *path)
1326 {
1327     struct DirEntry *ep, **eplist = NULL, **eprecurse = NULL;
1328     struct DirCursor cursor;
1329     struct vnodeData *lvdata;
1330
1331     int i, j, numentries = 0, longestname = 0, numcols, col, numrows;
1332     int numrecurse = 0;
1333
1334     if (!vdata->filedata) {
1335         fprintf(stderr, "There is no vnode data for this " "directory!\n");
1336         return;
1337     }
1338
1339     ResetDirCursor(&cursor, vdata);
1340
1341     /*
1342      * Scan through the whole directory
1343      */
1344
1345     while ((ep = ReadNextDir(&cursor, vdata)) != NULL) {
1346
1347         /*
1348          * If we didn't get any filenames on the command line,
1349          * get them all.
1350          */
1351
1352         if (numpathnames == 0) {
1353             eplist =
1354                 realloc(eplist, sizeof(struct DirEntry *) * ++numentries);
1355             eplist[numentries - 1] = ep;
1356             if (strlen(ep->name) > longestname)
1357                 longestname = strlen(ep->name);
1358             if (Rflag)
1359                 if ((lvdata = GetVnode(ntohl(ep->fid.vnode)))
1360                     && lvdata->vnode->type == vDirectory
1361                     && !(strcmp(ep->name, ".") == 0
1362                          || strcmp(ep->name, "..") == 0)) {
1363                     eprecurse =
1364                         realloc(eprecurse,
1365                                 sizeof(struct DirEntry *) * ++numrecurse);
1366                     eprecurse[numrecurse - 1] = ep;
1367                 }
1368
1369         } else {
1370             /*
1371              * Do glob matching via fnmatch()
1372              */
1373
1374             for (i = 0; i < numpathnames; i++)
1375                 if (fnmatch(pathnames[i], ep->name, FNM_PATHNAME) == 0) {
1376                     eplist =
1377                         realloc(eplist,
1378                                 sizeof(struct DirEntry *) * ++numentries);
1379                     eplist[numentries - 1] = ep;
1380                     if (strlen(ep->name) > longestname)
1381                         longestname = strlen(ep->name);
1382                     if (Rflag)
1383                         if ((lvdata = GetVnode(ntohl(ep->fid.vnode)))
1384                             && lvdata->vnode->type == vDirectory
1385                             && !(strcmp(ep->name, ".") == 0
1386                                  || strcmp(ep->name, "..") == 0)) {
1387                             eprecurse =
1388                                 realloc(eprecurse,
1389                                         sizeof(struct DirEntry *) *
1390                                         ++numrecurse);
1391                             eprecurse[numrecurse - 1] = ep;
1392                         }
1393                     break;
1394                 }
1395         }
1396     }
1397
1398     qsort((void *)eplist, numentries, sizeof(struct DirEntry *),
1399           CompareDirEntry);
1400
1401     if (Rflag && eprecurse)
1402         qsort((void *)eprecurse, numrecurse, sizeof(struct DirEntry *),
1403               CompareDirEntry);
1404     /*
1405      * We don't have to do column printing if we have the -l or the -i
1406      * options.  Sigh, column printing is WAY TOO FUCKING COMPLICATED!
1407      */
1408
1409     if (!lflag && !iflag) {
1410         char c;
1411
1412         if (Fflag)
1413             longestname++;
1414
1415         longestname++;
1416
1417         numcols = termsize / longestname ? termsize / longestname : 1;
1418         numrows = numentries / numcols + (numentries % numcols ? 1 : 0);
1419
1420         for (i = 0; i < numrows; i++) {
1421             col = 0;
1422             while (col < numcols && (i + col * numrows) < numentries) {
1423                 ep = eplist[i + col++ * numrows];
1424                 if (Fflag) {
1425                     if (!(lvdata = GetVnode(ntohl(ep->fid.vnode))))
1426                         c = ' ';
1427                     else if (lvdata->vnode->type == vDirectory)
1428                         c = '/';
1429                     else if (lvdata->vnode->type == vSymlink)
1430                         c = '@';
1431                     else if ((lvdata->vnode->modeBits & 0111) != 0)
1432                         c = '*';
1433                     else
1434                         c = ' ';
1435                     printf("%s%-*c", ep->name, (int)(longestname -
1436                                                      strlen(ep->name)), c);
1437                 } else
1438                     printf("%-*s", longestname, ep->name);
1439             }
1440
1441             printf("\n");
1442         }
1443     } else if (iflag)
1444         for (i = 0; i < numentries; i++)
1445             if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
1446                 printf("%" AFS_VOLID_FMT ".0.0\t%s\n",
1447                        vol->parentId ? afs_printable_VolumeId_lu(vol->parentId)
1448                        : afs_printable_VolumeId_lu(vol->id),
1449                        eplist[i]->name);
1450             else if (path)
1451                 printf("%" AFS_VOLID_FMT ".%d.%d\t%s/%s\n",
1452                        afs_printable_VolumeId_lu(vol->id),
1453                        ntohl(eplist[i]->fid.vnode),
1454                        ntohl(eplist[i]->fid.vunique), path, eplist[i]->name);
1455             else
1456                 printf("%" AFS_VOLID_FMT ".%d.%d\t%s\n",
1457                        afs_printable_VolumeId_lu(vol->id),
1458                        ntohl(eplist[i]->fid.vnode),
1459                        ntohl(eplist[i]->fid.vunique), eplist[i]->name);
1460     else if (lflag) {
1461         for (i = 0; i < numentries; i++)
1462             if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode))))
1463                 printf("----------   0 0        " "0                 0 %s\n",
1464                        eplist[i]->name);
1465             else {
1466                 switch (lvdata->vnode->type) {
1467                 case vDirectory:
1468                     printf("d");
1469                     break;
1470                 case vSymlink:
1471                     printf("l");
1472                     break;
1473                 default:
1474                     printf("-");
1475                 }
1476
1477                 for (j = 8; j >= 0; j--) {
1478                     if (lvdata->vnode->modeBits & (1 << j))
1479                         switch (j % 3) {
1480                         case 2:
1481                             printf("r");
1482                             break;
1483                         case 1:
1484                             printf("w");
1485                             break;
1486                         case 0:
1487                             printf("x");
1488                     } else
1489                         printf("-");
1490                 }
1491
1492                 printf(" %-3d %-8d %-8d %10d %s\n", lvdata->vnode->linkCount,
1493                        lvdata->vnode->owner, lvdata->vnode->group,
1494                        lvdata->vnode->length, eplist[i]->name);
1495             }
1496     }
1497
1498     free(eplist);
1499
1500     if (Rflag && eprecurse) {
1501         char *lpath;
1502         lpath = NULL;
1503         for (i = 0; i < numrecurse; i++) {
1504             if (verbose)
1505                 printf("\n%s:\n", eprecurse[i]->name);
1506             if (path) {
1507                 lpath = malloc(strlen(path) + strlen(eprecurse[i]->name) + 2);
1508                 if (lpath)
1509                     sprintf(lpath, "%s/%s", path, eprecurse[i]->name);
1510             }
1511             DirListInternal(GetVnode(ntohl(eprecurse[i]->fid.vnode)), NULL, 0,
1512                             lflag, iflag, Fflag, Rflag, verbose, vol, lpath);
1513             if (lpath) {
1514                 free(lpath);
1515                 lpath = NULL;
1516             }
1517         }
1518     }
1519
1520     if (eprecurse)
1521         free(eprecurse);
1522
1523     return;
1524 }
1525
1526
1527 /*
1528  * Directory name comparison function, used by qsort
1529  */
1530
1531 static int
1532 CompareDirEntry(const void *e1, const void *e2)
1533 {
1534     struct DirEntry **ep1 = (struct DirEntry **)e1;
1535     struct DirEntry **ep2 = (struct DirEntry **)e2;
1536
1537     return strcmp((*ep1)->name, (*ep2)->name);
1538 }
1539
1540 /*
1541  * Change a directory.  Return a pointer to our new vdata structure for
1542  * this directory.
1543  */
1544
1545 static struct vnodeData *
1546 ChangeDirectory(int argc, char **argv, struct vnodeData *vdatacwd)
1547 {
1548     struct vnodeData *newvdatacwd;
1549
1550     if (argc != 2) {
1551         fprintf(stderr, "Usage: %s directory\n", argv[0]);
1552         return NULL;
1553     }
1554
1555     if ((newvdatacwd = FindFile(vdatacwd, argv[1])) == NULL)
1556         return NULL;
1557
1558     if (newvdatacwd->vnode->type != vDirectory) {
1559         fprintf(stderr, "%s: Not a directory\n", argv[1]);
1560         return NULL;
1561     }
1562
1563     if (newvdatacwd->filedata == NULL) {
1564         fprintf(stderr, "%s: No directory data found.\n", argv[1]);
1565         return NULL;
1566     }
1567
1568     return newvdatacwd;
1569 }
1570
1571 /*
1572  * Copy a file from out of the dump file
1573  */
1574
1575 #define COPYBUFSIZE 8192
1576
1577 static void
1578 CopyFile(int argc, char **argv, struct vnodeData *vdatacwd, FILE * f)
1579 {
1580     struct vnodeData *vdata;
1581     FILE *out;
1582     off64_t cur = 0;
1583     int bytes, ret;
1584     char buffer[COPYBUFSIZE];
1585
1586     if (argc != 3) {
1587         fprintf(stderr, "Usage: %s dumpfile destfile\n", argv[0]);
1588         return;
1589     }
1590
1591     if ((vdata = FindFile(vdatacwd, argv[1])) == NULL)
1592         return;
1593
1594     if (vdata->dumpdata == 0) {
1595         fprintf(stderr, "File %s has no data in dump file\n", argv[1]);
1596         return;
1597     }
1598
1599     if ((out = fopen(argv[2], "wb")) == NULL) {
1600         fprintf(stderr, "Open of %s failed: %s\n", argv[2], strerror(errno));
1601         return;
1602     }
1603
1604     if (fseeko64(f, vdata->dumpdata, SEEK_SET)) {
1605         fprintf(stderr, "Seek failed: %s\n", strerror(errno));
1606         fclose(out);
1607         return;
1608     }
1609
1610     while (cur < vdata->datalength) {
1611
1612         bytes =
1613             cur + COPYBUFSIZE <
1614             vdata->datalength ? COPYBUFSIZE : vdata->datalength - cur;
1615
1616         ret = fread(buffer, sizeof(char), bytes, f);
1617         if (ret != bytes) {
1618             if (ret != 0)
1619                 fprintf(stderr, "Short read (expected %d, " "got %d)\n",
1620                         bytes, ret);
1621             else
1622                 fprintf(stderr, "Error during read: %s\n", strerror(errno));
1623             fclose(out);
1624             return;
1625         }
1626
1627         ret = fwrite(buffer, sizeof(char), bytes, out);
1628         if (ret != bytes) {
1629             if (ret != 0)
1630                 fprintf(stderr, "Short write (expected %d, " "got %d)\n",
1631                         bytes, ret);
1632             else
1633                 fprintf(stderr, "Error during write: %s\n", strerror(errno));
1634             fclose(out);
1635             return;
1636         }
1637
1638         cur += bytes;
1639     }
1640
1641     fclose(out);
1642 }
1643
1644 /*
1645  * Copy a file from out of the dump file, by using the vnode
1646  */
1647
1648 static void
1649 CopyVnode(int argc, char *argv[], FILE * f)
1650 {
1651     struct vnodeData *vdata;
1652     FILE *out;
1653     off64_t cur = 0;
1654     int bytes, ret;
1655     char buffer[COPYBUFSIZE];
1656     unsigned int vnode, uniquifier = 0;
1657
1658     if (argc != 3) {
1659         fprintf(stderr, "Usage: %s vnode[.uniqifier] destfile\n", argv[0]);
1660         return;
1661     }
1662
1663     ret = sscanf(argv[1], "%d.%d", &vnode, &uniquifier);
1664
1665     if (ret < 1) {
1666         fprintf(stderr, "Invalid file identifier: %s\n", argv[1]);
1667         return;
1668     }
1669
1670     if (!(vdata = GetVnode(vnode))) {
1671         fprintf(stderr, "Vnode %d not in dump file\n", vnode);
1672         return;
1673     }
1674
1675     if (ret == 2 && vdata->vnode->uniquifier != uniquifier) {
1676         fprintf(stderr,
1677                 "Specified uniquifier %d did not match "
1678                 "uniquifier %d found in dump file!\n", uniquifier,
1679                 vdata->vnode->uniquifier);
1680         return;
1681     }
1682
1683     if (vdata->dumpdata == 0) {
1684         fprintf(stderr, "File %s has no data in dump file\n", argv[1]);
1685         return;
1686     }
1687
1688     if ((out = fopen(argv[2], "wb")) == NULL) {
1689         fprintf(stderr, "Open of %s failed: %s\n", argv[2], strerror(errno));
1690         return;
1691     }
1692
1693     if (fseeko64(f, vdata->dumpdata, SEEK_SET)) {
1694         fprintf(stderr, "Seek failed: %s\n", strerror(errno));
1695         fclose(out);
1696         return;
1697     }
1698
1699     while (cur < vdata->datalength) {
1700
1701         bytes =
1702             cur + COPYBUFSIZE <
1703             vdata->datalength ? COPYBUFSIZE : vdata->datalength - cur;
1704
1705         ret = fread(buffer, sizeof(char), bytes, f);
1706         if (ret != bytes) {
1707             if (ret != 0)
1708                 fprintf(stderr, "Short read (expected %d, " "got %d)\n",
1709                         bytes, ret);
1710             else
1711                 fprintf(stderr, "Error during read: %s\n", strerror(errno));
1712             fclose(out);
1713             return;
1714         }
1715
1716         ret = fwrite(buffer, sizeof(char), bytes, out);
1717         if (ret != bytes) {
1718             if (ret != 0)
1719                 fprintf(stderr, "Short write (expected %d, " "got %d)\n",
1720                         bytes, ret);
1721             else
1722                 fprintf(stderr, "Error during write: %s\n", strerror(errno));
1723             fclose(out);
1724             return;
1725         }
1726
1727         cur += bytes;
1728     }
1729
1730     fclose(out);
1731 }
1732
1733 /*
1734  * Dump all residency filenames associated with a file, or all files
1735  * within a directory.
1736  */
1737
1738 static void
1739 DumpAllFiles(int argc, char **argv, struct vnodeData *vdatacwd,
1740              VolumeDiskData * vol)
1741 {
1742 #ifdef RESIDENCY
1743     struct vnodeData *vdata, *nvdata;
1744     struct DirCursor cursor;
1745     struct DirEntry *ep;
1746     FILE *f = stdout;
1747     int c, i;
1748     int dflag = 0, fflag = 0, errflg = 0;
1749
1750     optind = 1;
1751
1752     while ((c = getopt(argc, argv, "df:")) != EOF)
1753         switch (c) {
1754         case 'd':
1755             dflag++;
1756             break;
1757         case 'f':
1758             if ((f = fopen(optarg, "a")) == NULL) {
1759                 fprintf(stderr, "Cannot open \"%s\": %s\n", optarg,
1760                         strerror(errno));
1761                 return;
1762             }
1763             fflag++;
1764             break;
1765         case 'h':
1766         case '?':
1767         default:
1768             errflg++;
1769         }
1770
1771     if (errflg || argc == optind) {
1772         fprintf(stderr, "Usage: %s [-d] [-f filename] file " "[file ...]\n",
1773                 argv[0]);
1774         if (fflag)
1775             fclose(f);
1776         return;
1777     }
1778
1779     for (i = optind; i < argc; i++) {
1780
1781         if ((vdata = FindFile(vdatacwd, argv[i])) == NULL)
1782             continue;
1783
1784         if (vdata->vnode->type == vDirectory && !dflag) {
1785
1786             ResetDirCursor(&cursor, vdata);
1787
1788             while ((ep = ReadNextDir(&cursor, vdata)) != NULL) {
1789
1790                 if (!(nvdata = GetVnode(ntohl(ep->fid.vnode)))) {
1791                     fprintf(stderr,
1792                             "Cannot find vnode " "entry for %s (%d)\n",
1793                             ep->name, ntohl(ep->fid.vnode));
1794                     continue;
1795                 }
1796
1797
1798                 if (!fflag) {
1799                     printf("Residency locations for %s:\n", ep->name);
1800
1801                     if (nvdata->dumpdata)
1802                         printf("Local disk (in dump " "file)\n");
1803                 }
1804
1805                 DumpAllResidencies(f, nvdata, vol);
1806
1807             }
1808
1809         } else {
1810             if (!fflag) {
1811                 printf("Residency locations for %s:\n", argv[i]);
1812
1813                 if (vdata->dumpdata)
1814                     printf("Local disk (in dump file)\n");
1815             }
1816
1817             DumpAllResidencies(f, vdata, vol);
1818         }
1819     }
1820
1821     if (fflag)
1822         fclose(f);
1823 #else /* RESIDENCY */
1824     fprintf(stderr,
1825             "The \"file\" command is not available in the non-"
1826             "MRAFS version of dumptool.\n");
1827 #endif /* RESIDENCY */
1828     return;
1829 }
1830
1831 /*
1832  * Take a vnode, traverse the vnode chain, and dump out all files on
1833  * all residencies corresponding to that parent vnode.
1834  */
1835
1836 #ifdef RESIDENCY
1837 static void
1838 DumpAllResidencies(FILE * f, struct vnodeData *vdata,
1839                    struct VolumeDiskData *vol)
1840 {
1841     unsigned int nextVnodeNum;
1842
1843     while (nextVnodeNum = vdata->vnode->NextVnodeId) {
1844         if ((vdata = GetVnode(nextVnodeNum)) == NULL) {
1845             fprintf(stderr,
1846                     "We had a pointer to %lu in it's "
1847                     "vnode chain, but there\nisn't a record of "
1848                     "it!  The dump might be corrupt.\n", nextVnodeNum);
1849             return;
1850         }
1851
1852         if (vdata->vnode->type == vFidLookup)
1853             DumpVnodeFile(f, vdata->vnode, vol);
1854     }
1855
1856     return;
1857 }
1858 #endif
1859
1860
1861 /*
1862  * Given a directory vnode and a filename, return the vnode corresponding
1863  * to the file in that directory.
1864  *
1865  * We now handle pathnames with directories in them.
1866  */
1867
1868 static struct vnodeData *
1869 FindFile(struct vnodeData *vdatacwd, char *filename)
1870 {
1871     struct DirHeader *dhp;
1872     struct DirEntry *ep;
1873     int i, num;
1874     struct vnodeData *vdata;
1875     char *c, newstr[MAXPATHLEN];
1876
1877     if (!vdatacwd->filedata) {
1878         fprintf(stderr, "There is no vnode data for this " "directory!\n");
1879         return NULL;
1880     }
1881
1882     /*
1883      * If we have a "/" in here, look up the vnode data for the
1884      * directory (everything before the "/") and use that as our
1885      * current directory.  We automagically handle multiple directories
1886      * by using FindFile recursively.
1887      */
1888
1889     if ((c = strrchr(filename, '/')) != NULL) {
1890
1891         strncpy(newstr, filename, c - filename);
1892         newstr[c - filename] = '\0';
1893
1894         if ((vdatacwd = FindFile(vdatacwd, newstr)) == NULL)
1895             return NULL;
1896
1897         if (vdatacwd->vnode->type != vDirectory) {
1898             fprintf(stderr, "%s: Not a directory\n", newstr);
1899             return NULL;
1900         }
1901
1902         filename = c + 1;
1903     }
1904
1905     dhp = (struct DirHeader *)vdatacwd->filedata;
1906
1907     i = DirHash(filename);
1908
1909     num = ntohs(dhp->hashTable[i]);
1910
1911     while (num) {
1912         ep = (struct DirEntry *)(vdatacwd->filedata + (num * 32));
1913         if (strcmp(ep->name, filename) == 0)
1914             break;
1915         num = ntohs(ep->next);
1916     }
1917
1918     if (!num) {
1919         fprintf(stderr, "%s: No such file or directory\n", filename);
1920         return NULL;
1921     }
1922
1923     if ((vdata = GetVnode(ntohl(ep->fid.vnode))) == NULL) {
1924         fprintf(stderr, "%s: No vnode information for %u found\n", filename,
1925                 ntohl(ep->fid.vnode));
1926         return NULL;
1927     }
1928
1929     return vdata;
1930 }
1931
1932 /*
1933  * Reset a structure containing the current directory scan location
1934  */
1935
1936 static void
1937 ResetDirCursor(struct DirCursor *cursor, struct vnodeData *vdata)
1938 {
1939     struct DirHeader *dhp;
1940
1941     cursor->hashbucket = 0;
1942
1943     dhp = (struct DirHeader *)vdata->filedata;
1944
1945     cursor->entry = ntohs(dhp->hashTable[0]);
1946 }
1947
1948 /*
1949  * Given a cursor and a directory entry, return the next entry in the
1950  * directory.
1951  */
1952
1953 static struct DirEntry *
1954 ReadNextDir(struct DirCursor *cursor, struct vnodeData *vdata)
1955 {
1956     struct DirHeader *dhp;
1957     struct DirEntry *ep;
1958
1959     dhp = (struct DirHeader *)vdata->filedata;
1960
1961     if (cursor->entry) {
1962         ep = (struct DirEntry *)(vdata->filedata + (cursor->entry * 32));
1963         cursor->entry = ntohs(ep->next);
1964         return ep;
1965     } else {
1966         while (++(cursor->hashbucket) < NHASHENT) {
1967             cursor->entry = ntohs(dhp->hashTable[cursor->hashbucket]);
1968             if (cursor->entry) {
1969                 ep = (struct DirEntry *)(vdata->filedata +
1970                                          (cursor->entry * 32));
1971                 cursor->entry = ntohs(ep->next);
1972                 return ep;
1973             }
1974         }
1975     }
1976
1977     return NULL;
1978 }
1979
1980 /*
1981  * Given a string, split it up into components a la Unix argc/argv.
1982  *
1983  * This code is most stolen from ftp.
1984  */
1985
1986 static void
1987 MakeArgv(char *string, int *argc, char ***argv)
1988 {
1989     static char *largv[64];
1990     char **la = largv;
1991     char *s = string;
1992     static char argbuf[CMDBUFSIZE];
1993     char *ap = argbuf;
1994
1995     *argc = 0;
1996     *argv = largv;
1997
1998     while ((*la++ = GetToken(s, &s, ap, &ap)) != NULL)
1999         (*argc)++;
2000 }
2001
2002 /*
2003  * Return a pointer to the next token, and update the current string
2004  * position.
2005  */
2006
2007 static char *
2008 GetToken(char *string, char **nexttoken, char argbuf[], char *nextargbuf[])
2009 {
2010     char *sp = string;
2011     char *ap = argbuf;
2012     int got_one = 0;
2013
2014   S0:
2015     switch (*sp) {
2016
2017     case '\0':
2018         goto OUTTOKEN;
2019
2020     case ' ':
2021     case '\t':
2022         sp++;
2023         goto S0;
2024
2025     default:
2026         goto S1;
2027     }
2028
2029   S1:
2030     switch (*sp) {
2031
2032     case ' ':
2033     case '\t':
2034     case '\0':
2035         goto OUTTOKEN;          /* End of our token */
2036
2037     case '\\':
2038         sp++;
2039         goto S2;                /* Get next character */
2040
2041     case '"':
2042         sp++;
2043         goto S3;                /* Get quoted string */
2044
2045     default:
2046         *ap++ = *sp++;          /* Add a character to our token */
2047         got_one = 1;
2048         goto S1;
2049     }
2050
2051   S2:
2052     switch (*sp) {
2053
2054     case '\0':
2055         goto OUTTOKEN;
2056
2057     default:
2058         *ap++ = *sp++;
2059         got_one = 1;
2060         goto S1;
2061     }
2062
2063   S3:
2064     switch (*sp) {
2065
2066     case '\0':
2067         goto OUTTOKEN;
2068
2069     case '"':
2070         sp++;
2071         goto S1;
2072
2073     default:
2074         *ap++ = *sp++;
2075         got_one = 1;
2076         goto S3;
2077     }
2078
2079   OUTTOKEN:
2080     if (got_one)
2081         *ap++ = '\0';
2082     *nextargbuf = ap;           /* Update storage pointer */
2083     *nexttoken = sp;            /* Update token pointer */
2084
2085     return got_one ? argbuf : NULL;
2086 }
2087
2088 /*
2089  * Insert vnodes into our hash table.
2090  */
2091
2092 static struct vnodeData *
2093 InsertVnode(unsigned int vnodeNumber, struct VnodeDiskObject *vnode)
2094 {
2095     struct VnodeDiskObject *nvnode;
2096     struct vnodeData *vdata;
2097     static int curSmallVnodeIndex = 0;
2098     static int curLargeVnodeIndex = 0;
2099     struct vnodeData ***vnodeIndex;
2100     int *curIndex;
2101
2102     nvnode = (struct VnodeDiskObject *)malloc(sizeof(struct VnodeDiskObject));
2103
2104     if (!nvnode) {
2105         if (verbose)
2106             fprintf(stderr, "Unable to allocate space for vnode\n");
2107         return NULL;
2108     }
2109
2110     memcpy((void *)nvnode, (void *)vnode, sizeof(struct VnodeDiskObject));
2111
2112     if (vnodeNumber & 1) {
2113         vnodeIndex = &LargeVnodeIndex;
2114         curIndex = &curLargeVnodeIndex;
2115     } else {
2116         vnodeIndex = &SmallVnodeIndex;
2117         curIndex = &curSmallVnodeIndex;
2118     }
2119
2120     vdata = (struct vnodeData *)malloc(sizeof(struct vnodeData));
2121
2122     vdata->vnode = nvnode;
2123     vdata->vnodeNumber = vnodeNumber;
2124     vdata->dumpdata = 0;
2125     vdata->filedata = 0;
2126     vdata->datalength = 0;
2127
2128     (*vnodeIndex)[(*curIndex)++] = vdata;
2129
2130     return vdata;
2131 }
2132
2133 /*
2134  * Routine to retrieve a vnode from the hash table.
2135  */
2136
2137 static struct vnodeData *
2138 GetVnode(unsigned int vnodeNumber)
2139 {
2140     struct vnodeData vnode, *vnodep, **tmp;
2141
2142     vnode.vnodeNumber = vnodeNumber;
2143     vnodep = &vnode;
2144
2145     tmp = (struct vnodeData **)
2146         bsearch((void *)&vnodep,
2147                 vnodeNumber & 1 ? LargeVnodeIndex : SmallVnodeIndex,
2148                 vnodeNumber & 1 ? numLargeVnodes : numSmallVnodes,
2149                 sizeof(struct vnodeData *), CompareVnode);
2150
2151     return tmp ? *tmp : NULL;
2152 }
2153
2154 /*
2155  * Our comparator function for bsearch
2156  */
2157
2158 static int
2159 CompareVnode(const void *node1, const void *node2)
2160 {
2161     struct vnodeData **vnode1 = (struct vnodeData **)node1;
2162     struct vnodeData **vnode2 = (struct vnodeData **)node2;
2163
2164     if ((*vnode1)->vnodeNumber == (*vnode2)->vnodeNumber)
2165         return 0;
2166     else if ((*vnode1)->vnodeNumber > (*vnode2)->vnodeNumber)
2167         return 1;
2168     else
2169         return -1;
2170 }
2171
2172 #ifdef RESIDENCY
2173 /*
2174  * Dump out the filename corresponding to a particular vnode.
2175  *
2176  * This routine has the following dependancies:
2177  *
2178  * - Only will work on UFS filesystems at this point
2179  * - Has to talk to the rsserver.
2180  * - Can only determine UFS algorithm type when run on the same machine
2181  *   as the residency (unless you manually specify algorithm information)
2182  */
2183
2184 static int
2185 DumpVnodeFile(FILE * f, struct VnodeDiskObject *vnode, VolumeDiskData * vol)
2186 {
2187     static int rscache = 0;
2188     static rsaccessinfoList rsnlist = { 0, 0 };
2189     char MountPoint[MAXPATHLEN + 1];
2190     char FileName[MAXPATHLEN + 1];
2191     unsigned int Size, Level[4];
2192     unsigned int DeviceTag, Algorithm;
2193     FileSystems *FSInfo;
2194     int i, found, FSType, rsindex;
2195
2196     /*
2197      * Maybe we found out something about this residency via the
2198      * command-line; check that first.
2199      */
2200
2201     rsindex = ffs(VLkp_Residencies(vnode)) - 1;
2202
2203     /*
2204      * We need to get information from the rsserver (so we can
2205      * find out the device tag for a given residency).  If we
2206      * haven't cached that, talk to the rsserver to get it.
2207      * If we have info about this already, then don't talk to
2208      * the rsserver (this lets us still do disaster recovery if
2209      * MR-AFS is completely hosed).
2210      */
2211
2212     if (!rscache && rscmdlineinfo[rsindex].DeviceTag == -1) {
2213         int code;
2214
2215         code = ServerInitResidencyConnection();
2216
2217         if (code) {
2218             fprintf(stderr,
2219                     "ServerInitResidencyConnection failed " "with code %d\n",
2220                     code);
2221             return -1;
2222         }
2223
2224         code = rs_GetResidencySummary(ServerRequestorId, &rsnlist);
2225
2226         if (code) {
2227             fprintf(stderr, "rs_GetResidencySummary failed " "with code %d\n",
2228                     code);
2229             return -1;
2230         }
2231
2232         rscache = 1;
2233     }
2234
2235     /*
2236      * For a given residency (as specified in the vnode),
2237      * find out it's device tag number, either via the rsserver
2238      * or via the command line.
2239      */
2240
2241     if (rscmdlineinfo[rsindex].DeviceTag != -1) {
2242         DeviceTag = rscmdlineinfo[rsindex].DeviceTag;
2243         found = 1;
2244     } else
2245         for (i = 0, found = 0; (i < rsnlist.rsaccessinfoList_len) && (!found);
2246              i++) {
2247             if (rsnlist.rsaccessinfoList_val[i].id.residency ==
2248                 VLkp_Residencies(vnode)) {
2249                 found = 1;
2250                 DeviceTag = rsnlist.rsaccessinfoList_val[i].devicetagnumber;
2251                 break;
2252             }
2253         }
2254
2255     if (!found) {
2256         if (verbose)
2257             fprintf(stderr,
2258                     "Unable to find residency %d in "
2259                     "rsserver database, aborting\n", VLkp_Residencies(vnode));
2260         return -1;
2261     }
2262
2263     /*
2264      * Okay, now we've got the DeviceTag ... which we can use to
2265      * lookup the on-disk configuration information (which we
2266      * assume is locally stored).  We also need the DeviceTag to
2267      * print out which partition we're using (but that comes later).
2268      *
2269      * We lookup the on-disk configuration information by calling
2270      * Ufs_GetFSInfo() to get the configuration information on the
2271      * filesystems specified by the given DeviceTag.
2272      *
2273      * Before we call Ufs_GetFSInfo, check the command-line cache;
2274      * if we got something via the command-line, don't go to disk.
2275      */
2276
2277     if (rscmdlineinfo[rsindex].FSType == -1
2278         && Ufs_GetFSInfo(&FSInfo, DeviceTag)) {
2279         if (verbose)
2280             fprintf(stderr,
2281                     "Ufs_GetFSInfo failed for DeviceTag "
2282                     "%d, Residency %d\n", DeviceTag, VLkp_Residencies(vnode));
2283         return -1;
2284     }
2285
2286     /*
2287      * The FSInfo structure has the last two things we need: the
2288      * FSType (ufs, slowufs, etc etc), and the usage algorithm (which
2289      * ends up being how many directories are being used on the
2290      * residency filesystem).
2291      *
2292      * With these last two parameters, use routines stolen from
2293      * ufsname to generate the filename.
2294      *
2295      * (Actually, I lied - we also need the "Size" parameter, which
2296      * we can also get from FSInfo);
2297      */
2298
2299     if (rscmdlineinfo[rsindex].FSType != -1) {
2300         FSType = rscmdlineinfo[rsindex].FSType;
2301         Algorithm = rscmdlineinfo[rsindex].Algorithm;
2302         Size = rscmdlineinfo[rsindex].Size;
2303     } else {
2304         FSType = FSInfo->FileSystems_u.UfsInterface.FSType;
2305         Algorithm = FSInfo->FileSystems_u.UfsInterface.Algorithm;
2306         if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 0)
2307             Size = 0;
2308         else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 16)
2309             Size = 1;
2310         else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 256)
2311             Size = 2;
2312         else {
2313             if (verbose)
2314                 fprintf(stderr, "Unknown directory size %d, " "aborting\n",
2315                         FSInfo->FileSystems_u.UfsInterface.Directories[1]);
2316             return -1;
2317         }
2318     }
2319
2320     /*
2321      * First, generate our mount point from the DeviceTag and
2322      * FSType.
2323      */
2324
2325     DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTag, FSType);
2326
2327     /*
2328      * Then, generate the "level" (directory bitmasks) from the
2329      * file tags, size, and algorithm
2330      */
2331
2332     UfsTagsToLevel(VLkp_FileTag1(vnode), VLkp_FileTag2(vnode), Algorithm,
2333                    Size, Level, VLkp_ParentVnodeId(vnode),
2334                    VLkp_ParentUniquifierId(vnode));
2335
2336     /*
2337      * Finally, take the above information and generate the
2338      * corresponding filename (this macro ends up being a
2339      * sprintf() call)
2340      */
2341
2342     TAGSTONAME(FileName, MountPoint, Level, Directories[Size][1],
2343                vol->parentId, VLkp_ParentVnodeId(vnode),
2344                VLkp_ParentUniquifierId(vnode), Algorithm);
2345
2346     fprintf(f, "%s\n", FileName);
2347
2348     return 0;
2349 }
2350 #endif
2351
2352 /*
2353  * Read a 16 bit integer in network order
2354  */
2355
2356 static int
2357 ReadInt16(FILE * f, unsigned short *s)
2358 {
2359     unsigned short in;
2360
2361     if (fread((void *)&in, sizeof(in), 1, f) != 1) {
2362         if (verbose)
2363             fprintf(stderr, "ReadInt16 failed!\n");
2364         return -1;
2365     }
2366
2367     *s = ntohs(in);
2368
2369     return 0;
2370 }
2371
2372
2373 /*
2374  * Read a 32 bit integer in network order
2375  */
2376
2377 static int
2378 ReadInt32(FILE * f, unsigned int *i)
2379 {
2380     unsigned int in;
2381
2382     if (fread((void *)&in, sizeof(in), 1, f) != 1) {
2383         if (verbose)
2384             fprintf(stderr, "ReadInt32 failed!\n");
2385         return -1;
2386     }
2387
2388     *i = ntohl((unsigned long)in);
2389
2390     return 0;
2391 }
2392
2393 /*
2394  * Read a string from a dump file
2395  */
2396
2397 static int
2398 ReadString(FILE * f, char *string, int maxlen)
2399 {
2400     int c;
2401
2402     while (maxlen--) {
2403         if ((*string++ = getc(f)) == 0)
2404             break;
2405     }
2406
2407     /*
2408      * I'm not sure what the _hell_ this is supposed to do ...
2409      * but it was in the original dump code
2410      */
2411
2412     if (string[-1]) {
2413         while ((c = getc(f)) && c != EOF);
2414         string[-1] = 0;
2415     }
2416
2417     return 0;
2418 }
2419
2420 static int
2421 ReadByteString(FILE * f, void *s, int size)
2422 {
2423     unsigned char *c = (unsigned char *)s;
2424
2425     while (size--)
2426         *c++ = getc(f);
2427
2428     return 0;
2429 }
2430
2431 /*
2432  * The directory hashing algorithm used by AFS
2433  */
2434
2435 static int
2436 DirHash(char *string)
2437 {
2438     /* Hash a string to a number between 0 and NHASHENT. */
2439     unsigned char tc;
2440     int hval;
2441     int tval;
2442     hval = 0;
2443     while ((tc = (*string++)) != '\0') {
2444         hval *= 173;
2445         hval += tc;
2446     }
2447     tval = hval & (NHASHENT - 1);
2448 #ifdef AFS_CRAY_ENV             /* actually, any > 32 bit environment */
2449     if (tval == 0)
2450         return tval;
2451     else if (hval & 0x80000000)
2452         tval = NHASHENT - tval;
2453 #else /* AFS_CRAY_ENV */
2454     if (tval == 0)
2455         return tval;
2456     else if (hval < 0)
2457         tval = NHASHENT - tval;
2458 #endif /* AFS_CRAY_ENV */
2459     return tval;
2460 }
2461
2462 #ifdef RESIDENCY
2463 /*
2464  * Sigh, we need this for the AFS libraries
2465  */
2466
2467 int
2468 LogErrors(int level, char *a, char *b, char *c, char *d, char *e, char *f,
2469           char *g, char *h, char *i, char *j, char *k)
2470 {
2471     if (level <= 0) {
2472         fprintf(stderr, a, b, c, d, e, f, g, h, i, j, k);
2473     }
2474     return 0;
2475 }
2476
2477 /*
2478  * These are routines taken from AFS libraries and programs.  Most of
2479  * them are from ufsname.c, but a few are from the dir library (the dir
2480  * library has a bunch of hidden dependancies, so it's not suitable to
2481  * include it outright).
2482  */
2483
2484 UfsEntropiesToTags(HighEntropy, LowEntropy, Algorithm, FileTag1, FileTag2)
2485      uint32_t HighEntropy;
2486      uint32_t LowEntropy;
2487      uint32_t Algorithm;
2488      uint32_t *FileTag1;
2489      uint32_t *FileTag2;
2490 {
2491     int i;
2492
2493     if ((Algorithm > UFS_ALGORITHMS) || (Algorithm <= 0))
2494         return -1;
2495     *FileTag1 = 0;
2496     *FileTag2 = 0;
2497     for (i = 0; i < 32; ++i) {
2498         if (UfsEntropy[Algorithm - 1][i] < 32)
2499             *FileTag1 |=
2500                 ((HighEntropy & (1 << i)) ==
2501                  0) ? 0 : 1 << UfsEntropy[Algorithm - 1][i];
2502         else
2503             *FileTag2 |=
2504                 ((HighEntropy & (1 << i)) ==
2505                  0) ? 0 : 1 << (UfsEntropy[Algorithm - 1][i] - 32);
2506     }
2507     for (i = 32; i < 64; ++i) {
2508         if (UfsEntropy[Algorithm - 1][i] < 32)
2509             *FileTag1 |=
2510                 ((LowEntropy & (1 << (i - 32))) ==
2511                  0) ? 0 : 1 << UfsEntropy[Algorithm - 1][i];
2512         else
2513             *FileTag2 |=
2514                 ((LowEntropy & (1 << (i - 32))) ==
2515                  0) ? 0 : 1 << (UfsEntropy[Algorithm - 1][i] - 32);
2516     }
2517     return 0;
2518 }
2519
2520 uint32_t
2521 UfsTagsToHighEntropy(FileTag1, FileTag2, Algorithm)
2522      uint32_t FileTag1;
2523      uint32_t FileTag2;
2524      uint32_t Algorithm;
2525 {
2526     int i;
2527     uint32_t Value;
2528
2529     Value = 0;
2530     for (i = 0; i < 32; ++i) {
2531         if (UfsEntropy[Algorithm - 1][i] < 32)
2532             Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm - 1][i]))
2533                       == 0) ? 0 : 1 << i;
2534         else
2535             Value |=
2536                 ((FileTag2 & (1 << (UfsEntropy[Algorithm - 1][i] - 32))) ==
2537                  0) ? 0 : 1 << i;
2538     }
2539     return Value;
2540 }
2541
2542 uint32_t
2543 UfsTagsToLowEntropy(FileTag1, FileTag2, Algorithm)
2544      uint32_t FileTag1;
2545      uint32_t FileTag2;
2546      uint32_t Algorithm;
2547 {
2548     int i;
2549     uint32_t Value;
2550
2551     Value = 0;
2552     for (i = 32; i < 64; ++i) {
2553         if (UfsEntropy[Algorithm - 1][i] < 32)
2554             Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm - 1][i]))
2555                       == 0) ? 0 : 1 << (i - 32);
2556         else
2557             Value |=
2558                 ((FileTag2 & (1 << (UfsEntropy[Algorithm - 1][i] - 32))) ==
2559                  0) ? 0 : 1 << (i - 32);
2560     }
2561     return Value;
2562 }
2563
2564 UfsTagsToLevel(FileTag1, FileTag2, Algorithm, Size, Sections, vnode,
2565                Uniquifier)
2566      uint32_t FileTag1;
2567      uint32_t FileTag2;
2568      uint32_t Algorithm;
2569      uint32_t Size;
2570      uint32_t Sections[4];
2571      uint32_t vnode;
2572      uint32_t Uniquifier;
2573 {
2574     uint32_t HighEntropy;
2575     uint32_t LowEntropy;
2576
2577     switch (Algorithm) {
2578     case 1:
2579         LowEntropy = UfsTagsToLowEntropy(FileTag1, FileTag2, Algorithm);
2580         HighEntropy = UfsTagsToHighEntropy(FileTag1, FileTag2, Algorithm);
2581         Sections[0] = HighEntropy % Directories[Size][0];
2582         HighEntropy /= Directories[Size][0];
2583         if (Directories[Size][1]) {
2584             Sections[1] = HighEntropy % Directories[Size][1];
2585             HighEntropy /= Directories[Size][1];
2586             Sections[2] = HighEntropy;
2587             Sections[3] = LowEntropy;
2588         } else {
2589             Sections[1] = HighEntropy;
2590             Sections[2] = LowEntropy;
2591         }
2592         break;
2593     case 2:
2594         Sections[0] = FileTag1 & 0xff;
2595         if (Directories[Size][1]) {
2596             Sections[1] = Uniquifier & 0xff;
2597             if (Directories[Size][1] == 16)
2598                 Sections[1] &= 0xf;
2599             Sections[2] = FileTag1;
2600             Sections[3] = FileTag2;
2601         } else {
2602             Sections[1] = FileTag1;
2603             Sections[2] = FileTag2;
2604         }
2605         break;
2606     case 3:
2607         Sections[0] = FileTag1 & 0xff;
2608         if (Directories[Size][1]) {
2609             Sections[1] = (vnode >> 1) & 0xff;
2610             if (Directories[Size][1] == 16)
2611                 Sections[1] &= 0xf;
2612             Sections[2] = FileTag1;
2613             Sections[3] = FileTag2;
2614         } else {
2615             Sections[1] = FileTag1;
2616             Sections[2] = FileTag2;
2617         }
2618         break;
2619     default:
2620         fprintf(stderr, "UfsTagsToLevel: bad algorithm %lu!\n", Algorithm);
2621         return -1;
2622     }
2623     return 0;
2624 }
2625
2626 #include <afs/afscbdummies.h>
2627 #endif /* RESIDENCY */