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