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