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