include-afsconfig-before-param-h-20010712
[openafs.git] / src / butc / recoverDb.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("$Header$");
14
15 #include <stdio.h>
16 #ifdef AFS_NT40_ENV
17 #include <winsock2.h>
18 #else
19 #include <sys/time.h>
20 #include <sys/file.h>
21 #include <netinet/in.h>
22 #include <netdb.h>
23 #include <strings.h>
24 #endif
25 #include <sys/types.h>
26 #include <rx/xdr.h>
27 #include <rx/rx.h>
28 #include <lwp.h>
29 #include <lock.h>
30 #include <afs/tcdata.h>
31 #include <afs/bubasics.h>
32 #include <afs/budb.h>
33 #include "error_macros.h"
34
35 #define BELLCHAR 7                              /* ascii for bell */
36
37 /* GLOBAL CONFIGURATION PARAMETERS */
38 extern int autoQuery;
39 extern int queryoperator;
40 extern int isafile;
41
42 /* Handle for the information read from all the tapes of a dump */
43 afs_int32 tapepos;      /* when read a label, remember its position */
44 struct tapeScanInfo
45 {
46     struct butm_tapeLabel     tapeLabel, dumpLabel;
47     struct budb_dumpEntry     dumpEntry;
48     afs_int32    initialDumpId;
49     int      addDbFlag;
50 };
51
52 extern struct tapeConfig globalTapeConfig;
53 extern struct deviceSyncNode *deviceLatch;
54
55 /* PrintDumpLabel
56  *      print out the tape (dump) label.
57  */
58
59 PrintDumpLabel(labelptr)
60      struct butm_tapeLabel *labelptr;
61 {
62     char tapeName[BU_MAXTAPELEN+32];
63
64     printf("Dump label\n");
65     printf("----------\n");
66     TAPENAME(tapeName, labelptr->pName, labelptr->dumpid);
67     printf("permanent tape name = %s\n", tapeName);
68     TAPENAME(tapeName, labelptr->AFSName, labelptr->dumpid);
69     printf("AFS tape name = %s\n", tapeName);
70     printf("creationTime = %s", ctime(&labelptr->creationTime));
71     if ( labelptr->expirationDate )
72         printf("expirationDate = %s", cTIME(&labelptr->expirationDate));
73     printf("cell = %s\n", labelptr->cell);
74     printf("size = %u Kbytes\n", labelptr->size);
75     printf("dump path = %s\n", labelptr->dumpPath);
76
77     if ( labelptr->structVersion >= TAPE_VERSION_3 )
78     {
79         printf("dump id = %u\n", labelptr->dumpid);
80         printf("useCount = %d\n", labelptr->useCount);
81     }
82     printf("-- End of dump label --\n\n");
83 }
84
85 /* PrintVolumeHeader
86  *      print the contents of a volume header. 
87  */
88 static
89 PrintVolumeHeader(volHeader)
90      struct volumeHeader *volHeader;
91 {
92     printf("-- volume --\n");
93     printf("volume name: %s\n", volHeader->volumeName);
94     printf("volume ID %d\n", volHeader->volumeID);
95     /* printf("server %d\n", volHeader->server); */
96     printf("dumpSetName: %s\n", volHeader->dumpSetName);
97     printf("dumpID %d\n", volHeader->dumpID);
98     printf("level %d\n", volHeader->level);
99     printf("parentID %d\n", volHeader->parentID);
100     printf("endTime %d\n", volHeader->endTime);
101     /* printf("versionflags %d\n", volHeader->versionflags); */
102     printf("clonedate %s\n", ctime(&volHeader->cloneDate));
103 }
104
105 /* Ask
106  *      ask a question. returns true or false
107  * exit:
108  *      1 - yes
109  *      0 - no
110  */
111
112 afs_int32
113 Ask(st)
114      char *st;
115 {
116     char response;
117
118     while (1)
119     {
120         FFlushInput();
121         putchar(BELLCHAR);
122         printf("%s? (y/n) ", st);
123         fflush(stdout);
124         response = getchar();
125         if      ( response == 'y' ) return(1);
126         else if ( response == 'n' ) return(0);
127         printf("please answer y/n\n");
128     }
129 }
130
131 /* Will read a dump, then see if there is a dump following it and
132  * try to read that dump too.
133  * The first tape label is the first dumpLabel.
134  */
135 readDumps(taskId, tapeInfoPtr, scanInfoPtr)
136     afs_uint32              taskId;
137     struct butm_tapeInfo *tapeInfoPtr;
138     struct tapeScanInfo  *scanInfoPtr;
139 {
140     afs_int32 code, c;
141
142     bcopy(&scanInfoPtr->tapeLabel, &scanInfoPtr->dumpLabel, sizeof(struct butm_tapeLabel));
143
144     while(1)
145     {
146         code = readDump(taskId, tapeInfoPtr, scanInfoPtr);
147         if (code) ERROR_EXIT(code);
148
149         if (scanInfoPtr->tapeLabel.structVersion < TAPE_VERSION_4)
150             break;
151
152         /* Remember the initial dump and see if appended dump exists */
153
154         if (!scanInfoPtr->initialDumpId)
155               scanInfoPtr->initialDumpId = scanInfoPtr->dumpEntry.id;
156
157         c = butm_ReadLabel(tapeInfoPtr, &scanInfoPtr->dumpLabel, 0);  /* no rewind */
158         tapepos = tapeInfoPtr->position - 1;
159         if (c) break;
160     }
161
162 error_exit:
163     return(code);
164 }
165
166 afs_int32 getScanTape(taskId, tapeInfoPtr, tname, tapeId, prompt, tapeLabelPtr)
167     afs_int32                 taskId;
168     struct butm_tapeInfo  *tapeInfoPtr;
169     char                  *tname;
170     afs_int32                 tapeId;
171     int                   prompt;
172     struct butm_tapeLabel *tapeLabelPtr;
173 {
174     afs_int32 code = 0;
175     int   tapecount = 1;
176     afs_int32 curseq;
177     char tapename[BU_MAXTAPELEN+32];
178     char gotname[BU_MAXTAPELEN+32];
179
180     while (1)
181     {
182         /* prompt for a tape */
183         if (prompt)
184         {
185             code = PromptForTape(SCANOPCODE, tname, tapeId, taskId, tapecount);
186             if (code) ERROR_EXIT(code);
187         }
188         prompt = 1;
189         tapecount++;
190
191         code = butm_Mount(tapeInfoPtr, "");             /* open the tape device */
192         if (code) 
193         {
194             TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
195             goto newtape;
196         }
197
198         /* read the label on the tape */
199         code = butm_ReadLabel(tapeInfoPtr, tapeLabelPtr, 1);         /* rewind tape */
200         if (code) 
201         {
202             ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't read tape label\n");
203             goto newtape;
204         }
205         tapepos = tapeInfoPtr->position - 1;
206
207         /* Now check that the tape is good */
208         TAPENAME(tapename, tname, tapeId);
209         TAPENAME(gotname, tapeLabelPtr->AFSName, tapeLabelPtr->dumpid);
210
211         curseq = extractTapeSeq(tapeLabelPtr->AFSName);
212
213         /* Label can't be null or a bad name */
214         if ( !strcmp(tapeLabelPtr->AFSName,"") || (curseq <= 0) )
215         {
216             TLog(taskId, "Expected tape with dump, label seen %s\n", gotname);
217             goto newtape;
218         }
219
220         /* Label can't be a database tape */
221         if (databaseTape(tapeLabelPtr->AFSName))
222         {
223             TLog(taskId, "Expected tape with dump. Can't scan database tape %s\n", gotname);
224             goto newtape;
225         }
226
227         /* If no name, accept any tape */
228         if (strcmp(tname,"") == 0)
229         {
230             break;                              /* Start scan on any tape */
231 #ifdef notdef
232             if (curseq == 1) break;                /* The first tape */
233             else
234             {
235                 TLog(taskId, "Expected first tape of dump, label seen %s\n", gotname);
236                 goto newtape;
237             }
238 #endif
239         }
240
241         if ( strcmp(tname,tapeLabelPtr->AFSName) ||
242              ((tapeLabelPtr->structVersion >= TAPE_VERSION_3) &&
243               (tapeLabelPtr->dumpid != tapeId)) )
244         {
245             TLog(taskId, "Tape label expected %s, label seen %s\n", tapename, gotname);
246             goto newtape;
247         }
248
249         /* We have the correct tape */
250         break;
251
252       newtape:
253         unmountTape(taskId, tapeInfoPtr);
254     }
255
256 error_exit:
257     return(code);
258 }
259
260 /* ScanDumps
261  *      This set of code fragments read a tape, and add the information to
262  *      the database. Builds a literal structure.
263  *      
264  */
265
266 ScanDumps(ptr)
267      struct scanTapeIf   *ptr;
268 {
269     struct butm_tapeInfo curTapeInfo;
270     struct tapeScanInfo  tapeScanInfo;
271     afs_uint32              taskId;
272     afs_int32                code = 0;
273
274     taskId = ptr->taskId;
275     setStatus(taskId, DRIVE_WAIT);
276     EnterDeviceQueue(deviceLatch);
277     clearStatus(taskId, DRIVE_WAIT);
278
279     printf("\n\n");
280     if (ptr->addDbFlag) TLog(taskId, "ScanTape and add to the database\n");
281     else                TLog(taskId, "Scantape\n");
282
283     bzero(&tapeScanInfo, sizeof(tapeScanInfo));
284     tapeScanInfo.addDbFlag = ptr->addDbFlag;
285
286     bzero(&curTapeInfo, sizeof(curTapeInfo));
287     curTapeInfo.structVersion = BUTM_MAJORVERSION;
288     code = butm_file_Instantiate (&curTapeInfo, &globalTapeConfig);
289     if (code)
290     {
291         ErrorLog(0, taskId, code, curTapeInfo.error, "Can't initialize tape module\n");
292         ERROR_EXIT(code);
293     }
294
295     code = getScanTape(taskId, &curTapeInfo, "", 0, autoQuery, &tapeScanInfo.tapeLabel);
296     if (code) ERROR_EXIT(code);
297
298     code = readDumps(taskId, &curTapeInfo, &tapeScanInfo);
299     if (code) ERROR_EXIT(code);
300
301 error_exit:
302     unmountTape(taskId, &curTapeInfo);
303     waitDbWatcher();
304
305     if (code == TC_ABORTEDBYREQUEST)
306     {
307         ErrorLog(0, taskId, 0, 0, "Scantape: Aborted by request\n");
308         clearStatus(taskId, ABORT_REQUEST);
309         setStatus(taskId, ABORT_DONE);
310     }
311     else if (code)
312     {
313         ErrorLog(0, taskId, code, 0, "Scantape: Finished with errors\n");
314         setStatus(taskId, TASK_ERROR);
315     }
316     else
317     {
318         TLog(taskId, "Scantape: Finished\n");
319     }
320
321     free(ptr);
322     setStatus(taskId, TASK_DONE);
323     LeaveDeviceQueue(deviceLatch);
324     return(code);
325 }
326
327 /* scanVolData
328  *      Skips the volume data on the tape. The end of the volume data is
329  *      detected by the presence of the volume trailer or by an EOF indication
330  *      from butm. This algorithm should be replaced by one that always
331  *      detects based on the volume trailer, returning the trailer to the
332  *      caller. This is of course more painful.
333  * entry:
334  *      curTapePtr - tape info structure
335  *      Tape must be positioned after volume header
336  * exit:
337  *      0 - success
338  *      3 - empty volume, requires special handling
339  *
340  *      Tape positioned after data, but before file end marker block.
341  *      In case of errors, positioned after the error causing block
342  */
343 #define BIGCHUNK 102400
344
345 static
346 scanVolData(taskId, curTapePtr, tapeVersion, volumeHeader, volumeTrailer, bytesRead)
347      afs_int32                taskId;
348      struct butm_tapeInfo *curTapePtr;
349      afs_int32                tapeVersion;
350      struct volumeHeader  *volumeHeader, *volumeTrailer;
351      afs_uint32              *bytesRead;
352 {
353     afs_int32 headBytes, tailBytes;
354     char  *block = (char *) 0;
355     char  *buffer[2];
356     int   hasdata[2], curr, prev;
357     afs_uint32 chunkSize = 0;
358     afs_int32 nbytes;
359     afs_int32 code = 0;
360     afs_int32 rcode, tcode;
361
362     bzero(volumeHeader, sizeof(struct volumeHeader));
363
364     block = (char *) malloc(2*BUTM_BLOCKSIZE);
365     if (!block) return(TC_NOMEMORY);
366     buffer[0] = &block[sizeof(struct blockMark)];
367     buffer[1] = &block[BUTM_BLOCKSIZE + sizeof(struct blockMark)];
368     hasdata[0] = hasdata[1] = 0;
369     curr = 0;
370
371     tcode = NextFile(curTapePtr);  /* guarantees we are at a filemark */
372     if (tcode) ERROR_EXIT(tcode)
373
374     /* Read the FileBegin FileMark */
375     code = butm_ReadFileBegin(curTapePtr);
376     if (code)
377     {
378         /*
379          * Tapes made with 3.0 have no software EOT markers. Therefore
380          * at this point, we will most likely get a read error, indicating
381          * the end of this dump
382          */
383         if ( (tapeVersion == TAPE_VERSION_0) || (tapeVersion == TAPE_VERSION_1) )
384         {
385             /*
386              * then a tape error is possible at this point, and it
387              * signals the end of the dump. Tapes that are continued
388              * have an EOT marker.
389              */
390             TapeLog(0, taskId, code, curTapePtr->error, "Read error - end-of-dump inferred\n");
391             code = BUTM_EOD;
392         }
393
394         if (code != BUTM_EOD)
395             ErrorLog(0, taskId, code, curTapePtr->error, "Can't read FileBegin on tape\n");
396         ERROR_EXIT(code);
397     }
398
399     /* now read the volume header */
400     code = ReadVolHeader(taskId, curTapePtr, volumeHeader);
401     if (code) ERROR_EXIT(code);
402
403     *bytesRead = 0;
404     while (1)
405     { /*w*/
406
407         /* Check for abort in the middle of scanning data */
408         if (*bytesRead >= chunkSize)
409         {
410             if ( checkAbortByTaskId(taskId) ) 
411                 ERROR_EXIT(TC_ABORTEDBYREQUEST);
412             chunkSize += BIGCHUNK;
413         }
414
415         /* 
416          * Read volume date - If prematurely hit the HW EOF 
417          * marker, check to see if data contains a volumetrailer.
418          */
419         rcode = butm_ReadFileData(curTapePtr, buffer[curr], BUTM_BLKSIZE, &nbytes);
420         if (rcode)
421         {
422             hasdata[curr] = 0;
423             if ( (rcode == BUTM_EOF) || (rcode == BUTM_ENDVOLUME) ) break;
424
425             ErrorLog(0, taskId, rcode, curTapePtr->error, "Can't read FileData on tape\n");
426             ERROR_EXIT(rcode)
427         }
428         hasdata[curr] = 1;
429         *bytesRead += nbytes;
430
431         if ( (nbytes != BUTM_BLKSIZE) ||
432              (FindVolTrailer(buffer[curr], nbytes, &tailBytes, volumeTrailer)) )
433             break;
434
435         curr = ((curr == 0) ? 1 : 0);          /* Switch buffers */
436     } /*w*/
437
438     /* Now verify that there is a volume trailer and its valid and copy it */
439     prev = ((curr == 0) ? 1 : 0);
440     if ( !FindVolTrailer2(buffer[prev], (hasdata[prev]?BUTM_BLKSIZE:0), &headBytes,
441                           buffer[curr], nbytes                        , &tailBytes,
442                           volumeTrailer) )
443     {
444         code = TC_MISSINGTRAILER;
445         ErrorLog(0, taskId, code, 0, "Missing volume trailer on tape\n");
446     }
447     else
448     {
449         /* subtract size of the volume trailer from data read */
450         *bytesRead -= sizeof(struct volumeHeader);    
451     }
452     
453     /* 
454      * If we didn't hit the EOF while reading data, read FileEnd marker 
455      * or EOF marker. 
456      */
457     if (!rcode)
458     {
459         tcode = butm_ReadFileEnd(curTapePtr);
460         if (tcode)
461         {
462             ErrorLog(0, taskId, tcode, curTapePtr->error, "Can't read EOF on tape\n");
463             ERROR_EXIT(tcode);
464         }
465     }
466
467 error_exit:
468     if (block) free(block);
469     return(code);
470 }
471
472 /* nextTapeLabel
473  *      generate the name of the next tape label expected
474  * exit: 
475  *      ptr to static string
476  */
477
478 char *
479 nextTapeLabel(prevTapeName)
480     char *prevTapeName;
481 {
482     char *prevdot;
483     char *retval;
484     int  seq;
485     static char buffer[BU_MAXTAPELEN];
486
487     retval = "";
488
489     /* extract information from previous tape label */
490     strcpy(buffer, prevTapeName);
491     prevdot = rindex(buffer, '.');
492     if (!prevdot) return(retval);
493     prevdot++;
494
495     seq = extractTapeSeq(prevTapeName);
496     seq++;
497     sprintf(prevdot, "%-d", seq);
498
499     return(&buffer[0]);
500 }
501
502 /* readDump
503  *      Read all the information on a tape. If to add to the database, queue
504  *      onto list so another thread adds the entries to the database.
505  * entry:
506  *      taskid      - butc task number.
507  *      tapeInfoPtr - Tape information.
508  *      scanInfoPtr - Info to keep so we can add entries to the db.
509  * exit:
510  *      0     - tape scanned.
511  *      non-0 - error. Abort the scan.
512  *      moreTapes set to 1 if this is not the last tape in the dump,
513  *                       0 if the last tape,
514  *                      -1 don't know if last tape or not.
515  */
516
517 afs_int32 RcreateDump();
518
519 static
520 readDump(taskId, tapeInfoPtr, scanInfoPtr)
521      afs_uint32              taskId;
522      struct butm_tapeInfo *tapeInfoPtr;
523      struct tapeScanInfo  *scanInfoPtr;
524 {
525     int moreTapes = 1;
526     afs_int32 nbytes, flags, seq;
527     int  newDump = 1, newTape = 1;
528     afs_int32 tapePosition, c;
529     afs_int32 code = 0, tcode;
530     int  interactiveFlag, badscan;
531
532     struct volumeHeader      volHeader, volTrailer;
533
534     int good;
535     struct budb_tapeEntry   tapeEntry;
536     struct budb_volumeEntry volEntry;
537
538     volEntry.dump = 0;
539     PrintDumpLabel(&scanInfoPtr->dumpLabel);
540
541     while (moreTapes)   /* While there is a tape to read */
542     { /*t*/
543         badscan = 0;
544         while (1)                /* Read each volume on the tape */
545         { /*w*/
546             moreTapes = -1;
547             tapePosition = tapeInfoPtr->position;    /* remember position */
548
549             /*
550              * Skip the volume data
551              */
552             tcode = scanVolData(taskId, tapeInfoPtr, 
553                                 scanInfoPtr->tapeLabel.structVersion, 
554                                 &volHeader, &volTrailer, &nbytes);
555             if (tcode) {
556                 badscan++;
557
558                 if (tcode == TC_ABORTEDBYREQUEST) {    /* Aborted */
559                     ERROR_EXIT(tcode);
560                 }
561
562                 if (tcode == BUTM_EOD) {
563                     moreTapes = 0;            /* the end of the dump */
564                     break;
565                 }
566
567                 /* Found a volume but it's incomplete. Skip over these */
568                 if (volHeader.volumeID) {
569                    TapeLog(0, taskId, tcode, 0, 
570                            "Warning: volume %s (%u) ignored. Incomplete\n",
571                            volHeader.volumeName, volHeader.volumeID);
572                    continue;
573                 }
574
575                 /* No volume was found. We may have hit the EOT or a 
576                  * bad-spot. Try to skip over this spot.
577                  */
578                 if (badscan < 2) {              /* allow 2 errors, then fail */
579                    TapeLog(0, taskId, tcode, 0, 
580                            "Warning: Error in scanning tape - will try skipping volume\n");
581                    continue;
582                 }
583                 if (scanInfoPtr->tapeLabel.structVersion >= TAPE_VERSION_4) {
584                    TapeLog(0, taskId, tcode, 0, 
585                            "Warning: Error in scanning tape - end-of-tape inferred\n");
586                    moreTapes = 1;              /* then assume next tape */
587                 }
588                 else {
589                    ErrorLog(0, taskId, tcode, 0, "Error in scanning tape\n");
590                    /* will ask if there is a next tape */
591                 }
592                 break;
593             }
594          
595             PrintVolumeHeader(&volHeader);
596
597             /* If this is not the first volume fragment, make sure it follows
598              * the last volume fragment 
599              */
600             if (volEntry.dump)
601             {
602                 if ( (volEntry.dump != volHeader.dumpID) ||
603                      (volEntry.id   != volHeader.volumeID) ||
604                      (volEntry.seq  != volHeader.frag - 2) ||
605                      (strcmp(volEntry.name, volHeader.volumeName)) )
606                 {
607                     TLog(taskId, 
608                          "Warning: volume %s (%u) ignored. Incomplete - no last fragment\n",
609                          volEntry.name, volEntry.id);
610
611                     if (scanInfoPtr->addDbFlag)
612                     {
613                         tcode = flushSavedEntries(DUMP_FAILED);
614                         if (tcode) ERROR_EXIT(tcode);
615                         volEntry.dump = 0;
616                     }
617                 }
618             }
619
620             /* If this is the first volume fragment, make sure says so */
621             if (scanInfoPtr->addDbFlag && 
622                 !volEntry.dump && (volHeader.frag != 1))
623             {
624                 TLog(taskId, 
625                      "Warning: volume %s (%u) ignored. Incomplete - no first fragment\n",
626                      volHeader.volumeName, volHeader.volumeID);
627             }
628
629             /* Check that this volume belongs to the dump we are scanning */
630             else if (scanInfoPtr->dumpLabel.dumpid && 
631                      (volHeader.dumpID != scanInfoPtr->dumpLabel.dumpid) )
632             {
633                 TLog(taskId, 
634                      "Warning: volume %s (%u) ignored. Expected DumpId %u, got %u\n",
635                      volHeader.volumeName, volHeader.volumeID,
636                      scanInfoPtr->dumpLabel.dumpid, volHeader.dumpID);
637             }
638
639             /* Passed tests, Now add to the database (if dbadd flag is set) */
640             else if (scanInfoPtr->addDbFlag)
641             {
642                 /* Have enough information to create a dump entry */
643                 if (newDump)
644                 {
645                     tcode = RcreateDump(scanInfoPtr, &volHeader);
646                     if (tcode)
647                     {
648                         ErrorLog(0, taskId, tcode, 0, "Can't add dump %u to database\n",
649                                  volHeader.dumpID);
650                         ERROR_EXIT(tcode);
651                     }
652                     newDump = 0;
653                 }
654
655                 /* Have enough information to create a tape entry */
656                 if (newTape)
657                 {
658                     seq = extractTapeSeq(scanInfoPtr->tapeLabel.AFSName);
659                     if (seq < 0) ERROR_EXIT(TC_INTERNALERROR);
660
661                     tcode = useTape(&tapeEntry,
662                                     volHeader.dumpID, 
663                                     TNAME(&scanInfoPtr->tapeLabel), seq,
664                                     scanInfoPtr->tapeLabel.useCount,
665                                     scanInfoPtr->dumpLabel.creationTime,
666                                     scanInfoPtr->dumpLabel.expirationDate,
667                                     tapepos);
668                     if (tcode)
669                     {
670                         char gotName[BU_MAXTAPELEN+32];
671
672                         LABELNAME(gotName, &scanInfoPtr->tapeLabel);
673                         ErrorLog(0, taskId, tcode, 0, 
674                                  "Can't add tape %s for dump %u to database\n",
675                                  gotName, volHeader.dumpID);
676                         ERROR_EXIT(tcode);
677                     }
678                     newTape = 0;
679                 }
680
681                 /* Create the volume entry */
682                 flags = ( (volHeader.frag == 1) ? BUDB_VOL_FIRSTFRAG : 0);
683                 if (!volTrailer.contd) flags |= BUDB_VOL_LASTFRAG;
684                 tcode = addVolume(&volEntry, 
685                                   volHeader.dumpID, 
686                                   TNAME(&scanInfoPtr->tapeLabel),
687                                   volHeader.volumeName,
688                                   volHeader.volumeID,
689                                   volHeader.cloneDate,
690                                   tapePosition, nbytes,
691                                   (volHeader.frag - 1), flags);
692                 if (tcode)
693                 {
694                     ErrorLog(0, taskId, tcode, 0, 
695                              "Can't add volume %s (%u) for dump %u to database\n",
696                              volHeader.volumeName, volHeader.volumeID,
697                              volHeader.dumpID);
698                     ERROR_EXIT(tcode);
699                 }
700             }
701             
702             if (volTrailer.contd) 
703             {
704                 /* No need to read the EOD marker, we know there is a next tape */
705                 moreTapes = 1;
706                 break;
707             }
708             else
709             {
710                 if (scanInfoPtr->addDbFlag)
711                 {
712                     tcode = flushSavedEntries(DUMP_SUCCESS);
713                     if (tcode)
714                        ERROR_EXIT(tcode);
715                     volEntry.dump = 0;
716                 }
717             }
718         } /*w*/
719
720         if (!newTape)
721         {
722             if (scanInfoPtr->addDbFlag)
723             {
724                 tcode = finishTape(&tapeEntry,
725                                   (tapeInfoPtr->kBytes + (tapeInfoPtr->nBytes ? 1 : 0)));
726                 if (tcode)
727                 {
728                     char gotName[BU_MAXTAPELEN+32];
729                         
730                     LABELNAME(gotName, &scanInfoPtr->tapeLabel);
731                     ErrorLog(0, taskId, tcode, 0, 
732                              "Can't mark tape %s 'completed' for dump %u in database\n",
733                              gotName, tapeEntry.dump);
734                     ERROR_EXIT(tcode);
735                 }
736             }
737         }
738
739         /* Ask if there is another tape if we can't figure it out */
740         if (moreTapes == -1)
741             moreTapes = (queryoperator ? Ask("Are there more tapes") : 1);
742
743         /* Get the next tape label */
744         if (moreTapes)
745         {
746             char *tapeName;
747             afs_int32 dumpid;
748
749             unmountTape(taskId, tapeInfoPtr);
750
751             tapeName = nextTapeLabel(scanInfoPtr->tapeLabel.AFSName);
752             dumpid   = scanInfoPtr->tapeLabel.dumpid;
753             tcode = getScanTape(taskId, tapeInfoPtr, tapeName, dumpid, 1, &scanInfoPtr->tapeLabel);
754             if (tcode) ERROR_EXIT(tcode);
755             newTape = 1;
756         }
757     } /*t*/
758
759     if (!newDump)
760     {
761         if (scanInfoPtr->addDbFlag)
762         {
763             tcode = finishDump(&scanInfoPtr->dumpEntry);
764             if (tcode)
765             {
766                 ErrorLog(0, taskId, tcode, 0, 
767                          "Can't mark dump %u 'completed' in database\n",
768                          scanInfoPtr->dumpEntry.id);
769             }
770
771             tcode = flushSavedEntries(DUMP_SUCCESS);
772             if (tcode) ERROR_EXIT(tcode);
773         }
774     }
775
776 error_exit:
777     return(code);
778 }
779
780 /* validatePath
781  * exit:
782  *      0 - not ok
783  *      1 - ok
784  */
785 validatePath(labelptr, pathptr)
786      struct butm_tapeLabel *labelptr;
787      char *pathptr;
788 {
789     char *up, *tp;
790     char tapeName[BU_MAXTAPELEN];
791
792     /* check length */
793     if ( strlen(pathptr) > BU_MAX_DUMP_PATH-1 )
794     {
795         fprintf(stderr, "Invalid pathname - too long\n");
796         return(0);
797     }
798
799     if ( !labelptr ) return(1);
800
801     strcpy(tapeName, labelptr->AFSName);
802
803     tp = rindex(tapeName, '.');
804     if ( !tp ) return(1);
805     tp++;
806
807     up = rindex(pathptr, '/');
808     if ( !up )
809     {
810         fprintf(stderr, "Invalid path name, missing /\n");
811         return(0);
812     }
813     up++;
814
815     if ( strcmp(up, tp) != 0 )
816     {
817         fprintf(stderr, "Invalid path name\n");
818         fprintf(stderr, "Mismatch between tape dump name '%s' and users dump name '%s'\n",
819                 tp, up);
820         return(0);
821     }
822     return(1);
823 }
824
825 /* volumesetNamePtr
826  *      return a pointer to a (static) volume set name string.
827  * entry:
828  *      ptr - ptr to a dump name
829  * exit:
830  *      0 - error. Can't extract volumeset name.
831  *      ptr - to static volumeset string.
832  */
833
834 char *
835 volumesetNamePtr(ptr)
836      char *ptr;
837 {
838     static char vsname[BU_MAXUNAMELEN];
839     char *dotPtr;
840     int dotIndex;
841
842     dotPtr = index(ptr, '.');
843     if ( !dotPtr ) return(0);
844
845     dotIndex = dotPtr - ptr;
846     if ((dotIndex + 1) > sizeof(vsname)) return(0);     /* name too long */
847
848     strncpy(&vsname[0], ptr, dotIndex);
849     vsname[dotIndex] = 0;                               /* ensure null terminated */
850
851     return(&vsname[0]);
852 }
853
854 char *
855 extractDumpName(ptr)
856      char *ptr;
857 {
858     static char dname[BU_MAXTAPELEN];
859     char *dotPtr;
860     int dotIndex;
861
862     dotPtr = rindex(ptr, '.');
863     if (!dotPtr) return(0);
864
865     dotIndex = dotPtr - ptr;
866     if ((dotIndex + 1) > sizeof(dname)) return(0);      /* name too long */
867
868     strncpy(&dname[0], ptr, dotIndex);
869     dname[dotIndex] = 0;                                /* ensure null terminated */
870
871     return(&dname[0]);
872 }
873
874 /* extractTapeSeq
875  *      The routine assumes that tape names have an embedded sequence number
876  *      as the trialing component. It is suggested that any tape naming
877  *      changes retain the trailing seq. number
878  * entry: 
879  *      tapename - ptr to tape name 
880  * exit:
881  *      0 or positive - sequence number
882  *      -1 - error, couldn't extract sequence number
883  */
884
885 extractTapeSeq(tapename)
886      char *tapename;
887 {
888     char *sptr;
889
890     sptr = rindex(tapename, '.');
891     if ( !sptr ) return(-1);
892     sptr++;
893     return(atol(sptr));
894 }
895
896 /* databaseTape
897  *   returns true or false depending on whether the tape is 
898  *   a database tape or not.
899  */
900 int databaseTape(tapeName)
901     char *tapeName;
902 {
903     char *sptr;
904     int  c;
905
906     sptr = rindex(tapeName, '.');
907     if ( !sptr ) return(0);
908     
909     c = (int)( (afs_int32)sptr - (afs_int32)tapeName );
910     if ( strncmp(tapeName, DUMP_TAPE_NAME, c) == 0 )
911         return(1);
912
913     return(0);
914 }
915
916 afs_int32 RcreateDump(tapeScanInfoPtr, volHeaderPtr)
917     struct tapeScanInfo *tapeScanInfoPtr;
918     struct volumeHeader *volHeaderPtr;
919 {
920     afs_int32 code;
921     struct butm_tapeLabel    *dumpLabelPtr  = &tapeScanInfoPtr->dumpLabel;
922     struct budb_dumpEntry    *dumpEntryPtr  = &tapeScanInfoPtr->dumpEntry;
923
924     /* construct dump entry */
925     bzero(dumpEntryPtr, sizeof(struct budb_dumpEntry));
926     dumpEntryPtr->id            = volHeaderPtr->dumpID;
927     dumpEntryPtr->initialDumpID = tapeScanInfoPtr->initialDumpId;
928     dumpEntryPtr->parent        = volHeaderPtr->parentID;
929     dumpEntryPtr->level         = volHeaderPtr->level;
930     dumpEntryPtr->created       = volHeaderPtr->dumpID;  /* time dump was created */
931     dumpEntryPtr->flags         = 0;
932     dumpEntryPtr->incTime       = 0;
933     dumpEntryPtr->nVolumes      = 0;
934     strcpy(dumpEntryPtr->volumeSetName, volumesetNamePtr(volHeaderPtr->dumpSetName));
935     strcpy(dumpEntryPtr->dumpPath,      dumpLabelPtr->dumpPath);
936     strcpy(dumpEntryPtr->name,          volHeaderPtr->dumpSetName);
937     default_tapeset(&dumpEntryPtr->tapes, volHeaderPtr->dumpSetName);
938     dumpEntryPtr->tapes.b = extractTapeSeq(dumpLabelPtr->AFSName);
939     copy_ktcPrincipal_to_budbPrincipal(&dumpLabelPtr->creator, &dumpEntryPtr->dumper);
940
941     code = bcdb_CreateDump(dumpEntryPtr);
942     return(code);
943 }