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