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