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