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>
20 #include <afs/afsutil.h>
21 #include <afs/tcdata.h>
22 #include <afs/bubasics.h>
24 #include <afs/budb_client.h>
25 #include <afs/budb_prototypes.h>
26 #include <afs/butm_prototypes.h>
27 #include <afs/bucoord_prototypes.h>
29 #include "error_macros.h"
30 #include "butc_internal.h"
32 #define BELLCHAR 7 /* ascii for bell */
34 /* GLOBAL CONFIGURATION PARAMETERS */
36 extern int queryoperator;
38 /* Handle for the information read from all the tapes of a dump */
39 afs_int32 tapepos; /* when read a label, remember its position */
41 struct butm_tapeLabel tapeLabel, dumpLabel;
42 struct budb_dumpEntry dumpEntry;
43 afs_int32 initialDumpId;
47 extern struct tapeConfig globalTapeConfig;
48 extern struct deviceSyncNode *deviceLatch;
50 static int readDump(afs_uint32, struct butm_tapeInfo *,
51 struct tapeScanInfo *);
52 afs_int32 getScanTape(afs_int32, struct butm_tapeInfo *, char *,
53 afs_int32, int prompt, struct butm_tapeLabel *);
54 afs_int32 RcreateDump(struct tapeScanInfo *, struct volumeHeader *);
55 void copy_ktcPrincipal_to_budbPrincipal(struct ktc_principal *,
56 struct budb_principal *);
59 * print out the tape (dump) label.
62 PrintDumpLabel(struct butm_tapeLabel *labelptr)
64 char tapeName[BU_MAXTAPELEN + 32];
67 printf("Dump label\n");
68 printf("----------\n");
69 TAPENAME(tapeName, labelptr->pName, labelptr->dumpid);
70 printf("permanent tape name = %s\n", tapeName);
71 TAPENAME(tapeName, labelptr->AFSName, labelptr->dumpid);
72 printf("AFS tape name = %s\n", tapeName);
73 t = labelptr->creationTime;
74 printf("creationTime = %s", ctime(&t));
75 if (labelptr->expirationDate) {
76 t = labelptr->expirationDate;
77 printf("expirationDate = %s", cTIME(&t));
79 printf("cell = %s\n", labelptr->cell);
80 printf("size = %u Kbytes\n", labelptr->size);
81 printf("dump path = %s\n", labelptr->dumpPath);
83 if (labelptr->structVersion >= TAPE_VERSION_3) {
84 printf("dump id = %u\n", labelptr->dumpid);
85 printf("useCount = %d\n", labelptr->useCount);
87 printf("-- End of dump label --\n\n");
91 * print the contents of a volume header.
94 PrintVolumeHeader(struct volumeHeader *volHeader)
98 printf("-- volume --\n");
99 printf("volume name: %s\n", volHeader->volumeName);
100 printf("volume ID %d\n", volHeader->volumeID);
101 /* printf("server %d\n", volHeader->server); */
102 printf("dumpSetName: %s\n", volHeader->dumpSetName);
103 printf("dumpID %d\n", volHeader->dumpID);
104 printf("level %d\n", volHeader->level);
105 printf("parentID %d\n", volHeader->parentID);
106 printf("endTime %d\n", volHeader->endTime);
107 /* printf("versionflags %d\n", volHeader->versionflags); */
108 t = volHeader->cloneDate;
109 printf("clonedate %s\n", ctime(&t));
113 * ask a question. returns true or false
127 printf("%s? (y/n) ", st);
129 response = getchar();
132 else if (response == 'n' || response == EOF)
134 printf("please answer y/n\n");
139 * Skips the volume data on the tape. The end of the volume data is
140 * detected by the presence of the volume trailer or by an EOF indication
141 * from butm. This algorithm should be replaced by one that always
142 * detects based on the volume trailer, returning the trailer to the
143 * caller. This is of course more painful.
145 * curTapePtr - tape info structure
146 * Tape must be positioned after volume header
149 * 3 - empty volume, requires special handling
151 * Tape positioned after data, but before file end marker block.
152 * In case of errors, positioned after the error causing block
154 #define BIGCHUNK 102400
157 scanVolData(afs_int32 taskId, struct butm_tapeInfo *curTapePtr,
158 afs_int32 tapeVersion, struct volumeHeader *volumeHeader,
159 struct volumeHeader *volumeTrailer, afs_uint32 *bytesRead)
161 afs_int32 headBytes, tailBytes;
164 int hasdata[2], curr, prev;
165 afs_uint32 chunkSize = 0;
168 afs_int32 rcode, tcode;
170 memset(volumeHeader, 0, sizeof(struct volumeHeader));
172 block = malloc(2 * BUTM_BLOCKSIZE);
174 return (TC_NOMEMORY);
175 buffer[0] = &block[sizeof(struct blockMark)];
176 buffer[1] = &block[BUTM_BLOCKSIZE + sizeof(struct blockMark)];
177 hasdata[0] = hasdata[1] = 0;
180 tcode = NextFile(curTapePtr); /* guarantees we are at a filemark */
184 /* Read the FileBegin FileMark */
185 code = butm_ReadFileBegin(curTapePtr);
188 * Tapes made with 3.0 have no software EOT markers. Therefore
189 * at this point, we will most likely get a read error, indicating
190 * the end of this dump
192 if ((tapeVersion == TAPE_VERSION_0)
193 || (tapeVersion == TAPE_VERSION_1)) {
195 * then a tape error is possible at this point, and it
196 * signals the end of the dump. Tapes that are continued
197 * have an EOT marker.
199 TapeLog(0, taskId, code, curTapePtr->error,
200 "Read error - end-of-dump inferred\n");
204 if (code != BUTM_EOD)
205 ErrorLog(0, taskId, code, curTapePtr->error,
206 "Can't read FileBegin on tape\n");
210 /* now read the volume header */
211 code = ReadVolHeader(taskId, curTapePtr, volumeHeader);
218 /* Check for abort in the middle of scanning data */
219 if (*bytesRead >= chunkSize) {
220 if (checkAbortByTaskId(taskId))
221 ERROR_EXIT(TC_ABORTEDBYREQUEST);
222 chunkSize += BIGCHUNK;
226 * Read volume date - If prematurely hit the HW EOF
227 * marker, check to see if data contains a volumetrailer.
230 butm_ReadFileData(curTapePtr, buffer[curr], BUTM_BLKSIZE,
234 if ((rcode == BUTM_EOF) || (rcode == BUTM_ENDVOLUME))
237 ErrorLog(0, taskId, rcode, curTapePtr->error,
238 "Can't read FileData on tape\n");
242 *bytesRead += nbytes;
244 if ((nbytes != BUTM_BLKSIZE)
246 (FindVolTrailer(buffer[curr], nbytes, &tailBytes, volumeTrailer)))
249 curr = ((curr == 0) ? 1 : 0); /* Switch buffers */
252 /* Now verify that there is a volume trailer and its valid and copy it */
253 prev = ((curr == 0) ? 1 : 0);
255 (buffer[prev], (hasdata[prev] ? BUTM_BLKSIZE : 0), &headBytes,
256 buffer[curr], nbytes, &tailBytes, volumeTrailer)) {
257 code = TC_MISSINGTRAILER;
258 ErrorLog(0, taskId, code, 0, "Missing volume trailer on tape\n");
260 /* subtract size of the volume trailer from data read */
261 *bytesRead -= sizeof(struct volumeHeader);
265 * If we didn't hit the EOF while reading data, read FileEnd marker
269 tcode = butm_ReadFileEnd(curTapePtr);
271 ErrorLog(0, taskId, tcode, curTapePtr->error,
272 "Can't read EOF on tape\n");
284 * generate the name of the next tape label expected
286 * ptr to static string
290 nextTapeLabel(char *prevTapeName)
295 static char buffer[BU_MAXTAPELEN];
299 /* extract information from previous tape label */
300 strcpy(buffer, prevTapeName);
301 prevdot = strrchr(buffer, '.');
306 seq = extractTapeSeq(prevTapeName);
308 sprintf(prevdot, "%-d", seq);
314 * Read all the information on a tape. If to add to the database, queue
315 * onto list so another thread adds the entries to the database.
317 * taskid - butc task number.
318 * tapeInfoPtr - Tape information.
319 * scanInfoPtr - Info to keep so we can add entries to the db.
322 * non-0 - error. Abort the scan.
323 * moreTapes set to 1 if this is not the last tape in the dump,
324 * 0 if the last tape,
325 * -1 don't know if last tape or not.
329 readDump(afs_uint32 taskId, struct butm_tapeInfo *tapeInfoPtr,
330 struct tapeScanInfo *scanInfoPtr)
333 afs_int32 flags, seq;
334 afs_uint32 nbytes = 0;
335 int newDump = 1, newTape = 1;
336 afs_int32 tapePosition;
337 afs_int32 code = 0, tcode;
339 struct volumeHeader volHeader, volTrailer;
340 struct budb_tapeEntry tapeEntry;
341 struct budb_volumeEntry volEntry;
344 PrintDumpLabel(&scanInfoPtr->dumpLabel);
346 while (moreTapes) { /* While there is a tape to read *//*t */
348 while (1) { /* Read each volume on the tape *//*w */
350 tapePosition = tapeInfoPtr->position; /* remember position */
353 * Skip the volume data
356 scanVolData(taskId, tapeInfoPtr,
357 scanInfoPtr->tapeLabel.structVersion, &volHeader,
358 &volTrailer, &nbytes);
362 if (tcode == TC_ABORTEDBYREQUEST) { /* Aborted */
366 if (tcode == BUTM_EOD) {
367 moreTapes = 0; /* the end of the dump */
371 /* Found a volume but it's incomplete. Skip over these */
372 if (volHeader.volumeID) {
373 TapeLog(0, taskId, tcode, 0,
374 "Warning: volume %s (%u) ignored. Incomplete\n",
375 volHeader.volumeName, volHeader.volumeID);
379 /* No volume was found. We may have hit the EOT or a
380 * bad-spot. Try to skip over this spot.
382 if (badscan < 2) { /* allow 2 errors, then fail */
383 TapeLog(0, taskId, tcode, 0,
384 "Warning: Error in scanning tape - will try skipping volume\n");
387 if (scanInfoPtr->tapeLabel.structVersion >= TAPE_VERSION_4) {
388 TapeLog(0, taskId, tcode, 0,
389 "Warning: Error in scanning tape - end-of-tape inferred\n");
390 moreTapes = 1; /* then assume next tape */
392 ErrorLog(0, taskId, tcode, 0, "Error in scanning tape\n");
393 /* will ask if there is a next tape */
398 PrintVolumeHeader(&volHeader);
400 /* If this is not the first volume fragment, make sure it follows
401 * the last volume fragment
404 if ((volEntry.dump != volHeader.dumpID)
405 || (volEntry.id != volHeader.volumeID)
406 || (volEntry.seq != volHeader.frag - 2)
407 || (strcmp(volEntry.name, volHeader.volumeName))) {
409 "Warning: volume %s (%u) ignored. Incomplete - no last fragment\n",
410 volEntry.name, volEntry.id);
412 if (scanInfoPtr->addDbFlag) {
413 tcode = flushSavedEntries(DUMP_FAILED);
421 /* If this is the first volume fragment, make sure says so */
422 if (scanInfoPtr->addDbFlag && !volEntry.dump
423 && (volHeader.frag != 1)) {
425 "Warning: volume %s (%u) ignored. Incomplete - no first fragment\n",
426 volHeader.volumeName, volHeader.volumeID);
429 /* Check that this volume belongs to the dump we are scanning */
430 else if (scanInfoPtr->dumpLabel.dumpid
431 && (volHeader.dumpID != scanInfoPtr->dumpLabel.dumpid)) {
433 "Warning: volume %s (%u) ignored. Expected DumpId %u, got %u\n",
434 volHeader.volumeName, volHeader.volumeID,
435 scanInfoPtr->dumpLabel.dumpid, volHeader.dumpID);
438 /* Passed tests, Now add to the database (if dbadd flag is set) */
439 else if (scanInfoPtr->addDbFlag) {
440 /* Have enough information to create a dump entry */
442 tcode = RcreateDump(scanInfoPtr, &volHeader);
444 ErrorLog(0, taskId, tcode, 0,
445 "Can't add dump %u to database\n",
452 /* Have enough information to create a tape entry */
454 seq = extractTapeSeq(scanInfoPtr->tapeLabel.AFSName);
456 ERROR_EXIT(TC_INTERNALERROR);
459 useTape(&tapeEntry, volHeader.dumpID,
460 TNAME(&scanInfoPtr->tapeLabel), seq,
461 scanInfoPtr->tapeLabel.useCount,
462 scanInfoPtr->dumpLabel.creationTime,
463 scanInfoPtr->dumpLabel.expirationDate,
466 char gotName[BU_MAXTAPELEN + 32];
468 LABELNAME(gotName, &scanInfoPtr->tapeLabel);
469 ErrorLog(0, taskId, tcode, 0,
470 "Can't add tape %s for dump %u to database\n",
471 gotName, volHeader.dumpID);
477 /* Create the volume entry */
478 flags = ((volHeader.frag == 1) ? BUDB_VOL_FIRSTFRAG : 0);
479 if (!volTrailer.contd)
480 flags |= BUDB_VOL_LASTFRAG;
482 addVolume(&volEntry, volHeader.dumpID,
483 TNAME(&scanInfoPtr->tapeLabel),
484 volHeader.volumeName, volHeader.volumeID,
485 volHeader.cloneDate, tapePosition, nbytes,
486 (volHeader.frag - 1), flags);
488 ErrorLog(0, taskId, tcode, 0,
489 "Can't add volume %s (%u) for dump %u to database\n",
490 volHeader.volumeName, volHeader.volumeID,
496 if (volTrailer.contd) {
497 /* No need to read the EOD marker, we know there is a next tape */
501 if (scanInfoPtr->addDbFlag) {
502 tcode = flushSavedEntries(DUMP_SUCCESS);
511 if (scanInfoPtr->addDbFlag) {
513 finishTape(&tapeEntry,
514 (tapeInfoPtr->kBytes +
515 (tapeInfoPtr->nBytes ? 1 : 0)));
517 char gotName[BU_MAXTAPELEN + 32];
519 LABELNAME(gotName, &scanInfoPtr->tapeLabel);
520 ErrorLog(0, taskId, tcode, 0,
521 "Can't mark tape %s 'completed' for dump %u in database\n",
522 gotName, tapeEntry.dump);
528 /* Ask if there is another tape if we can't figure it out */
530 moreTapes = (queryoperator ? Ask("Are there more tapes") : 1);
532 /* Get the next tape label */
537 unmountTape(taskId, tapeInfoPtr);
539 tapeName = nextTapeLabel(scanInfoPtr->tapeLabel.AFSName);
540 dumpid = scanInfoPtr->tapeLabel.dumpid;
542 getScanTape(taskId, tapeInfoPtr, tapeName, dumpid, 1,
543 &scanInfoPtr->tapeLabel);
551 if (scanInfoPtr->addDbFlag) {
552 tcode = finishDump(&scanInfoPtr->dumpEntry);
554 ErrorLog(0, taskId, tcode, 0,
555 "Can't mark dump %u 'completed' in database\n",
556 scanInfoPtr->dumpEntry.id);
559 tcode = flushSavedEntries(DUMP_SUCCESS);
569 /* Will read a dump, then see if there is a dump following it and
570 * try to read that dump too.
571 * The first tape label is the first dumpLabel.
574 readDumps(afs_uint32 taskId, struct butm_tapeInfo *tapeInfoPtr,
575 struct tapeScanInfo *scanInfoPtr)
579 memcpy(&scanInfoPtr->dumpLabel, &scanInfoPtr->tapeLabel,
580 sizeof(struct butm_tapeLabel));
583 code = readDump(taskId, tapeInfoPtr, scanInfoPtr);
587 if (scanInfoPtr->tapeLabel.structVersion < TAPE_VERSION_4)
590 /* Remember the initial dump and see if appended dump exists */
592 if (!scanInfoPtr->initialDumpId)
593 scanInfoPtr->initialDumpId = scanInfoPtr->dumpEntry.id;
595 c = butm_ReadLabel(tapeInfoPtr, &scanInfoPtr->dumpLabel, 0); /* no rewind */
596 tapepos = tapeInfoPtr->position - 1;
606 getScanTape(afs_int32 taskId, struct butm_tapeInfo *tapeInfoPtr, char *tname,
607 afs_int32 tapeId, int prompt, struct butm_tapeLabel *tapeLabelPtr)
612 char tapename[BU_MAXTAPELEN + 32];
613 char gotname[BU_MAXTAPELEN + 32];
616 /* prompt for a tape */
619 PromptForTape(SCANOPCODE, tname, tapeId, taskId, tapecount);
626 code = butm_Mount(tapeInfoPtr, ""); /* open the tape device */
628 TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
632 /* read the label on the tape */
633 code = butm_ReadLabel(tapeInfoPtr, tapeLabelPtr, 1); /* rewind tape */
635 ErrorLog(0, taskId, code, tapeInfoPtr->error,
636 "Can't read tape label\n");
639 tapepos = tapeInfoPtr->position - 1;
641 /* Now check that the tape is good */
642 TAPENAME(tapename, tname, tapeId);
643 TAPENAME(gotname, tapeLabelPtr->AFSName, tapeLabelPtr->dumpid);
645 curseq = extractTapeSeq(tapeLabelPtr->AFSName);
647 /* Label can't be null or a bad name */
648 if (!strcmp(tapeLabelPtr->AFSName, "") || (curseq <= 0)) {
649 TLog(taskId, "Expected tape with dump, label seen %s\n", gotname);
653 /* Label can't be a database tape */
654 if (databaseTape(tapeLabelPtr->AFSName)) {
656 "Expected tape with dump. Can't scan database tape %s\n",
661 /* If no name, accept any tape */
662 if (strcmp(tname, "") == 0) {
663 break; /* Start scan on any tape */
666 if (strcmp(tname, tapeLabelPtr->AFSName)
667 || ((tapeLabelPtr->structVersion >= TAPE_VERSION_3)
668 && (tapeLabelPtr->dumpid != tapeId))) {
669 TLog(taskId, "Tape label expected %s, label seen %s\n", tapename,
674 /* We have the correct tape */
678 unmountTape(taskId, tapeInfoPtr);
686 * This set of code fragments read a tape, and add the information to
687 * the database. Builds a literal structure.
692 ScanDumps(void *param)
694 struct scanTapeIf *ptr = (struct scanTapeIf *)param;
696 struct butm_tapeInfo curTapeInfo;
697 struct tapeScanInfo tapeScanInfo;
701 opr_threadname_set("scandump");
702 taskId = ptr->taskId;
703 setStatus(taskId, DRIVE_WAIT);
704 EnterDeviceQueue(deviceLatch);
705 clearStatus(taskId, DRIVE_WAIT);
709 TLog(taskId, "ScanTape and add to the database\n");
711 TLog(taskId, "Scantape\n");
713 memset(&tapeScanInfo, 0, sizeof(tapeScanInfo));
714 tapeScanInfo.addDbFlag = ptr->addDbFlag;
716 memset(&curTapeInfo, 0, sizeof(curTapeInfo));
717 curTapeInfo.structVersion = BUTM_MAJORVERSION;
718 code = butm_file_Instantiate(&curTapeInfo, &globalTapeConfig);
720 ErrorLog(0, taskId, code, curTapeInfo.error,
721 "Can't initialize tape module\n");
726 getScanTape(taskId, &curTapeInfo, "", 0, autoQuery,
727 &tapeScanInfo.tapeLabel);
731 code = readDumps(taskId, &curTapeInfo, &tapeScanInfo);
736 unmountTape(taskId, &curTapeInfo);
739 if (code == TC_ABORTEDBYREQUEST) {
740 ErrorLog(0, taskId, 0, 0, "Scantape: Aborted by request\n");
741 clearStatus(taskId, ABORT_REQUEST);
742 setStatus(taskId, ABORT_DONE);
744 ErrorLog(0, taskId, code, 0, "Scantape: Finished with errors\n");
745 setStatus(taskId, TASK_ERROR);
747 TLog(taskId, "Scantape: Finished\n");
751 setStatus(taskId, TASK_DONE);
752 LeaveDeviceQueue(deviceLatch);
753 return (void *)(intptr_t)(code);
763 validatePath(struct butm_tapeLabel *labelptr, char *pathptr)
766 char tapeName[BU_MAXTAPELEN];
769 if (strlen(pathptr) > BU_MAX_DUMP_PATH - 1) {
770 fprintf(stderr, "Invalid pathname - too long\n");
777 strcpy(tapeName, labelptr->AFSName);
779 tp = strrchr(tapeName, '.');
784 up = strrchr(pathptr, '/');
786 fprintf(stderr, "Invalid path name, missing /\n");
791 if (strcmp(up, tp) != 0) {
792 fprintf(stderr, "Invalid path name\n");
794 "Mismatch between tape dump name '%s' and users dump name '%s'\n",
802 * return a pointer to a (static) volume set name string.
804 * ptr - ptr to a dump name
806 * 0 - error. Can't extract volumeset name.
807 * ptr - to static volumeset string.
811 volumesetNamePtr(char *ptr)
813 static char vsname[BU_MAXUNAMELEN];
817 dotPtr = strchr(ptr, '.');
821 dotIndex = dotPtr - ptr;
822 if ((dotIndex + 1) > sizeof(vsname))
823 return (0); /* name too long */
825 strncpy(&vsname[0], ptr, dotIndex);
826 vsname[dotIndex] = 0; /* ensure null terminated */
832 extractDumpName(char *ptr)
834 static char dname[BU_MAXTAPELEN];
838 dotPtr = strrchr(ptr, '.');
842 dotIndex = dotPtr - ptr;
843 if ((dotIndex + 1) > sizeof(dname))
844 return (0); /* name too long */
846 strncpy(&dname[0], ptr, dotIndex);
847 dname[dotIndex] = 0; /* ensure null terminated */
853 * The routine assumes that tape names have an embedded sequence number
854 * as the trialing component. It is suggested that any tape naming
855 * changes retain the trailing seq. number
857 * tapename - ptr to tape name
859 * 0 or positive - sequence number
860 * -1 - error, couldn't extract sequence number
864 extractTapeSeq(char *tapename)
868 sptr = strrchr(tapename, '.');
876 * returns true or false depending on whether the tape is
877 * a database tape or not.
880 databaseTape(char *tapeName)
885 sptr = strrchr(tapeName, '.');
889 c = (int)((char *) sptr - (char *) tapeName);
890 if (strncmp(tapeName, DUMP_TAPE_NAME, c) == 0)
897 RcreateDump(struct tapeScanInfo *tapeScanInfoPtr,
898 struct volumeHeader *volHeaderPtr)
901 const char *volsetName;
902 struct butm_tapeLabel *dumpLabelPtr = &tapeScanInfoPtr->dumpLabel;
903 struct budb_dumpEntry *dumpEntryPtr = &tapeScanInfoPtr->dumpEntry;
905 /* construct dump entry */
906 memset(dumpEntryPtr, 0, sizeof(struct budb_dumpEntry));
907 dumpEntryPtr->id = volHeaderPtr->dumpID;
908 dumpEntryPtr->initialDumpID = tapeScanInfoPtr->initialDumpId;
909 dumpEntryPtr->parent = volHeaderPtr->parentID;
910 dumpEntryPtr->level = volHeaderPtr->level;
911 dumpEntryPtr->created = volHeaderPtr->dumpID; /* time dump was created */
912 dumpEntryPtr->flags = 0;
913 dumpEntryPtr->incTime = 0;
914 dumpEntryPtr->nVolumes = 0;
915 volsetName = volumesetNamePtr(volHeaderPtr->dumpSetName);
916 if (volsetName == NULL)
917 return BUDB_BADARGUMENT;
918 strcpy(dumpEntryPtr->volumeSetName, volsetName);
919 strcpy(dumpEntryPtr->dumpPath, dumpLabelPtr->dumpPath);
920 strcpy(dumpEntryPtr->name, volHeaderPtr->dumpSetName);
921 default_tapeset(&dumpEntryPtr->tapes, volHeaderPtr->dumpSetName);
922 dumpEntryPtr->tapes.b = extractTapeSeq(dumpLabelPtr->AFSName);
923 copy_ktcPrincipal_to_budbPrincipal(&dumpLabelPtr->creator,
924 &dumpEntryPtr->dumper);
926 code = bcdb_CreateDump(dumpEntryPtr);