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