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
14 #include <afsconfig.h>
15 #include <afs/param.h>
19 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
30 #include <afs/bubasics.h>
32 #include <afs/com_err.h>
35 #include <afs/vlserver.h>
36 #include "error_macros.h"
37 #include "bucoord_internal.h"
38 #include "bucoord_prototypes.h"
40 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
43 #define MAXTAPESATONCE 10
45 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
47 /* local structure to keep track of volumes and the dumps from which
48 * they should be restored
51 struct dumpinfo *next;
52 struct volinfo *volinfolist;
53 struct volinfo *lastinlist;
55 afs_int32 initialDumpId;
56 afs_int32 parentDumpId;
67 /* local structure used to keep track of a tape, including a list of
68 * volumes to restore from that tape (bc_tapeItem)
71 struct bc_tapeList *next; /* next guy in list */
72 char *tapeName; /* name of the tape */
73 afs_int32 dumpID; /* dump located on this tape */
74 afs_int32 initialDumpID; /* initial dump located on this tape */
75 afs_int32 tapeNumber; /* which tape in the dump */
76 struct bc_tapeItem *restoreList; /* volumes to restore from this tape */
79 /* each tape has a list of volumes to restore; hangs off of a struct
80 bc_tapeList. Kept sorted so that we can read the tape once to do
81 everything we need to do. */
83 struct bc_tapeItem *next;
84 char *volumeName; /* volume to restore */
85 afs_int32 position; /* file slot on this tape */
86 afs_int32 oid; /* id of the volume */
87 afs_int32 dumplevel; /* level # of the containing dump (0 == top level) */
88 afs_int32 vollevel; /* level # of the containing volume (0 == full dump) */
91 int lastdump; /* The last incremental to restore */
94 /* strip .backup from the end of a name */
96 StripBackup(char *aname)
100 if ((j = BackupName(aname))) {
108 BackupName(char *aname)
113 if ((j > 7) && (strcmp(".backup", aname + j - 7) == 0))
115 if ((j > 9) && (strcmp(".readonly", aname + j - 9) == 0))
121 extractTapeSeq(char *tapename)
125 sptr = strrchr(tapename, '.');
138 alph = "abcdefjhijklmnopqrstuvwxyz";
141 viceName(value / 26 - 1);
144 printf("%c", alph[r]);
149 * aindex - index into bc_dumpTasks that describes this dump.
152 bc_Restorer(afs_int32 aindex)
154 struct bc_dumpTask *dumpTaskPtr;
156 char vname[BU_MAXNAMELEN];
157 struct budb_dumpEntry *dumpDescr, dumpDescr1, dumpDescr2;
158 struct budb_volumeEntry *volumeEntries;
159 struct bc_volumeDump *tvol;
160 afs_int32 code = 0, tcode;
161 afs_int32 tapedumpid, parent;
163 afs_int32 nentries = 0;
165 afs_int32 next, ve, vecount;
166 struct bc_tapeItem *ti, *pti, *nti;
167 struct bc_tapeList *tapeList = (struct bc_tapeList *)0;
168 struct bc_tapeList *tle, *ptle, *ntle;
170 afs_int32 tapeid, tid;
172 struct tc_restoreArray rpcArray; /* the rpc structure we use */
173 struct tc_restoreDesc *tcarray = (struct tc_restoreDesc *)0;
175 afs_int32 i, startentry, todump;
176 afs_int32 port, nextport;
177 afs_int32 level, tapeseq;
178 afs_int32 foundvolume, voldumplevel;
179 struct rx_connection *aconn = (struct rx_connection *)0;
180 statusP statusPtr, newStatusPtr;
182 struct dumpinfo *dumpinfolist = NULL;
183 struct dumpinfo *pdi, *ndi, *di, *dlevels;
184 struct volinfo *nvi, *vi;
186 int num_dlevels = 20;
188 afs_int32 serverAll; /* The server to which all volumes are to be restore to */
189 afs_int32 partitionAll; /* Likewise for partition */
190 struct hostent *hostPtr;
195 dlevels = (struct dumpinfo *) malloc(num_dlevels * sizeof(*dlevels));
197 dumpTaskPtr = &bc_dumpTasks[aindex];
198 serverAll = HOSTADDR(&dumpTaskPtr->destServer);
199 partitionAll = dumpTaskPtr->destPartition;
201 volumeEntries = (struct budb_volumeEntry *)
202 malloc(MAXTAPESATONCE * sizeof(struct budb_volumeEntry));
203 if (!volumeEntries) {
204 afs_com_err(whoami, BC_NOMEM, "");
208 /* For each volume to restore, find which dump it's most recent full or
209 * incremental is on and thread onto our dump list (from oldest to newest
210 * dump). Also hang the volume off of the dump (no particular order).
212 for (tvol = dumpTaskPtr->volumes; tvol; tvol = tvol->next) { /*tvol */
213 strcpy(vname, tvol->name);
214 dumpDescr = &dumpDescr1;
215 if (dumpTaskPtr->parentDumpID > 0) /* Told which dump to try */
217 /* Right now, this assumes that all volumes listed will be
218 * from the given dumpID. FIXME
220 code = bcdb_FindDumpByID(dumpTaskPtr->parentDumpID, dumpDescr);
223 afs_com_err(whoami, code, "Couldn't look up info for dump %d\n",
224 dumpTaskPtr->parentDumpID);
227 code = bcdb_FindVolumes(dumpTaskPtr->parentDumpID, vname, volumeEntries,
228 last, &next, MAXTAPESATONCE, &vecount);
231 if (!BackupName(vname))
233 strcat(vname, ".backup");
234 code = bcdb_FindVolumes(dumpTaskPtr->parentDumpID, vname, volumeEntries,
235 last, &next, MAXTAPESATONCE, &vecount);
241 code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
242 if (!BackupName(vname)) { /* See if backup volume is there */
243 strcat(vname, ".backup");
244 dumpDescr = &dumpDescr2;
246 code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
248 if (code) { /* Can't find backup, go with first results */
249 strcpy(vname, tvol->name);
250 dumpDescr = &dumpDescr1;
252 } else if (!tcode) { /* Both found an entry, go with latest result */
253 if (dumpDescr1.created > dumpDescr2.created) {
254 strcpy(vname, tvol->name);
255 dumpDescr = &dumpDescr1;
262 if (code) { /* If FindDump took an error */
263 afs_com_err(whoami, code, "; Can't find any dump for volume %s",
268 /* look to see if this dump has already been found */
269 for (pdi = 0, di = dumpinfolist; di; pdi = di, di = di->next) {
270 if (di->DumpId < dumpDescr->id) {
273 } else if (di->DumpId == dumpDescr->id) {
278 /* If didn't find it, create one and thread into list */
280 di = (struct dumpinfo *)malloc(sizeof(struct dumpinfo));
282 afs_com_err(whoami, BC_NOMEM, "");
285 memset(di, 0, sizeof(struct dumpinfo));
287 di->DumpId = dumpDescr->id;
288 di->initialDumpId = dumpDescr->initialDumpID;
289 di->parentDumpId = dumpDescr->parent;
290 di->level = dumpDescr->level;
293 di->next = dumpinfolist;
296 di->next = pdi->next;
301 /* Create one and thread into list */
302 vi = (struct volinfo *)malloc(sizeof(struct volinfo));
304 afs_com_err(whoami, BC_NOMEM, "");
307 memset(vi, 0, sizeof(struct volinfo));
309 vi->volname = (char *)malloc(strlen(vname) + 1);
312 afs_com_err(whoami, BC_NOMEM, "");
316 strcpy(vi->volname, vname);
318 vi->server = serverAll;
319 vi->partition = partitionAll;
321 vi->server = HOSTADDR(&tvol->server);
322 vi->partition = tvol->partition;
325 /* thread onto end of list */
327 di->volinfolist = vi;
329 di->lastinlist->next = vi;
333 /* For each of the above dumps we found (they could be increments), find
334 * the dump's lineage (up to the full dump).
336 for (di = dumpinfolist; di; di = di->next) {
337 /* Find each of the parent dumps */
338 memcpy(&dlevels[0], di, sizeof(struct dumpinfo));
339 for (lvl = 1, parent = dlevels[0].parentDumpId; parent;
340 parent = dlevels[lvl].parentDumpId, lvl++) {
341 if (lvl >= num_dlevels) { /* running out of dump levels */
342 struct dumpinfo *tdl = dlevels;
344 num_dlevels += num_dlevels; /* double */
345 dlevels = (struct dumpinfo *) malloc(num_dlevels * sizeof(*dlevels));
346 memcpy(dlevels, tdl, (num_dlevels/2) * sizeof(*dlevels));
349 code = bcdb_FindDumpByID(parent, &dumpDescr1);
351 for (vi = di->volinfolist; vi; vi = vi->next) {
352 afs_com_err(whoami, code,
353 "; Can't find parent DumpID %u for volume %s",
354 parent, vi->volname);
359 dlevels[lvl].DumpId = dumpDescr1.id;
360 dlevels[lvl].initialDumpId = dumpDescr1.initialDumpID;
361 dlevels[lvl].parentDumpId = dumpDescr1.parent;
362 dlevels[lvl].level = dumpDescr1.level;
365 /* For each of the volumes that has a dump in this lineage (vi),
366 * find where it is in each dump level (lv) starting at level 0 and
367 * going to higest. Each dump level could contain one or more
368 * fragments (vecount) of the volume (volume fragments span tapes).
369 * Each volume fragment is sorted by tapeid, tape sequence, and tape
372 for (vi = di->volinfolist; vi; vi = vi->next) {
373 tle = tapeList; /* Use for searching the tapelist */
374 ptle = 0; /* The previous tape list entry */
375 tlid = 0; /* tapeid of first entry */
376 /* Volume's dump-level. May not be same as dump's dump-level. This
377 * value gets incremented everytime the volume is found on a dump.
381 /* Do from level 0 dump to highest level dump */
382 for (lv = (lvl - 1); lv >= 0; lv--) {
383 foundvolume = 0; /* If found volume on this dump */
385 (dlevels[lv].initialDumpId ? dlevels[lv].
386 initialDumpId : dlevels[lv].DumpId);
388 /* Cycle through the volume fragments for this volume on the tape */
389 for (last = 0, c = 0; last >= 0; last = next, c++) {
391 bcdb_FindVolumes(dlevels[lv].DumpId, vi->volname,
392 volumeEntries, last, &next,
393 MAXTAPESATONCE, &vecount);
395 /* Volume wasn't found on the tape - so continue.
396 * This can happen when volume doesn't exist during level 0 dump
397 * but exists for incremental dump.
399 if (code == BUDB_ENDOFLIST) {
404 afs_com_err(whoami, code,
405 "; Can't find volume %s in DumpID %u",
406 vi->volname, dlevels[lv].DumpId);
410 /* If we have made the Findvolumes call more than once for the same
411 * volume, then begin the search at the top. This sort assumes that
412 * we are collecting information from lowest level dump (full dump)
413 * to the highest level dump (highest incremental)
414 * and from first fragment to last fragment at each level. If
415 * there is more than one call to FindVolumes, this is not true
424 /* For each of the volume fragments that we read, sort them into
425 * the list of tapes they are on. Sort by tapeid, then tape sequence.
426 * Do from first fragment (where volume begins) to last fragment.
428 for (ve = (vecount - 1); ve >= 0; ve--) {
429 foundvolume = 1; /* Found the volume on this dump */
431 tapeseq = volumeEntries[ve].tapeSeq;
433 /* Determine where in the list of tapes this should go */
434 for (; tle; ptle = tle, tle = tle->next) {
436 (tle->initialDumpID ? tle->
437 initialDumpID : tle->dumpID);
439 /* Sort by tapeids. BUT, we don't want add an entry in the middle
440 * of a dumpset (might split a volume fragmented across tapes).
441 * So make sure we step to next dumpset. tlid is the tapeid of
442 * the last tape we added a volume to. This can happen when an
443 * incremental was appended to a tape created prior its parent-
444 * dump's tape, and needs to be restored after it.
450 } /* Allocate and insert a tape entry */
453 /* Found the tapeid (the dumpset). Check if its the correct
456 else if (tapeid == tid) {
457 if (tapeseq < tle->tapeNumber) {
461 /* Allocate and insert a tape entry */
462 if (tapeseq == tle->tapeNumber) {
465 /* Don't allocate tape entry */
466 foundtape = 1; /* Found dumpset but not the tape */
469 /* Prevously found the tapeid (the dumpset) but this tape
470 * sequence not included (the tapeid has changed). So add
471 * this tape entry to the end of its dumpset.
473 else if (foundtape) {
477 /* Allocate and insert a tape entry */
480 /* Allocate a new tapelist entry if one not found */
482 tle = (struct bc_tapeList *)
483 malloc(sizeof(struct bc_tapeList));
485 afs_com_err(whoami, BC_NOMEM, "");
488 memset(tle, 0, sizeof(struct bc_tapeList));
491 (char *)malloc(strlen(volumeEntries[ve].tape)
493 if (!tle->tapeName) {
495 afs_com_err(whoami, BC_NOMEM, "");
499 strcpy(tle->tapeName, volumeEntries[ve].tape);
500 tle->dumpID = dlevels[lv].DumpId;
501 tle->initialDumpID = dlevels[lv].initialDumpId;
502 tle->tapeNumber = tapeseq;
504 /* Now thread the tape onto the list */
506 tle->next = tapeList;
509 tle->next = ptle->next;
514 (tle->initialDumpID ? tle->initialDumpID : tle->
517 /* Now place the volume fragment into the correct position on
518 * this tapelist entry. Duplicate entries are ignored.
520 for (pti = 0, ti = tle->restoreList; ti;
521 pti = ti, ti = ti->next) {
522 if (volumeEntries[ve].position < ti->position) {
526 /* Allocate an entry */
527 else if (volumeEntries[ve].position ==
530 } /* Duplicate entry */
533 /* If didn't find one, allocate and thread onto list.
534 * Remember the server and partition.
537 ti = (struct bc_tapeItem *)
538 malloc(sizeof(struct bc_tapeItem));
540 afs_com_err(whoami, BC_NOMEM, "");
543 memset(ti, 0, sizeof(struct bc_tapeItem));
546 (char *)malloc(strlen(volumeEntries[ve].name)
548 if (!ti->volumeName) {
550 afs_com_err(whoami, BC_NOMEM, "");
554 strcpy(ti->volumeName, volumeEntries[ve].name);
555 ti->server = vi->server;
556 ti->partition = vi->partition;
557 ti->oid = volumeEntries[ve].id;
558 ti->position = volumeEntries[ve].position;
559 ti->vollevel = voldumplevel;
560 ti->dumplevel = dlevels[lv].level;
561 ti->lastdump = ((lv == 0) ? 1 : 0);
564 ti->next = tle->restoreList;
565 tle->restoreList = ti;
567 ti->next = pti->next;
573 } /* ve: for each returned volume fragment by bcdb_FindVolumes() */
574 } /* last: Multiple calls to bcdb_FindVolumes() */
578 } /* lv: For each dump level */
579 } /* vi: For each volume to search for in the dump hierarchy */
580 } /* di: For each dump */
583 afs_com_err(whoami, 0, "No volumes to restore");
587 if (dumpTaskPtr->dontExecute) {
588 for (tle = tapeList; tle; tle = tle->next) {
589 for (ti = tle->restoreList; ti; ti = ti->next) {
591 (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
592 strcpy(vname, ti->volumeName);
594 if (dumpTaskPtr->newExt)
595 strcat(vname, dumpTaskPtr->newExt);
597 /* print volume to restore and the tape and position its on */
600 ("Restore volume %s (ID %d) from tape %s (%u), position %d",
601 ti->volumeName, ti->oid, tle->tapeName, tapedumpid,
604 if (strcmp(ti->volumeName, vname) != 0)
605 printf(" as %s", vname);
610 /* print in format recognized by volsetrestore */
614 gethostbyaddr((char *)&haddr, sizeof(haddr), AF_INET);
616 printf("%s", hostPtr->h_name);
618 printf("%u", ti->server);
621 viceName(ti->partition);
623 printf(" %s # as %s; %s (%u); pos %d;", ti->volumeName,
624 vname, tle->tapeName, tapedumpid, ti->position);
627 printf(" %s", ctime(&did));
635 /* Allocate a list of volumes to restore */
637 (struct tc_restoreDesc *)malloc(nentries *
638 sizeof(struct tc_restoreDesc));
640 afs_com_err(whoami, BC_NOMEM, "");
643 memset(tcarray, 0, nentries * sizeof(struct tc_restoreDesc));
645 /* Fill in the array with the list above */
647 for (tle = tapeList; tle; tle = tle->next) {
648 for (ti = tle->restoreList; ti; ti = ti->next) {
649 tcarray[i].origVid = ti->oid; /* means unknown */
651 tcarray[i].flags = 0;
652 if (ti->vollevel == 0)
653 tcarray[i].flags |= RDFLAG_FIRSTDUMP;
655 tcarray[i].flags |= RDFLAG_LASTDUMP;
656 tcarray[i].position = ti->position;
657 tcarray[i].dbDumpId = tle->dumpID;
658 tcarray[i].initialDumpId = tle->initialDumpID;
659 tcarray[i].hostAddr = ti->server; /* just the internet address */
660 tcarray[i].partition = ti->partition;
662 strcpy(tcarray[i].tapeName, tle->tapeName);
663 strcpy(tcarray[i].oldName, ti->volumeName);
664 strcpy(tcarray[i].newName, ti->volumeName);
665 StripBackup(tcarray[i].newName);
666 if (dumpTaskPtr->newExt)
667 strcat(tcarray[i].newName, dumpTaskPtr->newExt);
669 tcarray[i].dumpLevel = ti->dumplevel;
674 printf("Starting restore\n");
676 /* Loop until all of the tape entries are used */
677 for (startentry = 0; startentry < nentries; startentry += todump) {
678 /* Get all the next tape entries with the same port offset */
679 if (dumpTaskPtr->portCount == 0) {
681 todump = nentries - startentry;
682 } else if (dumpTaskPtr->portCount == 1) {
683 port = dumpTaskPtr->portOffset[0];
684 todump = nentries - startentry;
686 level = tcarray[startentry].dumpLevel;
690 dumpTaskPtr->portCount -
691 1 ? level : dumpTaskPtr->portCount - 1)];
693 for (todump = 1; ((startentry + todump) < nentries); todump++) {
694 level = tcarray[startentry + todump].dumpLevel;
698 dumpTaskPtr->portCount -
699 1 ? level : dumpTaskPtr->portCount - 1)];
700 if (port != nextport)
701 break; /* break if we change ports */
705 rpcArray.tc_restoreArray_len = todump;
706 rpcArray.tc_restoreArray_val = &tcarray[startentry];
708 code = ConnectButc(dumpTaskPtr->config, port, &aconn);
712 if (tcarray[startentry].dumpLevel == 0)
713 printf("\nFull restore being processed on port %d\n", port);
715 printf("\nIncremental restore being processed on port %d\n",
719 TC_PerformRestore(aconn, "DumpSetName", &rpcArray,
720 &dumpTaskPtr->dumpID);
722 afs_com_err(whoami, code, "; Failed to start restore");
726 /* setup status monitor node */
727 statusPtr = createStatusNode();
729 statusPtr->port = port;
730 statusPtr->taskId = dumpTaskPtr->dumpID;
731 statusPtr->jobNumber = bc_jobNumber();
732 statusPtr->flags &= ~STARTING; /* clearStatus to examine */
733 if (tcarray[startentry].dumpLevel == 0)
734 sprintf(statusPtr->taskName, "Full Restore");
736 sprintf(statusPtr->taskName, "Incremental Restore");
739 /* Wait until this restore pass is complete before starting the next
740 * pass. Query the statusWatcher for the status
744 newStatusPtr = findStatus(dumpTaskPtr->dumpID);
747 flags & (ABORT_REQUEST | ABORT_SENT | ABORT_DONE |
751 } else if (newStatusPtr->flags & (TASK_DONE)) {
761 rx_DestroyConnection(aconn);
762 aconn = (struct rx_connection *)0;
765 /* free up everything */
768 rx_DestroyConnection(aconn);
773 /* Free the dumpinfo list and its volumes */
774 for (di = dumpinfolist; di; di = ndi) {
775 for (vi = di->volinfolist; vi; vi = nvi) {
785 /* Free the tape lists and items */
786 for (tle = tapeList; tle; tle = ntle) {
787 for (ti = tle->restoreList; ti; ti = nti) {
790 free(ti->volumeName);
799 /* free local-like things we alloacted to save stack space */