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