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