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>
50 #include <afs/ihandle.h>
51 #include <afs/vnode.h>
52 #include <afs/volume.h>
57 #include <sys/param.h>
58 #include <sys/types.h>
62 #include <netinet/in.h>
67 char rootdir[MAXPATHLEN];
68 char mntroot[MAXPATHLEN];
69 #define ADIR "AFSDir-"
70 #define AFILE "AFSFile-"
71 #define ODIR "__ORPHANEDIR__."
72 #define OFILE "__ORPHANFILE__."
77 afs_int32 readvalue(size)
84 ptr = (char *) &value;
86 s = sizeof(value) - size;
88 fprintf (stderr, "Too much data in afs_int32\n");
92 code = fread (&ptr[s], 1, size, dumpfile);
93 if (code != size) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
105 code = fread (&value, 1, 1, dumpfile);
106 if (code != 1) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
111 #define BUFSIZE 16384
114 char readdata(buffer, size)
123 s = ((size > BUFSIZE) ? BUFSIZE : size);
124 code = fread(buf, 1, s, dumpfile);
125 if (code != s) fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
130 code = fread (buffer, 1, size, dumpfile);
133 fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
135 fprintf (stderr, "Read %d bytes out of %d\n", code, size);
137 if ((code >= 0) && (code < BUFSIZE))
138 buffer[size] = 0; /* Add null char at end */
142 afs_int32 ReadDumpHeader(dh)
143 struct DumpHeader *dh; /* Defined in dump.h */
149 /* memset(&dh, 0, sizeof(dh)); */
151 magic = ntohl(readvalue(4));
152 dh->version = ntohl(readvalue(4));
159 dh->volumeId = ntohl(readvalue(4));
163 for (i=0, c='a'; c!='\0'; i++) {
164 dh->volumeName[i] = c = readchar();
166 dh->volumeName[i] = c;
170 dh->nDumpTimes = ntohl(readvalue(2)) >> 1;
171 for (i=0; i<dh->nDumpTimes; i++) {
172 dh->dumpTimes[i].from = ntohl(readvalue(4));
173 dh->dumpTimes[i].to = ntohl(readvalue(4));
183 return((afs_int32)tag);
189 char volumeName[100];
191 afs_int32 uniquifier;
198 afs_int32 accountNumber;
200 afs_int32 creationDate;
201 afs_int32 accessDate;
202 afs_int32 updateDate;
203 afs_int32 expirationDate;
204 afs_int32 backupDate;
205 afs_int32 dayUseDate;
208 afs_int32 weekUse[100]; /* weekCount of these */
215 afs_int32 ReadVolumeHeader(count)
218 struct volumeHeader vh;
219 int code, i, done, entries;
222 /* memset(&vh, 0, sizeof(vh)); */
229 vh.volumeId = ntohl(readvalue(4));
233 ntohl(readvalue(4)); /* version stamp - ignore */
237 for (i=0, c='a'; c!='\0'; i++) {
238 vh.volumeName[i] = c = readchar();
240 vh.volumeName[i] = c;
244 vh.inService = ntohl(readvalue(1));
248 vh.blessed = ntohl(readvalue(1));
252 vh.uniquifier = ntohl(readvalue(4));
256 vh.volType = ntohl(readvalue(1));
260 vh.parentVol = ntohl(readvalue(4));
264 vh.cloneId = ntohl(readvalue(4));
268 vh.maxQuota = ntohl(readvalue(4));
272 vh.minQuota = ntohl(readvalue(4));
276 vh.diskUsed = ntohl(readvalue(4));
280 vh.fileCount = ntohl(readvalue(4));
284 vh.accountNumber = ntohl(readvalue(4));
288 vh.owner = ntohl(readvalue(4));
292 vh.creationDate = ntohl(readvalue(4));
296 vh.accessDate = ntohl(readvalue(4));
300 vh.updateDate = ntohl(readvalue(4));
304 vh.expirationDate = ntohl(readvalue(4));
308 vh.backupDate = ntohl(readvalue(4));
312 for (i=0, c='a'; c!='\0'; i++) {
313 vh.message[i] = c = readchar();
315 vh.volumeName[i] = c;
319 vh.weekCount = ntohl(readvalue(2));
320 for (i=0; i<vh.weekCount; i++) {
321 vh.weekUse[i] = ntohl(readvalue(4));
326 for (i=0, c='a'; c!='\0'; i++) {
327 vh.motd[i] = c = readchar();
332 vh.dayUseDate = ntohl(readvalue(4));
336 vh.dayUse = ntohl(readvalue(4));
345 return((afs_int32)tag);
351 afs_int32 uniquifier;
354 afs_int32 dataVersion;
355 afs_int32 unixModTime;
356 afs_int32 servModTime;
364 struct acl_accessList
366 int size; /*size of this access list in bytes, including MySize itself*/
367 int version; /*to deal with upward compatibility ; <= ACL_ACLVERSION*/
369 int positive; /* number of positive entries */
370 int negative; /* number of minus entries */
371 struct acl_accessEntry
373 int id; /*internally-used ID of user or group*/
381 #define MAXNAMELEN 256
383 afs_int32 ReadVNode(count)
387 int code, i, done, entries;
389 char dirname[MAXNAMELEN], linkname[MAXNAMELEN], lname[MAXNAMELEN];
390 char parentdir[MAXNAMELEN], vflink[MAXNAMELEN];
391 char filename[MAXNAMELEN], fname[MAXNAMELEN];
396 /* memset(&vn, 0, sizeof(vn)); */
402 vn.vnode = ntohl(readvalue(4));
403 vn.uniquifier = ntohl(readvalue(4));
410 vn.type = ntohl(readvalue(1));
414 vn.linkCount = ntohl(readvalue(2));
418 vn.dataVersion = ntohl(readvalue(4));
422 vn.unixModTime = ntohl(readvalue(4));
426 vn.servModTime = ntohl(readvalue(4));
430 vn.author = ntohl(readvalue(4));
434 vn.owner = ntohl(readvalue(4));
438 vn.group = ntohl(readvalue(4));
442 vn.modebits = ntohl(readvalue(2));
446 vn.parent = ntohl(readvalue(4));
450 readdata(vn.acl, 192); /* Skip ACL data */
454 vn.dataSize = ntohl(readvalue(4));
456 /* parentdir is the name of this dir's vnode-file-link
457 * or this file's parent vnode-file-link.
458 * "./AFSDir-<#>". It's a symbolic link to its real dir.
459 * The parent dir and symbolic link to it must exist.
461 vnode = ((vn.type == 2) ? vn.vnode : vn.parent);
463 sprintf(parentdir, "%s", rootdir);
465 sprintf(parentdir, "%s/%s%d", rootdir, ADIR, vnode);
467 len = readlink(parentdir, linkname, MAXNAMELEN);
469 /* parentdir does not exist. So create an orphan dir.
470 * and then link the parentdir to the orphaned dir.
472 sprintf(linkname, "%s/%s%d", rootdir, ODIR, vnode);
473 code = mkdir(linkname, 0777);
474 if ((code < 0) && (errno != EEXIST)) {
475 fprintf(stderr, "Error creating directory %s code=%d;%d\n",
476 linkname, code, errno);
479 /* Link the parentdir to it - now parentdir exists */
480 sprintf(linkname, "%s%d/", ODIR, vnode);
481 code = symlink(linkname, parentdir);
483 fprintf(stderr, "Error creating symlink %s -> %s code=%d;%d\n",
484 parentdir, linkname, code, errno);
491 /* We read the directory entries. If the entry is a
492 * directory, the subdir is created and the root dir
493 * will contain a link to it. If its a file, we only
494 * create a symlink in the dir to the file name.
516 unsigned short pgcount;
525 struct Pageheader header;
527 unsigned short hashTable[128];
532 struct DirHeader header;
533 struct DirEntry entry[1];
538 buffer = (char *)malloc(vn.dataSize);
540 readdata(buffer, vn.dataSize);
541 page0 = (struct Page0 *)buffer;
543 /* Step through each bucket in the hash table, i,
544 * and follow each element in the hash chain, j.
545 * This gives us each entry of the dir.
547 for (i=0; i<128; i++) {
548 for (j=ntohs(page0->header.hashTable[i]); j;
549 j=ntohs(page0->entry[j].next)) {
551 this_vn = ntohl(page0->entry[j].fid.vnode);
552 this_name = page0->entry[j].name;
554 if ((strcmp(this_name,"." ) == 0) ||
555 (strcmp(this_name,"..") == 0))
556 continue; /* Skip these */
558 /* For a directory entry, create it. Then create the
559 * link (from the rootdir) to this directory.
561 if (this_vn & 1) { /*ADIRENTRY*/
562 /* dirname is the directory to create.
563 * vflink is what will link to it.
565 sprintf(dirname, "%s/%s", parentdir, this_name);
566 sprintf(vflink, "%s/%s%d", rootdir, ADIR, this_vn);
568 /* The link and directory may already exist */
569 len = readlink(vflink, linkname, MAXNAMELEN);
571 /* Doesn't already exist - so create the directory.
572 * umask will pare the mode bits down.
574 code = mkdir(dirname, 0777);
575 if ((code < 0) && (errno != EEXIST)) {
576 fprintf(stderr, "Error creating directory %s code=%d;%d\n",
577 dirname, code, errno);
580 /* Does already exist - so move the directory.
581 * It was created originally as orphaned.
583 linkname[len-1] = '\0'; /* remove '/' at end */
584 sprintf(lname, "%s/%s", rootdir, linkname);
585 code = rename(lname, dirname);
587 fprintf(stderr, "Error renaming %s to %s code=%d;%d\n",
588 lname, dirname, code, errno);
592 /* Now create/update the link to the new/moved directory */
594 sprintf(dirname, "%s/", this_name);
596 sprintf(dirname, "%s%d/%s/", ADIR, vn.vnode, this_name);
598 code = symlink(dirname, vflink);
600 fprintf(stderr, "Error creating symlink %s -> %s code=%d;%d\n",
601 vflink, dirname, code, errno);
605 /* For a file entry, we remember the name of the file
606 * by creating a link within the directory. Restoring
607 * the file will later remove the link.
611 sprintf(vflink, "%s/%s%d", parentdir, AFILE, this_vn);
613 code = symlink(this_name, vflink);
614 if ((code < 0) && (errno != EEXIST)) {
615 fprintf(stderr, "Error creating symlink %s -> %s code=%d;%d\n",
616 vflink, page0->entry[j].name, code, errno);
624 else if (vn.type == 1) {
626 /* A file vnode. So create it into the desired directory. A
627 * link should exist in the directory naming the file.
633 /* Check if its vnode-file-link exists. If not,
634 * then the file will be an orphaned file.
637 sprintf(filename, "%s/%s%d", parentdir, AFILE, vn.vnode);
638 len = readlink(filename, fname, MAXNAMELEN);
640 sprintf(filename, "%s/%s%d", rootdir, OFILE, vn.vnode);
641 lfile = 0; /* no longer a linked file; a direct path */
644 /* Create a mode for the file. Use the owner bits and
645 * duplicate them across group and other. The umask
646 * will remove what we don't want.
648 mode = (vn.modebits >> 6) & 0x7;
649 mode |= (mode << 6) | (mode << 3);
651 /* Write the file out */
652 fid = open(filename, (O_CREAT | O_WRONLY | O_TRUNC), mode);
655 s = ((size > BUFSIZE) ? BUFSIZE : size);
656 code = fread(buf, 1, s, dumpfile);
658 write(fid, buf, code);
663 fprintf (stderr, "Code = %d; Errno = %d\n", code, errno);
665 fprintf (stderr, "Read %d bytes out of %d\n",
666 (vn.dataSize - size), vn.dataSize);
672 fprintf(stderr, " File %s (%s) is incomplete\n", filename, fname);
675 /* Remove the link to the file */
681 else if (vn.type == 3) {
683 /* A symlink vnode. So read it into the desired directory. This could
684 * also be a mount point. If the volume is being restored to AFS, this
685 * will become a mountpoint. If not, it becomes a symlink to no-where.
690 /* Check if its vnode-file-link exists and create pathname
691 * of the symbolic link. If it doesn't exist,
692 * then the link will be an orphaned link.
694 sprintf(linkname, "%s/%s%d", parentdir, AFILE, vn.vnode);
695 len = readlink(linkname, fname, MAXNAMELEN);
697 sprintf(filename, "%s/%s%d", rootdir, OFILE, vn.vnode);
700 sprintf(filename, "%s/%s", parentdir, fname);
703 /* Read the link in, delete it, and then create it */
704 readdata(buf, vn.dataSize);
706 /* If a mountpoint, change its link path to mountroot */
708 if ( ((buf[0] == '%') || (buf[0] == '#')) && (buf[s-1] == '.') ) {
709 /* This is a symbolic link */
710 buf[s-1] = 0; /* Remove prefix '.' */
711 strcpy(lname, &buf[1]); /* Remove postfix '#' or '%' */
712 strcpy(buf, mntroot);
717 code = symlink(buf, filename);
719 fprintf(stderr, "Error creating symlink %s -> %s code=%d;%d\n",
720 filename, buf, code, errno);
723 /* Remove the symbolic link */
727 fprintf (stderr, "Unknown Vnode block\n");
739 return((afs_int32)tag);
743 struct cmd_syndesc *as;
747 afs_int32 type, count, vcount;
749 struct dirent *dirE, *dirF;
750 char fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN], mname[MAXNAMELEN];
751 char thisdir[MAXPATHLEN], *t;
752 struct DumpHeader dh; /* Defined in dump.h */
753 #if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */
754 extern char *getwd();
755 #define getcwd(x,y) getwd(x)
758 if (as->parms[0].items) { /* -file <dumpfile> */
759 dumpfile = fopen(as->parms[0].items->data, "r");
761 fprintf(stderr, "Cannot open '%s'. Code = %d\n",
762 as->parms[0].items->data, errno);
766 dumpfile = (FILE *)stdin; /* use stdin */
769 /* Read the dump header. From it we get the volume name */
770 type = ntohl(readvalue(1));
772 fprintf(stderr, "Expected DumpHeader\n");
776 type = ReadDumpHeader(&dh);
778 /* Get the root directory we restore to */
779 if (as->parms[1].items) { /* -dir <rootdir> */
780 strcpy(rootdir, as->parms[1].items->data);
786 /* Append the RW volume name to the root directory */
787 strcat(rootdir, dh.volumeName);
788 len = strlen(rootdir);
789 if (strcmp(".backup",rootdir+len-7) == 0) {
791 } else if (strcmp(".readonly",rootdir+len-9) == 0) {
795 /* Append the extension we asked for */
796 if (as->parms[2].items) {
797 strcat(rootdir, as->parms[2].items->data); /* -extension <ext> */
800 /* The mountpoint root is either specifid in -mountpoint
801 * or -dir or the current working dir.
803 if ((as->parms[3].items) || (as->parms[1].items)) { /* -mountpoint or -dir*/
804 t = (char *)getcwd(thisdir, MAXPATHLEN); /* remember current dir */
806 fprintf(stderr, "Cannot get pathname of current working directory: %s\n",
811 /* Change to the mount point dir */
812 code = chdir((as->parms[3].items ? as->parms[3].items->data :
813 as->parms[1].items->data));
815 fprintf(stderr, "Mount point directory not found: Error = %d\n", stderr);
818 t = (char *)getcwd(mntroot, MAXPATHLEN); /* get its full pathname */
820 fprintf(stderr, "Cannot determine pathname of mount point root directory: %s\n",
825 strcat(mntroot, "/"); /* append '/' to end of it */
826 chdir(thisdir); /* return to original working dir */
828 fprintf(stderr, "Cannot find working directory: Error = %d\n", stderr);
831 } else { /* use current directory */
832 t = (char *)getcwd(mntroot, MAXPATHLEN); /* get full pathname of current dir */
834 fprintf(stderr, "Cannot determine pathname of current working directory: %s\n",
840 strcat(mntroot, "/"); /* append '/' to end of it */
842 /* Set the umask for the restore */
843 if (as->parms[4].items) { /* -umask */
845 mask = strtol(as->parms[4].items->data, 0, 8);
846 fprintf(stderr, "Umask set to 0%03o\n", mask);
850 fprintf(stderr, "Restoring volume dump of '%s' to directory '%s'.\n",
851 dh.volumeName, rootdir);
852 code = mkdir(rootdir, 0777);
853 if ((code < 0) && (errno != EEXIST)) {
854 fprintf(stderr, "Error creating directory %s code=%d;%d\n",
855 rootdir, code, errno);
858 for (count=1; type==2; count++) {
859 type = ReadVolumeHeader(count);
860 for (vcount=1; type==3; vcount++)
861 type = ReadVNode(vcount);
865 fprintf(stderr, "Expected End-of-Dump\n");
871 /* For incremental restores, Follow each directory link and
872 * remove an "AFSFile" links.
875 fprintf(stderr, "An incremental dump.\n");
876 dirP = opendir(rootdir);
877 while (dirP && (dirE = readdir(dirP))) {
878 if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
879 sprintf(name, "%s/%s", rootdir, dirE->d_name);
880 dirQ = opendir(name);
881 while (dirQ && (dirF = readdir(dirQ))) {
882 if (strncmp(dirF->d_name, AFILE, strlen(AFILE)) == 0) {
883 sprintf(name, "%s/%s/%s", rootdir, dirE->d_name, dirF->d_name);
888 } else if (strncmp(dirE->d_name, AFILE, strlen(AFILE)) == 0) {
889 sprintf(name, "%s/%s", rootdir, dirE->d_name);
896 /* Now go through and remove all the directory links */
897 dirP = opendir(rootdir);
898 while (dirP && (dirE = readdir(dirP))) {
899 if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
900 sprintf(name, "%s/%s", rootdir, dirE->d_name);
913 struct cmd_syndesc *ts;
918 ts=cmd_CreateSyntax((char *)0, WorkerBee, (char *) 0, "vldb check");
919 cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file");
920 cmd_AddParm(ts, "-dir", CMD_SINGLE, CMD_OPTIONAL, "restore dir");
921 cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL, "name extension");
922 cmd_AddParm(ts, "-mountpoint", CMD_SINGLE, CMD_OPTIONAL, "mount point root");
923 cmd_AddParm(ts, "-umask", CMD_SINGLE, CMD_OPTIONAL, "mode mask");
925 return cmd_Dispatch(argc, argv);