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