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