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