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