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>
20 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
32 #include <afs/bubasics.h>
34 #include <afs/com_err.h>
37 #include <afs/vlserver.h>
38 #include "error_macros.h"
41 extern struct bc_config *bc_globalConfig;
42 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
43 extern void bc_HandleMisc();
45 extern struct rx_connection *bc_GetConn();
47 #define BC_MAXLEVELS 20
48 #define MAXTAPESATONCE 10
51 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.S_un.S_addr
53 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
56 /* local structure to keep track of volumes and the dumps from which
57 * they should be restored
61 struct dumpinfo *next;
62 struct volinfo *volinfolist;
63 struct volinfo *lastinlist;
65 afs_int32 initialDumpId;
66 afs_int32 parentDumpId;
78 /* local structure used to keep track of a tape, including a list of
79 * volumes to restore from that tape (bc_tapeItem)
83 struct bc_tapeList *next; /* next guy in list */
84 char *tapeName; /* name of the tape */
85 afs_int32 dumpID; /* dump located on this tape */
86 afs_int32 initialDumpID; /* initial dump located on this tape */
87 afs_int32 tapeNumber; /* which tape in the dump */
88 struct bc_tapeItem *restoreList; /* volumes to restore from this tape */
91 /* each tape has a list of volumes to restore; hangs off of a struct
92 bc_tapeList. Kept sorted so that we can read the tape once to do
93 everything we need to do. */
96 struct bc_tapeItem *next;
97 char *volumeName; /* volume to restore */
98 afs_int32 position; /* file slot on this tape */
99 afs_int32 oid; /* id of the volume */
100 afs_int32 dumplevel; /* level # of the containing dump (0 == top level) */
101 afs_int32 vollevel; /* level # of the containing volume (0 == full dump) */
104 int lastdump; /* The last incremental to restore */
107 /* strip .backup from the end of a name */
108 static StripBackup(aname)
109 register char *aname;
113 if (j = BackupName(aname)) {
120 int BackupName(aname)
126 if ((j > 7) && (strcmp(".backup", aname+j-7) == 0))
128 if ((j > 9) && (strcmp(".readonly", aname+j-9) == 0))
133 extractTapeSeq(tapename)
138 sptr = strrchr(tapename, '.');
139 if ( !sptr ) return(-1);
150 alph = "abcdefjhijklmnopqrstuvwxyz";
153 viceName(value/26 - 1);
156 printf("%c", alph[r]);
161 * aindex - index into bc_dumpTasks that describes this dump.
166 struct bc_dumpTask *dumpTaskPtr;
168 char vname[BU_MAXNAMELEN];
169 struct budb_dumpEntry *dumpDescr, dumpDescr1, dumpDescr2;
170 struct budb_volumeEntry *volumeEntries;
171 struct bc_volumeDump *tvol;
172 afs_int32 code = 0, tcode;
173 afs_int32 tapedumpid, parent;
175 afs_int32 nentries = 0;
176 afs_int32 last, next, ve, vecount;
177 struct bc_tapeItem *ti, *pti, *nti;
178 struct bc_tapeList *tapeList = (struct bc_tapeList *)0;
179 struct bc_tapeList *tle, *ptle, *ntle;
181 afs_int32 tapeid, tid;
183 struct tc_restoreArray rpcArray; /* the rpc structure we use */
184 struct tc_restoreDesc *tcarray = (struct tc_restoreDesc *)0;
186 afs_int32 i, startentry, todump;
187 afs_int32 port, nextport;
188 afs_int32 level, tapeseq;
189 afs_int32 foundvolume, voldumplevel;
190 struct rx_connection *aconn = (struct rx_connection *)0;
191 statusP statusPtr, newStatusPtr;
193 struct dumpinfo *dumpinfolist = NULL;
194 struct dumpinfo *pdi, *ndi, *di, dlevels[BC_MAXLEVELS];
195 struct volinfo *pvi, *nvi, *vi;
198 afs_int32 serverAll; /* The server to which all volumes are to be restore to */
199 afs_int32 partitionAll; /* Likewise for partition */
200 struct hostent *hostPtr;
205 extern statusP createStatusNode();
206 extern statusP findStatus();
208 dumpTaskPtr = &bc_dumpTasks[aindex];
209 serverAll = HOSTADDR(&dumpTaskPtr->destServer);
210 partitionAll = dumpTaskPtr->destPartition;
212 volumeEntries = (struct budb_volumeEntry *)
213 malloc(MAXTAPESATONCE * sizeof(struct budb_volumeEntry));
216 com_err(whoami,BC_NOMEM,"");
220 /* For each volume to restore, find which dump it's most recent full or
221 * incremental is on and thread onto our dump list (from oldest to newest
222 * dump). Also hang the volume off of the dump (no particular order).
224 for (tvol = dumpTaskPtr->volumes; tvol; tvol = tvol->next)
226 strcpy(vname, tvol->name);
227 dumpDescr = &dumpDescr1;
228 code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
230 if ( !BackupName(vname) ) /* See if backup volume is there */
232 strcat(vname, ".backup");
233 dumpDescr = &dumpDescr2;
235 code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
237 if (code) /* Can't find backup, go with first results */
239 strcpy(vname, tvol->name);
240 dumpDescr = &dumpDescr1;
243 else if (!tcode) /* Both found an entry, go with latest result */
245 if (dumpDescr1.created > dumpDescr2.created)
247 strcpy(vname, tvol->name);
248 dumpDescr = &dumpDescr1;
254 if (code) /* If FindDump took an error */
256 com_err(whoami, code, "; Can't find any dump for volume %s", tvol->name);
260 /* look to see if this dump has already been found */
261 for (pdi=0, di=dumpinfolist; di; pdi=di, di=di->next)
263 if (di->DumpId < dumpDescr->id) { di = 0; break; }
264 else if (di->DumpId == dumpDescr->id) { break; }
267 /* If didn't find it, create one and thread into list */
270 di = (struct dumpinfo *) malloc(sizeof(struct dumpinfo));
273 com_err(whoami,BC_NOMEM,"");
276 memset(di, 0, sizeof(struct dumpinfo));
278 di->DumpId = dumpDescr->id;
279 di->initialDumpId = dumpDescr->initialDumpID;
280 di->parentDumpId = dumpDescr->parent;
281 di->level = dumpDescr->level;
285 di->next = dumpinfolist;
290 di->next = pdi->next;
295 /* Create one and thread into list */
296 vi = (struct volinfo *) malloc(sizeof(struct volinfo));
299 com_err(whoami,BC_NOMEM,"");
302 memset(vi, 0, sizeof(struct volinfo));
304 vi->volname = (char*)malloc(strlen(vname)+1);
308 com_err(whoami,BC_NOMEM,"");
312 strcpy(vi->volname, vname);
315 vi->server = serverAll;
316 vi->partition = partitionAll;
320 vi->server = HOSTADDR(&tvol->server);
321 vi->partition = tvol->partition;
324 /* thread onto end of list */
325 if (!di->lastinlist) di->volinfolist = vi;
326 else di->lastinlist->next = vi;
330 /* For each of the above dumps we found (they could be increments), find
331 * the dump's lineage (up to the full dump).
333 for (di=dumpinfolist; di; di=di->next)
335 /* Find each of the parent dumps */
336 memcpy(&dlevels[0], di, sizeof(struct dumpinfo));
337 for (lvl=1, parent=dlevels[0].parentDumpId; parent;
338 parent=dlevels[lvl].parentDumpId, lvl++)
340 code = bcdb_FindDumpByID(parent, &dumpDescr1);
343 for (vi=di->volinfolist; vi; vi=vi->next)
345 com_err(whoami, code, "; Can't find parent DumpID %u for volume %s",
346 parent, vi->volname);
351 dlevels[lvl].DumpId = dumpDescr1.id;
352 dlevels[lvl].initialDumpId = dumpDescr1.initialDumpID;
353 dlevels[lvl].parentDumpId = dumpDescr1.parent;
354 dlevels[lvl].level = dumpDescr1.level;
357 /* For each of the volumes that has a dump in this lineage (vi),
358 * find where it is in each dump level (lv) starting at level 0 and
359 * going to higest. Each dump level could contain one or more
360 * fragments (vecount) of the volume (volume fragments span tapes).
361 * Each volume fragment is sorted by tapeid, tape sequence, and tape
364 for (vi=di->volinfolist; vi; vi=vi->next)
366 tle = tapeList; /* Use for searching the tapelist */
367 ptle = 0; /* The previous tape list entry */
368 tlid = 0; /* tapeid of first entry */
369 /* Volume's dump-level. May not be same as dump's dump-level. This
370 * value gets incremented everytime the volume is found on a dump.
374 /* Do from level 0 dump to highest level dump */
375 for (lv=(lvl-1); lv>=0; lv--)
377 foundvolume = 0; /* If found volume on this dump */
378 tapeid = (dlevels[lv].initialDumpId ? dlevels[lv].initialDumpId :
381 /* Cycle through the volume fragments for this volume on the tape */
382 for (last=0,c=0; last>=0; last=next,c++)
384 code = bcdb_FindVolumes(dlevels[lv].DumpId, vi->volname, volumeEntries,
385 last, &next, MAXTAPESATONCE, &vecount);
388 /* Volume wasn't found on the tape - so continue.
389 * This can happen when volume doesn't exist during level 0 dump
390 * but exists for incremental dump.
392 if (code == BUDB_ENDOFLIST)
398 com_err(whoami, code, "; Can't find volume %s in DumpID %u",
399 vi->volname, dlevels[lv].DumpId);
403 /* If we have made the Findvolumes call more than once for the same
404 * volume, then begin the search at the top. This sort assumes that
405 * we are collecting information from lowest level dump (full dump)
406 * to the highest level dump (highest incremental)
407 * and from first fragment to last fragment at each level. If
408 * there is more than one call to FindVolumes, this is not true
418 /* For each of the volume fragments that we read, sort them into
419 * the list of tapes they are on. Sort by tapeid, then tape sequence.
420 * Do from first fragment (where volume begins) to last fragment.
422 for (ve=(vecount-1); ve>=0; ve--)
424 foundvolume = 1; /* Found the volume on this dump */
426 tapeseq = volumeEntries[ve].tapeSeq;
428 /* Determine where in the list of tapes this should go */
429 for (; tle; ptle=tle, tle=tle->next)
431 tid = (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
433 /* Sort by tapeids. BUT, we don't want add an entry in the middle
434 * of a dumpset (might split a volume fragmented across tapes).
435 * So make sure we step to next dumpset. tlid is the tapeid of
436 * the last tape we added a volume to. This can happen when an
437 * incremental was appended to a tape created prior its parent-
438 * dump's tape, and needs to be restored after it.
443 { tle = 0; break; } /* Allocate and insert a tape entry */
446 /* Found the tapeid (the dumpset). Check if its the correct
449 else if (tapeid == tid)
451 if (tapeseq < tle->tapeNumber)
452 { tle = 0; break; } /* Allocate and insert a tape entry */
454 if (tapeseq == tle->tapeNumber)
455 { break; } /* Don't allocate tape entry */
457 foundtape = 1; /* Found dumpset but not the tape */
460 /* Prevously found the tapeid (the dumpset) but this tape
461 * sequence not included (the tapeid has changed). So add
462 * this tape entry to the end of its dumpset.
465 { tle = 0; break; } /* Allocate and insert a tape entry */
469 /* Allocate a new tapelist entry if one not found */
472 tle = (struct bc_tapeList *) malloc(sizeof(struct bc_tapeList));
475 com_err(whoami,BC_NOMEM,"");
478 memset(tle, 0, sizeof(struct bc_tapeList));
480 tle->tapeName = (char*)malloc(strlen(volumeEntries[ve].tape)+1);
484 com_err(whoami,BC_NOMEM,"");
488 strcpy(tle->tapeName, volumeEntries[ve].tape);
489 tle->dumpID = dlevels[lv].DumpId;
490 tle->initialDumpID = dlevels[lv].initialDumpId;
491 tle->tapeNumber = tapeseq;
493 /* Now thread the tape onto the list */
496 tle->next = tapeList;
501 tle->next = ptle->next;
505 tlid = (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
507 /* Now place the volume fragment into the correct position on
508 * this tapelist entry. Duplicate entries are ignored.
510 for (pti = 0, ti=tle->restoreList; ti; pti=ti, ti=ti->next)
512 if (volumeEntries[ve].position < ti->position)
513 { ti = 0; break; } /* Allocate an entry */
515 else if (volumeEntries[ve].position == ti->position)
516 { break; } /* Duplicate entry */
519 /* If didn't find one, allocate and thread onto list.
520 * Remember the server and partition.
524 ti = (struct bc_tapeItem *) malloc(sizeof(struct bc_tapeItem));
527 com_err(whoami,BC_NOMEM,"");
530 memset(ti, 0, sizeof(struct bc_tapeItem));
532 ti->volumeName = (char*)malloc(strlen(volumeEntries[ve].name)+1);
536 com_err(whoami,BC_NOMEM,"");
540 strcpy(ti->volumeName, volumeEntries[ve].name);
541 ti->server = vi->server;
542 ti->partition = vi->partition;
543 ti->oid = volumeEntries[ve].id;
544 ti->position = volumeEntries[ve].position;
545 ti->vollevel = voldumplevel;
546 ti->dumplevel = dlevels[lv].level;
547 ti->lastdump = ((lv == 0) ? 1 : 0);
551 ti->next = tle->restoreList;
552 tle->restoreList = ti;
556 ti->next = pti->next;
562 } /* ve: for each returned volume fragment by bcdb_FindVolumes() */
563 } /* last: Multiple calls to bcdb_FindVolumes() */
565 if (foundvolume) voldumplevel++;
566 } /* lv: For each dump level */
567 } /* vi: For each volume to search for in the dump hierarchy */
568 } /* di: For each dump */
572 com_err(whoami, 0, "No volumes to restore");
576 if (dumpTaskPtr->dontExecute)
578 for (tle=tapeList; tle; tle=tle->next)
580 for (ti=tle->restoreList; ti; ti=ti->next)
582 tapedumpid = (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
583 strcpy(vname, ti->volumeName);
585 if (dumpTaskPtr->newExt) strcat(vname, dumpTaskPtr->newExt);
587 /* print volume to restore and the tape and position its on */
590 printf("Restore volume %s (ID %d) from tape %s (%u), position %d",
591 ti->volumeName, ti->oid, tle->tapeName, tapedumpid, ti->position);
593 if (strcmp(ti->volumeName, vname) != 0)
594 printf(" as %s", vname);
599 /* print in format recognized by volsetrestore */
603 hostPtr = gethostbyaddr((char *) &haddr, sizeof(haddr), AF_INET);
604 if (hostPtr) printf("%s", hostPtr->h_name);
605 else printf("%u", ti->server);
608 viceName(ti->partition);
610 printf(" %s # as %s; %s (%u); pos %d;",
611 ti->volumeName, vname, tle->tapeName, tapedumpid, ti->position);
614 printf(" %s", ctime(&did));
622 /* Allocate a list of volumes to restore */
623 tcarray = (struct tc_restoreDesc *) malloc(nentries*sizeof(struct tc_restoreDesc));
626 com_err(whoami,BC_NOMEM,"");
629 memset(tcarray, 0, nentries*sizeof(struct tc_restoreDesc));
631 /* Fill in the array with the list above */
633 for (tle=tapeList; tle; tle=tle->next)
635 for (ti=tle->restoreList; ti; ti=ti->next)
637 tcarray[i].origVid = ti->oid; /* means unknown */
639 tcarray[i].flags = 0;
640 if (ti->vollevel == 0)
641 tcarray[i].flags |= RDFLAG_FIRSTDUMP;
643 tcarray[i].flags |= RDFLAG_LASTDUMP;
644 tcarray[i].position = ti->position;
645 tcarray[i].dbDumpId = tle->dumpID;
646 tcarray[i].initialDumpId = tle->initialDumpID;
647 tcarray[i].hostAddr = ti->server; /* just the internet address */
648 tcarray[i].partition = ti->partition;
650 strcpy(tcarray[i].tapeName, tle->tapeName);
651 strcpy(tcarray[i].oldName, ti->volumeName);
652 strcpy(tcarray[i].newName, ti->volumeName);
653 StripBackup(tcarray[i].newName);
654 if (dumpTaskPtr->newExt) strcat(tcarray[i].newName, dumpTaskPtr->newExt);
656 tcarray[i].dumpLevel = ti->dumplevel;
661 printf("Starting restore\n");
663 /* Loop until all of the tape entries are used */
664 for (startentry=0; startentry<nentries; startentry+=todump)
666 /* Get all the next tape entries with the same port offset */
667 if (dumpTaskPtr->portCount == 0)
670 todump = nentries - startentry;
672 else if (dumpTaskPtr->portCount == 1)
674 port = dumpTaskPtr->portOffset[0];
675 todump = nentries - startentry;
679 level = tcarray[startentry].dumpLevel;
680 port = dumpTaskPtr->portOffset[(level <= dumpTaskPtr->portCount-1 ?
681 level : dumpTaskPtr->portCount-1)];
683 for (todump=1; ((startentry+todump) < nentries); todump++)
685 level = tcarray[startentry+todump].dumpLevel;
686 nextport = dumpTaskPtr->portOffset[(level <= dumpTaskPtr->portCount-1 ?
687 level : dumpTaskPtr->portCount-1)];
688 if (port != nextport) break; /* break if we change ports */
692 rpcArray.tc_restoreArray_len = todump;
693 rpcArray.tc_restoreArray_val = &tcarray[startentry];
695 code = ConnectButc(dumpTaskPtr->config, port, &aconn);
696 if (code) return(code);
698 if (tcarray[startentry].dumpLevel == 0)
699 printf("\nFull restore being processed on port %d\n", port);
701 printf("\nIncremental restore being processed on port %d\n", port);
703 code = TC_PerformRestore(aconn, "DumpSetName", &rpcArray, &dumpTaskPtr->dumpID);
706 com_err(whoami,code,"; Failed to start restore");
710 /* setup status monitor node */
711 statusPtr = createStatusNode();
713 statusPtr->port = port;
714 statusPtr->taskId = dumpTaskPtr->dumpID;
715 statusPtr->jobNumber = bc_jobNumber();
716 statusPtr->flags &= ~STARTING; /* clearStatus to examine */
717 if (tcarray[startentry].dumpLevel == 0)
718 sprintf(statusPtr->taskName, "Full Restore");
720 sprintf(statusPtr->taskName, "Incremental Restore");
723 /* Wait until this restore pass is complete before starting the next
724 * pass. Query the statusWatcher for the status
729 newStatusPtr = findStatus(dumpTaskPtr->dumpID);
730 if (!newStatusPtr || (newStatusPtr->flags &
731 (ABORT_REQUEST|ABORT_SENT|ABORT_DONE|TASK_ERROR)) )
736 else if (newStatusPtr->flags & (TASK_DONE))
746 if (aconn) rx_DestroyConnection(aconn);
747 aconn = (struct rx_connection *)0;
750 /* free up everything */
752 if (aconn) rx_DestroyConnection(aconn);
754 if (tcarray) free(tcarray);
756 /* Free the dumpinfo list and its volumes */
757 for (di=dumpinfolist; di; di=ndi)
759 for (vi=di->volinfolist; vi; vi=nvi)
761 if (vi->volname) free(vi->volname);
769 /* Free the tape lists and items */
770 for (tle = tapeList; tle; tle=ntle)
772 for(ti = tle->restoreList; ti; ti = nti)
775 if (ti->volumeName) free(ti->volumeName);
779 if (tle->tapeName) free(tle->tapeName);
783 /* free local-like things we alloacted to save stack space */
784 if (volumeEntries) free(volumeEntries);