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