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