2 * Copyright 2000, International Business Machines Corporation and others.
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
11 * Read a vos dump and recreate the tree.
13 * restorevol [-file <dump file>]
14 * [-dir <restore dir>]
15 * [-extension <name extension>]
16 * [-mountpoint <mount point root>]
17 * [-umask <mode mask>]
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
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.
42 #include <afsconfig.h>
43 #include <afs/param.h>
47 #include <afs/afsint.h>
49 #include <rx/rx_queue.h>
51 #include <afs/ihandle.h>
52 #include <afs/vnode.h>
53 #include <afs/volume.h>
59 char rootdir
[MAXPATHLEN
];
60 char mntroot
[MAXPATHLEN
];
61 #define ADIR "AFSDir-"
62 #define AFILE "AFSFile-"
63 #define ODIR "__ORPHANEDIR__."
64 #define OFILE "__ORPHANFILE__."
79 s
= sizeof(value
) - size
;
81 fprintf(stderr
, "Too much data for afs_int32\n");
85 code
= fread(&ptr
[s
], 1, size
, dumpfile
);
87 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
99 code
= fread(&value
, 1, 1, dumpfile
);
101 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
106 #define BUFSIZE 16384
110 readdata(char *buffer
, afs_sfsize_t size
)
117 s
= (afs_int32
) ((size
> BUFSIZE
) ? BUFSIZE
: size
);
118 code
= fread(buf
, 1, s
, dumpfile
);
120 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
124 code
= fread(buffer
, 1, size
, dumpfile
);
127 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
129 fprintf(stderr
, "Read %d bytes out of %" AFS_INT64_FMT
"\n", code
, (afs_uintmax_t
)size
);
131 if ((code
>= 0) && (code
< BUFSIZE
))
132 buffer
[size
] = 0; /* Add null char at end */
137 ReadDumpHeader(struct DumpHeader
*dh
)
141 afs_int32 magic AFS_UNUSED
;
143 /* memset(&dh, 0, sizeof(dh)); */
145 magic
= ntohl(readvalue(4));
146 dh
->version
= ntohl(readvalue(4));
153 dh
->volumeId
= ntohl(readvalue(4));
157 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
158 dh
->volumeName
[i
] = c
= readchar();
160 dh
->volumeName
[i
] = c
;
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
;
170 for (i
= 0; i
< dh
->nDumpTimes
; i
++) {
171 dh
->dumpTimes
[i
].from
= ntohl(readvalue(4));
172 dh
->dumpTimes
[i
].to
= ntohl(readvalue(4));
182 return ((afs_int32
) tag
);
185 struct volumeHeader
{
187 char volumeName
[100];
189 afs_int32 uniquifier
;
196 afs_int32 accountNumber
;
198 afs_int32 creationDate
;
199 afs_int32 accessDate
;
200 afs_int32 updateDate
;
201 afs_int32 expirationDate
;
202 afs_int32 backupDate
;
203 afs_int32 dayUseDate
;
206 afs_int32 weekUse
[100]; /* weekCount of these */
211 afs_int32 volUpdateCounter
;
215 ReadVolumeHeader(afs_int32 count
)
217 struct volumeHeader vh
;
221 /* memset(&vh, 0, sizeof(vh)); */
228 vh
.volumeId
= ntohl(readvalue(4));
232 (void)ntohl(readvalue(4)); /* version stamp - ignore */
236 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
237 vh
.volumeName
[i
] = c
= readchar();
239 vh
.volumeName
[i
] = c
;
243 vh
.inService
= ntohl(readvalue(1));
247 vh
.blessed
= ntohl(readvalue(1));
251 vh
.uniquifier
= ntohl(readvalue(4));
255 vh
.volType
= ntohl(readvalue(1));
259 vh
.parentVol
= ntohl(readvalue(4));
263 vh
.cloneId
= ntohl(readvalue(4));
267 vh
.maxQuota
= ntohl(readvalue(4));
271 vh
.minQuota
= ntohl(readvalue(4));
275 vh
.diskUsed
= ntohl(readvalue(4));
279 vh
.fileCount
= ntohl(readvalue(4));
283 vh
.accountNumber
= ntohl(readvalue(4));
287 vh
.owner
= ntohl(readvalue(4));
291 vh
.creationDate
= ntohl(readvalue(4));
295 vh
.accessDate
= ntohl(readvalue(4));
299 vh
.updateDate
= ntohl(readvalue(4));
303 vh
.expirationDate
= ntohl(readvalue(4));
307 vh
.backupDate
= ntohl(readvalue(4));
311 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
312 vh
.message
[i
] = c
= readchar();
314 vh
.volumeName
[i
] = c
;
318 vh
.weekCount
= ntohl(readvalue(2));
319 for (i
= 0; i
< vh
.weekCount
; i
++) {
320 vh
.weekUse
[i
] = ntohl(readvalue(4));
325 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
326 vh
.motd
[i
] = c
= readchar();
331 vh
.dayUseDate
= ntohl(readvalue(4));
335 vh
.dayUse
= ntohl(readvalue(4));
339 readvalue(4); /*volUpCounter*/
348 return ((afs_int32
) tag
);
353 afs_int32 uniquifier
;
356 afs_int32 dataVersion
;
357 afs_int32 unixModTime
;
358 afs_int32 servModTime
;
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 */
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 */
378 afs_sfsize_t dataSize
;
381 #define MAXNAMELEN 256
384 ReadVNode(afs_int32 count
)
389 char dirname
[MAXNAMELEN
], linkname
[MAXNAMELEN
], lname
[MAXNAMELEN
];
390 char parentdir
[MAXNAMELEN
], vflink
[MAXNAMELEN
];
391 char filename
[MAXNAMELEN
], fname
[MAXNAMELEN
];
397 /* memset(&vn, 0, sizeof(vn)); */
403 vn
.vnode
= ntohl(readvalue(4));
404 vn
.uniquifier
= ntohl(readvalue(4));
411 vn
.type
= ntohl(readvalue(1));
415 vn
.linkCount
= ntohl(readvalue(2));
419 vn
.dataVersion
= ntohl(readvalue(4));
423 vn
.unixModTime
= ntohl(readvalue(4));
427 vn
.servModTime
= ntohl(readvalue(4));
431 vn
.author
= ntohl(readvalue(4));
435 vn
.owner
= ntohl(readvalue(4));
439 vn
.group
= ntohl(readvalue(4));
443 vn
.modebits
= ntohl(readvalue(2));
447 vn
.parent
= ntohl(readvalue(4));
451 readdata(vn
.acl
, 192); /* Skip ACL data */
455 hi
= ntohl(readvalue(4));
456 lo
= ntohl(readvalue(4));
457 FillInt64(vn
.dataSize
, hi
, lo
);
461 vn
.dataSize
= ntohl(readvalue(4));
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.
469 vnode
= ((vn
.type
== 2) ? vn
.vnode
: vn
.parent
);
471 strncpy(parentdir
, rootdir
, sizeof parentdir
);
473 snprintf(parentdir
, sizeof parentdir
,
474 "%s" OS_DIRSEP
"%s%d", rootdir
, ADIR
, vnode
);
476 len
= readlink(parentdir
, linkname
, MAXNAMELEN
);
478 /* parentdir does not exist. So create an orphan dir.
479 * and then link the parentdir to the orphaned dir.
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
)) {
486 "Error creating directory %s code=%d;%d\n",
487 linkname
, code
, errno
);
490 /* Link the parentdir to it - now parentdir exists */
491 snprintf(linkname
, sizeof linkname
, "%s%d/", ODIR
,
493 code
= symlink(linkname
, parentdir
);
496 "Error creating symlink %s -> %s code=%d;%d\n",
497 parentdir
, linkname
, code
, errno
);
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.
526 unsigned short pgcount
;
534 struct Pageheader header
;
536 unsigned short hashTable
[128];
540 struct DirHeader header
;
541 struct DirEntry entry
[1];
546 buffer
= malloc(vn
.dataSize
);
548 readdata(buffer
, vn
.dataSize
);
549 page0
= (struct Page0
*)buffer
;
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.
555 for (i
= 0; i
< 128; i
++) {
556 for (j
= ntohs(page0
->header
.hashTable
[i
]); j
;
557 j
= ntohs(page0
->entry
[j
].next
)) {
559 this_vn
= ntohl(page0
->entry
[j
].fid
.vnode
);
560 this_name
= page0
->entry
[j
].name
;
562 if ((strcmp(this_name
, ".") == 0)
563 || (strcmp(this_name
, "..") == 0))
564 continue; /* Skip these */
566 /* For a directory entry, create it. Then create the
567 * link (from the rootdir) to this directory.
571 /* dirname is the directory to create.
572 * vflink is what will link to it.
574 snprintf(dirname
, sizeof dirname
,
576 parentdir
, this_name
);
577 snprintf(vflink
, sizeof vflink
,
578 "%s" OS_DIRSEP
"%s%d",
579 rootdir
, ADIR
, this_vn
);
581 /* The link and directory may already exist */
582 len
= readlink(vflink
, linkname
, MAXNAMELEN
);
584 /* Doesn't already exist - so create the directory.
585 * umask will pare the mode bits down.
587 code
= mkdir(dirname
, 0777);
588 if ((code
< 0) && (errno
!= EEXIST
)) {
590 "Error creating directory %s code=%d;%d\n",
591 dirname
, code
, errno
);
594 /* Does already exist - so move the directory.
595 * It was created originally as orphaned.
597 linkname
[len
- 1] = '\0'; /* remove '/' at end */
598 snprintf(lname
, sizeof lname
,
601 code
= rename(lname
, dirname
);
604 "Error renaming %s to %s code=%d;%d\n",
605 lname
, dirname
, code
, errno
);
609 /* Now create/update the link to the new/moved directory */
611 snprintf(dirname
, sizeof dirname
, "%s/",
614 snprintf(dirname
, sizeof dirname
, "%s%d/%s/",
615 ADIR
, vn
.vnode
, this_name
);
617 code
= symlink(dirname
, vflink
);
620 "Error creating symlink %s -> %s code=%d;%d\n",
621 vflink
, dirname
, code
, errno
);
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.
631 snprintf(vflink
, sizeof vflink
,
632 "%s" OS_DIRSEP
"%s%d", parentdir
,
635 code
= symlink(this_name
, vflink
);
636 if ((code
< 0) && (errno
!= EEXIST
)) {
638 "Error creating symlink %s -> %s code=%d;%d\n",
639 vflink
, page0
->entry
[j
].name
, code
,
648 else if (vn
.type
== 1) {
650 /* A file vnode. So create it into the desired directory. A
651 * link should exist in the directory naming the file.
655 afs_sfsize_t size
, s
;
658 /* Check if its vnode-file-link exists. If not,
659 * then the file will be an orphaned file.
662 snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s%d",
663 parentdir
, AFILE
, vn
.vnode
);
664 len
= readlink(filename
, fname
, MAXNAMELEN
);
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 */
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.
675 mode
= (vn
.modebits
>> 6) & 0x7;
676 mode
|= (mode
<< 6) | (mode
<< 3);
678 /* Write the file out */
679 fid
= open(filename
, (O_CREAT
| O_WRONLY
| O_TRUNC
), mode
);
681 fprintf(stderr
, "Open %s: Errno = %d\n", filename
, errno
);
686 s
= (afs_int32
) ((size
> BUFSIZE
) ? BUFSIZE
: size
);
687 code
= fread(buf
, 1, s
, dumpfile
);
690 fprintf(stderr
, "Code = %d; Errno = %d\n", code
,
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
);
703 count
= write(fid
, buf
, code
);
705 fprintf(stderr
, "Count = %ld, Errno = %d\n",
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
);
719 fprintf(stderr
, " File %s (%s) is incomplete\n",
724 /* Remove the link to the file */
730 else if (vn
.type
== 3) {
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.
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.
742 snprintf(linkname
, sizeof linkname
, "%s" OS_DIRSEP
"%s%d",
743 parentdir
, AFILE
, vn
.vnode
);
744 len
= readlink(linkname
, fname
, MAXNAMELEN
- 1);
746 snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s%d",
747 rootdir
, OFILE
, vn
.vnode
);
750 snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s",
754 /* Read the link in, delete it, and then create it */
755 readdata(buf
, vn
.dataSize
);
757 /* If a mountpoint, change its link path to mountroot */
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
);
769 code
= symlink(buf
, filename
);
772 "Error creating symlink %s -> %s code=%d;%d\n",
773 filename
, buf
, code
, errno
);
776 /* Remove the symbolic link */
781 fprintf(stderr
, "Unknown Vnode block\n");
793 return ((afs_int32
) tag
);
797 WorkerBee(struct cmd_syndesc
*as
, void *arock
)
800 afs_int32 type
, count
, vcount
;
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)
811 if (as
->parms
[0].items
) { /* -file <dumpfile> */
812 dumpfile
= fopen(as
->parms
[0].items
->data
, "r");
814 fprintf(stderr
, "Cannot open '%s'. Code = %d\n",
815 as
->parms
[0].items
->data
, errno
);
819 dumpfile
= (FILE *) stdin
; /* use stdin */
822 /* Read the dump header. From it we get the volume name */
823 type
= ntohl(readvalue(1));
825 fprintf(stderr
, "Expected DumpHeader\n");
829 type
= ReadDumpHeader(&dh
);
831 /* Get the root directory we restore to */
832 if (as
->parms
[1].items
) { /* -dir <rootdir> */
833 strcpy(rootdir
, as
->parms
[1].items
->data
);
835 strcpy(rootdir
, ".");
837 strcat(rootdir
, "/");
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;
848 /* Append the extension we asked for */
849 if (as
->parms
[2].items
) {
850 strcat(rootdir
, as
->parms
[2].items
->data
); /* -extension <ext> */
853 /* The mountpoint root is either specifid in -mountpoint
854 * or -dir or the current working dir.
856 if ((as
->parms
[3].items
) || (as
->parms
[1].items
)) { /* -mountpoint or -dir */
857 t
= (char *)getcwd(thisdir
, MAXPATHLEN
); /* remember current dir */
860 "Cannot get pathname of current working directory: %s\n",
865 /* Change to the mount point dir */
867 chdir((as
->parms
[3].items
? as
->parms
[3].items
->data
: as
->
868 parms
[1].items
->data
));
870 fprintf(stderr
, "Mount point directory not found: Error = %d\n",
874 t
= (char *)getcwd(mntroot
, MAXPATHLEN
); /* get its full pathname */
877 "Cannot determine pathname of mount point root directory: %s\n",
882 strcat(mntroot
, "/"); /* append '/' to end of it */
883 code
= chdir(thisdir
); /* return to original working dir */
885 fprintf(stderr
, "Cannot find working directory: Error = %d\n",
889 } else { /* use current directory */
890 t
= (char *)getcwd(mntroot
, MAXPATHLEN
); /* get full pathname of current dir */
893 "Cannot determine pathname of current working directory: %s\n",
899 strcat(mntroot
, "/"); /* append '/' to end of it */
901 /* Set the umask for the restore */
902 if (as
->parms
[4].items
) { /* -umask */
904 mask
= strtol(as
->parms
[4].items
->data
, 0, 8);
905 fprintf(stderr
, "Umask set to 0%03o\n", mask
);
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
,
917 for (count
= 1; type
== 2; count
++) {
918 type
= ReadVolumeHeader(count
);
919 for (vcount
= 1; type
== 3; vcount
++)
920 type
= ReadVNode(vcount
);
924 fprintf(stderr
, "Expected End-of-Dump\n");
930 /* For incremental restores, Follow each directory link and
931 * remove an "AFSFile" links.
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
,
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
);
949 } else if (strncmp(dirE
->d_name
, AFILE
, strlen(AFILE
)) == 0) {
950 snprintf(name
, sizeof name
, "%s" OS_DIRSEP
"%s", rootdir
,
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
,
973 main(int argc
, char **argv
)
975 struct cmd_syndesc
*ts
;
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
,
985 cmd_AddParm(ts
, "-umask", CMD_SINGLE
, CMD_OPTIONAL
, "mode mask");
987 return cmd_Dispatch(argc
, argv
);