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