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