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