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