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);
109 code = fread(&value, 1, 1, dumpfile);
111 fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
116 #define BUFSIZE 16384
120 readdata(buffer, size)
129 s = (afs_int32) ((size > BUFSIZE) ? BUFSIZE : size);
130 code = fread(buf, 1, s, dumpfile);
132 fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
136 code = fread(buffer, 1, size, dumpfile);
139 fprintf(stderr, "Code = %d; Errno = %d\n", code, errno);
141 fprintf(stderr, "Read %d bytes out of %" AFS_INT64_FMT "\n", code, (afs_uintmax_t)size);
143 if ((code >= 0) && (code < BUFSIZE))
144 buffer[size] = 0; /* Add null char at end */
150 struct DumpHeader *dh; /* Defined in dump.h */
156 /* memset(&dh, 0, sizeof(dh)); */
158 magic = ntohl(readvalue(4));
159 dh->version = ntohl(readvalue(4));
166 dh->volumeId = ntohl(readvalue(4));
170 for (i = 0, c = 'a'; c != '\0'; i++) {
171 dh->volumeName[i] = c = readchar();
173 dh->volumeName[i] = c;
177 dh->nDumpTimes = ntohl(readvalue(2)) >> 1;
178 for (i = 0; i < dh->nDumpTimes; i++) {
179 dh->dumpTimes[i].from = ntohl(readvalue(4));
180 dh->dumpTimes[i].to = ntohl(readvalue(4));
190 return ((afs_int32) tag);
193 struct volumeHeader {
195 char volumeName[100];
197 afs_int32 uniquifier;
204 afs_int32 accountNumber;
206 afs_int32 creationDate;
207 afs_int32 accessDate;
208 afs_int32 updateDate;
209 afs_int32 expirationDate;
210 afs_int32 backupDate;
211 afs_int32 dayUseDate;
214 afs_int32 weekUse[100]; /* weekCount of these */
219 afs_int32 volUpdateCounter;
223 ReadVolumeHeader(count)
226 struct volumeHeader vh;
227 int code, i, done, entries;
230 /* memset(&vh, 0, sizeof(vh)); */
237 vh.volumeId = ntohl(readvalue(4));
241 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
397 int code, i, done, entries;
399 char dirname[MAXNAMELEN], linkname[MAXNAMELEN], lname[MAXNAMELEN];
400 char parentdir[MAXNAMELEN], vflink[MAXNAMELEN];
401 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 */
463 #ifdef AFS_LARGEFILE_ENV
467 hi = ntohl(readvalue(4));
468 lo = ntohl(readvalue(4));
469 FillInt64(vn.dataSize, hi, lo);
472 #endif /* !AFS_LARGEFILE_ENV */
475 vn.dataSize = ntohl(readvalue(4));
478 /* parentdir is the name of this dir's vnode-file-link
479 * or this file's parent vnode-file-link.
480 * "./AFSDir-<#>". It's a symbolic link to its real dir.
481 * The parent dir and symbolic link to it must exist.
483 vnode = ((vn.type == 2) ? vn.vnode : vn.parent);
485 strncpy(parentdir, rootdir, sizeof parentdir);
487 afs_snprintf(parentdir, sizeof parentdir, "%s/%s%d", rootdir,
490 len = readlink(parentdir, linkname, MAXNAMELEN);
492 /* parentdir does not exist. So create an orphan dir.
493 * and then link the parentdir to the orphaned dir.
495 afs_snprintf(linkname, sizeof linkname, "%s/%s%d",
496 rootdir, ODIR, vnode);
497 code = mkdir(linkname, 0777);
498 if ((code < 0) && (errno != EEXIST)) {
500 "Error creating directory %s code=%d;%d\n",
501 linkname, code, errno);
504 /* Link the parentdir to it - now parentdir exists */
505 afs_snprintf(linkname, sizeof linkname, "%s%d/", ODIR,
507 code = symlink(linkname, parentdir);
510 "Error creating symlink %s -> %s code=%d;%d\n",
511 parentdir, linkname, code, errno);
518 /* We read the directory entries. If the entry is a
519 * directory, the subdir is created and the root dir
520 * will contain a link to it. If its a file, we only
521 * create a symlink in the dir to the file name.
540 unsigned short pgcount;
548 struct Pageheader header;
550 unsigned short hashTable[128];
554 struct DirHeader header;
555 struct DirEntry entry[1];
560 buffer = (char *)malloc(vn.dataSize);
562 readdata(buffer, vn.dataSize);
563 page0 = (struct Page0 *)buffer;
565 /* Step through each bucket in the hash table, i,
566 * and follow each element in the hash chain, j.
567 * This gives us each entry of the dir.
569 for (i = 0; i < 128; i++) {
570 for (j = ntohs(page0->header.hashTable[i]); j;
571 j = ntohs(page0->entry[j].next)) {
573 this_vn = ntohl(page0->entry[j].fid.vnode);
574 this_name = page0->entry[j].name;
576 if ((strcmp(this_name, ".") == 0)
577 || (strcmp(this_name, "..") == 0))
578 continue; /* Skip these */
580 /* For a directory entry, create it. Then create the
581 * link (from the rootdir) to this directory.
585 /* dirname is the directory to create.
586 * vflink is what will link to it.
588 afs_snprintf(dirname, sizeof dirname, "%s/%s",
589 parentdir, this_name);
590 afs_snprintf(vflink, sizeof vflink, "%s/%s%d",
591 rootdir, ADIR, this_vn);
593 /* The link and directory may already exist */
594 len = readlink(vflink, linkname, MAXNAMELEN);
596 /* Doesn't already exist - so create the directory.
597 * umask will pare the mode bits down.
599 code = mkdir(dirname, 0777);
600 if ((code < 0) && (errno != EEXIST)) {
602 "Error creating directory %s code=%d;%d\n",
603 dirname, code, errno);
606 /* Does already exist - so move the directory.
607 * It was created originally as orphaned.
609 linkname[len - 1] = '\0'; /* remove '/' at end */
610 afs_snprintf(lname, sizeof lname, "%s/%s",
612 code = rename(lname, dirname);
615 "Error renaming %s to %s code=%d;%d\n",
616 lname, dirname, code, errno);
620 /* Now create/update the link to the new/moved directory */
622 afs_snprintf(dirname, sizeof dirname, "%s/",
625 afs_snprintf(dirname, sizeof dirname,
626 "%s%d/%s/", ADIR, vn.vnode,
629 code = symlink(dirname, vflink);
632 "Error creating symlink %s -> %s code=%d;%d\n",
633 vflink, dirname, code, errno);
637 /* For a file entry, we remember the name of the file
638 * by creating a link within the directory. Restoring
639 * the file will later remove the link.
642 /*AFILEENTRY*/ afs_snprintf(vflink,
644 "%s/%s%d", parentdir,
647 code = symlink(this_name, vflink);
648 if ((code < 0) && (errno != EEXIST)) {
650 "Error creating symlink %s -> %s code=%d;%d\n",
651 vflink, page0->entry[j].name, code,
660 else if (vn.type == 1) {
662 /* A file vnode. So create it into the desired directory. A
663 * link should exist in the directory naming the file.
667 afs_sfsize_t size, s;
669 /* Check if its vnode-file-link exists. If not,
670 * then the file will be an orphaned file.
673 afs_snprintf(filename, sizeof filename, "%s/%s%d", parentdir,
675 len = readlink(filename, fname, MAXNAMELEN);
677 afs_snprintf(filename, sizeof filename, "%s/%s%d",
678 rootdir, OFILE, vn.vnode);
679 lfile = 0; /* no longer a linked file; a direct path */
682 /* Create a mode for the file. Use the owner bits and
683 * duplicate them across group and other. The umask
684 * will remove what we don't want.
686 mode = (vn.modebits >> 6) & 0x7;
687 mode |= (mode << 6) | (mode << 3);
689 /* Write the file out */
690 fid = open(filename, (O_CREAT | O_WRONLY | O_TRUNC), mode);
693 s = (afs_int32) ((size > BUFSIZE) ? BUFSIZE : size);
694 code = fread(buf, 1, s, dumpfile);
696 (void)write(fid, buf, code);
701 fprintf(stderr, "Code = %d; Errno = %d\n", code,
705 (void)afs_snprintf(tmp, sizeof tmp,
706 "Read %llu bytes out of %llu",
707 (afs_uintmax_t) (vn.dataSize -
709 (afs_uintmax_t) vn.dataSize);
710 fprintf(stderr, "%s\n", tmp);
717 fprintf(stderr, " File %s (%s) is incomplete\n",
721 /* Remove the link to the file */
727 else if (vn.type == 3) {
729 /* A symlink vnode. So read it into the desired directory. This could
730 * also be a mount point. If the volume is being restored to AFS, this
731 * will become a mountpoint. If not, it becomes a symlink to no-where.
736 /* Check if its vnode-file-link exists and create pathname
737 * of the symbolic link. If it doesn't exist,
738 * then the link will be an orphaned link.
740 afs_snprintf(linkname, sizeof linkname, "%s/%s%d", parentdir,
742 len = readlink(linkname, fname, MAXNAMELEN);
744 afs_snprintf(filename, sizeof filename, "%s/%s%d",
745 rootdir, OFILE, vn.vnode);
748 afs_snprintf(filename, sizeof filename, "%s/%s",
752 /* Read the link in, delete it, and then create it */
753 readdata(buf, vn.dataSize);
755 /* If a mountpoint, change its link path to mountroot */
757 if (((buf[0] == '%') || (buf[0] == '#'))
758 && (buf[s - 1] == '.')) {
759 /* This is a symbolic link */
760 buf[s - 1] = 0; /* Remove prefix '.' */
761 strcpy(lname, &buf[1]); /* Remove postfix '#' or '%' */
762 strcpy(buf, mntroot);
767 code = symlink(buf, filename);
770 "Error creating symlink %s -> %s code=%d;%d\n",
771 filename, buf, code, errno);
774 /* Remove the symbolic link */
779 fprintf(stderr, "Unknown Vnode block\n");
791 return ((afs_int32) tag);
795 WorkerBee(struct cmd_syndesc *as, void *arock)
797 int code = 0, c, len;
798 afs_int32 type, count, vcount;
800 struct dirent *dirE, *dirF;
801 char fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN],
803 char thisdir[MAXPATHLEN], *t;
804 struct DumpHeader dh; /* Defined in dump.h */
805 #if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */
806 extern char *getwd();
807 #define getcwd(x,y) getwd(x)
810 if (as->parms[0].items) { /* -file <dumpfile> */
811 dumpfile = fopen(as->parms[0].items->data, "r");
813 fprintf(stderr, "Cannot open '%s'. Code = %d\n",
814 as->parms[0].items->data, errno);
818 dumpfile = (FILE *) stdin; /* use stdin */
821 /* Read the dump header. From it we get the volume name */
822 type = ntohl(readvalue(1));
824 fprintf(stderr, "Expected DumpHeader\n");
828 type = ReadDumpHeader(&dh);
830 /* Get the root directory we restore to */
831 if (as->parms[1].items) { /* -dir <rootdir> */
832 strcpy(rootdir, as->parms[1].items->data);
834 strcpy(rootdir, ".");
836 strcat(rootdir, "/");
838 /* Append the RW volume name to the root directory */
839 strcat(rootdir, dh.volumeName);
840 len = strlen(rootdir);
841 if (strcmp(".backup", rootdir + len - 7) == 0) {
842 rootdir[len - 7] = 0;
843 } else if (strcmp(".readonly", rootdir + len - 9) == 0) {
844 rootdir[len - 9] = 0;
847 /* Append the extension we asked for */
848 if (as->parms[2].items) {
849 strcat(rootdir, as->parms[2].items->data); /* -extension <ext> */
852 /* The mountpoint root is either specifid in -mountpoint
853 * or -dir or the current working dir.
855 if ((as->parms[3].items) || (as->parms[1].items)) { /* -mountpoint or -dir */
856 t = (char *)getcwd(thisdir, MAXPATHLEN); /* remember current dir */
859 "Cannot get pathname of current working directory: %s\n",
864 /* Change to the mount point dir */
866 chdir((as->parms[3].items ? as->parms[3].items->data : as->
867 parms[1].items->data));
869 fprintf(stderr, "Mount point directory not found: Error = %d\n",
873 t = (char *)getcwd(mntroot, MAXPATHLEN); /* get its full pathname */
876 "Cannot determine pathname of mount point root directory: %s\n",
881 strcat(mntroot, "/"); /* append '/' to end of it */
882 code = chdir(thisdir); /* return to original working dir */
884 fprintf(stderr, "Cannot find working directory: Error = %d\n",
888 } else { /* use current directory */
889 t = (char *)getcwd(mntroot, MAXPATHLEN); /* get full pathname of current dir */
892 "Cannot determine pathname of current working directory: %s\n",
898 strcat(mntroot, "/"); /* append '/' to end of it */
900 /* Set the umask for the restore */
901 if (as->parms[4].items) { /* -umask */
903 mask = strtol(as->parms[4].items->data, 0, 8);
904 fprintf(stderr, "Umask set to 0%03o\n", mask);
908 fprintf(stderr, "Restoring volume dump of '%s' to directory '%s'.\n",
909 dh.volumeName, rootdir);
910 code = mkdir(rootdir, 0777);
911 if ((code < 0) && (errno != EEXIST)) {
912 fprintf(stderr, "Error creating directory %s code=%d;%d\n", rootdir,
916 for (count = 1; type == 2; count++) {
917 type = ReadVolumeHeader(count);
918 for (vcount = 1; type == 3; vcount++)
919 type = ReadVNode(vcount);
923 fprintf(stderr, "Expected End-of-Dump\n");
929 /* For incremental restores, Follow each directory link and
930 * remove an "AFSFile" links.
933 fprintf(stderr, "An incremental dump.\n");
934 dirP = opendir(rootdir);
935 while (dirP && (dirE = readdir(dirP))) {
936 if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
937 afs_snprintf(name, sizeof name, "%s/%s", rootdir,
939 dirQ = opendir(name);
940 while (dirQ && (dirF = readdir(dirQ))) {
941 if (strncmp(dirF->d_name, AFILE, strlen(AFILE)) == 0) {
942 afs_snprintf(name, sizeof name, "%s/%s/%s", rootdir,
943 dirE->d_name, dirF->d_name);
948 } else if (strncmp(dirE->d_name, AFILE, strlen(AFILE)) == 0) {
949 afs_snprintf(name, sizeof name, "%s/%s", rootdir,
957 /* Now go through and remove all the directory links */
958 dirP = opendir(rootdir);
959 while (dirP && (dirE = readdir(dirP))) {
960 if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) {
961 afs_snprintf(name, sizeof name, "%s/%s", rootdir, dirE->d_name);
974 struct cmd_syndesc *ts;
979 ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "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);