2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
22 #include <netinet/in.h>
26 #include <sys/types.h>
31 #include <afs/tcdata.h>
32 #include <afs/bubasics.h>
34 #include "error_macros.h"
36 #define BELLCHAR 7 /* ascii for bell */
38 /* GLOBAL CONFIGURATION PARAMETERS */
40 extern int queryoperator;
42 /* Handle for the information read from all the tapes of a dump */
43 afs_int32 tapepos; /* when read a label, remember its position */
45 struct butm_tapeLabel tapeLabel, dumpLabel;
46 struct budb_dumpEntry dumpEntry;
47 afs_int32 initialDumpId;
51 extern struct tapeConfig globalTapeConfig;
52 extern struct deviceSyncNode *deviceLatch;
55 * print out the tape (dump) label.
58 PrintDumpLabel(labelptr)
59 struct butm_tapeLabel *labelptr;
61 char tapeName[BU_MAXTAPELEN + 32];
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 t = labelptr->creationTime;
71 printf("creationTime = %s", ctime(&t));
72 if (labelptr->expirationDate) {
73 t = labelptr->expirationDate;
74 printf("expirationDate = %s", cTIME(&t));
76 printf("cell = %s\n", labelptr->cell);
77 printf("size = %u Kbytes\n", labelptr->size);
78 printf("dump path = %s\n", labelptr->dumpPath);
80 if (labelptr->structVersion >= TAPE_VERSION_3) {
81 printf("dump id = %u\n", labelptr->dumpid);
82 printf("useCount = %d\n", labelptr->useCount);
84 printf("-- End of dump label --\n\n");
88 * print the contents of a volume header.
91 PrintVolumeHeader(volHeader)
92 struct volumeHeader *volHeader;
96 printf("-- volume --\n");
97 printf("volume name: %s\n", volHeader->volumeName);
98 printf("volume ID %d\n", volHeader->volumeID);
99 /* printf("server %d\n", volHeader->server); */
100 printf("dumpSetName: %s\n", volHeader->dumpSetName);
101 printf("dumpID %d\n", volHeader->dumpID);
102 printf("level %d\n", volHeader->level);
103 printf("parentID %d\n", volHeader->parentID);
104 printf("endTime %d\n", volHeader->endTime);
105 /* printf("versionflags %d\n", volHeader->versionflags); */
106 t = volHeader->cloneDate;
107 printf("clonedate %s\n", ctime(&t));
111 * ask a question. returns true or false
126 printf("%s? (y/n) ", st);
128 response = getchar();
131 else if (response == 'n' || response == EOF)
133 printf("please answer y/n\n");
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.
144 * curTapePtr - tape info structure
145 * Tape must be positioned after volume header
148 * 3 - empty volume, requires special handling
150 * Tape positioned after data, but before file end marker block.
151 * In case of errors, positioned after the error causing block
153 #define BIGCHUNK 102400
156 scanVolData(taskId, curTapePtr, tapeVersion, volumeHeader, volumeTrailer,
159 struct butm_tapeInfo *curTapePtr;
160 afs_int32 tapeVersion;
161 struct volumeHeader *volumeHeader, *volumeTrailer;
162 afs_uint32 *bytesRead;
164 afs_int32 headBytes, tailBytes;
167 int hasdata[2], curr, prev;
168 afs_uint32 chunkSize = 0;
171 afs_int32 rcode, tcode;
173 memset(volumeHeader, 0, sizeof(struct volumeHeader));
175 block = (char *)malloc(2 * BUTM_BLOCKSIZE);
177 return (TC_NOMEMORY);
178 buffer[0] = &block[sizeof(struct blockMark)];
179 buffer[1] = &block[BUTM_BLOCKSIZE + sizeof(struct blockMark)];
180 hasdata[0] = hasdata[1] = 0;
183 tcode = NextFile(curTapePtr); /* guarantees we are at a filemark */
187 /* Read the FileBegin FileMark */
188 code = butm_ReadFileBegin(curTapePtr);
191 * Tapes made with 3.0 have no software EOT markers. Therefore
192 * at this point, we will most likely get a read error, indicating
193 * the end of this dump
195 if ((tapeVersion == TAPE_VERSION_0)
196 || (tapeVersion == TAPE_VERSION_1)) {
198 * then a tape error is possible at this point, and it
199 * signals the end of the dump. Tapes that are continued
200 * have an EOT marker.
202 TapeLog(0, taskId, code, curTapePtr->error,
203 "Read error - end-of-dump inferred\n");
207 if (code != BUTM_EOD)
208 ErrorLog(0, taskId, code, curTapePtr->error,
209 "Can't read FileBegin on tape\n");
213 /* now read the volume header */
214 code = ReadVolHeader(taskId, curTapePtr, volumeHeader);
221 /* Check for abort in the middle of scanning data */
222 if (*bytesRead >= chunkSize) {
223 if (checkAbortByTaskId(taskId))
224 ERROR_EXIT(TC_ABORTEDBYREQUEST);
225 chunkSize += BIGCHUNK;
229 * Read volume date - If prematurely hit the HW EOF
230 * marker, check to see if data contains a volumetrailer.
233 butm_ReadFileData(curTapePtr, buffer[curr], BUTM_BLKSIZE,
237 if ((rcode == BUTM_EOF) || (rcode == BUTM_ENDVOLUME))
240 ErrorLog(0, taskId, rcode, curTapePtr->error,
241 "Can't read FileData on tape\n");
245 *bytesRead += nbytes;
247 if ((nbytes != BUTM_BLKSIZE)
249 (FindVolTrailer(buffer[curr], nbytes, &tailBytes, volumeTrailer)))
252 curr = ((curr == 0) ? 1 : 0); /* Switch buffers */
255 /* Now verify that there is a volume trailer and its valid and copy it */
256 prev = ((curr == 0) ? 1 : 0);
258 (buffer[prev], (hasdata[prev] ? BUTM_BLKSIZE : 0), &headBytes,
259 buffer[curr], nbytes, &tailBytes, volumeTrailer)) {
260 code = TC_MISSINGTRAILER;
261 ErrorLog(0, taskId, code, 0, "Missing volume trailer on tape\n");
263 /* subtract size of the volume trailer from data read */
264 *bytesRead -= sizeof(struct volumeHeader);
268 * If we didn't hit the EOF while reading data, read FileEnd marker
272 tcode = butm_ReadFileEnd(curTapePtr);
274 ErrorLog(0, taskId, tcode, curTapePtr->error,
275 "Can't read EOF on tape\n");
287 * generate the name of the next tape label expected
289 * ptr to static string
293 nextTapeLabel(prevTapeName)
299 static char buffer[BU_MAXTAPELEN];
303 /* extract information from previous tape label */
304 strcpy(buffer, prevTapeName);
305 prevdot = strrchr(buffer, '.');
310 seq = extractTapeSeq(prevTapeName);
312 sprintf(prevdot, "%-d", seq);
318 * Read all the information on a tape. If to add to the database, queue
319 * onto list so another thread adds the entries to the database.
321 * taskid - butc task number.
322 * tapeInfoPtr - Tape information.
323 * scanInfoPtr - Info to keep so we can add entries to the db.
326 * non-0 - error. Abort the scan.
327 * moreTapes set to 1 if this is not the last tape in the dump,
328 * 0 if the last tape,
329 * -1 don't know if last tape or not.
332 afs_int32 RcreateDump();
335 readDump(taskId, tapeInfoPtr, scanInfoPtr)
337 struct butm_tapeInfo *tapeInfoPtr;
338 struct tapeScanInfo *scanInfoPtr;
341 afs_int32 nbytes, flags, seq;
342 int newDump = 1, newTape = 1;
343 afs_int32 tapePosition;
344 afs_int32 code = 0, tcode;
346 struct volumeHeader volHeader, volTrailer;
347 struct budb_tapeEntry tapeEntry;
348 struct budb_volumeEntry volEntry;
351 PrintDumpLabel(&scanInfoPtr->dumpLabel);
353 while (moreTapes) { /* While there is a tape to read *//*t */
355 while (1) { /* Read each volume on the tape *//*w */
357 tapePosition = tapeInfoPtr->position; /* remember position */
360 * Skip the volume data
363 scanVolData(taskId, tapeInfoPtr,
364 scanInfoPtr->tapeLabel.structVersion, &volHeader,
365 &volTrailer, &nbytes);
369 if (tcode == TC_ABORTEDBYREQUEST) { /* Aborted */
373 if (tcode == BUTM_EOD) {
374 moreTapes = 0; /* the end of the dump */
378 /* Found a volume but it's incomplete. Skip over these */
379 if (volHeader.volumeID) {
380 TapeLog(0, taskId, tcode, 0,
381 "Warning: volume %s (%u) ignored. Incomplete\n",
382 volHeader.volumeName, volHeader.volumeID);
386 /* No volume was found. We may have hit the EOT or a
387 * bad-spot. Try to skip over this spot.
389 if (badscan < 2) { /* allow 2 errors, then fail */
390 TapeLog(0, taskId, tcode, 0,
391 "Warning: Error in scanning tape - will try skipping volume\n");
394 if (scanInfoPtr->tapeLabel.structVersion >= TAPE_VERSION_4) {
395 TapeLog(0, taskId, tcode, 0,
396 "Warning: Error in scanning tape - end-of-tape inferred\n");
397 moreTapes = 1; /* then assume next tape */
399 ErrorLog(0, taskId, tcode, 0, "Error in scanning tape\n");
400 /* will ask if there is a next tape */
405 PrintVolumeHeader(&volHeader);
407 /* If this is not the first volume fragment, make sure it follows
408 * the last volume fragment
411 if ((volEntry.dump != volHeader.dumpID)
412 || (volEntry.id != volHeader.volumeID)
413 || (volEntry.seq != volHeader.frag - 2)
414 || (strcmp(volEntry.name, volHeader.volumeName))) {
416 "Warning: volume %s (%u) ignored. Incomplete - no last fragment\n",
417 volEntry.name, volEntry.id);
419 if (scanInfoPtr->addDbFlag) {
420 tcode = flushSavedEntries(DUMP_FAILED);
428 /* If this is the first volume fragment, make sure says so */
429 if (scanInfoPtr->addDbFlag && !volEntry.dump
430 && (volHeader.frag != 1)) {
432 "Warning: volume %s (%u) ignored. Incomplete - no first fragment\n",
433 volHeader.volumeName, volHeader.volumeID);
436 /* Check that this volume belongs to the dump we are scanning */
437 else if (scanInfoPtr->dumpLabel.dumpid
438 && (volHeader.dumpID != scanInfoPtr->dumpLabel.dumpid)) {
440 "Warning: volume %s (%u) ignored. Expected DumpId %u, got %u\n",
441 volHeader.volumeName, volHeader.volumeID,
442 scanInfoPtr->dumpLabel.dumpid, volHeader.dumpID);
445 /* Passed tests, Now add to the database (if dbadd flag is set) */
446 else if (scanInfoPtr->addDbFlag) {
447 /* Have enough information to create a dump entry */
449 tcode = RcreateDump(scanInfoPtr, &volHeader);
451 ErrorLog(0, taskId, tcode, 0,
452 "Can't add dump %u to database\n",
459 /* Have enough information to create a tape entry */
461 seq = extractTapeSeq(scanInfoPtr->tapeLabel.AFSName);
463 ERROR_EXIT(TC_INTERNALERROR);
466 useTape(&tapeEntry, volHeader.dumpID,
467 TNAME(&scanInfoPtr->tapeLabel), seq,
468 scanInfoPtr->tapeLabel.useCount,
469 scanInfoPtr->dumpLabel.creationTime,
470 scanInfoPtr->dumpLabel.expirationDate,
473 char gotName[BU_MAXTAPELEN + 32];
475 LABELNAME(gotName, &scanInfoPtr->tapeLabel);
476 ErrorLog(0, taskId, tcode, 0,
477 "Can't add tape %s for dump %u to database\n",
478 gotName, volHeader.dumpID);
484 /* Create the volume entry */
485 flags = ((volHeader.frag == 1) ? BUDB_VOL_FIRSTFRAG : 0);
486 if (!volTrailer.contd)
487 flags |= BUDB_VOL_LASTFRAG;
489 addVolume(&volEntry, volHeader.dumpID,
490 TNAME(&scanInfoPtr->tapeLabel),
491 volHeader.volumeName, volHeader.volumeID,
492 volHeader.cloneDate, tapePosition, nbytes,
493 (volHeader.frag - 1), flags);
495 ErrorLog(0, taskId, tcode, 0,
496 "Can't add volume %s (%u) for dump %u to database\n",
497 volHeader.volumeName, volHeader.volumeID,
503 if (volTrailer.contd) {
504 /* No need to read the EOD marker, we know there is a next tape */
508 if (scanInfoPtr->addDbFlag) {
509 tcode = flushSavedEntries(DUMP_SUCCESS);
518 if (scanInfoPtr->addDbFlag) {
520 finishTape(&tapeEntry,
521 (tapeInfoPtr->kBytes +
522 (tapeInfoPtr->nBytes ? 1 : 0)));
524 char gotName[BU_MAXTAPELEN + 32];
526 LABELNAME(gotName, &scanInfoPtr->tapeLabel);
527 ErrorLog(0, taskId, tcode, 0,
528 "Can't mark tape %s 'completed' for dump %u in database\n",
529 gotName, tapeEntry.dump);
535 /* Ask if there is another tape if we can't figure it out */
537 moreTapes = (queryoperator ? Ask("Are there more tapes") : 1);
539 /* Get the next tape label */
544 unmountTape(taskId, tapeInfoPtr);
546 tapeName = nextTapeLabel(scanInfoPtr->tapeLabel.AFSName);
547 dumpid = scanInfoPtr->tapeLabel.dumpid;
549 getScanTape(taskId, tapeInfoPtr, tapeName, dumpid, 1,
550 &scanInfoPtr->tapeLabel);
558 if (scanInfoPtr->addDbFlag) {
559 tcode = finishDump(&scanInfoPtr->dumpEntry);
561 ErrorLog(0, taskId, tcode, 0,
562 "Can't mark dump %u 'completed' in database\n",
563 scanInfoPtr->dumpEntry.id);
566 tcode = flushSavedEntries(DUMP_SUCCESS);
576 /* Will read a dump, then see if there is a dump following it and
577 * try to read that dump too.
578 * The first tape label is the first dumpLabel.
580 readDumps(taskId, tapeInfoPtr, scanInfoPtr)
582 struct butm_tapeInfo *tapeInfoPtr;
583 struct tapeScanInfo *scanInfoPtr;
587 memcpy(&scanInfoPtr->dumpLabel, &scanInfoPtr->tapeLabel,
588 sizeof(struct butm_tapeLabel));
591 code = readDump(taskId, tapeInfoPtr, scanInfoPtr);
595 if (scanInfoPtr->tapeLabel.structVersion < TAPE_VERSION_4)
598 /* Remember the initial dump and see if appended dump exists */
600 if (!scanInfoPtr->initialDumpId)
601 scanInfoPtr->initialDumpId = scanInfoPtr->dumpEntry.id;
603 c = butm_ReadLabel(tapeInfoPtr, &scanInfoPtr->dumpLabel, 0); /* no rewind */
604 tapepos = tapeInfoPtr->position - 1;
614 getScanTape(taskId, tapeInfoPtr, tname, tapeId, prompt, tapeLabelPtr)
616 struct butm_tapeInfo *tapeInfoPtr;
620 struct butm_tapeLabel *tapeLabelPtr;
625 char tapename[BU_MAXTAPELEN + 32];
626 char gotname[BU_MAXTAPELEN + 32];
629 /* prompt for a tape */
632 PromptForTape(SCANOPCODE, tname, tapeId, taskId, tapecount);
639 code = butm_Mount(tapeInfoPtr, ""); /* open the tape device */
641 TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
645 /* read the label on the tape */
646 code = butm_ReadLabel(tapeInfoPtr, tapeLabelPtr, 1); /* rewind tape */
648 ErrorLog(0, taskId, code, tapeInfoPtr->error,
649 "Can't read tape label\n");
652 tapepos = tapeInfoPtr->position - 1;
654 /* Now check that the tape is good */
655 TAPENAME(tapename, tname, tapeId);
656 TAPENAME(gotname, tapeLabelPtr->AFSName, tapeLabelPtr->dumpid);
658 curseq = extractTapeSeq(tapeLabelPtr->AFSName);
660 /* Label can't be null or a bad name */
661 if (!strcmp(tapeLabelPtr->AFSName, "") || (curseq <= 0)) {
662 TLog(taskId, "Expected tape with dump, label seen %s\n", gotname);
666 /* Label can't be a database tape */
667 if (databaseTape(tapeLabelPtr->AFSName)) {
669 "Expected tape with dump. Can't scan database tape %s\n",
674 /* If no name, accept any tape */
675 if (strcmp(tname, "") == 0) {
676 break; /* Start scan on any tape */
679 break; /* The first tape */
681 TLog(taskId, "Expected first tape of dump, label seen %s\n",
688 if (strcmp(tname, tapeLabelPtr->AFSName)
689 || ((tapeLabelPtr->structVersion >= TAPE_VERSION_3)
690 && (tapeLabelPtr->dumpid != tapeId))) {
691 TLog(taskId, "Tape label expected %s, label seen %s\n", tapename,
696 /* We have the correct tape */
700 unmountTape(taskId, tapeInfoPtr);
708 * This set of code fragments read a tape, and add the information to
709 * the database. Builds a literal structure.
714 struct scanTapeIf *ptr;
716 struct butm_tapeInfo curTapeInfo;
717 struct tapeScanInfo tapeScanInfo;
721 taskId = ptr->taskId;
722 setStatus(taskId, DRIVE_WAIT);
723 EnterDeviceQueue(deviceLatch);
724 clearStatus(taskId, DRIVE_WAIT);
728 TLog(taskId, "ScanTape and add to the database\n");
730 TLog(taskId, "Scantape\n");
732 memset(&tapeScanInfo, 0, sizeof(tapeScanInfo));
733 tapeScanInfo.addDbFlag = ptr->addDbFlag;
735 memset(&curTapeInfo, 0, sizeof(curTapeInfo));
736 curTapeInfo.structVersion = BUTM_MAJORVERSION;
737 code = butm_file_Instantiate(&curTapeInfo, &globalTapeConfig);
739 ErrorLog(0, taskId, code, curTapeInfo.error,
740 "Can't initialize tape module\n");
745 getScanTape(taskId, &curTapeInfo, "", 0, autoQuery,
746 &tapeScanInfo.tapeLabel);
750 code = readDumps(taskId, &curTapeInfo, &tapeScanInfo);
755 unmountTape(taskId, &curTapeInfo);
758 if (code == TC_ABORTEDBYREQUEST) {
759 ErrorLog(0, taskId, 0, 0, "Scantape: Aborted by request\n");
760 clearStatus(taskId, ABORT_REQUEST);
761 setStatus(taskId, ABORT_DONE);
763 ErrorLog(0, taskId, code, 0, "Scantape: Finished with errors\n");
764 setStatus(taskId, TASK_ERROR);
766 TLog(taskId, "Scantape: Finished\n");
770 setStatus(taskId, TASK_DONE);
771 LeaveDeviceQueue(deviceLatch);
781 validatePath(labelptr, pathptr)
782 struct butm_tapeLabel *labelptr;
786 char tapeName[BU_MAXTAPELEN];
789 if (strlen(pathptr) > BU_MAX_DUMP_PATH - 1) {
790 fprintf(stderr, "Invalid pathname - too long\n");
797 strcpy(tapeName, labelptr->AFSName);
799 tp = strrchr(tapeName, '.');
804 up = strrchr(pathptr, '/');
806 fprintf(stderr, "Invalid path name, missing /\n");
811 if (strcmp(up, tp) != 0) {
812 fprintf(stderr, "Invalid path name\n");
814 "Mismatch between tape dump name '%s' and users dump name '%s'\n",
822 * return a pointer to a (static) volume set name string.
824 * ptr - ptr to a dump name
826 * 0 - error. Can't extract volumeset name.
827 * ptr - to static volumeset string.
831 volumesetNamePtr(ptr)
834 static char vsname[BU_MAXUNAMELEN];
838 dotPtr = strchr(ptr, '.');
842 dotIndex = dotPtr - ptr;
843 if ((dotIndex + 1) > sizeof(vsname))
844 return (0); /* name too long */
846 strncpy(&vsname[0], ptr, dotIndex);
847 vsname[dotIndex] = 0; /* ensure null terminated */
856 static char dname[BU_MAXTAPELEN];
860 dotPtr = strrchr(ptr, '.');
864 dotIndex = dotPtr - ptr;
865 if ((dotIndex + 1) > sizeof(dname))
866 return (0); /* name too long */
868 strncpy(&dname[0], ptr, dotIndex);
869 dname[dotIndex] = 0; /* ensure null terminated */
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
879 * tapename - ptr to tape name
881 * 0 or positive - sequence number
882 * -1 - error, couldn't extract sequence number
885 extractTapeSeq(tapename)
890 sptr = strrchr(tapename, '.');
898 * returns true or false depending on whether the tape is
899 * a database tape or not.
902 databaseTape(tapeName)
908 sptr = strrchr(tapeName, '.');
912 c = (int)((char *) sptr - (char *) tapeName);
913 if (strncmp(tapeName, DUMP_TAPE_NAME, c) == 0)
920 RcreateDump(tapeScanInfoPtr, volHeaderPtr)
921 struct tapeScanInfo *tapeScanInfoPtr;
922 struct volumeHeader *volHeaderPtr;
925 struct butm_tapeLabel *dumpLabelPtr = &tapeScanInfoPtr->dumpLabel;
926 struct budb_dumpEntry *dumpEntryPtr = &tapeScanInfoPtr->dumpEntry;
928 /* construct dump entry */
929 memset(dumpEntryPtr, 0, sizeof(struct budb_dumpEntry));
930 dumpEntryPtr->id = volHeaderPtr->dumpID;
931 dumpEntryPtr->initialDumpID = tapeScanInfoPtr->initialDumpId;
932 dumpEntryPtr->parent = volHeaderPtr->parentID;
933 dumpEntryPtr->level = volHeaderPtr->level;
934 dumpEntryPtr->created = volHeaderPtr->dumpID; /* time dump was created */
935 dumpEntryPtr->flags = 0;
936 dumpEntryPtr->incTime = 0;
937 dumpEntryPtr->nVolumes = 0;
938 strcpy(dumpEntryPtr->volumeSetName,
939 volumesetNamePtr(volHeaderPtr->dumpSetName));
940 strcpy(dumpEntryPtr->dumpPath, dumpLabelPtr->dumpPath);
941 strcpy(dumpEntryPtr->name, volHeaderPtr->dumpSetName);
942 default_tapeset(&dumpEntryPtr->tapes, volHeaderPtr->dumpSetName);
943 dumpEntryPtr->tapes.b = extractTapeSeq(dumpLabelPtr->AFSName);
944 copy_ktcPrincipal_to_budbPrincipal(&dumpLabelPtr->creator,
945 &dumpEntryPtr->dumper);
947 code = bcdb_CreateDump(dumpEntryPtr);