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>
46 #include <afs/afsint.h>
49 #include <afs/ihandle.h>
50 #include <afs/vnode.h>
51 #include <afs/volume.h>
56 #include <sys/param.h>
57 #include <sys/types.h>
61 #include <netinet/in.h>
68 char rootdir
[MAXPATHLEN
];
69 char mntroot
[MAXPATHLEN
];
70 #define ADIR "AFSDir-"
71 #define AFILE "AFSFile-"
72 #define ODIR "__ORPHANEDIR__."
73 #define OFILE "__ORPHANFILE__."
88 s
= sizeof(value
) - size
;
90 fprintf(stderr
, "Too much data in afs_int32\n");
94 code
= fread(&ptr
[s
], 1, size
, dumpfile
);
96 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
108 code
= fread(&value
, 1, 1, dumpfile
);
110 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
115 #define BUFSIZE 16384
119 readdata(char *buffer
, afs_sfsize_t size
)
126 s
= (afs_int32
) ((size
> BUFSIZE
) ? BUFSIZE
: size
);
127 code
= fread(buf
, 1, s
, dumpfile
);
129 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
133 code
= fread(buffer
, 1, size
, dumpfile
);
136 fprintf(stderr
, "Code = %d; Errno = %d\n", code
, errno
);
138 fprintf(stderr
, "Read %d bytes out of %" AFS_INT64_FMT
"\n", code
, (afs_uintmax_t
)size
);
140 if ((code
>= 0) && (code
< BUFSIZE
))
141 buffer
[size
] = 0; /* Add null char at end */
146 ReadDumpHeader(struct DumpHeader
*dh
)
150 afs_int32 magic AFS_UNUSED
;
152 /* memset(&dh, 0, sizeof(dh)); */
154 magic
= ntohl(readvalue(4));
155 dh
->version
= ntohl(readvalue(4));
162 dh
->volumeId
= ntohl(readvalue(4));
166 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
167 dh
->volumeName
[i
] = c
= readchar();
169 dh
->volumeName
[i
] = c
;
173 dh
->nDumpTimes
= ntohl(readvalue(2)) >> 1;
174 if (dh
->nDumpTimes
> MAXDUMPTIMES
) {
175 fprintf(stderr
, "Too many dump times in header (%d > %d)\n",
176 dh
->nDumpTimes
, MAXDUMPTIMES
);
177 dh
->nDumpTimes
= MAXDUMPTIMES
;
179 for (i
= 0; i
< dh
->nDumpTimes
; i
++) {
180 dh
->dumpTimes
[i
].from
= ntohl(readvalue(4));
181 dh
->dumpTimes
[i
].to
= ntohl(readvalue(4));
191 return ((afs_int32
) tag
);
194 struct volumeHeader
{
196 char volumeName
[100];
198 afs_int32 uniquifier
;
205 afs_int32 accountNumber
;
207 afs_int32 creationDate
;
208 afs_int32 accessDate
;
209 afs_int32 updateDate
;
210 afs_int32 expirationDate
;
211 afs_int32 backupDate
;
212 afs_int32 dayUseDate
;
215 afs_int32 weekUse
[100]; /* weekCount of these */
220 afs_int32 volUpdateCounter
;
224 ReadVolumeHeader(afs_int32 count
)
226 struct volumeHeader vh
;
230 /* memset(&vh, 0, sizeof(vh)); */
237 vh
.volumeId
= ntohl(readvalue(4));
241 (void)ntohl(readvalue(4)); /* version stamp - ignore */
245 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
246 vh
.volumeName
[i
] = c
= readchar();
248 vh
.volumeName
[i
] = c
;
252 vh
.inService
= ntohl(readvalue(1));
256 vh
.blessed
= ntohl(readvalue(1));
260 vh
.uniquifier
= ntohl(readvalue(4));
264 vh
.volType
= ntohl(readvalue(1));
268 vh
.parentVol
= ntohl(readvalue(4));
272 vh
.cloneId
= ntohl(readvalue(4));
276 vh
.maxQuota
= ntohl(readvalue(4));
280 vh
.minQuota
= ntohl(readvalue(4));
284 vh
.diskUsed
= ntohl(readvalue(4));
288 vh
.fileCount
= ntohl(readvalue(4));
292 vh
.accountNumber
= ntohl(readvalue(4));
296 vh
.owner
= ntohl(readvalue(4));
300 vh
.creationDate
= ntohl(readvalue(4));
304 vh
.accessDate
= ntohl(readvalue(4));
308 vh
.updateDate
= ntohl(readvalue(4));
312 vh
.expirationDate
= ntohl(readvalue(4));
316 vh
.backupDate
= ntohl(readvalue(4));
320 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
321 vh
.message
[i
] = c
= readchar();
323 vh
.volumeName
[i
] = c
;
327 vh
.weekCount
= ntohl(readvalue(2));
328 for (i
= 0; i
< vh
.weekCount
; i
++) {
329 vh
.weekUse
[i
] = ntohl(readvalue(4));
334 for (i
= 0, c
= 'a'; c
!= '\0'; i
++) {
335 vh
.motd
[i
] = c
= readchar();
340 vh
.dayUseDate
= ntohl(readvalue(4));
344 vh
.dayUse
= ntohl(readvalue(4));
348 readvalue(4); /*volUpCounter*/
357 return ((afs_int32
) tag
);
362 afs_int32 uniquifier
;
365 afs_int32 dataVersion
;
366 afs_int32 unixModTime
;
367 afs_int32 servModTime
;
375 struct acl_accessList
{
376 int size
; /*size of this access list in bytes, including MySize itself */
377 int version
; /*to deal with upward compatibility ; <= ACL_ACLVERSION */
379 int positive
; /* number of positive entries */
380 int negative
; /* number of minus entries */
381 struct acl_accessEntry
{
382 int id
; /*internally-used ID of user or group */
383 int rights
; /*mask */
387 afs_sfsize_t dataSize
;
390 #define MAXNAMELEN 256
393 ReadVNode(afs_int32 count
)
398 char dirname
[MAXNAMELEN
], linkname
[MAXNAMELEN
], lname
[MAXNAMELEN
];
399 char parentdir
[MAXNAMELEN
], vflink
[MAXNAMELEN
];
400 char filename
[MAXNAMELEN
], fname
[MAXNAMELEN
];
406 /* memset(&vn, 0, sizeof(vn)); */
412 vn
.vnode
= ntohl(readvalue(4));
413 vn
.uniquifier
= ntohl(readvalue(4));
420 vn
.type
= ntohl(readvalue(1));
424 vn
.linkCount
= ntohl(readvalue(2));
428 vn
.dataVersion
= ntohl(readvalue(4));
432 vn
.unixModTime
= ntohl(readvalue(4));
436 vn
.servModTime
= ntohl(readvalue(4));
440 vn
.author
= ntohl(readvalue(4));
444 vn
.owner
= ntohl(readvalue(4));
448 vn
.group
= ntohl(readvalue(4));
452 vn
.modebits
= ntohl(readvalue(2));
456 vn
.parent
= ntohl(readvalue(4));
460 readdata(vn
.acl
, 192); /* Skip ACL data */
464 hi
= ntohl(readvalue(4));
465 lo
= ntohl(readvalue(4));
466 FillInt64(vn
.dataSize
, hi
, lo
);
470 vn
.dataSize
= ntohl(readvalue(4));
473 /* parentdir is the name of this dir's vnode-file-link
474 * or this file's parent vnode-file-link.
475 * "./AFSDir-<#>". It's a symbolic link to its real dir.
476 * The parent dir and symbolic link to it must exist.
478 vnode
= ((vn
.type
== 2) ? vn
.vnode
: vn
.parent
);
480 strncpy(parentdir
, rootdir
, sizeof parentdir
);
482 afs_snprintf(parentdir
, sizeof parentdir
, "%s" OS_DIRSEP
"%s%d", rootdir
,
485 len
= readlink(parentdir
, linkname
, MAXNAMELEN
);
487 /* parentdir does not exist. So create an orphan dir.
488 * and then link the parentdir to the orphaned dir.
490 afs_snprintf(linkname
, sizeof linkname
, "%s" OS_DIRSEP
"%s%d",
491 rootdir
, ODIR
, vnode
);
492 code
= mkdir(linkname
, 0777);
493 if ((code
< 0) && (errno
!= EEXIST
)) {
495 "Error creating directory %s code=%d;%d\n",
496 linkname
, code
, errno
);
499 /* Link the parentdir to it - now parentdir exists */
500 afs_snprintf(linkname
, sizeof linkname
, "%s%d/", ODIR
,
502 code
= symlink(linkname
, parentdir
);
505 "Error creating symlink %s -> %s code=%d;%d\n",
506 parentdir
, linkname
, code
, errno
);
513 /* We read the directory entries. If the entry is a
514 * directory, the subdir is created and the root dir
515 * will contain a link to it. If its a file, we only
516 * create a symlink in the dir to the file name.
535 unsigned short pgcount
;
543 struct Pageheader header
;
545 unsigned short hashTable
[128];
549 struct DirHeader header
;
550 struct DirEntry entry
[1];
555 buffer
= (char *)malloc(vn
.dataSize
);
557 readdata(buffer
, vn
.dataSize
);
558 page0
= (struct Page0
*)buffer
;
560 /* Step through each bucket in the hash table, i,
561 * and follow each element in the hash chain, j.
562 * This gives us each entry of the dir.
564 for (i
= 0; i
< 128; i
++) {
565 for (j
= ntohs(page0
->header
.hashTable
[i
]); j
;
566 j
= ntohs(page0
->entry
[j
].next
)) {
568 this_vn
= ntohl(page0
->entry
[j
].fid
.vnode
);
569 this_name
= page0
->entry
[j
].name
;
571 if ((strcmp(this_name
, ".") == 0)
572 || (strcmp(this_name
, "..") == 0))
573 continue; /* Skip these */
575 /* For a directory entry, create it. Then create the
576 * link (from the rootdir) to this directory.
580 /* dirname is the directory to create.
581 * vflink is what will link to it.
583 afs_snprintf(dirname
, sizeof dirname
, "%s" OS_DIRSEP
"%s",
584 parentdir
, this_name
);
585 afs_snprintf(vflink
, sizeof vflink
, "%s" OS_DIRSEP
"%s%d",
586 rootdir
, ADIR
, this_vn
);
588 /* The link and directory may already exist */
589 len
= readlink(vflink
, linkname
, MAXNAMELEN
);
591 /* Doesn't already exist - so create the directory.
592 * umask will pare the mode bits down.
594 code
= mkdir(dirname
, 0777);
595 if ((code
< 0) && (errno
!= EEXIST
)) {
597 "Error creating directory %s code=%d;%d\n",
598 dirname
, code
, errno
);
601 /* Does already exist - so move the directory.
602 * It was created originally as orphaned.
604 linkname
[len
- 1] = '\0'; /* remove '/' at end */
605 afs_snprintf(lname
, sizeof lname
, "%s" OS_DIRSEP
"%s",
607 code
= rename(lname
, dirname
);
610 "Error renaming %s to %s code=%d;%d\n",
611 lname
, dirname
, code
, errno
);
615 /* Now create/update the link to the new/moved directory */
617 afs_snprintf(dirname
, sizeof dirname
, "%s/",
620 afs_snprintf(dirname
, sizeof dirname
,
621 "%s%d/%s/", ADIR
, vn
.vnode
,
624 code
= symlink(dirname
, vflink
);
627 "Error creating symlink %s -> %s code=%d;%d\n",
628 vflink
, dirname
, code
, errno
);
632 /* For a file entry, we remember the name of the file
633 * by creating a link within the directory. Restoring
634 * the file will later remove the link.
637 /*AFILEENTRY*/ afs_snprintf(vflink
,
639 "%s" OS_DIRSEP
"%s%d", parentdir
,
642 code
= symlink(this_name
, vflink
);
643 if ((code
< 0) && (errno
!= EEXIST
)) {
645 "Error creating symlink %s -> %s code=%d;%d\n",
646 vflink
, page0
->entry
[j
].name
, code
,
655 else if (vn
.type
== 1) {
657 /* A file vnode. So create it into the desired directory. A
658 * link should exist in the directory naming the file.
662 afs_sfsize_t size
, s
;
665 /* Check if its vnode-file-link exists. If not,
666 * then the file will be an orphaned file.
669 afs_snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s%d", parentdir
,
671 len
= readlink(filename
, fname
, MAXNAMELEN
);
673 afs_snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s%d",
674 rootdir
, OFILE
, vn
.vnode
);
675 lfile
= 0; /* no longer a linked file; a direct path */
678 /* Create a mode for the file. Use the owner bits and
679 * duplicate them across group and other. The umask
680 * will remove what we don't want.
682 mode
= (vn
.modebits
>> 6) & 0x7;
683 mode
|= (mode
<< 6) | (mode
<< 3);
685 /* Write the file out */
686 fid
= open(filename
, (O_CREAT
| O_WRONLY
| O_TRUNC
), mode
);
688 fprintf(stderr
, "Open %s: Errno = %d\n", filename
, errno
);
693 s
= (afs_int32
) ((size
> BUFSIZE
) ? BUFSIZE
: size
);
694 code
= fread(buf
, 1, s
, dumpfile
);
697 fprintf(stderr
, "Code = %d; Errno = %d\n", code
,
701 (void)afs_snprintf(tmp
, sizeof tmp
,
702 "Read %llu bytes out of %llu",
703 (afs_uintmax_t
) (vn
.dataSize
-
705 (afs_uintmax_t
) vn
.dataSize
);
706 fprintf(stderr
, "%s\n", tmp
);
711 count
= write(fid
, buf
, code
);
713 fprintf(stderr
, "Count = %ld, Errno = %d\n",
716 } else if (count
!= code
) {
717 fprintf(stderr
, "Wrote %llu bytes out of %llu\n",
718 (afs_uintmax_t
) count
,
719 (afs_uintmax_t
) code
);
727 fprintf(stderr
, " File %s (%s) is incomplete\n",
732 /* Remove the link to the file */
738 else if (vn
.type
== 3) {
740 /* A symlink vnode. So read it into the desired directory. This could
741 * also be a mount point. If the volume is being restored to AFS, this
742 * will become a mountpoint. If not, it becomes a symlink to no-where.
746 /* Check if its vnode-file-link exists and create pathname
747 * of the symbolic link. If it doesn't exist,
748 * then the link will be an orphaned link.
750 afs_snprintf(linkname
, sizeof linkname
, "%s" OS_DIRSEP
"%s%d", parentdir
,
752 len
= readlink(linkname
, fname
, MAXNAMELEN
- 1);
754 afs_snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s%d",
755 rootdir
, OFILE
, vn
.vnode
);
758 afs_snprintf(filename
, sizeof filename
, "%s" OS_DIRSEP
"%s",
762 /* Read the link in, delete it, and then create it */
763 readdata(buf
, vn
.dataSize
);
765 /* If a mountpoint, change its link path to mountroot */
767 if (((buf
[0] == '%') || (buf
[0] == '#'))
768 && (buf
[s
- 1] == '.')) {
769 /* This is a symbolic link */
770 buf
[s
- 1] = 0; /* Remove prefix '.' */
771 strcpy(lname
, &buf
[1]); /* Remove postfix '#' or '%' */
772 strcpy(buf
, mntroot
);
777 code
= symlink(buf
, filename
);
780 "Error creating symlink %s -> %s code=%d;%d\n",
781 filename
, buf
, code
, errno
);
784 /* Remove the symbolic link */
789 fprintf(stderr
, "Unknown Vnode block\n");
801 return ((afs_int32
) tag
);
805 WorkerBee(struct cmd_syndesc
*as
, void *arock
)
808 afs_int32 type
, count
, vcount
;
810 struct dirent
*dirE
, *dirF
;
811 char name
[MAXNAMELEN
];
812 char thisdir
[MAXPATHLEN
], *t
;
813 struct DumpHeader dh
; /* Defined in dump.h */
814 #if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */
815 extern char *getwd();
816 #define getcwd(x,y) getwd(x)
819 if (as
->parms
[0].items
) { /* -file <dumpfile> */
820 dumpfile
= fopen(as
->parms
[0].items
->data
, "r");
822 fprintf(stderr
, "Cannot open '%s'. Code = %d\n",
823 as
->parms
[0].items
->data
, errno
);
827 dumpfile
= (FILE *) stdin
; /* use stdin */
830 /* Read the dump header. From it we get the volume name */
831 type
= ntohl(readvalue(1));
833 fprintf(stderr
, "Expected DumpHeader\n");
837 type
= ReadDumpHeader(&dh
);
839 /* Get the root directory we restore to */
840 if (as
->parms
[1].items
) { /* -dir <rootdir> */
841 strcpy(rootdir
, as
->parms
[1].items
->data
);
843 strcpy(rootdir
, ".");
845 strcat(rootdir
, "/");
847 /* Append the RW volume name to the root directory */
848 strcat(rootdir
, dh
.volumeName
);
849 len
= strlen(rootdir
);
850 if (strcmp(".backup", rootdir
+ len
- 7) == 0) {
851 rootdir
[len
- 7] = 0;
852 } else if (strcmp(".readonly", rootdir
+ len
- 9) == 0) {
853 rootdir
[len
- 9] = 0;
856 /* Append the extension we asked for */
857 if (as
->parms
[2].items
) {
858 strcat(rootdir
, as
->parms
[2].items
->data
); /* -extension <ext> */
861 /* The mountpoint root is either specifid in -mountpoint
862 * or -dir or the current working dir.
864 if ((as
->parms
[3].items
) || (as
->parms
[1].items
)) { /* -mountpoint or -dir */
865 t
= (char *)getcwd(thisdir
, MAXPATHLEN
); /* remember current dir */
868 "Cannot get pathname of current working directory: %s\n",
873 /* Change to the mount point dir */
875 chdir((as
->parms
[3].items
? as
->parms
[3].items
->data
: as
->
876 parms
[1].items
->data
));
878 fprintf(stderr
, "Mount point directory not found: Error = %d\n",
882 t
= (char *)getcwd(mntroot
, MAXPATHLEN
); /* get its full pathname */
885 "Cannot determine pathname of mount point root directory: %s\n",
890 strcat(mntroot
, "/"); /* append '/' to end of it */
891 code
= chdir(thisdir
); /* return to original working dir */
893 fprintf(stderr
, "Cannot find working directory: Error = %d\n",
897 } else { /* use current directory */
898 t
= (char *)getcwd(mntroot
, MAXPATHLEN
); /* get full pathname of current dir */
901 "Cannot determine pathname of current working directory: %s\n",
907 strcat(mntroot
, "/"); /* append '/' to end of it */
909 /* Set the umask for the restore */
910 if (as
->parms
[4].items
) { /* -umask */
912 mask
= strtol(as
->parms
[4].items
->data
, 0, 8);
913 fprintf(stderr
, "Umask set to 0%03o\n", mask
);
917 fprintf(stderr
, "Restoring volume dump of '%s' to directory '%s'.\n",
918 dh
.volumeName
, rootdir
);
919 code
= mkdir(rootdir
, 0777);
920 if ((code
< 0) && (errno
!= EEXIST
)) {
921 fprintf(stderr
, "Error creating directory %s code=%d;%d\n", rootdir
,
925 for (count
= 1; type
== 2; count
++) {
926 type
= ReadVolumeHeader(count
);
927 for (vcount
= 1; type
== 3; vcount
++)
928 type
= ReadVNode(vcount
);
932 fprintf(stderr
, "Expected End-of-Dump\n");
938 /* For incremental restores, Follow each directory link and
939 * remove an "AFSFile" links.
942 fprintf(stderr
, "An incremental dump.\n");
943 dirP
= opendir(rootdir
);
944 while (dirP
&& (dirE
= readdir(dirP
))) {
945 if (strncmp(dirE
->d_name
, ADIR
, strlen(ADIR
)) == 0) {
946 afs_snprintf(name
, sizeof name
, "%s" OS_DIRSEP
"%s", rootdir
,
948 dirQ
= opendir(name
);
949 while (dirQ
&& (dirF
= readdir(dirQ
))) {
950 if (strncmp(dirF
->d_name
, AFILE
, strlen(AFILE
)) == 0) {
951 afs_snprintf(name
, sizeof name
, "%s" OS_DIRSEP
"%s/%s", rootdir
,
952 dirE
->d_name
, dirF
->d_name
);
957 } else if (strncmp(dirE
->d_name
, AFILE
, strlen(AFILE
)) == 0) {
958 afs_snprintf(name
, sizeof name
, "%s" OS_DIRSEP
"%s", rootdir
,
966 /* Now go through and remove all the directory links */
967 dirP
= opendir(rootdir
);
968 while (dirP
&& (dirE
= readdir(dirP
))) {
969 if (strncmp(dirE
->d_name
, ADIR
, strlen(ADIR
)) == 0) {
970 afs_snprintf(name
, sizeof name
, "%s" OS_DIRSEP
"%s", rootdir
, dirE
->d_name
);
980 main(int argc
, char **argv
)
982 struct cmd_syndesc
*ts
;
986 ts
= cmd_CreateSyntax(NULL
, WorkerBee
, NULL
, "vldb check");
987 cmd_AddParm(ts
, "-file", CMD_SINGLE
, CMD_OPTIONAL
, "dump file");
988 cmd_AddParm(ts
, "-dir", CMD_SINGLE
, CMD_OPTIONAL
, "restore dir");
989 cmd_AddParm(ts
, "-extension", CMD_SINGLE
, CMD_OPTIONAL
, "name extension");
990 cmd_AddParm(ts
, "-mountpoint", CMD_SINGLE
, CMD_OPTIONAL
,
992 cmd_AddParm(ts
, "-umask", CMD_SINGLE
, CMD_OPTIONAL
, "mode mask");
994 return cmd_Dispatch(argc
, argv
);