Import Upstream version 1.8.5
[hcoop/debian/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 }