Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
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 | |
8 | */ | |
9 | ||
10 | #include <afsconfig.h> | |
11 | #include <afs/param.h> | |
12 | ||
13 | #include <afs/procmgmt.h> | |
14 | #include <roken.h> | |
15 | ||
16 | #ifdef IGNORE_SOME_GCC_WARNINGS | |
17 | # pragma GCC diagnostic warning "-Wimplicit-function-declaration" | |
18 | #endif | |
19 | ||
20 | #include <afs/opr.h> | |
21 | #include <rx/rx.h> | |
22 | #include <afs/afsint.h> | |
23 | #include <afs/prs_fs.h> | |
24 | #include <afs/nfs.h> | |
25 | #include <lwp.h> | |
26 | #include <lock.h> | |
27 | #include <afs/cellconfig.h> | |
28 | #include <afs/keys.h> | |
29 | #include <ubik.h> | |
30 | #include <afs/acl.h> | |
31 | #include <afs/volser.h> | |
32 | #include <afs/vlserver.h> | |
33 | #include <afs/tcdata.h> | |
34 | #include <afs/budb.h> | |
35 | #include <afs/budb_client.h> | |
36 | #include <afs/bubasics.h> | |
37 | #include <afs/bucoord_prototypes.h> | |
38 | #include <afs/butm_prototypes.h> | |
39 | #include <afs/budb_prototypes.h> | |
40 | #include <afs/afsutil.h> | |
41 | ||
42 | #include "butc_internal.h" | |
43 | #include "error_macros.h" | |
44 | ||
45 | /* GLOBAL CONFIGURATION PARAMETERS */ | |
46 | #define BIGCHUNK 102400 | |
47 | ||
48 | extern int dump_namecheck; | |
49 | extern int autoQuery; | |
50 | ||
51 | struct rstTapeInfo { | |
52 | afs_int32 taskId; | |
53 | afs_int32 tapeSeq; | |
54 | afs_uint32 dumpid; | |
55 | }; | |
56 | ||
57 | static void initTapeBuffering(void); | |
58 | static int writeDbDump(struct butm_tapeInfo *, afs_uint32, Date, afs_uint32); | |
59 | static int restoreDbEntries(struct butm_tapeInfo *, struct rstTapeInfo *); | |
60 | ||
61 | int getTapeData(struct butm_tapeInfo *, struct rstTapeInfo *, void *, | |
62 | afs_int32); | |
63 | int restoreDbHeader(struct butm_tapeInfo *, struct rstTapeInfo *, | |
64 | struct structDumpHeader *); | |
65 | int restoreDbDump(struct butm_tapeInfo *, struct rstTapeInfo *, | |
66 | struct structDumpHeader *); | |
67 | int restoreText(struct butm_tapeInfo *, struct rstTapeInfo *, | |
68 | struct structDumpHeader *); | |
69 | ||
70 | ||
71 | ||
72 | void * KeepAlive(void *); | |
73 | /* CreateDBDump | |
74 | * create a dump entry for a saved database | |
75 | */ | |
76 | ||
77 | afs_int32 | |
78 | CreateDBDump(struct budb_dumpEntry *dumpEntryPtr) | |
79 | { | |
80 | afs_int32 code = 0; | |
81 | ||
82 | memset(dumpEntryPtr, 0, sizeof(struct budb_dumpEntry)); | |
83 | ||
84 | strcpy(dumpEntryPtr->name, DUMP_TAPE_NAME); | |
85 | strcpy(dumpEntryPtr->tapes.format, DUMP_TAPE_NAME); | |
86 | strcat(dumpEntryPtr->tapes.format, ".%d"); | |
87 | strcpy(dumpEntryPtr->volumeSetName, ""); | |
88 | strcpy(dumpEntryPtr->dumpPath, ""); | |
89 | dumpEntryPtr->created = 0; /* let database assign it */ | |
90 | dumpEntryPtr->incTime = 0; | |
91 | dumpEntryPtr->nVolumes = 0; | |
92 | dumpEntryPtr->initialDumpID = 0; | |
93 | dumpEntryPtr->parent = 0; | |
94 | dumpEntryPtr->level = 0; | |
95 | dumpEntryPtr->tapes.maxTapes = 0; | |
96 | dumpEntryPtr->tapes.b = 1; | |
97 | ||
98 | /* now call the database to create the entry */ | |
99 | code = bcdb_CreateDump(dumpEntryPtr); | |
100 | return (code); | |
101 | } | |
102 | ||
103 | struct tapeEntryList { | |
104 | struct tapeEntryList *next; | |
105 | afs_uint32 oldDumpId; | |
106 | struct budb_tapeEntry tapeEnt; | |
107 | }; | |
108 | struct tapeEntryList *listEntryHead; | |
109 | struct tapeEntryList *listEntryPtr; | |
110 | #define tapeEntryPtr (&listEntryPtr->tapeEnt) | |
111 | struct budb_dumpEntry lastDump; /* the last dump of this volset */ | |
112 | ||
113 | /* GetDBTape | |
114 | * Load a DB tape, read and over write its label. | |
115 | * Leave the tape mounted. | |
116 | */ | |
117 | afs_int32 | |
118 | GetDBTape(afs_int32 taskId, Date expires, struct butm_tapeInfo *tapeInfoPtr, | |
119 | afs_uint32 dumpid, afs_int32 sequence, int queryFlag, | |
120 | int *wroteLabel) | |
121 | { | |
122 | afs_int32 code = 0; | |
123 | int interactiveFlag; | |
124 | char tapeName[BU_MAXTAPELEN]; | |
125 | char strlevel[5]; | |
126 | struct timeval tp; | |
127 | afs_int32 curTime; | |
128 | int tapecount = 1; | |
129 | ||
130 | struct butm_tapeLabel oldTapeLabel, newLabel; | |
131 | struct tapeEntryList *endList; | |
132 | ||
133 | /* construct the name of the tape */ | |
134 | sprintf(tapeName, "%s.%-d", DUMP_TAPE_NAME, sequence); | |
135 | ||
136 | interactiveFlag = queryFlag; | |
137 | *wroteLabel = 0; | |
138 | ||
139 | while (!*wroteLabel) { /*w */ | |
140 | if (interactiveFlag) { /* need a tape to write */ | |
141 | code = | |
142 | PromptForTape(SAVEDBOPCODE, tapeName, dumpid, taskId, | |
143 | tapecount); | |
144 | if (code) | |
145 | ERROR_EXIT(code); | |
146 | } | |
147 | interactiveFlag = 1; | |
148 | tapecount++; | |
149 | ||
150 | code = butm_Mount(tapeInfoPtr, tapeName); | |
151 | if (code) { | |
152 | TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n"); | |
153 | goto getNewTape; | |
154 | } | |
155 | ||
156 | memset(&oldTapeLabel, 0, sizeof(oldTapeLabel)); | |
157 | code = butm_ReadLabel(tapeInfoPtr, &oldTapeLabel, 1); /* rewind tape */ | |
158 | if (code) { | |
159 | oldTapeLabel.useCount = 0; /* no label exists */ | |
160 | oldTapeLabel.structVersion = 0; | |
161 | strcpy(oldTapeLabel.pName, ""); | |
162 | } else { | |
163 | /* If tape has a name, it must be null or database tape name */ | |
164 | if (dump_namecheck && strcmp(oldTapeLabel.AFSName, "") | |
165 | && !databaseTape(oldTapeLabel.AFSName)) { | |
166 | char gotName[BU_MAXTAPELEN + 32]; | |
167 | ||
168 | LABELNAME(gotName, &oldTapeLabel); | |
169 | TLog(taskId, | |
170 | "This tape %s must be a database tape or NULL tape\n", | |
171 | gotName); | |
172 | ||
173 | getNewTape: | |
174 | unmountTape(taskId, tapeInfoPtr); | |
175 | continue; | |
176 | } | |
177 | ||
178 | /* Do not overwrite a tape that belongs to this dump */ | |
179 | if (oldTapeLabel.dumpid && (oldTapeLabel.dumpid == dumpid)) { | |
180 | ErrorLog(0, taskId, 0, 0, | |
181 | "Can't overwrite tape containing the dump in progress\n"); | |
182 | goto getNewTape; | |
183 | } | |
184 | ||
185 | /* On first tape, the savedb has not started yet, so the database is not locked | |
186 | * and we can therefore, access information from it. This is easier to do because | |
187 | * database dumps don't have appended dumps (nor appended). | |
188 | */ | |
189 | if (sequence == 1) { | |
190 | afs_uint32 dmp; | |
191 | struct budb_dumpEntry de, de2; | |
192 | ||
193 | /* Verify the tape has not expired | |
194 | * Early database dumps don't have a dumpid | |
195 | */ | |
196 | if (!tapeExpired(&oldTapeLabel)) { | |
197 | TLog(taskId, "This tape has not expired\n"); | |
198 | goto getNewTape; | |
199 | } | |
200 | ||
201 | /* Since the dumpset on this tape will be deleted from database, check if | |
202 | * any of the dumps in this dumpset are most-recent-dumps. | |
203 | */ | |
204 | for (dmp = oldTapeLabel.dumpid; dmp; dmp = de.appendedDumpID) { | |
205 | if (dmp == lastDump.id) { | |
206 | memcpy(&de, &lastDump, sizeof(de)); | |
207 | memcpy(&de2, &lastDump, sizeof(de2)); | |
208 | } else { | |
209 | code = bcdb_FindDumpByID(dmp, &de); | |
210 | if (code) | |
211 | break; | |
212 | sprintf(strlevel, "%d", de.level); | |
213 | code = | |
214 | bcdb_FindLatestDump(de.volumeSetName, strlevel, | |
215 | &de2); | |
216 | if (code) | |
217 | continue; | |
218 | } | |
219 | ||
220 | if (de.id == de2.id) { | |
221 | if (strcmp(DUMP_TAPE_NAME, de2.name) == 0) { | |
222 | ErrorLog(0, taskId, 0, 0, | |
223 | "Warning: Overwriting most recent dump %s (DumpID %u)\n", | |
224 | de.name, de.id); | |
225 | } else { | |
226 | ErrorLog(0, taskId, 0, 0, | |
227 | "Warning: Overwriting most recent dump of the '%s' volumeset: %s (DumpID %u)\n", | |
228 | de.volumeSetName, de.name, de.id); | |
229 | } | |
230 | } | |
231 | } | |
232 | } | |
233 | ||
234 | /* Otherwise, the savedb is in progress and we can't | |
235 | * access the database (it's locked). So we rely on the | |
236 | * information available (and not the backup database). | |
237 | */ | |
238 | else { | |
239 | /* Check the tape's expiration date. Use the expiration on the label */ | |
240 | gettimeofday(&tp, NULL); | |
241 | curTime = tp.tv_sec; | |
242 | if (curTime < oldTapeLabel.expirationDate) { | |
243 | TLog(taskId, "This tape has not expired\n"); | |
244 | goto getNewTape; | |
245 | } | |
246 | ||
247 | /* Check if this previous-dump of the dump-in-progress is on this tape */ | |
248 | if (oldTapeLabel.dumpid | |
249 | && (oldTapeLabel.dumpid == lastDump.id)) { | |
250 | ErrorLog(0, taskId, 0, 0, | |
251 | "Warning: Overwriting most recent dump %s (DumpID %u)\n", | |
252 | lastDump.name, lastDump.id); | |
253 | } | |
254 | ||
255 | } | |
256 | } | |
257 | ||
258 | GetNewLabel(tapeInfoPtr, oldTapeLabel.pName, tapeName, &newLabel); | |
259 | newLabel.expirationDate = expires; | |
260 | newLabel.useCount = oldTapeLabel.useCount + 1; | |
261 | newLabel.dumpid = dumpid; | |
262 | newLabel.size = tapeInfoPtr->tapeSize; | |
263 | ||
264 | code = butm_Create(tapeInfoPtr, &newLabel, 1); /* rewind tape */ | |
265 | if (code) { | |
266 | TapeLog(0, taskId, code, tapeInfoPtr->error, | |
267 | "Can't label tape\n"); | |
268 | goto getNewTape; | |
269 | } | |
270 | ||
271 | *wroteLabel = 1; | |
272 | ||
273 | /* Initialize a tapeEntry for later inclusion into the database */ | |
274 | listEntryPtr = calloc(1, sizeof(struct tapeEntryList)); | |
275 | if (!listEntryPtr) | |
276 | ERROR_EXIT(TC_NOMEMORY); | |
277 | ||
278 | /* Remember dumpid so we can delete it later */ | |
279 | if ((oldTapeLabel.structVersion >= TAPE_VERSION_3) | |
280 | && oldTapeLabel.dumpid) | |
281 | listEntryPtr->oldDumpId = oldTapeLabel.dumpid; | |
282 | ||
283 | /* Fill in tape entry so we can save it later */ | |
284 | strcpy(tapeEntryPtr->name, TNAME(&newLabel)); | |
285 | tapeEntryPtr->flags = BUDB_TAPE_BEINGWRITTEN; | |
286 | tapeEntryPtr->written = newLabel.creationTime; | |
287 | tapeEntryPtr->expires = expires; | |
288 | tapeEntryPtr->seq = sequence; | |
289 | tapeEntryPtr->useCount = oldTapeLabel.useCount + 1; | |
290 | tapeEntryPtr->dump = dumpid; | |
291 | tapeEntryPtr->useKBytes = 0; | |
292 | tapeEntryPtr->labelpos = 0; | |
293 | ||
294 | /* Thread onto end of single-linked list */ | |
295 | if (listEntryHead) { | |
296 | endList = listEntryHead; | |
297 | while (endList->next) | |
298 | endList = endList->next; | |
299 | endList->next = listEntryPtr; | |
300 | } else | |
301 | listEntryHead = listEntryPtr; | |
302 | } /*w */ | |
303 | ||
304 | error_exit: | |
305 | return (code); | |
306 | } | |
307 | ||
308 | /* freeTapeList | |
309 | * With the list of tapes, free the structures. | |
310 | */ | |
311 | ||
312 | afs_int32 | |
313 | freeTapeList(void) | |
314 | { | |
315 | struct tapeEntryList *next; | |
316 | ||
317 | listEntryPtr = listEntryHead; | |
318 | while (listEntryPtr) { | |
319 | next = listEntryPtr->next; | |
320 | free(listEntryPtr); | |
321 | listEntryPtr = next; | |
322 | } | |
323 | ||
324 | listEntryHead = NULL; | |
325 | return (0); | |
326 | } | |
327 | ||
328 | /* addTapesToDb | |
329 | * With the list of tapes, add them to the database. | |
330 | * Also delete any olddumpids that are around. | |
331 | */ | |
332 | ||
333 | afs_int32 | |
334 | addTapesToDb(afs_int32 taskId) | |
335 | { | |
336 | afs_int32 code = 0; | |
337 | afs_int32 i, new; | |
338 | struct tapeEntryList *next; | |
339 | ||
340 | listEntryPtr = listEntryHead; | |
341 | while (listEntryPtr) { | |
342 | next = listEntryPtr->next; | |
343 | ||
344 | /* Remove the old database entry */ | |
345 | if (listEntryPtr->oldDumpId) { | |
346 | i = bcdb_deleteDump(listEntryPtr->oldDumpId, 0, 0, 0); | |
347 | if (i && (i != BUDB_NOENT)) { | |
348 | ErrorLog(0, taskId, i, 0, | |
349 | "Unable to delete old DB entry %u.\n", | |
350 | listEntryPtr->oldDumpId); | |
351 | } | |
352 | } | |
353 | ||
354 | /* Add the tape to the database */ | |
355 | code = bcdb_UseTape(tapeEntryPtr, &new); | |
356 | if (code) { | |
357 | ErrorLog(0, taskId, code, 0, "Can't add tape to database: %s\n", | |
358 | tapeEntryPtr->name); | |
359 | ERROR_EXIT(code); | |
360 | } | |
361 | ||
362 | code = bcdb_FinishTape(tapeEntryPtr); | |
363 | if (code) { | |
364 | ErrorLog(0, taskId, code, 0, "Can't finish tape: %s\n", | |
365 | tapeEntryPtr->name); | |
366 | ERROR_EXIT(code); | |
367 | } | |
368 | ||
369 | listEntryPtr = next; | |
370 | } | |
371 | ||
372 | error_exit: | |
373 | return (code); | |
374 | } | |
375 | ||
376 | /* writeDbDump | |
377 | * notes: | |
378 | * this code assumes that the blocksize on reads is smaller than | |
379 | * the blocksize on writes | |
380 | */ | |
381 | ||
382 | static int | |
383 | writeDbDump(struct butm_tapeInfo *tapeInfoPtr, afs_uint32 taskId, | |
384 | Date expires, afs_uint32 dumpid) | |
385 | { | |
386 | afs_int32 blockSize; | |
387 | afs_int32 writeBufNbytes = 0; | |
388 | char *writeBlock = 0; | |
389 | char *writeBuffer = 0; | |
390 | char *writeBufPtr; | |
391 | afs_int32 transferSize; | |
392 | ||
393 | char *readBufPtr = NULL; | |
394 | afs_int32 maxReadSize; | |
395 | ||
396 | charListT charList; | |
397 | afs_int32 done; | |
398 | afs_int32 code; | |
399 | afs_int32 chunksize = 0; | |
400 | afs_int32 tc_EndMargin, tc_KEndMargin, kRemaining; | |
401 | int sequence; | |
402 | int wroteLabel; | |
403 | int firstcall; | |
404 | #ifdef AFS_PTHREAD_ENV | |
405 | pthread_t alivePid; | |
406 | pthread_attr_t tattr; | |
407 | AFS_SIGSET_DECL; | |
408 | #else | |
409 | PROCESS alivePid; | |
410 | #endif | |
411 | ||
412 | extern struct tapeConfig globalTapeConfig; | |
413 | extern struct udbHandleS udbHandle; | |
414 | ||
415 | charList.charListT_val = 0; | |
416 | charList.charListT_len = 0; | |
417 | blockSize = BUTM_BLKSIZE; | |
418 | writeBlock = malloc(BUTM_BLOCKSIZE); | |
419 | if (!writeBlock) | |
420 | ERROR_EXIT(TC_NOMEMORY); | |
421 | ||
422 | writeBuffer = writeBlock + sizeof(struct blockMark); | |
423 | memset(writeBuffer, 0, BUTM_BLKSIZE); | |
424 | maxReadSize = 1024; | |
425 | ||
426 | /* | |
427 | * The margin of space to check for end of tape is set to the | |
428 | * amount of space used to write an end-of-tape multiplied by 2. | |
429 | * The amount of space is size of a 16K EODump marker, its EOF | |
430 | * marker, and up to two EOF markers done on close (1 16K blocks + | |
431 | * 3 EOF * markers). | |
432 | */ | |
433 | tc_EndMargin = (16384 + 3 * globalTapeConfig.fileMarkSize) * 2; | |
434 | tc_KEndMargin = tc_EndMargin / 1024; | |
435 | ||
436 | /* have to write enclose the dump in file marks */ | |
437 | code = butm_WriteFileBegin(tapeInfoPtr); | |
438 | if (code) { | |
439 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
440 | "Can't write FileBegin on tape\n"); | |
441 | ERROR_EXIT(code); | |
442 | } | |
443 | ||
444 | writeBufPtr = &writeBuffer[0]; | |
445 | firstcall = 1; | |
446 | sequence = 1; | |
447 | ||
448 | while (1) { /*w */ | |
449 | /* When no data in buffer, read data from the budb_server */ | |
450 | if (charList.charListT_len == 0) { | |
451 | /* get more data. let rx allocate space */ | |
452 | if (charList.charListT_val) { | |
453 | free(charList.charListT_val); | |
454 | charList.charListT_val = 0; | |
455 | } | |
456 | ||
457 | /* get the data */ | |
458 | code = | |
459 | ubik_Call_SingleServer(BUDB_DumpDB, udbHandle.uh_client, | |
460 | UF_SINGLESERVER, firstcall, | |
461 | maxReadSize, &charList, &done); | |
462 | if (code) { | |
463 | ErrorLog(0, taskId, code, 0, "Can't read database\n"); | |
464 | ERROR_EXIT(code); | |
465 | } | |
466 | ||
467 | /* If this if the first call to the budb server, create a thread | |
468 | * that will keep the connection alive (during tape changes). | |
469 | */ | |
470 | if (firstcall) { | |
471 | #ifdef AFS_PTHREAD_ENV | |
472 | code = pthread_attr_init(&tattr); | |
473 | if (code) { | |
474 | ErrorLog(0, taskId, code, 0, | |
475 | "Can't pthread_attr_init Keep-alive process\n"); | |
476 | ERROR_EXIT(code); | |
477 | } | |
478 | ||
479 | code = | |
480 | pthread_attr_setdetachstate(&tattr, | |
481 | PTHREAD_CREATE_DETACHED); | |
482 | if (code) { | |
483 | ErrorLog(0, taskId, code, 0, | |
484 | "Can't pthread_attr_setdetachstate Keep-alive process\n"); | |
485 | ERROR_EXIT(code); | |
486 | } | |
487 | ||
488 | AFS_SIGSET_CLEAR(); | |
489 | code = pthread_create(&alivePid, &tattr, KeepAlive, 0); | |
490 | AFS_SIGSET_RESTORE(); | |
491 | #else | |
492 | code = | |
493 | LWP_CreateProcess(KeepAlive, 16384, 1, NULL, | |
494 | "Keep-alive process", &alivePid); | |
495 | #endif | |
496 | if (code) { | |
497 | ErrorLog(0, taskId, code, 0, | |
498 | "Failed to create keep alive process\n"); | |
499 | ERROR_EXIT(code); | |
500 | } | |
501 | } | |
502 | firstcall = 0; | |
503 | ||
504 | readBufPtr = charList.charListT_val; | |
505 | } | |
506 | ||
507 | if ((charList.charListT_len == 0) && done) | |
508 | break; | |
509 | ||
510 | /* compute how many bytes and transfer to the write Buffer */ | |
511 | transferSize = | |
512 | (charList.charListT_len < | |
513 | (blockSize - | |
514 | writeBufNbytes)) ? charList.charListT_len : (blockSize - | |
515 | writeBufNbytes); | |
516 | ||
517 | memcpy(writeBufPtr, readBufPtr, transferSize); | |
518 | charList.charListT_len -= transferSize; | |
519 | writeBufPtr += transferSize; | |
520 | readBufPtr += transferSize; | |
521 | writeBufNbytes += transferSize; | |
522 | ||
523 | /* If filled the write buffer, then write it to tape */ | |
524 | if (writeBufNbytes == blockSize) { | |
525 | code = butm_WriteFileData(tapeInfoPtr, writeBuffer, 1, blockSize); | |
526 | if (code) { | |
527 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
528 | "Can't write data on tape\n"); | |
529 | ERROR_EXIT(code); | |
530 | } | |
531 | ||
532 | memset(writeBuffer, 0, blockSize); | |
533 | writeBufPtr = &writeBuffer[0]; | |
534 | writeBufNbytes = 0; | |
535 | ||
536 | /* Every BIGCHUNK bytes check if aborted */ | |
537 | chunksize += blockSize; | |
538 | if (chunksize > BIGCHUNK) { | |
539 | chunksize = 0; | |
540 | if (checkAbortByTaskId(taskId)) | |
541 | ERROR_EXIT(TC_ABORTEDBYREQUEST); | |
542 | } | |
543 | ||
544 | /* | |
545 | * check if tape is full - since we filled a blockSize worth of data | |
546 | * assume that there is more data. | |
547 | */ | |
548 | kRemaining = butm_remainingKSpace(tapeInfoPtr); | |
549 | if (kRemaining < tc_KEndMargin) { | |
550 | code = butm_WriteFileEnd(tapeInfoPtr); | |
551 | if (code) { | |
552 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
553 | "Can't write FileEnd on tape\n"); | |
554 | ERROR_EXIT(code); | |
555 | } | |
556 | ||
557 | code = butm_WriteEOT(tapeInfoPtr); | |
558 | if (code) { | |
559 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
560 | "Can't write end-of-dump on tape\n"); | |
561 | ERROR_EXIT(code); | |
562 | } | |
563 | ||
564 | /* Mark tape as having been written */ | |
565 | tapeEntryPtr->useKBytes = | |
566 | tapeInfoPtr->kBytes + (tapeInfoPtr->nBytes ? 1 : 0); | |
567 | tapeEntryPtr->flags = BUDB_TAPE_WRITTEN; | |
568 | ||
569 | unmountTape(taskId, tapeInfoPtr); | |
570 | ||
571 | /* Get next tape and writes its label */ | |
572 | sequence++; | |
573 | code = | |
574 | GetDBTape(taskId, expires, tapeInfoPtr, dumpid, sequence, | |
575 | 1, &wroteLabel); | |
576 | if (code) | |
577 | ERROR_EXIT(code); | |
578 | ||
579 | code = butm_WriteFileBegin(tapeInfoPtr); | |
580 | if (code) { | |
581 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
582 | "Can't write FileBegin on tape\n"); | |
583 | ERROR_EXIT(code); | |
584 | } | |
585 | } | |
586 | } | |
587 | } /*w */ | |
588 | ||
589 | /* no more data to be read - if necessary, flush out the last buffer */ | |
590 | if (writeBufNbytes > 0) { | |
591 | code = butm_WriteFileData(tapeInfoPtr, writeBuffer, 1, blockSize); | |
592 | if (code) { | |
593 | ErrorLog(1, taskId, code, tapeInfoPtr->error, | |
594 | "Can't write data on tape\n"); | |
595 | ERROR_EXIT(code); | |
596 | } | |
597 | } | |
598 | ||
599 | code = butm_WriteFileEnd(tapeInfoPtr); | |
600 | if (code) { | |
601 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
602 | "Can't write FileEnd on tape\n"); | |
603 | ERROR_EXIT(code); | |
604 | } | |
605 | ||
606 | /* Mark tape as having been written */ | |
607 | tapeEntryPtr->useKBytes = | |
608 | tapeInfoPtr->kBytes + (tapeInfoPtr->nBytes ? 1 : 0); | |
609 | tapeEntryPtr->flags = BUDB_TAPE_WRITTEN; | |
610 | ||
611 | error_exit: | |
612 | /* Let the KeepAlive process stop on its own */ | |
613 | code = | |
614 | ubik_Call_SingleServer(BUDB_DumpDB, udbHandle.uh_client, | |
615 | UF_END_SINGLESERVER, 0); | |
616 | ||
617 | if (writeBlock) | |
618 | free(writeBlock); | |
619 | if (charList.charListT_val) | |
620 | free(charList.charListT_val); | |
621 | return (code); | |
622 | } | |
623 | ||
624 | /* saveDbToTape | |
625 | * dump backup database to tape | |
626 | */ | |
627 | ||
628 | void * | |
629 | saveDbToTape(void *param) | |
630 | { | |
631 | struct saveDbIf *saveDbIfPtr = (struct saveDbIf *)param; | |
632 | afs_int32 code; | |
633 | afs_int32 i; | |
634 | int wroteLabel; | |
635 | afs_uint32 taskId; | |
636 | Date expires; | |
637 | ||
638 | struct butm_tapeInfo tapeInfo; | |
639 | struct budb_dumpEntry dumpEntry; | |
640 | ||
641 | extern struct deviceSyncNode *deviceLatch; | |
642 | extern struct tapeConfig globalTapeConfig; | |
643 | ||
644 | afs_pthread_setname_self("Db save"); | |
645 | expires = (saveDbIfPtr->archiveTime ? NEVERDATE : 0); | |
646 | taskId = saveDbIfPtr->taskId; | |
647 | dumpEntry.id = 0; | |
648 | ||
649 | setStatus(taskId, DRIVE_WAIT); | |
650 | EnterDeviceQueue(deviceLatch); /* lock tape device */ | |
651 | clearStatus(taskId, DRIVE_WAIT); | |
652 | ||
653 | printf("\n\n"); | |
654 | TLog(taskId, "SaveDb\n"); | |
655 | ||
656 | tapeInfo.structVersion = BUTM_MAJORVERSION; | |
657 | code = butm_file_Instantiate(&tapeInfo, &globalTapeConfig); | |
658 | if (code) { | |
659 | ErrorLog(0, taskId, code, tapeInfo.error, | |
660 | "Can't initialize tape module\n"); | |
661 | ERROR_EXIT(code); | |
662 | } | |
663 | ||
664 | /* Determine what the last database dump was */ | |
665 | memset(&lastDump, 0, sizeof(lastDump)); | |
666 | code = bcdb_FindLatestDump("", "", &lastDump); | |
667 | if (code) { | |
668 | if (code != BUDB_NODUMPNAME) { | |
669 | ErrorLog(0, taskId, code, 0, "Can't read backup database\n"); | |
670 | ERROR_EXIT(code); | |
671 | } | |
672 | memset(&lastDump, 0, sizeof(lastDump)); | |
673 | } | |
674 | ||
675 | code = CreateDBDump(&dumpEntry); /* Create a dump for this tape */ | |
676 | if (code) { | |
677 | ErrorLog(0, taskId, code, 0, "Can't create dump in database\n"); | |
678 | ERROR_EXIT(code); | |
679 | } | |
680 | ||
681 | ||
682 | listEntryHead = NULL; | |
683 | ||
684 | /* Get the tape and write a new label to it */ | |
685 | code = | |
686 | GetDBTape(taskId, expires, &tapeInfo, dumpEntry.id, 1, autoQuery, | |
687 | &wroteLabel); | |
688 | ||
689 | /* | |
690 | * If did not write the label, remove created dump | |
691 | * Else if wrote the label, remove old dump from db so it's not saved. | |
692 | */ | |
693 | if (!wroteLabel) { | |
694 | i = bcdb_deleteDump(dumpEntry.id, 0, 0, 0); | |
695 | dumpEntry.id = 0; | |
696 | if (i && (i != BUDB_NOENT)) | |
697 | ErrorLog(0, taskId, i, 0, "Unable to delete DB entry %u.\n", | |
698 | dumpEntry.id); | |
699 | } else if (listEntryHead->oldDumpId) { | |
700 | i = bcdb_deleteDump(listEntryHead->oldDumpId, 0, 0, 0); | |
701 | listEntryHead->oldDumpId = 0; | |
702 | if (i && (i != BUDB_NOENT)) { | |
703 | ErrorLog(0, taskId, i, 0, "Unable to delete old DB entry %u.\n", | |
704 | listEntryHead->oldDumpId); | |
705 | ERROR_EXIT(i); | |
706 | } | |
707 | } | |
708 | if (code) | |
709 | ERROR_EXIT(code); | |
710 | ||
711 | TapeLog(1, taskId, 0, 0, "Tape accepted - now dumping database\n"); | |
712 | ||
713 | /* we have a writable tape */ | |
714 | code = writeDbDump(&tapeInfo, taskId, expires, dumpEntry.id); | |
715 | if (code) | |
716 | ERROR_EXIT(code); | |
717 | ||
718 | /* Now delete the entries between time 0 and archive-time */ | |
719 | if (saveDbIfPtr->archiveTime) | |
720 | code = bcdb_deleteDump(0, 0, saveDbIfPtr->archiveTime, 0); | |
721 | ||
722 | error_exit: | |
723 | unmountTape(taskId, &tapeInfo); | |
724 | ||
725 | /* Add this dump's tapes to the database and mark it finished */ | |
726 | if (dumpEntry.id) { | |
727 | i = addTapesToDb(taskId); | |
728 | if (!code) | |
729 | code = i; | |
730 | ||
731 | i = bcdb_FinishDump(&dumpEntry); | |
732 | if (!code) | |
733 | code = i; | |
734 | } | |
735 | freeTapeList(); | |
736 | ||
737 | if (code == TC_ABORTEDBYREQUEST) { | |
738 | TLog(taskId, "SaveDb: Aborted by request\n"); | |
739 | clearStatus(taskId, ABORT_REQUEST); | |
740 | setStatus(taskId, ABORT_DONE); | |
741 | } else if (code) { | |
742 | TapeLog(0, taskId, code, 0, "SaveDb: Finished with errors\n"); | |
743 | setStatus(taskId, TASK_ERROR); | |
744 | } else { | |
745 | TLog(taskId, "SaveDb: Finished\n"); | |
746 | } | |
747 | setStatus(taskId, TASK_DONE); | |
748 | ||
749 | free(saveDbIfPtr); | |
750 | LeaveDeviceQueue(deviceLatch); | |
751 | return (void *)(intptr_t)(code); | |
752 | } | |
753 | ||
754 | ||
755 | /* makeDbDumpEntry() | |
756 | * Make a database dump entry given a tape label. | |
757 | */ | |
758 | ||
759 | afs_int32 | |
760 | makeDbDumpEntry(struct budb_tapeEntry *tapeEntPtr, | |
761 | struct budb_dumpEntry *dumpEntryPtr) | |
762 | { | |
763 | memset(dumpEntryPtr, 0, sizeof(struct budb_dumpEntry)); | |
764 | ||
765 | dumpEntryPtr->id = tapeEntPtr->dump; | |
766 | dumpEntryPtr->initialDumpID = 0; | |
767 | dumpEntryPtr->parent = 0; | |
768 | dumpEntryPtr->level = 0; | |
769 | dumpEntryPtr->flags = 0; | |
770 | ||
771 | strcpy(dumpEntryPtr->volumeSetName, ""); | |
772 | strcpy(dumpEntryPtr->dumpPath, ""); | |
773 | strcpy(dumpEntryPtr->name, DUMP_TAPE_NAME); | |
774 | ||
775 | dumpEntryPtr->created = tapeEntPtr->dump; | |
776 | dumpEntryPtr->incTime = 0; | |
777 | dumpEntryPtr->nVolumes = 0; | |
778 | ||
779 | strcpy(dumpEntryPtr->tapes.format, DUMP_TAPE_NAME); | |
780 | strcat(dumpEntryPtr->tapes.format, ".%d"); | |
781 | dumpEntryPtr->tapes.b = tapeEntPtr->seq; | |
782 | dumpEntryPtr->tapes.maxTapes = 0; | |
783 | return 0; | |
784 | } | |
785 | ||
786 | /* readDbTape | |
787 | * prompt for a specific database tape | |
788 | */ | |
789 | ||
790 | afs_int32 | |
791 | readDbTape(struct butm_tapeInfo *tapeInfoPtr, | |
792 | struct rstTapeInfo *rstTapeInfoPtr, int query) | |
793 | { | |
794 | afs_int32 code = 0; | |
795 | int interactiveFlag; | |
796 | afs_int32 taskId; | |
797 | struct butm_tapeLabel oldTapeLabel; | |
798 | char AFStapeName[BU_MAXTAPELEN], tapeName[BU_MAXTAPELEN]; | |
799 | struct tapeEntryList *endList; | |
800 | int tapecount = 1; | |
801 | struct budb_dumpEntry de; | |
802 | struct budb_tapeEntry te; | |
803 | ||
804 | taskId = rstTapeInfoPtr->taskId; | |
805 | interactiveFlag = query; | |
806 | ||
807 | /* construct the name of the tape */ | |
808 | sprintf(AFStapeName, "%s.%-d", DUMP_TAPE_NAME, rstTapeInfoPtr->tapeSeq); | |
809 | strcpy(tapeName, AFStapeName); | |
810 | ||
811 | /* Will prompt for the latest saved database tape, but will accept any one */ | |
812 | if (rstTapeInfoPtr->tapeSeq == 1) { | |
813 | code = bcdb_FindLatestDump("", "", &de); | |
814 | if (!code) | |
815 | rstTapeInfoPtr->dumpid = de.id; | |
816 | } | |
817 | if (rstTapeInfoPtr->dumpid) { | |
818 | code = | |
819 | bcdb_FindTapeSeq(rstTapeInfoPtr->dumpid, rstTapeInfoPtr->tapeSeq, | |
820 | &te); | |
821 | if (!code) | |
822 | strcpy(tapeName, te.name); | |
823 | } | |
824 | ||
825 | while (1) { /*w */ | |
826 | if (interactiveFlag) { /* need a tape to read */ | |
827 | code = | |
828 | PromptForTape(RESTOREDBOPCODE, tapeName, | |
829 | rstTapeInfoPtr->dumpid, taskId, tapecount); | |
830 | if (code) | |
831 | ERROR_EXIT(code); | |
832 | } | |
833 | interactiveFlag = 1; | |
834 | tapecount++; | |
835 | ||
836 | code = butm_Mount(tapeInfoPtr, tapeName); | |
837 | if (code) { | |
838 | TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n"); | |
839 | goto getNewTape; | |
840 | } | |
841 | ||
842 | code = butm_ReadLabel(tapeInfoPtr, &oldTapeLabel, 1); /* will rewind the tape */ | |
843 | if (code) { | |
844 | TapeLog(0, taskId, code, tapeInfoPtr->error, | |
845 | "Can't read tape label\n"); | |
846 | goto getNewTape; | |
847 | } | |
848 | ||
849 | /* Check for name of tape and matching dump id (if applicable). */ | |
850 | if ((strcmp(oldTapeLabel.AFSName, AFStapeName) != 0) | |
851 | || ((rstTapeInfoPtr->tapeSeq != 1) | |
852 | && (oldTapeLabel.dumpid != rstTapeInfoPtr->dumpid))) { | |
853 | char expTape[BU_MAXTAPELEN + 32]; | |
854 | char gotTape[BU_MAXTAPELEN + 32]; | |
855 | ||
856 | TAPENAME(expTape, tapeName, rstTapeInfoPtr->dumpid); | |
857 | TAPENAME(gotTape, oldTapeLabel.AFSName, oldTapeLabel.dumpid); | |
858 | ||
859 | TLog(taskId, "Tape label expected %s, label seen %s\n", expTape, | |
860 | gotTape); | |
861 | goto getNewTape; | |
862 | } | |
863 | ||
864 | if (rstTapeInfoPtr->tapeSeq == 1) /* Remember this dumpId */ | |
865 | rstTapeInfoPtr->dumpid = oldTapeLabel.dumpid; | |
866 | ||
867 | break; | |
868 | ||
869 | getNewTape: | |
870 | unmountTape(taskId, tapeInfoPtr); | |
871 | } /*w */ | |
872 | ||
873 | ||
874 | /* Initialize a tapeEntry for later inclusion into the database */ | |
875 | listEntryPtr = calloc(1, sizeof(struct tapeEntryList)); | |
876 | if (!listEntryPtr) | |
877 | ERROR_EXIT(TC_NOMEMORY); | |
878 | ||
879 | /* Fill in tape entry so we can save it later */ | |
880 | strcpy(tapeEntryPtr->name, TNAME(&oldTapeLabel)); | |
881 | tapeEntryPtr->dump = oldTapeLabel.dumpid; | |
882 | tapeEntryPtr->flags = BUDB_TAPE_BEINGWRITTEN; | |
883 | tapeEntryPtr->written = oldTapeLabel.creationTime; | |
884 | tapeEntryPtr->expires = oldTapeLabel.expirationDate; | |
885 | tapeEntryPtr->seq = extractTapeSeq(oldTapeLabel.AFSName); | |
886 | tapeEntryPtr->useCount = oldTapeLabel.useCount; | |
887 | tapeEntryPtr->useKBytes = 0; | |
888 | tapeEntryPtr->labelpos = 0; | |
889 | ||
890 | /* Thread onto end of single-linked list */ | |
891 | if (listEntryHead) { | |
892 | endList = listEntryHead; | |
893 | while (endList->next) | |
894 | endList = endList->next; | |
895 | endList->next = listEntryPtr; | |
896 | } else | |
897 | listEntryHead = listEntryPtr; | |
898 | ||
899 | error_exit: | |
900 | return (code); | |
901 | } | |
902 | ||
903 | static afs_int32 nbytes = 0; /* # bytes left in buffer */ | |
904 | static void | |
905 | initTapeBuffering(void) | |
906 | { | |
907 | nbytes = 0; | |
908 | } | |
909 | ||
910 | ||
911 | /* restoreDbEntries | |
912 | * restore all the items on the tape | |
913 | * entry: | |
914 | * tape positioned after tape label | |
915 | */ | |
916 | ||
917 | static int | |
918 | restoreDbEntries(struct butm_tapeInfo *tapeInfoPtr, | |
919 | struct rstTapeInfo *rstTapeInfoPtr) | |
920 | { | |
921 | struct structDumpHeader netItemHeader, hostItemHeader; | |
922 | afs_int32 more = 1; | |
923 | afs_int32 taskId, code = 0; | |
924 | int count = 0; | |
925 | ||
926 | taskId = rstTapeInfoPtr->taskId; | |
927 | ||
928 | /* clear state for the buffer routine(s) */ | |
929 | initTapeBuffering(); | |
930 | ||
931 | code = butm_ReadFileBegin(tapeInfoPtr); | |
932 | if (code) { | |
933 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
934 | "Can't read FileBegin on tape\n"); | |
935 | ERROR_EXIT(code); | |
936 | } | |
937 | ||
938 | /* get the first item-header */ | |
939 | memset(&netItemHeader, 0, sizeof(netItemHeader)); | |
940 | code = | |
941 | getTapeData(tapeInfoPtr, rstTapeInfoPtr, &netItemHeader, | |
942 | sizeof(netItemHeader)); | |
943 | if (code) | |
944 | ERROR_EXIT(code); | |
945 | structDumpHeader_ntoh(&netItemHeader, &hostItemHeader); | |
946 | ||
947 | while (more) { | |
948 | switch (hostItemHeader.type) { | |
949 | case SD_DBHEADER: | |
950 | code = | |
951 | restoreDbHeader(tapeInfoPtr, rstTapeInfoPtr, &hostItemHeader); | |
952 | if (code) | |
953 | ERROR_EXIT(code); | |
954 | break; | |
955 | ||
956 | case SD_DUMP: | |
957 | if (++count > 25) { /*every 25 dumps, wait */ | |
958 | waitDbWatcher(); | |
959 | count = 0; | |
960 | } | |
961 | code = | |
962 | restoreDbDump(tapeInfoPtr, rstTapeInfoPtr, &hostItemHeader); | |
963 | if (code) | |
964 | ERROR_EXIT(code); | |
965 | break; | |
966 | ||
967 | case SD_TAPE: | |
968 | case SD_VOLUME: | |
969 | ERROR_EXIT(-1); | |
970 | break; | |
971 | ||
972 | case SD_TEXT_DUMPSCHEDULE: | |
973 | case SD_TEXT_VOLUMESET: | |
974 | case SD_TEXT_TAPEHOSTS: | |
975 | code = restoreText(tapeInfoPtr, rstTapeInfoPtr, &hostItemHeader); | |
976 | if (code) | |
977 | ERROR_EXIT(code); | |
978 | break; | |
979 | ||
980 | case SD_END: | |
981 | more = 0; | |
982 | break; | |
983 | ||
984 | default: | |
985 | TLog(taskId, "Unknown database header type %d\n", | |
986 | hostItemHeader.type); | |
987 | ERROR_EXIT(-1); | |
988 | break; | |
989 | } | |
990 | } | |
991 | ||
992 | code = butm_ReadFileEnd(tapeInfoPtr); | |
993 | if (code) { | |
994 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
995 | "Can't read EOF on tape\n"); | |
996 | ERROR_EXIT(code); | |
997 | } | |
998 | ||
999 | /* Mark tape as having been written */ | |
1000 | tapeEntryPtr->useKBytes = | |
1001 | tapeInfoPtr->kBytes + (tapeInfoPtr->nBytes ? 1 : 0); | |
1002 | tapeEntryPtr->flags = BUDB_TAPE_WRITTEN; | |
1003 | ||
1004 | error_exit: | |
1005 | return (code); | |
1006 | } | |
1007 | ||
1008 | /* restoreDbFromTape | |
1009 | * restore the backup database from tape. | |
1010 | */ | |
1011 | ||
1012 | void * | |
1013 | restoreDbFromTape(void *param) | |
1014 | { | |
1015 | afs_uint32 taskId = (intptr_t) param; | |
1016 | afs_int32 code = 0; | |
1017 | afs_int32 i; | |
1018 | struct butm_tapeInfo tapeInfo; | |
1019 | struct rstTapeInfo rstTapeInfo; | |
1020 | struct budb_dumpEntry dumpEntry; | |
1021 | ||
1022 | extern struct tapeConfig globalTapeConfig; | |
1023 | extern struct deviceSyncNode *deviceLatch; | |
1024 | ||
1025 | afs_pthread_setname_self("Db restore"); | |
1026 | setStatus(taskId, DRIVE_WAIT); | |
1027 | EnterDeviceQueue(deviceLatch); /* lock tape device */ | |
1028 | clearStatus(taskId, DRIVE_WAIT); | |
1029 | ||
1030 | printf("\n\n"); | |
1031 | TLog(taskId, "RestoreDb\n"); | |
1032 | ||
1033 | tapeInfo.structVersion = BUTM_MAJORVERSION; | |
1034 | code = butm_file_Instantiate(&tapeInfo, &globalTapeConfig); | |
1035 | if (code) { | |
1036 | ErrorLog(0, taskId, code, tapeInfo.error, | |
1037 | "Can't initialize tape module\n"); | |
1038 | ERROR_EXIT(code); | |
1039 | } | |
1040 | ||
1041 | listEntryHead = NULL; | |
1042 | ||
1043 | rstTapeInfo.taskId = taskId; | |
1044 | rstTapeInfo.tapeSeq = 1; | |
1045 | rstTapeInfo.dumpid = 0; | |
1046 | ||
1047 | code = readDbTape(&tapeInfo, &rstTapeInfo, autoQuery); | |
1048 | if (code) | |
1049 | ERROR_EXIT(code); | |
1050 | ||
1051 | code = restoreDbEntries(&tapeInfo, &rstTapeInfo); | |
1052 | if (code) | |
1053 | ERROR_EXIT(code); | |
1054 | ||
1055 | error_exit: | |
1056 | /* Now put this dump into the database */ | |
1057 | /* Make a dump entry from first tape */ | |
1058 | listEntryPtr = listEntryHead; | |
1059 | if (listEntryPtr) { | |
1060 | makeDbDumpEntry(tapeEntryPtr, &dumpEntry); | |
1061 | if (dumpEntry.id != 0) { | |
1062 | i = bcdb_CreateDump(&dumpEntry); | |
1063 | if (i) { | |
1064 | if (i == BUDB_DUMPIDEXISTS) | |
1065 | fprintf(stderr, | |
1066 | "Dump id %d not added to database - already exists\n", | |
1067 | dumpEntry.id); | |
1068 | else | |
1069 | TapeLog(0, taskId, i, 0, | |
1070 | "Dump id %d not added to database\n", | |
1071 | dumpEntry.id); | |
1072 | } else { | |
1073 | i = addTapesToDb(taskId); | |
1074 | if (!code) | |
1075 | code = i; | |
1076 | ||
1077 | i = bcdb_FinishDump(&dumpEntry); | |
1078 | if (!code) | |
1079 | code = i; | |
1080 | } | |
1081 | } | |
1082 | freeTapeList(); | |
1083 | } | |
1084 | ||
1085 | unmountTape(taskId, &tapeInfo); | |
1086 | waitDbWatcher(); | |
1087 | ||
1088 | if (code == TC_ABORTEDBYREQUEST) { | |
1089 | TLog(taskId, "RestoreDb: Aborted by request\n"); | |
1090 | clearStatus(taskId, ABORT_REQUEST); | |
1091 | setStatus(taskId, ABORT_DONE); | |
1092 | } else if (code) { | |
1093 | TapeLog(0, taskId, code, 0, "RestoreDb: Finished with errors\n"); | |
1094 | setStatus(taskId, TASK_ERROR); | |
1095 | } else { | |
1096 | TLog(taskId, "RestoreDb: Finished\n"); | |
1097 | } | |
1098 | ||
1099 | LeaveDeviceQueue(deviceLatch); | |
1100 | setStatus(taskId, TASK_DONE); | |
1101 | ||
1102 | return (void *)(intptr_t)(code); | |
1103 | } | |
1104 | ||
1105 | /* KeepAlive | |
1106 | * | |
1107 | * While dumping the database, keeps the connection alive. | |
1108 | * Every 10 seconds, wake up and ask to read 0 bytes of the database. | |
1109 | * This resets the database's internal timer so that it does not | |
1110 | * prematuraly quit (on asking for new tapes and such). | |
1111 | * | |
1112 | * Use the same udbHandle as writeDbDump so we go to the same server. | |
1113 | */ | |
1114 | void * | |
1115 | KeepAlive(void *unused) | |
1116 | { | |
1117 | charListT charList; | |
1118 | afs_int32 code; | |
1119 | afs_int32 done; | |
1120 | ||
1121 | extern struct udbHandleS udbHandle; | |
1122 | ||
1123 | afs_pthread_setname_self("Keep-alive"); | |
1124 | while (1) { | |
1125 | #ifdef AFS_PTHREAD_ENV | |
1126 | sleep(5); | |
1127 | #else | |
1128 | IOMGR_Sleep(5); | |
1129 | #endif | |
1130 | charList.charListT_val = 0; | |
1131 | charList.charListT_len = 0; | |
1132 | code = | |
1133 | ubik_Call_SingleServer(BUDB_DumpDB, udbHandle.uh_client, | |
1134 | UF_SINGLESERVER, 0, 0, &charList, &done); | |
1135 | if (code || done) | |
1136 | break; | |
1137 | } | |
1138 | return 0; | |
1139 | } | |
1140 | ||
1141 | ||
1142 | /* restoreDbHeader | |
1143 | * restore special items in the header | |
1144 | */ | |
1145 | ||
1146 | int | |
1147 | restoreDbHeader(struct butm_tapeInfo *tapeInfo, | |
1148 | struct rstTapeInfo *rstTapeInfoPtr, | |
1149 | struct structDumpHeader *nextHeader) | |
1150 | { | |
1151 | struct structDumpHeader netItemHeader; | |
1152 | struct DbHeader netDbHeader, hostDbHeader; | |
1153 | afs_int32 code = 0; | |
1154 | ||
1155 | extern struct udbHandleS udbHandle; | |
1156 | ||
1157 | /* Read the database header */ | |
1158 | memset(&netDbHeader, 0, sizeof(netDbHeader)); | |
1159 | code = | |
1160 | getTapeData(tapeInfo, rstTapeInfoPtr, &netDbHeader, | |
1161 | sizeof(netDbHeader)); | |
1162 | if (code) | |
1163 | ERROR_EXIT(code); | |
1164 | DbHeader_ntoh(&netDbHeader, &hostDbHeader); | |
1165 | ||
1166 | /* Add the database header to the database */ | |
1167 | code = | |
1168 | ubik_BUDB_RestoreDbHeader(udbHandle.uh_client, 0, | |
1169 | &hostDbHeader); | |
1170 | if (code) { | |
1171 | ErrorLog(0, rstTapeInfoPtr->taskId, code, 0, | |
1172 | "Can't restore DB Header\n"); | |
1173 | ERROR_EXIT(code); | |
1174 | } | |
1175 | ||
1176 | /* get the next item-header */ | |
1177 | memset(nextHeader, 0, sizeof(*nextHeader)); | |
1178 | code = | |
1179 | getTapeData(tapeInfo, rstTapeInfoPtr, &netItemHeader, | |
1180 | sizeof(netItemHeader)); | |
1181 | if (code) | |
1182 | ERROR_EXIT(code); | |
1183 | structDumpHeader_ntoh(&netItemHeader, nextHeader); | |
1184 | ||
1185 | error_exit: | |
1186 | return (code); | |
1187 | } | |
1188 | ||
1189 | ||
1190 | /* restoreDbDump | |
1191 | * restore a single dump, including all its tapes and volumes, from | |
1192 | * the tape. | |
1193 | * entry: | |
1194 | * nextHeader - ptr to structure for return value | |
1195 | * exit: | |
1196 | * nextHeader - next structure header from tape | |
1197 | * notes: | |
1198 | * upon entry, the dump structure header has been read confirming that | |
1199 | * a database dump tree exists on the tape | |
1200 | */ | |
1201 | ||
1202 | int | |
1203 | restoreDbDump(struct butm_tapeInfo *tapeInfo, | |
1204 | struct rstTapeInfo *rstTapeInfoPtr, | |
1205 | struct structDumpHeader *nextHeader) | |
1206 | { | |
1207 | struct budb_dumpEntry netDumpEntry, hostDumpEntry; | |
1208 | struct budb_tapeEntry netTapeEntry, hostTapeEntry; | |
1209 | struct budb_volumeEntry netVolumeEntry, hostVolumeEntry; | |
1210 | struct structDumpHeader netItemHeader; | |
1211 | int restoreThisDump = 1; | |
1212 | afs_int32 code = 0; | |
1213 | ||
1214 | extern struct udbHandleS udbHandle; | |
1215 | ||
1216 | /* read dump entry */ | |
1217 | memset(&netDumpEntry, 0, sizeof(netDumpEntry)); | |
1218 | code = | |
1219 | getTapeData(tapeInfo, rstTapeInfoPtr, &netDumpEntry, | |
1220 | sizeof(netDumpEntry)); | |
1221 | if (code) | |
1222 | ERROR_EXIT(code); | |
1223 | ||
1224 | /* If database tape does not have a dumpid (AFS 3.3) then no initial/appended dumps */ | |
1225 | if (rstTapeInfoPtr->dumpid == 0) { | |
1226 | netDumpEntry.initialDumpID = 0; | |
1227 | netDumpEntry.appendedDumpID = 0; | |
1228 | } | |
1229 | ||
1230 | dumpEntry_ntoh(&netDumpEntry, &hostDumpEntry); | |
1231 | ||
1232 | /* The dump entry for this database tape is incomplete, so don't include it */ | |
1233 | if (hostDumpEntry.id == rstTapeInfoPtr->dumpid) | |
1234 | restoreThisDump = 0; | |
1235 | ||
1236 | /* add the dump to the database */ | |
1237 | if (restoreThisDump) { | |
1238 | code = | |
1239 | threadEntryDir(&hostDumpEntry, sizeof(hostDumpEntry), | |
1240 | DLQ_USEDUMP); | |
1241 | if (code) | |
1242 | ERROR_EXIT(code); | |
1243 | } | |
1244 | ||
1245 | /* get the next item-header */ | |
1246 | memset(nextHeader, 0, sizeof(*nextHeader)); | |
1247 | code = | |
1248 | getTapeData(tapeInfo, rstTapeInfoPtr, &netItemHeader, | |
1249 | sizeof(netItemHeader)); | |
1250 | if (code) | |
1251 | ERROR_EXIT(code); | |
1252 | structDumpHeader_ntoh(&netItemHeader, nextHeader); | |
1253 | ||
1254 | /* Add every tape to the db */ | |
1255 | while (nextHeader->type == SD_TAPE) { /*t */ | |
1256 | ||
1257 | /* read the tape entry */ | |
1258 | memset(&netTapeEntry, 0, sizeof(netTapeEntry)); | |
1259 | code = | |
1260 | getTapeData(tapeInfo, rstTapeInfoPtr, &netTapeEntry, | |
1261 | sizeof(netTapeEntry)); | |
1262 | if (code) | |
1263 | ERROR_EXIT(code); | |
1264 | tapeEntry_ntoh(&netTapeEntry, &hostTapeEntry); | |
1265 | ||
1266 | /* Add the tape to the database */ | |
1267 | if (restoreThisDump) { | |
1268 | code = | |
1269 | threadEntryDir(&hostTapeEntry, sizeof(hostTapeEntry), | |
1270 | DLQ_USETAPE); | |
1271 | if (code) | |
1272 | ERROR_EXIT(code); | |
1273 | } | |
1274 | ||
1275 | /* get the next item-header */ | |
1276 | memset(nextHeader, 0, sizeof(*nextHeader)); | |
1277 | code = | |
1278 | getTapeData(tapeInfo, rstTapeInfoPtr, &netItemHeader, | |
1279 | sizeof(netItemHeader)); | |
1280 | if (code) | |
1281 | ERROR_EXIT(code); | |
1282 | structDumpHeader_ntoh(&netItemHeader, nextHeader); | |
1283 | ||
1284 | /* Add every volume to the db */ | |
1285 | while (nextHeader->type == SD_VOLUME) { /*v */ | |
1286 | ||
1287 | /* read the volume entry */ | |
1288 | memset(&netVolumeEntry, 0, sizeof(netVolumeEntry)); | |
1289 | code = | |
1290 | getTapeData(tapeInfo, rstTapeInfoPtr, &netVolumeEntry, | |
1291 | sizeof(netVolumeEntry)); | |
1292 | if (code) | |
1293 | ERROR_EXIT(code); | |
1294 | volumeEntry_ntoh(&netVolumeEntry, &hostVolumeEntry); | |
1295 | ||
1296 | if (restoreThisDump) { | |
1297 | code = | |
1298 | threadEntryDir(&hostVolumeEntry, sizeof(hostVolumeEntry), | |
1299 | DLQ_VOLENTRY); | |
1300 | if (code) | |
1301 | ERROR_EXIT(code); | |
1302 | } | |
1303 | ||
1304 | /* get the next item-header */ | |
1305 | memset(nextHeader, 0, sizeof(*nextHeader)); | |
1306 | code = | |
1307 | getTapeData(tapeInfo, rstTapeInfoPtr, &netItemHeader, | |
1308 | sizeof(netItemHeader)); | |
1309 | if (code) | |
1310 | ERROR_EXIT(code); | |
1311 | structDumpHeader_ntoh(&netItemHeader, nextHeader); | |
1312 | } /*v */ | |
1313 | ||
1314 | /* Finish the tape */ | |
1315 | if (restoreThisDump) { | |
1316 | code = | |
1317 | threadEntryDir(&hostTapeEntry, sizeof(hostTapeEntry), | |
1318 | DLQ_FINISHTAPE); | |
1319 | if (code) | |
1320 | ERROR_EXIT(code); | |
1321 | } | |
1322 | } /*t */ | |
1323 | ||
1324 | /* Finish the dump */ | |
1325 | if (restoreThisDump) { | |
1326 | code = | |
1327 | threadEntryDir(&hostDumpEntry, sizeof(hostDumpEntry), | |
1328 | DLQ_FINISHDUMP); | |
1329 | if (code) | |
1330 | ERROR_EXIT(code); | |
1331 | } | |
1332 | ||
1333 | error_exit: | |
1334 | return (code); | |
1335 | } | |
1336 | ||
1337 | /* saveTextFile | |
1338 | * Save the specified file as configuration text in the ubik database. | |
1339 | * Have to setup the client text structure so that we can call | |
1340 | * the routine to transmit the text to the db. | |
1341 | */ | |
1342 | ||
1343 | afs_int32 | |
1344 | saveTextFile(afs_int32 taskId, afs_int32 textType, char *fileName) | |
1345 | { | |
1346 | udbClientTextP ctPtr = 0; | |
1347 | afs_int32 code = 0; | |
1348 | int tlock = 0; | |
1349 | ||
1350 | ctPtr = calloc(1, sizeof(*ctPtr)); | |
1351 | if (!ctPtr) | |
1352 | ERROR_EXIT(TC_NOMEMORY); | |
1353 | ||
1354 | ctPtr->textType = textType; | |
1355 | ||
1356 | /* lock the text in the database */ | |
1357 | code = bc_LockText(ctPtr); | |
1358 | if (code) { | |
1359 | ErrorLog(0, taskId, code, 0, "Can't lock text file\n"); | |
1360 | ERROR_EXIT(code); | |
1361 | } | |
1362 | tlock = 1; | |
1363 | ||
1364 | ctPtr->textStream = fopen(fileName, "r"); | |
1365 | if (!ctPtr->textStream) { | |
1366 | ErrorLog(0, taskId, errno, 0, "Can't open text file\n"); | |
1367 | ERROR_EXIT(errno); | |
1368 | } | |
1369 | ||
1370 | /* now send the text to the database */ | |
1371 | code = bcdb_SaveTextFile(ctPtr); | |
1372 | if (code) { | |
1373 | ErrorLog(0, taskId, code, 0, "Can't save text file\n"); | |
1374 | ERROR_EXIT(code); | |
1375 | } | |
1376 | ||
1377 | error_exit: | |
1378 | if (ctPtr) { | |
1379 | if (ctPtr->textStream) | |
1380 | fclose(ctPtr->textStream); | |
1381 | if (tlock) | |
1382 | bc_UnlockText(ctPtr); | |
1383 | free(ctPtr); | |
1384 | } | |
1385 | return (code); | |
1386 | } | |
1387 | ||
1388 | /* restoreText | |
1389 | * read the text off the tape, and store it in the appropriate | |
1390 | * text type in the database. | |
1391 | * entry: | |
1392 | * nextHeader - ptr to struct for return information | |
1393 | * exit: | |
1394 | * nextHeader - struct header for next item on the tape | |
1395 | */ | |
1396 | ||
1397 | int | |
1398 | restoreText(struct butm_tapeInfo *tapeInfo, | |
1399 | struct rstTapeInfo *rstTapeInfoPtr, | |
1400 | struct structDumpHeader *nextHeader) | |
1401 | { | |
1402 | char filename[64]; | |
1403 | afs_int32 nbytes; | |
1404 | char *readBuffer = 0; | |
1405 | afs_int32 readBlockSize; | |
1406 | afs_int32 transferSize; | |
1407 | struct structDumpHeader netItemHeader; | |
1408 | int fid = -1; | |
1409 | afs_int32 code = 0; | |
1410 | ||
1411 | udbClientTextP ctPtr = 0; | |
1412 | afs_int32 textType; | |
1413 | ||
1414 | ctPtr = malloc(sizeof(*ctPtr)); | |
1415 | if (!ctPtr) | |
1416 | ERROR_EXIT(TC_NOMEMORY); | |
1417 | ||
1418 | /* determine the type of text block */ | |
1419 | switch (nextHeader->type) { | |
1420 | case SD_TEXT_DUMPSCHEDULE: | |
1421 | textType = TB_DUMPSCHEDULE; | |
1422 | break; | |
1423 | ||
1424 | case SD_TEXT_VOLUMESET: | |
1425 | textType = TB_VOLUMESET; | |
1426 | break; | |
1427 | ||
1428 | case SD_TEXT_TAPEHOSTS: | |
1429 | textType = TB_TAPEHOSTS; | |
1430 | break; | |
1431 | ||
1432 | default: | |
1433 | ErrorLog(0, rstTapeInfoPtr->taskId, TC_INTERNALERROR, 0, | |
1434 | "Unknown text block\n"); | |
1435 | ERROR_EXIT(TC_INTERNALERROR); | |
1436 | break; | |
1437 | } | |
1438 | ||
1439 | /* open the text file */ | |
1440 | sprintf(filename, "%s/bu_XXXXXX", gettmpdir()); | |
1441 | fid = mkstemp(filename); | |
1442 | if (fid < 0) { | |
1443 | ErrorLog(0, rstTapeInfoPtr->taskId, errno, 0, | |
1444 | "Can't open temporary text file: %s\n", filename); | |
1445 | ERROR_EXIT(errno); | |
1446 | } | |
1447 | ||
1448 | /* allocate buffer for text */ | |
1449 | readBlockSize = BUTM_BLKSIZE; | |
1450 | readBuffer = malloc(readBlockSize); | |
1451 | if (!readBuffer) | |
1452 | ERROR_EXIT(TC_NOMEMORY); | |
1453 | ||
1454 | /* read the text into the temporary file */ | |
1455 | nbytes = nextHeader->size; | |
1456 | while (nbytes > 0) { | |
1457 | transferSize = (readBlockSize < nbytes) ? readBlockSize : nbytes; | |
1458 | ||
1459 | /* read it from the tape */ | |
1460 | code = | |
1461 | getTapeData(tapeInfo, rstTapeInfoPtr, readBuffer, transferSize); | |
1462 | if (code) | |
1463 | ERROR_EXIT(code); | |
1464 | ||
1465 | /* write to the file */ | |
1466 | if (write(fid, readBuffer, transferSize) != transferSize) { | |
1467 | ErrorLog(0, rstTapeInfoPtr->taskId, errno, 0, | |
1468 | "Can't write temporary text file: %s\n", filename); | |
1469 | ERROR_EXIT(errno); | |
1470 | } | |
1471 | ||
1472 | nbytes -= transferSize; | |
1473 | } | |
1474 | ||
1475 | close(fid); | |
1476 | fid = -1; | |
1477 | code = saveTextFile(rstTapeInfoPtr->taskId, textType, filename); | |
1478 | if (code) | |
1479 | ERROR_EXIT(code); | |
1480 | unlink(filename); | |
1481 | ||
1482 | /* get the next item-header */ | |
1483 | memset(nextHeader, 0, sizeof(*nextHeader)); | |
1484 | code = | |
1485 | getTapeData(tapeInfo, rstTapeInfoPtr, &netItemHeader, | |
1486 | sizeof(netItemHeader)); | |
1487 | if (code) | |
1488 | ERROR_EXIT(code); | |
1489 | structDumpHeader_ntoh(&netItemHeader, nextHeader); | |
1490 | ||
1491 | error_exit: | |
1492 | if (ctPtr) | |
1493 | free(ctPtr); | |
1494 | if (readBuffer) | |
1495 | free(readBuffer); | |
1496 | if (fid != -1) { | |
1497 | close(fid); | |
1498 | unlink(filename); | |
1499 | } | |
1500 | return (code); | |
1501 | } | |
1502 | ||
1503 | ||
1504 | /* ---------------------------------- | |
1505 | * Tape data buffering - for reading database dumps | |
1506 | * ---------------------------------- | |
1507 | */ | |
1508 | ||
1509 | static char *tapeReadBuffer = 0; /* input buffer */ | |
1510 | static char *tapeReadBufferPtr = 0; /* position in buffer */ | |
1511 | ||
1512 | /* getTapeData | |
1513 | * Read information from tape, and place the requested number of bytes | |
1514 | * in the buffer supplied | |
1515 | * entry: | |
1516 | * tapeInfo | |
1517 | * rstTapeInfoPtr - Info about the dump being restored. | |
1518 | * buffer - buffer for requested data | |
1519 | * requestedBytes - no. of bytes requested | |
1520 | * exit: | |
1521 | * fn retn - 0, ok, n, error | |
1522 | */ | |
1523 | ||
1524 | int | |
1525 | getTapeData(struct butm_tapeInfo *tapeInfoPtr, | |
1526 | struct rstTapeInfo *rstTapeInfoPtr, | |
1527 | void *out, afs_int32 requestedBytes) | |
1528 | { | |
1529 | char *buffer = (char *) out; | |
1530 | afs_int32 taskId, transferBytes; | |
1531 | afs_int32 code = 0; | |
1532 | ||
1533 | taskId = rstTapeInfoPtr->taskId; | |
1534 | ||
1535 | if (checkAbortByTaskId(taskId)) | |
1536 | ERROR_EXIT(TC_ABORTEDBYREQUEST); | |
1537 | ||
1538 | if (!tapeReadBuffer) { | |
1539 | tapeReadBuffer = malloc(BUTM_BLOCKSIZE); | |
1540 | if (!tapeReadBuffer) | |
1541 | ERROR_EXIT(TC_NOMEMORY); | |
1542 | } | |
1543 | ||
1544 | while (requestedBytes > 0) { | |
1545 | if (nbytes == 0) { | |
1546 | tapeReadBufferPtr = &tapeReadBuffer[sizeof(struct blockMark)]; | |
1547 | ||
1548 | /* get more data */ | |
1549 | code = | |
1550 | butm_ReadFileData(tapeInfoPtr, tapeReadBufferPtr, | |
1551 | BUTM_BLKSIZE, &nbytes); | |
1552 | if (code) { | |
1553 | /* detect if we hit the end-of-tape and get next tape */ | |
1554 | if (code == BUTM_ENDVOLUME) { | |
1555 | /* Update fields in tape entry for this tape */ | |
1556 | tapeEntryPtr->flags = BUDB_TAPE_WRITTEN; | |
1557 | tapeEntryPtr->useKBytes = | |
1558 | tapeInfoPtr->kBytes + (tapeInfoPtr->nBytes ? 1 : 0); | |
1559 | ||
1560 | unmountTape(taskId, tapeInfoPtr); | |
1561 | ||
1562 | rstTapeInfoPtr->tapeSeq++; | |
1563 | code = readDbTape(tapeInfoPtr, rstTapeInfoPtr, 1); | |
1564 | if (code) | |
1565 | ERROR_EXIT(code); | |
1566 | ||
1567 | code = butm_ReadFileBegin(tapeInfoPtr); | |
1568 | if (code) { | |
1569 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
1570 | "Can't read FileBegin on tape\n"); | |
1571 | ERROR_EXIT(code); | |
1572 | } | |
1573 | ||
1574 | continue; | |
1575 | } | |
1576 | ||
1577 | ErrorLog(0, taskId, code, tapeInfoPtr->error, | |
1578 | "Can't read FileData on tape\n"); | |
1579 | ERROR_EXIT(code); | |
1580 | } | |
1581 | } | |
1582 | ||
1583 | /* copy out data */ | |
1584 | transferBytes = (nbytes < requestedBytes) ? nbytes : requestedBytes; | |
1585 | memcpy(buffer, tapeReadBufferPtr, transferBytes); | |
1586 | tapeReadBufferPtr += transferBytes; | |
1587 | buffer += transferBytes; | |
1588 | nbytes -= transferBytes; | |
1589 | requestedBytes -= transferBytes; | |
1590 | } | |
1591 | ||
1592 | error_exit: | |
1593 | return (code); | |
1594 | } |