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