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