pull-prototypes-to-head-20020821
[openafs.git] / src / volser / restorevol.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Read a vos dump and recreate the tree.
12  *
13  * restorevol [-file <dump file>]
14  *            [-dir <restore dir>]
15  *            [-extension <name extension>]
16  *            [-mountpoint <mount point root>]
17  *            [-umask <mode mask>]
18  *
19  * 1. The dump file will be restored within the current or that specified with -dir.
20  * 2. Within this dir, a subdir is created. It's name is the RW volume name
21  *    that was dumped. An extension can be appended to this directory name
22  *    with -extension.
23  * 3. All mountpoints will appear as symbolic links to the volume. The
24  *    pathname to the volume will be either that in -mountpoint, or -dir.
25  *    Symbolic links remain untouched.
26  * 4. You can change your umask during the restore with -umask. Otherwise, it
27  *    uses your current umask. Mode bits for directories are 0777 (then
28  *    AND'ed with the umask). Mode bits for files are the owner mode bits
29  *    duplicated accross group and user (then AND'ed with the umask).
30  * 5. For restores of full dumps, if a directory says it has a file and 
31  *    the file is not found, then a symbolic link "AFSFile-<#>" will 
32  *    appear in that restored tree. Restores of incremental dumps remove 
33  *    all these files at the end (expensive because it is a tree search).  
34  * 6. If a file or directory was found in the dump but found not to be 
35  *    connected to the hierarchical tree, then the file or directory 
36  *    will be connected at the root of the tree as "__ORPHANEDIR__.<#>" 
37  *    or "__ORPHANFILE__.<#>".  
38  * 7. ACLs are not restored.
39  *
40  */
41
42 #include <afsconfig.h>
43 #include <afs/param.h>
44
45 RCSID("$Header$");
46
47 #include <afs/afsint.h>
48 #include <afs/nfs.h>
49 #include <lock.h>
50 #include <afs/ihandle.h>
51 #include <afs/vnode.h>
52 #include <afs/volume.h>
53 #include "volint.h"
54 #include "dump.h"
55 #include <afs/cmd.h>
56
57 #include <sys/param.h>
58 #include <sys/types.h>
59 #include <sys/uio.h>
60 #include <stdio.h>
61 #include <errno.h>
62 #include <netinet/in.h>
63 #include <sys/stat.h>
64 #include <fcntl.h>
65 #include <dirent.h>
66
67 #ifdef HAVE_STRING_H
68 #include <string.h>
69 #else
70 #ifdef HAVE_STRINGS_H
71 #include <strings.h>
72 #endif
73 #endif
74
75
76 char rootdir[MAXPATHLEN];
77 char mntroot[MAXPATHLEN];
78 #define ADIR  "AFSDir-"
79 #define AFILE "AFSFile-"
80 #define ODIR  "__ORPHANEDIR__."
81 #define OFILE "__ORPHANFILE__."
82
83 int  inc_dump = 0;
84 FILE *dumpfile;
85
86 afs_int32 readvalue(size)
87 {
88   afs_int32 value, s;
89   int   code;
90   char  *ptr;
91
92   value = 0;
93   ptr = (char *) &value;
94
95   s = sizeof(value) - size;
96   if (size < 0) {
97       fprintf (stderr, "Too much data in afs_int32\n");
98       return 0;
99   }
100   
101   code = fread (&ptr[s], 1, size, dumpfile);
102   if (code != size) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
103
104   return (value);
105 }
106
107 char readchar()
108 {
109   char  value;
110   int   code;
111   char  *ptr;
112
113   value = '\0';
114   code = fread (&value, 1, 1, dumpfile);
115   if (code != 1) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
116
117   return (value);
118 }
119
120 #define BUFSIZE 16384
121 char buf[BUFSIZE];
122
123 char readdata(buffer, size)
124   char  *buffer;
125   afs_int32 size;
126 {
127   int   code;
128   afs_int32 s;
129
130   if (!buffer) {
131       while (size > 0) {
132           s = ((size > BUFSIZE) ? BUFSIZE : size);
133           code = fread(buf, 1, s, dumpfile);
134           if (code != s) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
135           size -= s;
136       }
137   }
138   else {
139       code = fread (buffer, 1, size, dumpfile);
140       if (code != size) {
141          if (code < 0)
142             fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
143          else 
144             fprintf (stderr, "Read %d bytes out of %d\n", code, size);
145       }
146       if ((code >= 0) && (code < BUFSIZE))
147          buffer[size] = 0;      /* Add null char at end */
148   }
149 }
150
151 afs_int32 ReadDumpHeader(dh)
152   struct DumpHeader *dh;                  /* Defined in dump.h */
153 {
154   int               code, i, done;
155   char              tag, c;
156   afs_int32             magic;
157
158 /*  memset(&dh, 0, sizeof(dh)); */
159
160   magic   = ntohl(readvalue(4));
161   dh->version = ntohl(readvalue(4));
162
163   done = 0;
164   while (!done) {
165       tag = readchar();
166       switch (tag) {
167           case 'v':
168               dh->volumeId = ntohl(readvalue(4));
169               break;
170
171           case 'n':
172               for (i=0, c='a'; c!='\0'; i++) {
173                   dh->volumeName[i] = c = readchar();
174               }
175               dh->volumeName[i] = c;
176               break;
177
178           case 't':
179               dh->nDumpTimes = ntohl(readvalue(2)) >> 1;
180               for (i=0; i<dh->nDumpTimes; i++) {
181                   dh->dumpTimes[i].from = ntohl(readvalue(4));
182                   dh->dumpTimes[i].to   = ntohl(readvalue(4));
183               }
184               break;
185
186           default:
187               done = 1;
188               break;
189       }
190   }
191
192   return((afs_int32)tag);
193 }
194
195 struct volumeHeader
196 {
197   afs_int32         volumeId;
198   char          volumeName[100];
199   afs_int32         volType;
200   afs_int32         uniquifier;
201   afs_int32         parentVol;
202   afs_int32         cloneId;
203   afs_int32         maxQuota;
204   afs_int32         minQuota;
205   afs_int32         diskUsed;
206   afs_int32         fileCount;
207   afs_int32         accountNumber;
208   afs_int32         owner;
209   afs_int32         creationDate;
210   afs_int32         accessDate;
211   afs_int32         updateDate;
212   afs_int32         expirationDate;
213   afs_int32         backupDate;
214   afs_int32         dayUseDate;
215   afs_int32         dayUse;
216   afs_int32         weekCount;
217   afs_int32         weekUse[100];    /* weekCount of these */
218   char          motd[1024];
219   int           inService;
220   int           blessed;
221   char          message[1024];
222 };
223
224 afs_int32 ReadVolumeHeader(count)
225   afs_int32 count;
226 {
227   struct volumeHeader vh;
228   int               code, i, done, entries;
229   char              tag, c;
230
231 /*  memset(&vh, 0, sizeof(vh)); */
232
233   done = 0;
234   while (!done) {
235       tag = readchar();
236       switch (tag) {
237           case 'i':
238               vh.volumeId = ntohl(readvalue(4));
239               break;
240
241           case 'v':
242               ntohl(readvalue(4));   /* version stamp - ignore */
243               break;
244
245           case 'n':
246               for (i=0, c='a'; c!='\0'; i++) {
247                   vh.volumeName[i] = c = readchar();
248               }
249               vh.volumeName[i] = c;
250               break;
251
252           case 's':
253               vh.inService = ntohl(readvalue(1));
254               break;
255
256           case 'b':
257               vh.blessed = ntohl(readvalue(1));
258               break;
259
260           case 'u':
261               vh.uniquifier = ntohl(readvalue(4));
262               break;
263
264           case 't':
265               vh.volType = ntohl(readvalue(1));
266               break;
267
268           case 'p':
269               vh.parentVol = ntohl(readvalue(4));
270               break;
271
272           case 'c':
273               vh.cloneId = ntohl(readvalue(4));
274               break;
275
276           case 'q':
277               vh.maxQuota = ntohl(readvalue(4));
278               break;
279
280           case 'm':
281               vh.minQuota = ntohl(readvalue(4));
282               break;
283
284           case 'd':
285               vh.diskUsed = ntohl(readvalue(4));
286               break;
287
288           case 'f':
289               vh.fileCount = ntohl(readvalue(4));
290               break;
291
292           case 'a':
293               vh.accountNumber = ntohl(readvalue(4));
294               break;
295
296           case 'o':
297               vh.owner = ntohl(readvalue(4));
298               break;
299
300           case 'C':
301               vh.creationDate = ntohl(readvalue(4));
302               break;
303
304           case 'A':
305               vh.accessDate = ntohl(readvalue(4));
306               break;
307
308           case 'U':
309               vh.updateDate = ntohl(readvalue(4));
310               break;
311
312           case 'E':
313               vh.expirationDate = ntohl(readvalue(4));
314               break;
315
316           case 'B':
317               vh.backupDate = ntohl(readvalue(4));
318               break;
319
320           case 'O':
321               for (i=0, c='a'; c!='\0'; i++) {
322                   vh.message[i] = c = readchar();
323               }
324               vh.volumeName[i] = c;
325               break;
326
327           case 'W':
328               vh.weekCount = ntohl(readvalue(2));
329               for (i=0; i<vh.weekCount; i++) {
330                   vh.weekUse[i] = ntohl(readvalue(4));
331               }
332               break;
333
334           case 'M':
335               for (i=0, c='a'; c!='\0'; i++) {
336                   vh.motd[i] = c = readchar();
337               }
338               break;
339
340           case 'D':
341               vh.dayUseDate = ntohl(readvalue(4));
342               break;
343
344           case 'Z':
345               vh.dayUse = ntohl(readvalue(4));
346               break;
347
348           default:
349               done = 1;
350               break;
351       }
352   }
353
354   return((afs_int32)tag);
355 }
356
357 struct vNode
358 {
359   afs_int32 vnode;
360   afs_int32 uniquifier;
361   afs_int32 type;
362   afs_int32 linkCount;
363   afs_int32 dataVersion;
364   afs_int32 unixModTime;
365   afs_int32 servModTime;
366   afs_int32 author;
367   afs_int32 owner;
368   afs_int32 group;
369   afs_int32 modebits;
370   afs_int32 parent;
371   char acl[192];
372 #ifdef notdef
373   struct acl_accessList 
374   {
375     int size;           /*size of this access list in bytes, including MySize itself*/    
376     int version;        /*to deal with upward compatibility ; <= ACL_ACLVERSION*/
377     int total;
378     int positive;       /* number of positive entries */
379     int negative;       /* number of minus entries */
380     struct acl_accessEntry 
381     {
382       int id;         /*internally-used ID of user or group*/
383       int rights;     /*mask*/
384     } entries[100];
385   } acl;
386 #endif
387   afs_int32 dataSize;
388 };
389
390 #define MAXNAMELEN 256
391
392 afs_int32 ReadVNode(count)
393   afs_int32 count;
394 {
395   struct vNode      vn;
396   int               code, i, done, entries;
397   char              tag, c;
398   char              dirname[MAXNAMELEN], linkname[MAXNAMELEN], lname[MAXNAMELEN];
399   char              parentdir[MAXNAMELEN], vflink[MAXNAMELEN];
400   char              filename[MAXNAMELEN], fname[MAXNAMELEN];
401   int               len;
402   afs_int32             vnode;
403   afs_int32             mode=0;
404
405 /*  memset(&vn, 0, sizeof(vn)); */
406   vn.dataSize = 0;
407   vn.vnode = 0;
408   vn.parent = 0;
409   vn.type = 0;
410
411   vn.vnode = ntohl(readvalue(4));
412   vn.uniquifier = ntohl(readvalue(4));
413   
414   done = 0;
415   while (!done) {
416       tag = readchar();
417       switch (tag) {
418           case 't':
419               vn.type = ntohl(readvalue(1));
420               break;
421
422           case 'l':
423               vn.linkCount = ntohl(readvalue(2));
424               break;
425
426           case 'v':
427               vn.dataVersion = ntohl(readvalue(4));
428               break;
429
430           case 'm':
431               vn.unixModTime = ntohl(readvalue(4));
432               break;
433
434           case 's':
435               vn.servModTime = ntohl(readvalue(4));
436               break;
437
438           case 'a':
439               vn.author = ntohl(readvalue(4));
440               break;
441
442           case 'o':
443               vn.owner = ntohl(readvalue(4));
444               break;
445
446           case 'g':
447               vn.group = ntohl(readvalue(4));
448               break;
449
450           case 'b':
451               vn.modebits = ntohl(readvalue(2));
452               break;
453
454           case 'p':
455               vn.parent = ntohl(readvalue(4));
456               break;
457
458           case 'A':
459               readdata(vn.acl, 192);             /* Skip ACL data */
460               break;
461
462           case 'f':
463               vn.dataSize = ntohl(readvalue(4));
464
465               /* parentdir is the name of this dir's vnode-file-link
466                * or this file's parent vnode-file-link.
467                * "./AFSDir-<#>". It's a symbolic link to its real dir.
468                * The parent dir and symbolic link to it must exist.
469                */
470               vnode = ((vn.type == 2) ? vn.vnode : vn.parent);
471               if (vnode == 1)
472                  sprintf(parentdir, "%s", rootdir);
473               else {
474                  sprintf(parentdir, "%s/%s%d", rootdir, ADIR, vnode);
475                  
476                  len = readlink(parentdir, linkname, MAXNAMELEN);
477                  if (len < 0) {
478                     /* parentdir does not exist. So create an orphan dir.
479                      * and then link the parentdir to the orphaned dir.
480                      */
481                     sprintf(linkname, "%s/%s%d", rootdir, ODIR, vnode);
482                     code = mkdir(linkname, 0777);
483                     if ((code < 0) && (errno != EEXIST)) {
484                        fprintf(stderr, "Error creating directory %s  code=%d;%d\n", 
485                                linkname, code, errno);
486                     }
487                         
488                     /* Link the parentdir to it - now parentdir exists */
489                     sprintf(linkname, "%s%d/", ODIR, vnode);
490                     code = symlink(linkname, parentdir);
491                     if (code) {
492                        fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
493                                parentdir, linkname, code, errno);
494                     }
495                  }
496               }
497
498               if (vn.type == 2) {
499                   /*ITSADIR*/
500                   /* We read the directory entries. If the entry is a
501                    * directory, the subdir is created and the root dir
502                    * will contain a link to it. If its a file, we only
503                    * create a symlink in the dir to the file name.
504                    */
505                   char           *buffer;
506                   unsigned short j;
507                   afs_int32          this_vn;
508                   char           *this_name;
509
510                   struct DirEntry
511                   {
512                       char flag;
513                       char length;
514                       unsigned short next;
515                       struct MKFid
516                       {
517                           afs_int32 vnode;
518                           afs_int32 vunique;
519                       } fid;
520                       char name[20];
521                   };
522
523                   struct Pageheader
524                   {
525                       unsigned short pgcount;
526                       unsigned short tag;
527                       char           freecount;
528                       char           freebitmap[8];
529                       char           padding[19];
530                   };
531
532                   struct DirHeader
533                   {
534                       struct Pageheader  header;
535                       char               alloMap[128];
536                       unsigned short     hashTable[128];
537                   };
538
539                   struct Page0
540                   {
541                       struct DirHeader header;
542                       struct DirEntry  entry[1];
543                   } *page0;
544                   
545
546                   buffer = NULL;
547                   buffer = (char *)malloc(vn.dataSize);
548
549                   readdata(buffer, vn.dataSize);
550                   page0 = (struct Page0 *)buffer;
551
552                   /* Step through each bucket in the hash table, i,
553                    * and follow each element in the hash chain, j.
554                    * This gives us each entry of the dir.
555                    */
556                   for (i=0; i<128; i++) {
557                      for (j=ntohs(page0->header.hashTable[i]); j;
558                           j=ntohs(page0->entry[j].next)) {
559                         j -= 13;
560                         this_vn   = ntohl(page0->entry[j].fid.vnode);
561                         this_name = page0->entry[j].name;
562
563                         if ((strcmp(this_name,"." ) == 0) ||
564                             (strcmp(this_name,"..") == 0))
565                            continue;                      /* Skip these */
566
567                         /* For a directory entry, create it. Then create the
568                          * link (from the rootdir) to this directory.
569                          */
570                         if (this_vn & 1) { /*ADIRENTRY*/
571                            /* dirname is the directory to create.
572                             * vflink is what will link to it. 
573                             */
574                            sprintf(dirname, "%s/%s", parentdir, this_name);
575                            sprintf(vflink, "%s/%s%d", rootdir, ADIR, this_vn);
576
577                            /* The link and directory may already exist */
578                            len = readlink(vflink, linkname, MAXNAMELEN);
579                            if (len < 0) {
580                               /* Doesn't already exist - so create the directory.
581                                * umask will pare the mode bits down.
582                                */
583                               code = mkdir(dirname, 0777);
584                               if ((code < 0) && (errno != EEXIST)) {
585                                  fprintf(stderr, "Error creating directory %s  code=%d;%d\n", 
586                                          dirname, code, errno);
587                               }
588                            } else {
589                               /* Does already exist - so move the directory.
590                                * It was created originally as orphaned.
591                                */
592                               linkname[len-1] = '\0';  /* remove '/' at end */
593                               sprintf(lname, "%s/%s", rootdir, linkname);
594                               code = rename(lname, dirname);
595                               if (code) {
596                                  fprintf(stderr, "Error renaming %s to %s  code=%d;%d\n", 
597                                          lname, dirname, code, errno);
598                               }
599                            }
600
601                            /* Now create/update the link to the new/moved directory */
602                            if (vn.vnode == 1)
603                               sprintf(dirname, "%s/", this_name);
604                            else
605                               sprintf(dirname, "%s%d/%s/", ADIR, vn.vnode, this_name);
606                            unlink(vflink);
607                            code = symlink(dirname, vflink);
608                            if (code) {
609                               fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
610                                       vflink, dirname, code, errno);
611                            }
612                         } /*ADIRENTRY*/
613
614                         /* For a file entry, we remember the name of the file
615                          * by creating a link within the directory. Restoring
616                          * the file will later remove the link.
617                          */
618                         else {
619                            /*AFILEENTRY*/
620                            sprintf(vflink, "%s/%s%d", parentdir, AFILE, this_vn);
621                            
622                            code = symlink(this_name, vflink);
623                            if ((code < 0) && (errno != EEXIST)) {
624                               fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
625                                       vflink, page0->entry[j].name, code, errno);
626                            }
627                         } /*AFILEENTRY*/
628                      }
629                   }
630                   free(buffer);
631               } /*ITSADIR*/
632
633               else if (vn.type == 1) {
634                   /*ITSAFILE*/
635                   /* A file vnode. So create it into the desired directory. A
636                    * link should exist in the directory naming the file.
637                    */
638                   int fid;
639                   int lfile;
640                   afs_int32 size, s;
641
642                   /* Check if its vnode-file-link exists. If not,
643                    * then the file will be an orphaned file.
644                    */
645                   lfile = 1;
646                   sprintf(filename, "%s/%s%d", parentdir, AFILE, vn.vnode);
647                   len = readlink(filename, fname, MAXNAMELEN);
648                   if (len < 0) {
649                      sprintf(filename, "%s/%s%d", rootdir, OFILE, vn.vnode);
650                      lfile = 0; /* no longer a linked file; a direct path */
651                   }
652
653                   /* Create a mode for the file. Use the owner bits and
654                    * duplicate them across group and other. The umask
655                    * will remove what we don't want.
656                    */
657                   mode  = (vn.modebits >> 6) & 0x7;
658                   mode |= (mode << 6) | (mode << 3);
659
660                   /* Write the file out */
661                   fid = open(filename, (O_CREAT | O_WRONLY | O_TRUNC), mode);
662                   size = vn.dataSize;
663                   while (size > 0) {
664                      s = ((size > BUFSIZE) ? BUFSIZE : size);
665                      code = fread(buf, 1, s, dumpfile);
666                      if (code > 0) {
667                         write(fid, buf, code);
668                         size -= code;
669                      }
670                      if (code != s) {
671                         if (code < 0)
672                            fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
673                         else 
674                            fprintf (stderr, "Read %d bytes out of %d\n", 
675                                     (vn.dataSize - size), vn.dataSize);
676                         break;
677                      }
678                   }
679                   close(fid);
680                   if (size != 0) {
681                      fprintf(stderr, "   File %s (%s) is incomplete\n", filename, fname);
682                   }
683
684                   /* Remove the link to the file */
685                   if (lfile) {
686                      unlink(filename);
687                   }
688               } /*ITSAFILE*/
689
690               else if (vn.type == 3) {
691                   /*ITSASYMLINK*/
692                   /* A symlink vnode. So read it into the desired directory. This could
693                    * also be a mount point. If the volume is being restored to AFS, this
694                    * will become a mountpoint. If not, it becomes a symlink to no-where.
695                    */
696                   int fid;
697                   afs_int32 size, s;
698
699                   /* Check if its vnode-file-link exists and create pathname
700                    * of the symbolic link. If it doesn't exist,
701                    * then the link will be an orphaned link.
702                    */
703                   sprintf(linkname, "%s/%s%d", parentdir, AFILE, vn.vnode);
704                   len = readlink(linkname, fname, MAXNAMELEN);
705                   if (len < 0) {
706                      sprintf(filename, "%s/%s%d", rootdir, OFILE, vn.vnode);
707                   } else {
708                      fname[len] = '\0';
709                      sprintf(filename, "%s/%s", parentdir, fname);
710                   }
711
712                   /* Read the link in, delete it, and then create it */
713                   readdata(buf, vn.dataSize);
714
715                   /* If a mountpoint, change its link path to mountroot */
716                   s = strlen(buf);
717                   if ( ((buf[0] == '%') || (buf[0] == '#')) && (buf[s-1] == '.') ) {
718                      /* This is a symbolic link */
719                      buf[s-1] = 0;             /* Remove prefix '.' */
720                      strcpy(lname, &buf[1]);   /* Remove postfix '#' or '%' */
721                      strcpy(buf, mntroot);
722                      strcat(buf, lname);
723                   }
724
725                   unlink(filename);
726                   code = symlink(buf, filename);
727                   if (code) {
728                      fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
729                              filename, buf, code, errno);
730                   }
731
732                   /* Remove the symbolic link */
733                   unlink(linkname);
734               } /*ITSASYMLINK*/
735               else {
736                  fprintf (stderr, "Unknown Vnode block\n");
737               }
738               break;
739
740           default:
741               done = 1;
742               break;
743       }
744   }
745   if (vn.type == 0)
746      inc_dump = 1;
747
748   return((afs_int32)tag);
749 }
750
751 WorkerBee(as, arock)
752   struct cmd_syndesc *as;
753   char *arock;
754 {
755   int    code=0, c, len;
756   afs_int32  type, count, vcount;
757   DIR           *dirP, *dirQ;
758   struct dirent *dirE, *dirF;
759   char   fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN], mname[MAXNAMELEN];
760   char   thisdir[MAXPATHLEN], *t;
761   struct DumpHeader dh;                  /* Defined in dump.h */
762 #if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */
763   extern char *getwd();
764 #define getcwd(x,y) getwd(x)
765 #endif
766   
767   if (as->parms[0].items) {                         /* -file <dumpfile> */
768      dumpfile = fopen(as->parms[0].items->data, "r");
769      if (!dumpfile) {
770         fprintf(stderr, "Cannot open '%s'. Code = %d\n",
771                 as->parms[0].items->data, errno);
772         goto cleanup;
773      }
774   } else {
775      dumpfile = (FILE *)stdin;                     /* use stdin */
776   }
777
778   /* Read the dump header. From it we get the volume name */
779   type = ntohl(readvalue(1));
780   if (type != 1) {
781       fprintf(stderr, "Expected DumpHeader\n");
782       code = -1;
783       goto cleanup;
784   }
785   type = ReadDumpHeader(&dh);
786
787   /* Get the root directory we restore to */
788   if (as->parms[1].items) {                         /* -dir <rootdir> */
789      strcpy(rootdir, as->parms[1].items->data);
790   } else {
791      strcpy(rootdir,".");
792   }
793   strcat(rootdir,"/");
794
795   /* Append the RW volume name to the root directory */
796   strcat(rootdir, dh.volumeName);
797   len = strlen(rootdir);
798   if (strcmp(".backup",rootdir+len-7) == 0) {
799      rootdir[len-7] = 0;
800   } else if (strcmp(".readonly",rootdir+len-9) == 0) {
801      rootdir[len-9] = 0;
802   }
803
804   /* Append the extension we asked for */
805   if (as->parms[2].items) {
806      strcat(rootdir, as->parms[2].items->data);    /* -extension <ext> */
807   }
808
809   /* The mountpoint root is either specifid in -mountpoint
810    * or -dir or the current working dir.
811    */
812   if ((as->parms[3].items) || (as->parms[1].items)) {   /* -mountpoint  or -dir*/
813      t = (char *)getcwd(thisdir, MAXPATHLEN);      /* remember current dir */
814      if (!t) {
815         fprintf(stderr, "Cannot get pathname of current working directory: %s\n",
816                 thisdir);
817         code = -1;
818         goto cleanup;
819      }
820      /* Change to the mount point dir */
821      code = chdir((as->parms[3].items ? as->parms[3].items->data :
822                                         as->parms[1].items->data));
823      if (code) {
824         fprintf(stderr, "Mount point directory not found: Error = %d\n", stderr);
825         goto cleanup;
826      }
827      t = (char *)getcwd(mntroot, MAXPATHLEN);     /* get its full pathname */
828      if (!t) {
829         fprintf(stderr, "Cannot determine pathname of mount point root directory: %s\n",
830                 mntroot);
831         code = -1;
832         goto cleanup;
833      }
834      strcat(mntroot, "/");                   /* append '/' to end of it */
835      chdir(thisdir);                         /* return to original working dir */
836      if (code) {
837         fprintf(stderr, "Cannot find working directory: Error = %d\n", stderr);
838         goto cleanup;
839      }
840   } else {                                   /* use current directory */
841      t = (char *)getcwd(mntroot, MAXPATHLEN); /* get full pathname of current dir */
842      if (!t) {
843         fprintf(stderr, "Cannot determine pathname of current working directory: %s\n",
844                 mntroot);
845         code = -1;
846         goto cleanup;
847      }
848   }
849   strcat(mntroot, "/");                   /* append '/' to end of it */
850
851   /* Set the umask for the restore */
852   if (as->parms[4].items) {              /* -umask */
853      afs_int32 mask;
854      mask  = strtol(as->parms[4].items->data, 0, 8);
855      fprintf(stderr, "Umask set to 0%03o\n", mask);
856      umask(mask);
857   }
858
859   fprintf(stderr, "Restoring volume dump of '%s' to directory '%s'.\n",
860           dh.volumeName, rootdir);
861   code = mkdir(rootdir, 0777);
862   if ((code < 0) && (errno != EEXIST)) {
863      fprintf(stderr, "Error creating directory %s  code=%d;%d\n", 
864              rootdir, code, errno);
865   }
866
867   for (count=1; type==2; count++) {
868       type = ReadVolumeHeader(count);
869       for (vcount=1; type==3; vcount++)
870           type = ReadVNode(vcount);
871   }
872
873   if (type != 4) {
874       fprintf(stderr, "Expected End-of-Dump\n");
875       code = -1;
876       goto cleanup;
877   }
878
879  cleanup:
880   /* For incremental restores, Follow each directory link and
881    * remove an "AFSFile" links.
882    */
883   if (inc_dump) {
884      fprintf(stderr, "An incremental dump.\n");
885      dirP = opendir(rootdir);
886      while (dirP && (dirE = readdir(dirP))) {
887         if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
888            sprintf(name, "%s/%s", rootdir, dirE->d_name);
889            dirQ = opendir(name);
890            while (dirQ && (dirF = readdir(dirQ))) {
891               if (strncmp(dirF->d_name, AFILE, strlen(AFILE)) == 0) {
892                  sprintf(name, "%s/%s/%s", rootdir, dirE->d_name, dirF->d_name);
893                  unlink(name);
894               }
895            }
896            closedir(dirQ);
897         } else if (strncmp(dirE->d_name, AFILE, strlen(AFILE)) == 0) {
898            sprintf(name, "%s/%s", rootdir, dirE->d_name);
899            unlink(name);
900         }
901      }
902      closedir(dirP);
903   }
904
905   /* Now go through and remove all the directory links */
906   dirP = opendir(rootdir);
907   while (dirP && (dirE = readdir(dirP))) {
908       if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
909           sprintf(name, "%s/%s", rootdir, dirE->d_name);
910           unlink(name);
911       }
912   }
913   closedir(dirP);
914   
915   return(code);
916 }
917
918 main(argc, argv)
919   int  argc;
920   char **argv;
921 {
922   struct cmd_syndesc *ts;
923   struct cmd_item    *ti;
924
925   setlinebuf(stdout);
926
927   ts=cmd_CreateSyntax(NULL, WorkerBee, NULL, "vldb check");
928   cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
929   cmd_AddParm(ts, "-dir",  CMD_SINGLE, CMD_OPTIONAL, "restore dir");
930   cmd_AddParm(ts, "-extension",  CMD_SINGLE, CMD_OPTIONAL, "name extension");
931   cmd_AddParm(ts, "-mountpoint", CMD_SINGLE, CMD_OPTIONAL, "mount point root");
932   cmd_AddParm(ts, "-umask",      CMD_SINGLE, CMD_OPTIONAL, "mode mask");
933
934   return cmd_Dispatch(argc, argv);
935 }