logging-changes-for-large-files-20030619
[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                  strncpy(parentdir, rootdir, sizeof parentdir);
473               else {
474                   afs_snprintf(parentdir, sizeof parentdir,
475                                "%s/%s%d", rootdir, ADIR, vnode);
476                  
477                  len = readlink(parentdir, linkname, MAXNAMELEN);
478                  if (len < 0) {
479                     /* parentdir does not exist. So create an orphan dir.
480                      * and then link the parentdir to the orphaned dir.
481                      */
482                     afs_snprintf(linkname, sizeof linkname,
483                                  "%s/%s%d", rootdir, ODIR, vnode);
484                     code = mkdir(linkname, 0777);
485                     if ((code < 0) && (errno != EEXIST)) {
486                        fprintf(stderr, "Error creating directory %s  code=%d;%d\n", 
487                                linkname, code, errno);
488                     }
489                         
490                     /* Link the parentdir to it - now parentdir exists */
491                     afs_snprintf(linkname, sizeof linkname,
492                                 "%s%d/", ODIR, vnode);
493                     code = symlink(linkname, parentdir);
494                     if (code) {
495                        fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
496                                parentdir, linkname, code, errno);
497                     }
498                  }
499               }
500
501               if (vn.type == 2) {
502                   /*ITSADIR*/
503                   /* We read the directory entries. If the entry is a
504                    * directory, the subdir is created and the root dir
505                    * will contain a link to it. If its a file, we only
506                    * create a symlink in the dir to the file name.
507                    */
508                   char           *buffer;
509                   unsigned short j;
510                   afs_int32          this_vn;
511                   char           *this_name;
512
513                   struct DirEntry
514                   {
515                       char flag;
516                       char length;
517                       unsigned short next;
518                       struct MKFid
519                       {
520                           afs_int32 vnode;
521                           afs_int32 vunique;
522                       } fid;
523                       char name[20];
524                   };
525
526                   struct Pageheader
527                   {
528                       unsigned short pgcount;
529                       unsigned short tag;
530                       char           freecount;
531                       char           freebitmap[8];
532                       char           padding[19];
533                   };
534
535                   struct DirHeader
536                   {
537                       struct Pageheader  header;
538                       char               alloMap[128];
539                       unsigned short     hashTable[128];
540                   };
541
542                   struct Page0
543                   {
544                       struct DirHeader header;
545                       struct DirEntry  entry[1];
546                   } *page0;
547                   
548
549                   buffer = NULL;
550                   buffer = (char *)malloc(vn.dataSize);
551
552                   readdata(buffer, vn.dataSize);
553                   page0 = (struct Page0 *)buffer;
554
555                   /* Step through each bucket in the hash table, i,
556                    * and follow each element in the hash chain, j.
557                    * This gives us each entry of the dir.
558                    */
559                   for (i=0; i<128; i++) {
560                      for (j=ntohs(page0->header.hashTable[i]); j;
561                           j=ntohs(page0->entry[j].next)) {
562                         j -= 13;
563                         this_vn   = ntohl(page0->entry[j].fid.vnode);
564                         this_name = page0->entry[j].name;
565
566                         if ((strcmp(this_name,"." ) == 0) ||
567                             (strcmp(this_name,"..") == 0))
568                            continue;                      /* Skip these */
569
570                         /* For a directory entry, create it. Then create the
571                          * link (from the rootdir) to this directory.
572                          */
573                         if (this_vn & 1) { /*ADIRENTRY*/
574                            /* dirname is the directory to create.
575                             * vflink is what will link to it. 
576                             */
577                            afs_snprintf(dirname, sizeof dirname,
578                                         "%s/%s", parentdir, this_name);
579                            afs_snprintf(vflink, sizeof vflink,
580                                         "%s/%s%d", rootdir, ADIR, this_vn);
581
582                            /* The link and directory may already exist */
583                            len = readlink(vflink, linkname, MAXNAMELEN);
584                            if (len < 0) {
585                               /* Doesn't already exist - so create the directory.
586                                * umask will pare the mode bits down.
587                                */
588                               code = mkdir(dirname, 0777);
589                               if ((code < 0) && (errno != EEXIST)) {
590                                  fprintf(stderr, "Error creating directory %s  code=%d;%d\n", 
591                                          dirname, code, errno);
592                               }
593                            } else {
594                               /* Does already exist - so move the directory.
595                                * It was created originally as orphaned.
596                                */
597                               linkname[len-1] = '\0';  /* remove '/' at end */
598                               afs_snprintf(lname, sizeof lname,
599                                            "%s/%s", rootdir, linkname);
600                               code = rename(lname, dirname);
601                               if (code) {
602                                  fprintf(stderr, "Error renaming %s to %s  code=%d;%d\n", 
603                                          lname, dirname, code, errno);
604                               }
605                            }
606
607                            /* Now create/update the link to the new/moved directory */
608                            if (vn.vnode == 1)
609                               afs_snprintf(dirname, sizeof dirname,
610                                            "%s/", this_name);
611                            else
612                               afs_snprintf(dirname, sizeof dirname,
613                                            "%s%d/%s/", ADIR, vn.vnode, this_name);
614                            unlink(vflink);
615                            code = symlink(dirname, vflink);
616                            if (code) {
617                               fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
618                                       vflink, dirname, code, errno);
619                            }
620                         } /*ADIRENTRY*/
621
622                         /* For a file entry, we remember the name of the file
623                          * by creating a link within the directory. Restoring
624                          * the file will later remove the link.
625                          */
626                         else {
627                            /*AFILEENTRY*/
628                            afs_snprintf(vflink, sizeof vflink,
629                                         "%s/%s%d", parentdir, AFILE, this_vn);
630                            
631                            code = symlink(this_name, vflink);
632                            if ((code < 0) && (errno != EEXIST)) {
633                               fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
634                                       vflink, page0->entry[j].name, code, errno);
635                            }
636                         } /*AFILEENTRY*/
637                      }
638                   }
639                   free(buffer);
640               } /*ITSADIR*/
641
642               else if (vn.type == 1) {
643                   /*ITSAFILE*/
644                   /* A file vnode. So create it into the desired directory. A
645                    * link should exist in the directory naming the file.
646                    */
647                   int fid;
648                   int lfile;
649                   afs_int32 size, s;
650
651                   /* Check if its vnode-file-link exists. If not,
652                    * then the file will be an orphaned file.
653                    */
654                   lfile = 1;
655                   afs_snprintf(filename, sizeof filename,
656                                "%s/%s%d", parentdir, AFILE, vn.vnode);
657                   len = readlink(filename, fname, MAXNAMELEN);
658                   if (len < 0) {
659                      afs_snprintf(filename, sizeof filename,
660                                   "%s/%s%d", rootdir, OFILE, vn.vnode);
661                      lfile = 0; /* no longer a linked file; a direct path */
662                   }
663
664                   /* Create a mode for the file. Use the owner bits and
665                    * duplicate them across group and other. The umask
666                    * will remove what we don't want.
667                    */
668                   mode  = (vn.modebits >> 6) & 0x7;
669                   mode |= (mode << 6) | (mode << 3);
670
671                   /* Write the file out */
672                   fid = open(filename, (O_CREAT | O_WRONLY | O_TRUNC), mode);
673                   size = vn.dataSize;
674                   while (size > 0) {
675                      s = ((size > BUFSIZE) ? BUFSIZE : size);
676                      code = fread(buf, 1, s, dumpfile);
677                      if (code > 0) {
678                         (void) write(fid, buf, code);
679                         size -= code;
680                      }
681                      if (code != s) {
682                         if (code < 0)
683                            fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
684                         else {
685                            char tmp[100];
686                            (void) afs_snprintf(tmp, sizeof tmp,
687                                                "Read %llu bytes out of %llu",
688                                                (afs_uintmax_t)(vn.dataSize - size),
689                                                (afs_uintmax_t)vn.dataSize);
690                            fprintf (stderr, "%s\n", tmp);
691                         }
692                         break;
693                      }
694                   }
695                   close(fid);
696                   if (size != 0) {
697                      fprintf(stderr, "   File %s (%s) is incomplete\n", filename, fname);
698                   }
699
700                   /* Remove the link to the file */
701                   if (lfile) {
702                      unlink(filename);
703                   }
704               } /*ITSAFILE*/
705
706               else if (vn.type == 3) {
707                   /*ITSASYMLINK*/
708                   /* A symlink vnode. So read it into the desired directory. This could
709                    * also be a mount point. If the volume is being restored to AFS, this
710                    * will become a mountpoint. If not, it becomes a symlink to no-where.
711                    */
712                   int fid;
713                   afs_int32 size, s;
714
715                   /* Check if its vnode-file-link exists and create pathname
716                    * of the symbolic link. If it doesn't exist,
717                    * then the link will be an orphaned link.
718                    */
719                   afs_snprintf(linkname, sizeof linkname,
720                                "%s/%s%d", parentdir, AFILE, vn.vnode);
721                   len = readlink(linkname, fname, MAXNAMELEN);
722                   if (len < 0) {
723                      afs_snprintf(filename, sizeof filename,
724                                   "%s/%s%d", rootdir, OFILE, vn.vnode);
725                   } else {
726                      fname[len] = '\0';
727                      afs_snprintf(filename, sizeof filename,
728                                   "%s/%s", parentdir, fname);
729                   }
730
731                   /* Read the link in, delete it, and then create it */
732                   readdata(buf, vn.dataSize);
733
734                   /* If a mountpoint, change its link path to mountroot */
735                   s = strlen(buf);
736                   if ( ((buf[0] == '%') || (buf[0] == '#')) && (buf[s-1] == '.') ) {
737                      /* This is a symbolic link */
738                      buf[s-1] = 0;             /* Remove prefix '.' */
739                      strcpy(lname, &buf[1]);   /* Remove postfix '#' or '%' */
740                      strcpy(buf, mntroot);
741                      strcat(buf, lname);
742                   }
743
744                   unlink(filename);
745                   code = symlink(buf, filename);
746                   if (code) {
747                      fprintf(stderr, "Error creating symlink %s -> %s  code=%d;%d\n", 
748                              filename, buf, code, errno);
749                   }
750
751                   /* Remove the symbolic link */
752                   unlink(linkname);
753               } /*ITSASYMLINK*/
754               else {
755                  fprintf (stderr, "Unknown Vnode block\n");
756               }
757               break;
758
759           default:
760               done = 1;
761               break;
762       }
763   }
764   if (vn.type == 0)
765      inc_dump = 1;
766
767   return((afs_int32)tag);
768 }
769
770 WorkerBee(as, arock)
771   struct cmd_syndesc *as;
772   char *arock;
773 {
774   int    code=0, c, len;
775   afs_int32  type, count, vcount;
776   DIR           *dirP, *dirQ;
777   struct dirent *dirE, *dirF;
778   char   fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN], mname[MAXNAMELEN];
779   char   thisdir[MAXPATHLEN], *t;
780   struct DumpHeader dh;                  /* Defined in dump.h */
781 #if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */
782   extern char *getwd();
783 #define getcwd(x,y) getwd(x)
784 #endif
785   
786   if (as->parms[0].items) {                         /* -file <dumpfile> */
787      dumpfile = fopen(as->parms[0].items->data, "r");
788      if (!dumpfile) {
789         fprintf(stderr, "Cannot open '%s'. Code = %d\n",
790                 as->parms[0].items->data, errno);
791         goto cleanup;
792      }
793   } else {
794      dumpfile = (FILE *)stdin;                     /* use stdin */
795   }
796
797   /* Read the dump header. From it we get the volume name */
798   type = ntohl(readvalue(1));
799   if (type != 1) {
800       fprintf(stderr, "Expected DumpHeader\n");
801       code = -1;
802       goto cleanup;
803   }
804   type = ReadDumpHeader(&dh);
805
806   /* Get the root directory we restore to */
807   if (as->parms[1].items) {                         /* -dir <rootdir> */
808      strcpy(rootdir, as->parms[1].items->data);
809   } else {
810      strcpy(rootdir,".");
811   }
812   strcat(rootdir,"/");
813
814   /* Append the RW volume name to the root directory */
815   strcat(rootdir, dh.volumeName);
816   len = strlen(rootdir);
817   if (strcmp(".backup",rootdir+len-7) == 0) {
818      rootdir[len-7] = 0;
819   } else if (strcmp(".readonly",rootdir+len-9) == 0) {
820      rootdir[len-9] = 0;
821   }
822
823   /* Append the extension we asked for */
824   if (as->parms[2].items) {
825      strcat(rootdir, as->parms[2].items->data);    /* -extension <ext> */
826   }
827
828   /* The mountpoint root is either specifid in -mountpoint
829    * or -dir or the current working dir.
830    */
831   if ((as->parms[3].items) || (as->parms[1].items)) {   /* -mountpoint  or -dir*/
832      t = (char *)getcwd(thisdir, MAXPATHLEN);      /* remember current dir */
833      if (!t) {
834         fprintf(stderr, "Cannot get pathname of current working directory: %s\n",
835                 thisdir);
836         code = -1;
837         goto cleanup;
838      }
839      /* Change to the mount point dir */
840      code = chdir((as->parms[3].items ? as->parms[3].items->data :
841                                         as->parms[1].items->data));
842      if (code) {
843         fprintf(stderr, "Mount point directory not found: Error = %d\n", errno);
844         goto cleanup;
845      }
846      t = (char *)getcwd(mntroot, MAXPATHLEN);     /* get its full pathname */
847      if (!t) {
848         fprintf(stderr, "Cannot determine pathname of mount point root directory: %s\n",
849                 mntroot);
850         code = -1;
851         goto cleanup;
852      }
853      strcat(mntroot, "/");                   /* append '/' to end of it */
854      code = chdir(thisdir);                  /* return to original working dir */
855      if (code) {
856         fprintf(stderr, "Cannot find working directory: Error = %d\n", errno);
857         goto cleanup;
858      }
859   } else {                                   /* use current directory */
860      t = (char *)getcwd(mntroot, MAXPATHLEN); /* get full pathname of current dir */
861      if (!t) {
862         fprintf(stderr, "Cannot determine pathname of current working directory: %s\n",
863                 mntroot);
864         code = -1;
865         goto cleanup;
866      }
867   }
868   strcat(mntroot, "/");                   /* append '/' to end of it */
869
870   /* Set the umask for the restore */
871   if (as->parms[4].items) {              /* -umask */
872      afs_int32 mask;
873      mask  = strtol(as->parms[4].items->data, 0, 8);
874      fprintf(stderr, "Umask set to 0%03o\n", mask);
875      umask(mask);
876   }
877
878   fprintf(stderr, "Restoring volume dump of '%s' to directory '%s'.\n",
879           dh.volumeName, rootdir);
880   code = mkdir(rootdir, 0777);
881   if ((code < 0) && (errno != EEXIST)) {
882      fprintf(stderr, "Error creating directory %s  code=%d;%d\n", 
883              rootdir, code, errno);
884   }
885
886   for (count=1; type==2; count++) {
887       type = ReadVolumeHeader(count);
888       for (vcount=1; type==3; vcount++)
889           type = ReadVNode(vcount);
890   }
891
892   if (type != 4) {
893       fprintf(stderr, "Expected End-of-Dump\n");
894       code = -1;
895       goto cleanup;
896   }
897
898  cleanup:
899   /* For incremental restores, Follow each directory link and
900    * remove an "AFSFile" links.
901    */
902   if (inc_dump) {
903      fprintf(stderr, "An incremental dump.\n");
904      dirP = opendir(rootdir);
905      while (dirP && (dirE = readdir(dirP))) {
906         if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
907            afs_snprintf(name, sizeof name, "%s/%s", rootdir, dirE->d_name);
908            dirQ = opendir(name);
909            while (dirQ && (dirF = readdir(dirQ))) {
910               if (strncmp(dirF->d_name, AFILE, strlen(AFILE)) == 0) {
911                  afs_snprintf(name, sizeof name,
912                               "%s/%s/%s", rootdir, dirE->d_name, dirF->d_name);
913                  unlink(name);
914               }
915            }
916            closedir(dirQ);
917         } else if (strncmp(dirE->d_name, AFILE, strlen(AFILE)) == 0) {
918            afs_snprintf(name, sizeof name, "%s/%s", rootdir, dirE->d_name);
919            unlink(name);
920         }
921      }
922      closedir(dirP);
923   }
924
925   /* Now go through and remove all the directory links */
926   dirP = opendir(rootdir);
927   while (dirP && (dirE = readdir(dirP))) {
928       if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
929           afs_snprintf(name, sizeof name, "%s/%s", rootdir, dirE->d_name);
930           unlink(name);
931       }
932   }
933   closedir(dirP);
934   
935   return(code);
936 }
937
938 main(argc, argv)
939   int  argc;
940   char **argv;
941 {
942   struct cmd_syndesc *ts;
943   struct cmd_item    *ti;
944
945   setlinebuf(stdout);
946
947   ts=cmd_CreateSyntax(NULL, WorkerBee, NULL, "vldb check");
948   cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
949   cmd_AddParm(ts, "-dir",  CMD_SINGLE, CMD_OPTIONAL, "restore dir");
950   cmd_AddParm(ts, "-extension",  CMD_SINGLE, CMD_OPTIONAL, "name extension");
951   cmd_AddParm(ts, "-mountpoint", CMD_SINGLE, CMD_OPTIONAL, "mount point root");
952   cmd_AddParm(ts, "-umask",      CMD_SINGLE, CMD_OPTIONAL, "mode mask");
953
954   return cmd_Dispatch(argc, argv);
955 }