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