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>
22 #include <afs/bubasics.h>
24 #include <afs/com_err.h>
27 #include <afs/vlserver.h>
29 #include "error_macros.h"
30 #include "bucoord_internal.h"
31 #include "bucoord_prototypes.h"
33 extern struct bc_dumpTask bc_dumpTasks
[BC_MAXSIMDUMPS
];
36 #define MAXTAPESATONCE 10
38 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
40 /* local structure to keep track of volumes and the dumps from which
41 * they should be restored
44 struct dumpinfo
*next
;
45 struct volinfo
*volinfolist
;
46 struct volinfo
*lastinlist
;
48 afs_int32 initialDumpId
;
49 afs_int32 parentDumpId
;
60 /* local structure used to keep track of a tape, including a list of
61 * volumes to restore from that tape (bc_tapeItem)
64 struct bc_tapeList
*next
; /* next guy in list */
65 char *tapeName
; /* name of the tape */
66 afs_int32 dumpID
; /* dump located on this tape */
67 afs_int32 initialDumpID
; /* initial dump located on this tape */
68 afs_int32 tapeNumber
; /* which tape in the dump */
69 struct bc_tapeItem
*restoreList
; /* volumes to restore from this tape */
72 /* each tape has a list of volumes to restore; hangs off of a struct
73 bc_tapeList. Kept sorted so that we can read the tape once to do
74 everything we need to do. */
76 struct bc_tapeItem
*next
;
77 char *volumeName
; /* volume to restore */
78 afs_int32 position
; /* file slot on this tape */
79 afs_int32 oid
; /* id of the volume */
80 afs_int32 dumplevel
; /* level # of the containing dump (0 == top level) */
81 afs_int32 vollevel
; /* level # of the containing volume (0 == full dump) */
84 int lastdump
; /* The last incremental to restore */
87 /* strip .backup from the end of a name */
89 StripBackup(char *aname
)
93 if ((j
= BackupName(aname
))) {
101 BackupName(char *aname
)
106 if ((j
> 7) && (strcmp(".backup", aname
+ j
- 7) == 0))
108 if ((j
> 9) && (strcmp(".readonly", aname
+ j
- 9) == 0))
114 extractTapeSeq(char *tapename
)
118 sptr
= strrchr(tapename
, '.');
131 alph
= "abcdefjhijklmnopqrstuvwxyz";
134 viceName(value
/ 26 - 1);
137 printf("%c", alph
[r
]);
142 * aindex - index into bc_dumpTasks that describes this dump.
145 bc_Restorer(afs_int32 aindex
)
147 struct bc_dumpTask
*dumpTaskPtr
;
149 char vname
[BU_MAXNAMELEN
];
150 struct budb_dumpEntry
*dumpDescr
, dumpDescr1
, dumpDescr2
;
151 struct budb_volumeEntry
*volumeEntries
;
152 struct bc_volumeDump
*tvol
;
153 afs_int32 code
= 0, tcode
;
154 afs_int32 tapedumpid
, parent
;
156 afs_int32 nentries
= 0;
158 afs_int32 next
, ve
, vecount
;
159 struct bc_tapeItem
*ti
, *pti
, *nti
;
160 struct bc_tapeList
*tapeList
= (struct bc_tapeList
*)0;
161 struct bc_tapeList
*tle
, *ptle
, *ntle
;
163 afs_int32 tapeid
, tid
;
165 struct tc_restoreArray rpcArray
; /* the rpc structure we use */
166 struct tc_restoreDesc
*tcarray
= (struct tc_restoreDesc
*)0;
168 afs_int32 i
, startentry
, todump
;
169 afs_int32 port
, nextport
;
170 afs_int32 level
, tapeseq
;
171 afs_int32 foundvolume
, voldumplevel
;
172 struct rx_connection
*aconn
= (struct rx_connection
*)0;
173 statusP statusPtr
, newStatusPtr
;
175 struct dumpinfo
*dumpinfolist
= NULL
;
176 struct dumpinfo
*pdi
, *ndi
, *di
, *dlevels
;
177 struct volinfo
*nvi
, *vi
;
179 int num_dlevels
= 20;
181 afs_uint32 serverAll
; /* The server to which all volumes are to be restore to */
182 afs_int32 partitionAll
; /* Likewise for partition */
183 struct hostent
*hostPtr
;
188 dlevels
= malloc(num_dlevels
* sizeof(*dlevels
));
190 dumpTaskPtr
= &bc_dumpTasks
[aindex
];
191 serverAll
= HOSTADDR(&dumpTaskPtr
->destServer
);
192 partitionAll
= dumpTaskPtr
->destPartition
;
194 volumeEntries
= malloc(MAXTAPESATONCE
* sizeof(struct budb_volumeEntry
));
195 if (!volumeEntries
) {
196 afs_com_err(whoami
, BC_NOMEM
, NULL
);
200 /* For each volume to restore, find which dump it's most recent full or
201 * incremental is on and thread onto our dump list (from oldest to newest
202 * dump). Also hang the volume off of the dump (no particular order).
204 for (tvol
= dumpTaskPtr
->volumes
; tvol
; tvol
= tvol
->next
) { /*tvol */
205 strcpy(vname
, tvol
->name
);
206 dumpDescr
= &dumpDescr1
;
207 if (dumpTaskPtr
->parentDumpID
> 0) /* Told which dump to try */
209 /* Right now, this assumes that all volumes listed will be
210 * from the given dumpID. FIXME
212 code
= bcdb_FindDumpByID(dumpTaskPtr
->parentDumpID
, dumpDescr
);
215 afs_com_err(whoami
, code
, "Couldn't look up info for dump %d\n",
216 dumpTaskPtr
->parentDumpID
);
219 code
= bcdb_FindVolumes(dumpTaskPtr
->parentDumpID
, vname
, volumeEntries
,
220 last
, &next
, MAXTAPESATONCE
, &vecount
);
223 if (!BackupName(vname
))
225 strcat(vname
, ".backup");
226 code
= bcdb_FindVolumes(dumpTaskPtr
->parentDumpID
, vname
, volumeEntries
,
227 last
, &next
, MAXTAPESATONCE
, &vecount
);
233 code
= bcdb_FindDump(vname
, dumpTaskPtr
->fromDate
, dumpDescr
);
234 if (!BackupName(vname
)) { /* See if backup volume is there */
235 strcat(vname
, ".backup");
236 dumpDescr
= &dumpDescr2
;
238 code
= bcdb_FindDump(vname
, dumpTaskPtr
->fromDate
, dumpDescr
);
240 if (code
) { /* Can't find backup, go with first results */
241 strcpy(vname
, tvol
->name
);
242 dumpDescr
= &dumpDescr1
;
244 } else if (!tcode
) { /* Both found an entry, go with latest result */
245 if (dumpDescr1
.created
> dumpDescr2
.created
) {
246 strcpy(vname
, tvol
->name
);
247 dumpDescr
= &dumpDescr1
;
254 if (code
) { /* If FindDump took an error */
255 afs_com_err(whoami
, code
, "; Can't find any dump for volume %s",
260 /* look to see if this dump has already been found */
261 for (pdi
= 0, di
= dumpinfolist
; di
; pdi
= di
, di
= di
->next
) {
262 if (di
->DumpId
< dumpDescr
->id
) {
265 } else if (di
->DumpId
== dumpDescr
->id
) {
270 /* If didn't find it, create one and thread into list */
272 di
= calloc(1, sizeof(struct dumpinfo
));
274 afs_com_err(whoami
, BC_NOMEM
, NULL
);
278 di
->DumpId
= dumpDescr
->id
;
279 di
->initialDumpId
= dumpDescr
->initialDumpID
;
280 di
->parentDumpId
= dumpDescr
->parent
;
281 di
->level
= dumpDescr
->level
;
284 di
->next
= dumpinfolist
;
287 di
->next
= pdi
->next
;
292 /* Create one and thread into list */
293 vi
= calloc(1, sizeof(struct volinfo
));
295 afs_com_err(whoami
, BC_NOMEM
, NULL
);
299 vi
->volname
= strdup(vname
);
302 afs_com_err(whoami
, BC_NOMEM
, NULL
);
307 vi
->server
= serverAll
;
308 vi
->partition
= partitionAll
;
310 vi
->server
= HOSTADDR(&tvol
->server
);
311 vi
->partition
= tvol
->partition
;
314 /* thread onto end of list */
316 di
->volinfolist
= vi
;
318 di
->lastinlist
->next
= vi
;
322 /* For each of the above dumps we found (they could be increments), find
323 * the dump's lineage (up to the full dump).
325 for (di
= dumpinfolist
; di
; di
= di
->next
) {
326 /* Find each of the parent dumps */
327 memcpy(&dlevels
[0], di
, sizeof(struct dumpinfo
));
328 for (lvl
= 1, parent
= dlevels
[0].parentDumpId
; parent
;
329 parent
= dlevels
[lvl
].parentDumpId
, lvl
++) {
330 if (lvl
>= num_dlevels
) { /* running out of dump levels */
331 struct dumpinfo
*tdl
= dlevels
;
333 num_dlevels
+= num_dlevels
; /* double */
334 dlevels
= malloc(num_dlevels
* sizeof(*dlevels
));
335 memcpy(dlevels
, tdl
, (num_dlevels
/2) * sizeof(*dlevels
));
338 code
= bcdb_FindDumpByID(parent
, &dumpDescr1
);
340 for (vi
= di
->volinfolist
; vi
; vi
= vi
->next
) {
341 afs_com_err(whoami
, code
,
342 "; Can't find parent DumpID %u for volume %s",
343 parent
, vi
->volname
);
348 dlevels
[lvl
].DumpId
= dumpDescr1
.id
;
349 dlevels
[lvl
].initialDumpId
= dumpDescr1
.initialDumpID
;
350 dlevels
[lvl
].parentDumpId
= dumpDescr1
.parent
;
351 dlevels
[lvl
].level
= dumpDescr1
.level
;
354 /* For each of the volumes that has a dump in this lineage (vi),
355 * find where it is in each dump level (lv) starting at level 0 and
356 * going to higest. Each dump level could contain one or more
357 * fragments (vecount) of the volume (volume fragments span tapes).
358 * Each volume fragment is sorted by tapeid, tape sequence, and tape
361 for (vi
= di
->volinfolist
; vi
; vi
= vi
->next
) {
362 tle
= tapeList
; /* Use for searching the tapelist */
363 ptle
= 0; /* The previous tape list entry */
364 tlid
= 0; /* tapeid of first entry */
365 /* Volume's dump-level. May not be same as dump's dump-level. This
366 * value gets incremented everytime the volume is found on a dump.
370 /* Do from level 0 dump to highest level dump */
371 for (lv
= (lvl
- 1); lv
>= 0; lv
--) {
372 foundvolume
= 0; /* If found volume on this dump */
374 (dlevels
[lv
].initialDumpId
? dlevels
[lv
].
375 initialDumpId
: dlevels
[lv
].DumpId
);
377 /* Cycle through the volume fragments for this volume on the tape */
378 for (last
= 0, c
= 0; last
>= 0; last
= next
, c
++) {
380 bcdb_FindVolumes(dlevels
[lv
].DumpId
, vi
->volname
,
381 volumeEntries
, last
, &next
,
382 MAXTAPESATONCE
, &vecount
);
384 /* Volume wasn't found on the tape - so continue.
385 * This can happen when volume doesn't exist during level 0 dump
386 * but exists for incremental dump.
388 if (code
== BUDB_ENDOFLIST
) {
393 afs_com_err(whoami
, code
,
394 "; Can't find volume %s in DumpID %u",
395 vi
->volname
, dlevels
[lv
].DumpId
);
399 /* If we have made the Findvolumes call more than once for the same
400 * volume, then begin the search at the top. This sort assumes that
401 * we are collecting information from lowest level dump (full dump)
402 * to the highest level dump (highest incremental)
403 * and from first fragment to last fragment at each level. If
404 * there is more than one call to FindVolumes, this is not true
413 /* For each of the volume fragments that we read, sort them into
414 * the list of tapes they are on. Sort by tapeid, then tape sequence.
415 * Do from first fragment (where volume begins) to last fragment.
417 for (ve
= (vecount
- 1); ve
>= 0; ve
--) {
418 foundvolume
= 1; /* Found the volume on this dump */
420 tapeseq
= volumeEntries
[ve
].tapeSeq
;
422 /* Determine where in the list of tapes this should go */
423 for (; tle
; ptle
= tle
, tle
= tle
->next
) {
425 (tle
->initialDumpID
? tle
->
426 initialDumpID
: tle
->dumpID
);
428 /* Sort by tapeids. BUT, we don't want add an entry in the middle
429 * of a dumpset (might split a volume fragmented across tapes).
430 * So make sure we step to next dumpset. tlid is the tapeid of
431 * the last tape we added a volume to. This can happen when an
432 * incremental was appended to a tape created prior its parent-
433 * dump's tape, and needs to be restored after it.
439 } /* Allocate and insert a tape entry */
442 /* Found the tapeid (the dumpset). Check if its the correct
445 else if (tapeid
== tid
) {
446 if (tapeseq
< tle
->tapeNumber
) {
450 /* Allocate and insert a tape entry */
451 if (tapeseq
== tle
->tapeNumber
) {
454 /* Don't allocate tape entry */
455 foundtape
= 1; /* Found dumpset but not the tape */
458 /* Prevously found the tapeid (the dumpset) but this tape
459 * sequence not included (the tapeid has changed). So add
460 * this tape entry to the end of its dumpset.
462 else if (foundtape
) {
466 /* Allocate and insert a tape entry */
469 /* Allocate a new tapelist entry if one not found */
471 tle
= calloc(1, sizeof(struct bc_tapeList
));
473 afs_com_err(whoami
, BC_NOMEM
, NULL
);
477 tle
->tapeName
= strdup(volumeEntries
[ve
].tape
);
478 if (!tle
->tapeName
) {
480 afs_com_err(whoami
, BC_NOMEM
, NULL
);
484 tle
->dumpID
= dlevels
[lv
].DumpId
;
485 tle
->initialDumpID
= dlevels
[lv
].initialDumpId
;
486 tle
->tapeNumber
= tapeseq
;
488 /* Now thread the tape onto the list */
490 tle
->next
= tapeList
;
493 tle
->next
= ptle
->next
;
498 (tle
->initialDumpID
? tle
->initialDumpID
: tle
->
501 /* Now place the volume fragment into the correct position on
502 * this tapelist entry. Duplicate entries are ignored.
504 for (pti
= 0, ti
= tle
->restoreList
; ti
;
505 pti
= ti
, ti
= ti
->next
) {
506 if (volumeEntries
[ve
].position
< ti
->position
) {
510 /* Allocate an entry */
511 else if (volumeEntries
[ve
].position
==
514 } /* Duplicate entry */
517 /* If didn't find one, allocate and thread onto list.
518 * Remember the server and partition.
521 ti
= calloc(1, sizeof(struct bc_tapeItem
));
523 afs_com_err(whoami
, BC_NOMEM
, NULL
);
527 ti
->volumeName
= strdup(volumeEntries
[ve
].name
);
528 if (!ti
->volumeName
) {
530 afs_com_err(whoami
, BC_NOMEM
, NULL
);
534 ti
->server
= vi
->server
;
535 ti
->partition
= vi
->partition
;
536 ti
->oid
= volumeEntries
[ve
].id
;
537 ti
->position
= volumeEntries
[ve
].position
;
538 ti
->vollevel
= voldumplevel
;
539 ti
->dumplevel
= dlevels
[lv
].level
;
540 ti
->lastdump
= ((lv
== 0) ? 1 : 0);
543 ti
->next
= tle
->restoreList
;
544 tle
->restoreList
= ti
;
546 ti
->next
= pti
->next
;
552 } /* ve: for each returned volume fragment by bcdb_FindVolumes() */
553 } /* last: Multiple calls to bcdb_FindVolumes() */
557 } /* lv: For each dump level */
558 } /* vi: For each volume to search for in the dump hierarchy */
559 } /* di: For each dump */
562 afs_com_err(whoami
, 0, "No volumes to restore");
566 if (dumpTaskPtr
->dontExecute
) {
567 for (tle
= tapeList
; tle
; tle
= tle
->next
) {
568 for (ti
= tle
->restoreList
; ti
; ti
= ti
->next
) {
570 (tle
->initialDumpID
? tle
->initialDumpID
: tle
->dumpID
);
571 strcpy(vname
, ti
->volumeName
);
573 if (dumpTaskPtr
->newExt
)
574 strcat(vname
, dumpTaskPtr
->newExt
);
576 /* print volume to restore and the tape and position its on */
579 ("Restore volume %s (ID %d) from tape %s (%u), position %d",
580 ti
->volumeName
, ti
->oid
, tle
->tapeName
, tapedumpid
,
583 if (strcmp(ti
->volumeName
, vname
) != 0)
584 printf(" as %s", vname
);
589 /* print in format recognized by volsetrestore */
593 gethostbyaddr((char *)&haddr
, sizeof(haddr
), AF_INET
);
595 printf("%s", hostPtr
->h_name
);
597 printf("%u", ti
->server
);
600 viceName(ti
->partition
);
602 printf(" %s # as %s; %s (%u); pos %d;", ti
->volumeName
,
603 vname
, tle
->tapeName
, tapedumpid
, ti
->position
);
606 printf(" %s", ctime(&did
));
614 /* Allocate a list of volumes to restore */
615 tcarray
= calloc(nentries
, sizeof(struct tc_restoreDesc
));
617 afs_com_err(whoami
, BC_NOMEM
, NULL
);
621 /* Fill in the array with the list above */
623 for (tle
= tapeList
; tle
; tle
= tle
->next
) {
624 for (ti
= tle
->restoreList
; ti
; ti
= ti
->next
) {
625 tcarray
[i
].origVid
= ti
->oid
; /* means unknown */
627 tcarray
[i
].flags
= 0;
628 if (ti
->vollevel
== 0)
629 tcarray
[i
].flags
|= RDFLAG_FIRSTDUMP
;
631 tcarray
[i
].flags
|= RDFLAG_LASTDUMP
;
632 tcarray
[i
].position
= ti
->position
;
633 tcarray
[i
].dbDumpId
= tle
->dumpID
;
634 tcarray
[i
].initialDumpId
= tle
->initialDumpID
;
635 tcarray
[i
].hostAddr
= ti
->server
; /* just the internet address */
636 tcarray
[i
].partition
= ti
->partition
;
638 strcpy(tcarray
[i
].tapeName
, tle
->tapeName
);
639 strcpy(tcarray
[i
].oldName
, ti
->volumeName
);
640 strcpy(tcarray
[i
].newName
, ti
->volumeName
);
641 StripBackup(tcarray
[i
].newName
);
642 if (dumpTaskPtr
->newExt
)
643 strcat(tcarray
[i
].newName
, dumpTaskPtr
->newExt
);
645 tcarray
[i
].dumpLevel
= ti
->dumplevel
;
650 printf("Starting restore\n");
652 /* Loop until all of the tape entries are used */
653 for (startentry
= 0; startentry
< nentries
; startentry
+= todump
) {
654 /* Get all the next tape entries with the same port offset */
655 if (dumpTaskPtr
->portCount
== 0) {
657 todump
= nentries
- startentry
;
658 } else if (dumpTaskPtr
->portCount
== 1) {
659 port
= dumpTaskPtr
->portOffset
[0];
660 todump
= nentries
- startentry
;
662 level
= tcarray
[startentry
].dumpLevel
;
666 dumpTaskPtr
->portCount
-
667 1 ? level
: dumpTaskPtr
->portCount
- 1)];
669 for (todump
= 1; ((startentry
+ todump
) < nentries
); todump
++) {
670 level
= tcarray
[startentry
+ todump
].dumpLevel
;
674 dumpTaskPtr
->portCount
-
675 1 ? level
: dumpTaskPtr
->portCount
- 1)];
676 if (port
!= nextport
)
677 break; /* break if we change ports */
681 rpcArray
.tc_restoreArray_len
= todump
;
682 rpcArray
.tc_restoreArray_val
= &tcarray
[startentry
];
684 code
= ConnectButc(dumpTaskPtr
->config
, port
, &aconn
);
688 if (tcarray
[startentry
].dumpLevel
== 0)
689 printf("\nFull restore being processed on port %d\n", port
);
691 printf("\nIncremental restore being processed on port %d\n",
695 TC_PerformRestore(aconn
, "DumpSetName", &rpcArray
,
696 &dumpTaskPtr
->dumpID
);
698 afs_com_err(whoami
, code
, "; Failed to start restore");
702 /* setup status monitor node */
703 statusPtr
= createStatusNode();
705 statusPtr
->port
= port
;
706 statusPtr
->taskId
= dumpTaskPtr
->dumpID
;
707 statusPtr
->jobNumber
= bc_jobNumber();
708 statusPtr
->flags
&= ~STARTING
; /* clearStatus to examine */
709 if (tcarray
[startentry
].dumpLevel
== 0)
710 sprintf(statusPtr
->taskName
, "Full Restore");
712 sprintf(statusPtr
->taskName
, "Incremental Restore");
715 /* Wait until this restore pass is complete before starting the next
716 * pass. Query the statusWatcher for the status
720 newStatusPtr
= findStatus(dumpTaskPtr
->dumpID
);
723 flags
& (ABORT_REQUEST
| ABORT_SENT
| ABORT_DONE
|
727 } else if (newStatusPtr
->flags
& (TASK_DONE
)) {
737 rx_DestroyConnection(aconn
);
738 aconn
= (struct rx_connection
*)0;
741 /* free up everything */
744 rx_DestroyConnection(aconn
);
749 /* Free the dumpinfo list and its volumes */
750 for (di
= dumpinfolist
; di
; di
= ndi
) {
751 for (vi
= di
->volinfolist
; vi
; vi
= nvi
) {
761 /* Free the tape lists and items */
762 for (tle
= tapeList
; tle
; tle
= ntle
) {
763 for (ti
= tle
->restoreList
; ti
; ti
= nti
) {
766 free(ti
->volumeName
);
775 /* free local-like things we alloacted to save stack space */