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