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