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>
19 #include <afs/tcdata.h>
20 #include <afs/bubasics.h>
21 #include <afs/budb_client.h>
22 #include <afs/butm_prototypes.h>
23 #include <afs/vldbint.h>
24 #include <afs/ktime.h>
25 #include <afs/vlserver.h>
26 #include <afs/afsint.h>
27 #include <afs/volser.h>
28 #include <afs/volser_prototypes.h>
29 #include <afs/volint.h>
30 #include <afs/cellconfig.h>
31 #include <afs/bucoord_prototypes.h>
33 #include "butc_internal.h"
34 #include "error_macros.h"
35 #include "butc_xbsa.h"
38 /* GLOBAL CONFIGURATION PARAMETERS */
39 extern int dump_namecheck;
40 extern int queryoperator;
42 extern int forcemultiple;
44 extern struct ubik_client *cstruct;
45 dlqlinkT savedEntries;
46 dlqlinkT entries_to_flush;
48 extern afs_int32 groupId;
49 extern afs_int32 BufferSize;
50 extern afs_int32 statusSize;
51 extern FILE *centralLogIO;
52 afs_int32 lastPass = 0;
54 extern afs_int32 xbsaType;
55 char *butcdumpIdStr = "/backup_afs_volume_dumps";
56 extern struct butx_transactionInfo butxInfo;
57 extern char *xbsaObjectOwner;
58 extern char *appObjectOwner;
59 extern char *xbsaSecToken;
60 extern char *xbsalGName;
61 extern char *globalButcLog;
64 afs_int32 dataSize; /* Size of data to read on each rx_Read() call */
65 afs_int32 tapeblocks; /* Number of 16K tape datablocks in buffer (!CONF_XBSA) */
69 * Done 1) dump id generation
70 * Done xx) volume fragment number accounting !! I think.
71 * 2) check abort - check after subroutine calls
72 * Done 3) trailer anomaly
73 * 4) trailer damage indicator after partial dumps ( affects scandump )
74 * Done 5) Ensure mount failure logged
75 * 6) Ensure bucoord status calls work
79 * keep token timeout. If no user reponse (idle time > some period)
80 * and tokens about to time out, terminate dump. This provides at
81 * least something usable.
84 #define DUMPNAME(dumpname, name, dbDumpId) \
86 sprintf(dumpname, "%s", name); \
88 sprintf(dumpname, "%s (DumpId %u)", name, dbDumpId);
93 int curVolume; /* index in dumpNode of volume */
94 int curVolumeStatus; /* more explicit dump state */
95 afs_uint32 curVolStartPos; /* Starting position of the current volume */
96 afs_uint32 databaseDumpId; /* real dump id, for db */
97 afs_uint32 initialDumpId; /* the initial dump, for appended dumps */
98 afs_int32 volumesDumped; /* # volumes successfully dumped */
99 afs_int32 volumesFailed; /* # volumes that failed to dump */
100 afs_int32 volumesNotDumped; /* # volumes that were not dumped (didn't fail) */
102 /* tape management */
103 char tapeName[TC_MAXTAPENAMELEN];
104 struct butm_tapeInfo *tapeInfoPtr;
105 struct butm_tapeLabel tapeLabel;
106 int wroteLabel; /* If the tape label is written */
108 /* database information */
109 struct budb_dumpEntry lastDump; /* the last dump of this volset */
110 struct budb_dumpEntry dump; /* current dump */
111 struct budb_tapeEntry tape; /* current tape, not used -VA */
113 /* links to existing info */
114 struct dumpNode *node;
117 /* Forward declarations */
119 int makeVolumeHeader(struct volumeHeader *, struct dumpRock *, int);
120 int volumeHeader_hton(struct volumeHeader *, struct volumeHeader *);
121 char retryPrompt(char *, afs_int32, afs_uint32);
122 int getDumpTape(struct dumpRock *, int, afs_int32);
123 int getXBSATape(struct dumpRock *);
124 afs_int32 createDump(struct dumpRock *);
126 /* configuration variables */
127 #define HITEOT(code) ((code == BUTM_IO) || (code == BUTM_EOT) || (code == BUTM_IOCTL))
128 extern int autoQuery;
131 afs_int32 tc_EndMargin;
132 afs_int32 tc_KEndMargin;
133 static char *bufferBlock;
135 /* compute the absolute expiration date */
137 calcExpirationDate(afs_int32 expType, afs_int32 expDate, afs_int32 createTime)
139 struct ktime_date kd;
143 /* expiration date is relative to the creation time of the dump.
144 * This is the only case that requires any work
146 Int32To_ktimeRelDate(expDate, &kd);
147 return (Add_RelDate_to_Time(&kd, createTime));
158 afs_uint32 curr_bserver = 0;
159 struct rx_connection *curr_fromconn = (struct rx_connection *)0;
161 struct rx_connection *
162 Bind(afs_uint32 server)
165 if (curr_bserver == server) /* Keep connection if have it */
166 return (curr_fromconn);
168 rx_DestroyConnection(curr_fromconn); /* Otherwise get rid of it */
169 curr_fromconn = (struct rx_connection *)0;
174 curr_fromconn = UV_Bind(server, AFSCONF_VOLUMEPORT); /* Establish new connection */
176 curr_bserver = server;
179 return (curr_fromconn);
183 * 1) save the chunksize or otherwise ensure tape space remaining is
184 * check frequently enough
185 * 2) This is called once. For partial dumps, need to
186 * ensure that the tape device is left in the correct state for
190 #define BIGCHUNK 102400
193 dumpVolume(struct tc_dumpDesc * curDump, struct dumpRock * dparamsPtr)
195 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
196 struct dumpNode *nodePtr = dparamsPtr->node;
197 afs_int32 taskId = nodePtr->taskID;
200 afs_int32 volumeFlags;
201 afs_int32 kRemaining;
202 afs_int32 rc, code = 0;
204 afs_uint32 volBytesRead;
205 afs_uint32 chunkSize;
206 afs_int32 bytesread; /* rx reads */
207 int endofvolume = 0; /* Have we read all volume data */
210 struct volumeHeader hostVolumeHeader;
212 struct rx_call *fromcall = (struct rx_call *)0;
213 struct rx_connection *fromconn;
214 afs_int32 updatedate, fromtid = 0;
215 volEntries volumeInfo;
216 afs_int32 bytesWritten;
217 afs_uint32 statuscount = statusSize, tsize = 0;
219 dparamsPtr->curVolumeStatus = DUMP_NOTHING;
221 fromconn = Bind(htonl(curDump->hostAddr)); /* get connection to the server */
223 /* Determine when the volume was last cloned and updated */
224 volumeInfo.volEntries_val = (volintInfo *) 0;
225 volumeInfo.volEntries_len = 0;
226 rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid,
230 updatedate = volumeInfo.volEntries_val[0].updateDate;
233 RWVOL) ? time(0) : volumeInfo.volEntries_val[0].creationDate);
235 if (curDump->date >= curDump->cloneDate)
236 ERROR_EXIT(0); /* not recloned since last dump */
237 if (curDump->date > updatedate) {
238 dparamsPtr->curVolumeStatus = DUMP_NODUMP; /* not modified since last dump */
242 /* Start the volserver transaction and dump */
243 rc = AFSVolTransCreate(fromconn, curDump->vid, curDump->partition, ITBusy,
247 fromcall = rx_NewCall(fromconn);
249 rc = StartAFSVolDump(fromcall, fromtid, curDump->date);
253 dparamsPtr->curVolumeStatus = DUMP_PARTIAL;
254 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
256 /* buffer is place in bufferBlock to write volume data.
257 * butm_writeFileData() assumes the previous BUTM_HDRSIZE bytes
258 * is available to write the tape block header.
260 buffer = bufferBlock + BUTM_HDRSIZE;
262 /* Dump one volume fragment at a time until we dump the full volume.
263 * A volume with more than 1 fragment means the volume will 'span'
266 for (fragmentNumber = 1; !endofvolume; fragmentNumber++) { /*frag */
267 rc = butm_WriteFileBegin(tapeInfoPtr);
269 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
270 "Can't write FileBegin on tape\n");
273 indump = 1; /* first write to tape */
275 /* Create and Write the volume header */
276 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, fragmentNumber);
277 hostVolumeHeader.contd = ((fragmentNumber == 1) ? 0 : TC_VOLCONTD);
278 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)buffer);
280 rc = butm_WriteFileData(tapeInfoPtr, buffer, 1,
281 sizeof(hostVolumeHeader));
283 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
284 "Can't write VolumeHeader on tape\n");
288 bytesWritten = BUTM_BLOCKSIZE; /* Wrote one tapeblock */
289 tsize += bytesWritten;
291 /* Start reading volume data, rx_Read(), and dumping to the tape
292 * until we've dumped the entire volume (endofvolume == 1). We can
293 * exit this loop early if we find we are close to the end of the
294 * tape; in which case we dump the next fragment on the next tape.
299 while (!endofvolume && !fragmentvolume) { /*w */
300 /* Check for abort in the middle of writing data */
301 if (volBytesRead >= chunkSize) {
302 chunkSize += BIGCHUNK;
303 if (checkAbortByTaskId(taskId))
304 ABORT_EXIT(TC_ABORTEDBYREQUEST);
306 /* set bytes dumped for backup */
308 nodePtr->statusNodePtr->nKBytes = tapeInfoPtr->kBytes;
312 /* Determine how much data to read in upcoming RX_Read() call */
314 /* Check if we are close to the EOT. There should at least be some
315 * data on the tape before it is switched. HACK: we have to split a
316 * volume across tapes because the volume trailer says the dump
317 * continues on the next tape (and not the filemark). This could
318 * result in a volume starting on one tape (no volume data dumped) and
319 * continued on the next tape. It'll work, just requires restore to
320 * switch tapes. This allows many small volumes (<16K) to be dumped.
322 kRemaining = butm_remainingKSpace(tapeInfoPtr);
323 if ((kRemaining < tc_KEndMargin)
325 || (tapeInfoPtr->position > (isafile ? 3 : 2)))) {
330 /* Guess at how much data to read. So we don't write off end of tape */
331 if (kRemaining < (tapeblocks * 16)) {
332 if (kRemaining < 0) {
333 toread = BUTM_BLKSIZE;
335 toread = ((kRemaining / 16) + 1) * BUTM_BLKSIZE;
336 if (toread > dataSize)
342 /* Set aside space for the trailing volume header when using large buffers. */
343 if (XBSAMAXBUFFER < toread + sizeof(hostVolumeHeader)) {
344 toread = XBSAMAXBUFFER - sizeof(hostVolumeHeader);
348 /* Read some volume data. */
349 if (fragmentvolume) {
352 bytesread = rx_Read(fromcall, buffer, toread);
353 volBytesRead += bytesread;
354 if (bytesread != toread) {
355 /* Make sure were at end of volume and not a communication error */
356 rc = rx_Error(fromcall);
363 if (fragmentvolume || endofvolume) {
364 /* Create a volume trailer appending it to this data block */
365 makeVolumeHeader(&hostVolumeHeader, dparamsPtr,
367 hostVolumeHeader.contd = (endofvolume ? 0 : TC_VOLCONTD);
368 hostVolumeHeader.magic = TC_VOLENDMAGIC;
369 hostVolumeHeader.endTime = (endofvolume ? time(0) : 0);
370 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)&buffer[bytesread]);
371 bytesread += sizeof(hostVolumeHeader);
374 /* Write the datablock out */
375 /* full data buffer - write it to tape */
376 rc = butm_WriteFileData(tapeInfoPtr, buffer, tapeblocks,
379 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
380 "Can't write VolumeData on tape\n");
383 bytesWritten = tapeblocks * BUTM_BLOCKSIZE;
384 tsize += bytesWritten;
386 /* Display a status line every statusSize or at end of volume */
388 && ((tsize >= statuscount) || endofvolume
389 || fragmentvolume)) {
392 localtime_r(&t, &tm);
393 printf("%02d:%02d:%02d: Task %u: %u KB: %s: %u B\n",
394 tm.tm_hour, tm.tm_min, tm.tm_sec, taskId,
395 tapeInfoPtr->kBytes, hostVolumeHeader.volumeName,
397 statuscount = tsize + statusSize;
401 /* End the dump before recording it in BUDB as successfully dumped */
402 rc = butm_WriteFileEnd(tapeInfoPtr);
405 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
406 "Can't write FileEnd on tape\n");
410 /* Record in BUDB the volume fragment as succcessfully dumped */
411 volumeFlags = ((fragmentNumber == 1) ? BUDB_VOL_FIRSTFRAG : 0);
413 volumeFlags |= BUDB_VOL_LASTFRAG;
414 rc = addVolume(0, dparamsPtr->databaseDumpId, dparamsPtr->tapeName,
415 nodePtr->dumps[dparamsPtr->curVolume].name,
416 nodePtr->dumps[dparamsPtr->curVolume].vid,
417 nodePtr->dumps[dparamsPtr->curVolume].cloneDate,
418 dparamsPtr->curVolStartPos, volBytesRead,
419 (fragmentNumber - 1), volumeFlags);
423 /* If haven't finished dumping the volume, end this
424 * tape and get the next tape.
427 /* Write an EOT marker.
428 * Log the error but ignore it since the dump is effectively done.
429 * Scantape will detect continued volume and not read the EOT.
431 rc = butm_WriteEOT(tapeInfoPtr);
433 TapeLog(1, taskId, rc, tapeInfoPtr->error,
434 "Warning: Can't write End-Of-Dump on tape\n");
436 /* Unmount the tape */
437 unmountTape(taskId, tapeInfoPtr);
439 /* Tell the database the tape is complete (and ok) */
440 rc = finishTape(&dparamsPtr->tape,
441 dparamsPtr->tapeInfoPtr->kBytes +
442 (dparamsPtr->tapeInfoPtr->nBytes ? 1 : 0));
446 /* get the next tape. Prompt, mount, and add it into the database */
447 dparamsPtr->tapeSeq++;
448 rc = getDumpTape(dparamsPtr, 1, 0); /* interactive - no append */
452 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
456 dparamsPtr->curVolumeStatus = DUMP_SUCCESS;
460 * If we hit the end, see if this is the first volume on the tape or not.
461 * Also, mark the tape as finished if the tape contains other dumps.
466 ErrorLog(2, taskId, code, tapeInfoPtr->error,
467 "Warning: Dump (%s) hit end-of-tape inferred\n",
468 nodePtr->dumpSetName);
470 if (tapeInfoPtr->position == 2) {
471 dparamsPtr->curVolumeStatus = DUMP_NORETRYEOT;
473 dparamsPtr->curVolumeStatus = DUMP_RETRY;
474 rc = finishTape(&dparamsPtr->tape,
475 dparamsPtr->tapeInfoPtr->kBytes +
476 (dparamsPtr->tapeInfoPtr->nBytes ? 1 : 0));
483 * This is used when an error occurs part way into a volume dump. Clean
484 * the tape state by writing an FileEnd mark. Forgo this action if we hit
488 rc = butm_WriteFileEnd(tapeInfoPtr);
491 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
492 "Can't write FileEnd on tape\n");
497 rc = rx_EndCall(fromcall, 0);
504 rc = AFSVolEndTrans(fromconn, fromtid, &rcode);
506 code = (rc ? rc : rcode);
512 dparamsPtr->curVolumeStatus = DUMP_FAILED;
517 xbsaDumpVolume(struct tc_dumpDesc * curDump, struct dumpRock * dparamsPtr)
520 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
521 struct dumpNode *nodePtr = dparamsPtr->node;
522 char *buffer = bufferBlock;
523 afs_int32 taskId = nodePtr->taskID;
524 afs_int32 rc, code = 0;
526 afs_uint32 volBytesRead;
527 afs_uint32 chunkSize;
528 afs_int32 bytesread; /* rx reads */
529 int endofvolume = 0; /* Have we read all volume data */
530 int begindump = 0, indump = 0; /* if dump transaction started; if dumping data */
531 struct volumeHeader hostVolumeHeader;
533 struct rx_call *fromcall = (struct rx_call *)0;
534 struct rx_connection *fromconn;
535 afs_int32 updatedate, fromtid = 0;
536 volEntries volumeInfo;
537 afs_int32 bytesWritten;
538 afs_uint32 statuscount = statusSize, tsize = 0, esize;
541 char dumpIdStr[XBSA_MAX_OSNAME];
542 char volumeNameStr[XBSA_MAX_PATHNAME];
543 static char *dumpDescription = "AFS volume dump";
544 static char *objectDescription = "XBSA - butc";
546 dparamsPtr->curVolumeStatus = DUMP_NOTHING;
548 fromconn = Bind(htonl(curDump->hostAddr)); /* get connection to the server */
550 /* Determine when the volume was last cloned and updated */
551 volumeInfo.volEntries_val = (volintInfo *) 0;
552 volumeInfo.volEntries_len = 0;
553 rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid,
557 updatedate = volumeInfo.volEntries_val[0].updateDate;
560 RWVOL) ? time(0) : volumeInfo.volEntries_val[0].creationDate);
562 /* Get the volume size (in KB) and increase by 25%. Then set as a hyper */
563 esize = volumeInfo.volEntries_val[0].size;
564 esize += (esize / 4) + 1;
566 if (curDump->date >= curDump->cloneDate)
567 ERROR_EXIT(0); /* not recloned since last dump */
568 if (curDump->date > updatedate) {
569 dparamsPtr->curVolumeStatus = DUMP_NODUMP; /* not modified since last dump */
573 /* Start a new XBSA Transaction */
574 rc = xbsa_BeginTrans(&butxInfo);
575 if (rc != XBSA_SUCCESS) {
576 ErrorLog(1, taskId, rc, 0, "Unable to create a new transaction\n");
579 begindump = 1; /* Will need to do an xbsa_EndTrans */
581 /* Start the volserver transaction and dump. Once started, the
582 * volume status is "partial dump". Also, the transaction with
583 * the volserver is idle until the first read. An idle transaction
584 * will time out in 600 seconds. After the first rx_Read,
585 * the transaction is not idle. See GCTrans().
587 rc = AFSVolTransCreate(fromconn, curDump->vid, curDump->partition, ITBusy,
591 fromcall = rx_NewCall(fromconn);
593 rc = StartAFSVolDump(fromcall, fromtid, curDump->date);
597 dparamsPtr->curVolumeStatus = DUMP_PARTIAL;
598 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
600 /* Tell XBSA what the name and size of volume to write */
601 strcpy(dumpIdStr, butcdumpIdStr); /* "backup_afs_volume_dumps" */
602 sprintf(volumeNameStr, "/%d", dparamsPtr->databaseDumpId);
603 strcat(volumeNameStr, "/");
604 strcat(volumeNameStr, curDump->name); /* <dumpid>/<volname> */
605 hset32(estSize, esize);
606 hshlft(estSize, 10); /* Multiply by 1024 so its in KB */
608 rc = xbsa_WriteObjectBegin(&butxInfo, dumpIdStr, volumeNameStr,
609 xbsalGName, estSize, dumpDescription,
611 if (rc != XBSA_SUCCESS) {
612 ErrorLog(1, taskId, rc, 0,
613 "Unable to begin writing of the fileset data to the server\n");
616 indump = 1; /* Will need to do an xbsa_WriteObjectEnd */
618 /* Create and Write the volume header */
619 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, 1);
620 hostVolumeHeader.contd = 0;
621 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)buffer);
623 rc = xbsa_WriteObjectData(&butxInfo, buffer,
624 sizeof(struct volumeHeader), &bytesWritten);
625 if (rc != XBSA_SUCCESS) {
626 ErrorLog(1, taskId, rc, 0,
627 "Unable to write VolumeHeader data to the server\n");
630 /* There is a bug in the ADSM library where the bytesWritten is
631 * not filled in, so we set it as correct anyway.
633 bytesWritten = sizeof(struct volumeHeader);
634 if (bytesWritten != sizeof(struct volumeHeader)) {
635 ErrorLog(1, taskId, rc, 0,
636 "The size of VolumeHeader written (%d) does not equal its actual size (%" AFS_SIZET_FMT ")\n",
637 bytesWritten, sizeof(struct volumeHeader));
638 ERROR_EXIT(TC_INTERNALERROR);
641 incSize(tapeInfoPtr, sizeof(struct volumeHeader)); /* Increment amount we've written */
642 tsize += bytesWritten;
644 /* Start reading volume data, rx_Read(), and dumping to the tape
645 * until we've dumped the entire volume (endofvolume == 1).
649 while (!endofvolume) { /*w */
650 /* Check for abort in the middle of writing data */
651 if (volBytesRead >= chunkSize) {
652 chunkSize += BIGCHUNK;
653 if (checkAbortByTaskId(taskId))
654 ABORT_EXIT(TC_ABORTEDBYREQUEST);
656 /* set bytes dumped for backup */
658 nodePtr->statusNodePtr->nKBytes = tapeInfoPtr->kBytes;
662 /* Determine how much data to read in upcoming RX_Read() call */
665 /* Read some volume data. */
666 bytesread = rx_Read(fromcall, buffer, toread);
667 volBytesRead += bytesread;
668 if (bytesread != toread) {
671 /* Make sure were at end of volume and not a communication error */
672 rc = rx_Error(fromcall);
678 /* Create a volume trailer appending it to this data block (if not XBSA) */
679 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, 1);
680 hostVolumeHeader.contd = 0;
681 hostVolumeHeader.magic = TC_VOLENDMAGIC;
682 hostVolumeHeader.endTime = time(0);
683 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)&buffer[bytesread]);
684 bytesread += sizeof(hostVolumeHeader);
686 /* End the dump and transaction with the volserver. We end it now, before
687 * we make the XBSA call because if XBSA blocks, we could time out on the
688 * volserver (After last read, the transaction with the volserver is idle).
690 rc = rx_EndCall(fromcall, 0);
695 rc = AFSVolEndTrans(fromconn, fromtid, &rcode);
701 /* Write the datablock out */
702 rc = xbsa_WriteObjectData(&butxInfo, buffer, bytesread,
704 if (rc != XBSA_SUCCESS) {
705 ErrorLog(1, taskId, rc, 0,
706 "Unable to write data to the server\n");
709 /* There is a bug in the ADSM library where the bytesWritten is
710 * not filled in, so we set it as correct anyway.
712 bytesWritten = bytesread;
713 if (bytesWritten != bytesread) {
714 ErrorLog(1, taskId, rc, 0,
715 "The size of data written (%d) does not equal size read (%d)\n",
716 bytesWritten, bytesread);
717 ERROR_EXIT(TC_INTERNALERROR);
720 incSize(tapeInfoPtr, bytesread); /* Increment amount we've written */
721 tsize += bytesWritten;
723 /* Display a status line every statusSize or at end of volume */
724 if (statusSize && ((tsize >= statuscount) || endofvolume)) {
727 localtime_r(&t, &tm);
728 printf("%02d:%02d:%02d: Task %u: %u KB: %s: %u B\n", tm.tm_hour,
729 tm.tm_min, tm.tm_sec, taskId, tapeInfoPtr->kBytes,
730 hostVolumeHeader.volumeName, tsize);
731 statuscount = tsize + statusSize;
735 /* End the XBSA transaction before recording it in BUDB as successfully dumped */
736 rc = xbsa_WriteObjectEnd(&butxInfo);
738 if (rc != XBSA_SUCCESS) {
739 ErrorLog(1, taskId, rc, 0,
740 "Unable to terminate writing of the volume data to the server");
743 rc = xbsa_EndTrans(&butxInfo);
745 tapeInfoPtr->position++;
746 if (rc != XBSA_SUCCESS) {
747 ErrorLog(1, taskId, rc, 0,
748 "Unable to terminate the current transaction");
752 /* Record in BUDB the volume fragment as succcessfully dumped */
753 rc = addVolume(0, dparamsPtr->databaseDumpId, dparamsPtr->tapeName,
754 nodePtr->dumps[dparamsPtr->curVolume].name,
755 nodePtr->dumps[dparamsPtr->curVolume].vid,
756 nodePtr->dumps[dparamsPtr->curVolume].cloneDate,
757 dparamsPtr->curVolStartPos, volBytesRead, 0 /*frag0 */ ,
758 (BUDB_VOL_FIRSTFRAG | BUDB_VOL_LASTFRAG));
762 dparamsPtr->curVolumeStatus = DUMP_SUCCESS;
765 /* Cleanup after an error occurs part way into a volume dump */
767 rc = rx_EndCall(fromcall, 0);
774 rc = AFSVolEndTrans(fromconn, fromtid, &rcode);
776 code = (rc ? rc : rcode);
779 /* If this dump failed, what happens to successive retries
780 * of the volume? How do they get recorded in the XBSA database
781 * (overwritten)? If not, we don't record this in the BUDB database
782 * so it will not be removed when we delete the dump. What to do?
783 * Also if the volume was never recorded in the DB (partial dump).
787 rc = xbsa_WriteObjectEnd(&butxInfo);
789 if (rc != XBSA_SUCCESS) {
790 ErrorLog(1, taskId, rc, 0,
791 "Unable to terminate writing of the volume data to the server");
793 tapeInfoPtr->position++;
797 /* End the XBSA Transaction */
798 rc = xbsa_EndTrans(&butxInfo);
800 if (rc != XBSA_SUCCESS) {
801 ErrorLog(1, taskId, rc, 0,
802 "Unable to terminate the current transaction");
809 dparamsPtr->curVolumeStatus = DUMP_FAILED;
816 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
819 * Go through the list of volumes to dump, dumping each one. The action
820 * taken when a volume dump fails, depends on the passNumber. At minimum,
821 * the failed volume is remembered.
823 * flushSavedEntries - inconsistent treatment for errors. What should
824 * be done for user aborts?
828 dumpPass(struct dumpRock * dparamsPtr, int passNumber)
830 struct dumpNode *nodePtr = dparamsPtr->node;
831 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
832 afs_int32 taskId = nodePtr->taskID;
833 struct tc_dumpDesc *curDump;
835 afs_int32 code = 0, tcode, dvcode;
837 struct vldbentry vldbEntry;
838 struct sockaddr_in server;
841 TapeLog(2, taskId, 0, 0, "Starting pass %d\n", passNumber);
843 /* while there are more volumes to dump */
844 for (dparamsPtr->curVolume = 0; dparamsPtr->curVolume < nodePtr->arraySize; dparamsPtr->curVolume++) { /*w */
845 curDump = &nodePtr->dumps[dparamsPtr->curVolume];
846 if (curDump->hostAddr == 0)
849 /* set name of current volume being dumped */
851 strcpy(nodePtr->statusNodePtr->volumeName, curDump->name);
854 /* Determine location of the volume.
855 * In case the volume moved has moved.
857 if (passNumber > 1) { /*pass */
859 bc_GetEntryByID(cstruct, curDump->vid, curDump->vtype,
862 ErrorLog(0, taskId, tcode, 0,
863 "Volume %s (%u) failed - Can't find volume in VLDB\n",
864 curDump->name, curDump->vid);
865 curDump->hostAddr = 0;
866 dparamsPtr->volumesFailed++;
870 switch (curDump->vtype) {
872 if (!(vldbEntry.flags & VLF_BACKEXISTS)) {
873 ErrorLog(0, taskId, 0, 0,
874 "Volume %s (%u) failed - Backup volume no longer exists\n",
875 curDump->name, curDump->vid);
876 curDump->hostAddr = 0;
877 dparamsPtr->volumesFailed++;
880 /* Fall into RWVOL case */
883 for (e = 0; e < vldbEntry.nServers; e++) { /* Find the RW volume */
884 if (vldbEntry.serverFlags[e] & VLSF_RWVOL)
890 /* Try to use the server and partition we found the volume on
891 * Otherwise, use the first RO volume.
893 for (e = 0; e < vldbEntry.nServers; e++) { /* Find the RO volume */
894 if ((curDump->hostAddr == vldbEntry.serverNumber[e])
895 && (curDump->partition ==
896 vldbEntry.serverPartition[e]))
900 if (e >= vldbEntry.nServers) { /* Didn't find RO volume */
901 for (e = 0; e < vldbEntry.nServers; e++) { /* Find the first RO volume */
902 if (vldbEntry.serverFlags[e] & VLSF_ROVOL)
909 ErrorLog(0, taskId, 0, 0,
910 "Volume %s (%u) failed - Unknown volume type\n",
911 curDump->name, curDump->vid);
912 curDump->hostAddr = 0;
916 if (e >= vldbEntry.nServers) {
917 ErrorLog(0, taskId, 0, 0,
918 "Volume %s (%u) failed - Can't find volume entry in VLDB\n",
919 curDump->name, curDump->vid);
920 curDump->hostAddr = 0;
921 dparamsPtr->volumesFailed++;
925 /* Remember the server and partition the volume exists on */
926 memset(&server, 0, sizeof(server));
927 server.sin_addr.s_addr = vldbEntry.serverNumber[e];
929 server.sin_family = AF_INET;
930 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
931 server.sin_len = sizeof(struct sockaddr_in);
933 curDump->hostAddr = HOSTADDR(&server);
934 curDump->partition = vldbEntry.serverPartition[e];
936 /* Determine date from which to do an incremental dump
938 if (nodePtr->parent) {
940 bcdb_FindClone(nodePtr->parent, curDump->name,
945 curDump->date = 0; /* do a full dump */
949 if (checkAbortByTaskId(taskId))
950 ERROR_EXIT(TC_ABORTEDBYREQUEST);
952 /* Establish connection to volume - UV_ routine expects
953 * host address in network order
956 dvcode = xbsaDumpVolume(curDump, dparamsPtr);
958 dvcode = dumpVolume(curDump, dparamsPtr);
960 action = dparamsPtr->curVolumeStatus;
962 /* Flush volume and tape entries to the database */
963 tcode = flushSavedEntries(action);
969 TapeLog(1, taskId, 0, 0, "Volume %s (%u) successfully dumped\n",
970 curDump->name, curDump->vid);
972 ErrorLog(1, taskId, dvcode, 0,
973 "Warning: Termination processing error on volume %s (%u)\n",
974 curDump->name, curDump->vid);
976 curDump->hostAddr = 0;
977 dparamsPtr->volumesDumped++;
982 if (action == DUMP_PARTIAL) {
983 ErrorLog(1, taskId, dvcode, 0,
984 "Volume %s (%u) failed - partially dumped\n",
985 curDump->name, curDump->vid);
987 ErrorLog(0, taskId, dvcode, 0, "Volume %s (%u) failed\n",
988 curDump->name, curDump->vid);
990 ErrorLog(0, taskId, dvcode, 0,
991 "Volume %s (%u) not dumped - has not been re-cloned since last dump\n",
992 curDump->name, curDump->vid);
995 if (passNumber == maxpass) {
999 ch = retryPrompt(curDump->name, curDump->vid, taskId);
1002 case 'r': /* retry */
1003 dparamsPtr->curVolume--; /* redump this volume */
1005 case 'o': /* omit */
1006 ErrorLog(1, taskId, 0, 0, "Volume %s (%u) omitted\n",
1007 curDump->name, curDump->vid);
1008 dparamsPtr->volumesFailed++;
1010 case 'a': /* abort */
1011 TapeLog(1, taskId, 0, 0, "Dump aborted\n");
1012 ERROR_EXIT(TC_ABORTEDBYREQUEST);
1015 ERROR_EXIT(TC_INTERNALERROR);
1022 TapeLog(1, taskId, dvcode, 0,
1023 "Volume %s (%u) hit end-of-tape inferred - will retry on next tape\n",
1024 curDump->name, curDump->vid);
1026 /* Get the next tape */
1027 unmountTape(taskId, tapeInfoPtr);
1029 dparamsPtr->tapeSeq++;
1030 tcode = getDumpTape(dparamsPtr, 1, 0); /* interactive - no appends */
1034 dparamsPtr->curVolume--; /* redump this volume */
1037 case DUMP_NORETRYEOT:
1038 ErrorLog(1, taskId, 0, 0,
1039 "Volume %s (%u) failed - volume larger than tape\n",
1040 curDump->name, curDump->vid);
1042 /* rewrite the label on the tape - rewind - no need to switch tapes */
1043 tcode = butm_Create(tapeInfoPtr, &dparamsPtr->tapeLabel, 1);
1045 ErrorLog(0, taskId, tcode, tapeInfoPtr->error,
1046 "Can't relabel tape\n");
1048 unmountTape(taskId, tapeInfoPtr);
1049 tcode = getDumpTape(dparamsPtr, 1, 0); /* interactive - no appends */
1052 } else { /* Record the tape in database */
1053 tapepos = tapeInfoPtr->position;
1055 useTape(&dparamsPtr->tape, dparamsPtr->databaseDumpId,
1056 dparamsPtr->tapeName,
1057 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1058 dparamsPtr->tapeLabel.useCount,
1059 dparamsPtr->tapeLabel.creationTime,
1060 dparamsPtr->tapeLabel.expirationDate, tapepos);
1063 curDump->hostAddr = 0;
1064 dparamsPtr->volumesFailed++;
1068 TapeLog(1, taskId, dvcode, 0,
1069 "Volume %s (%u) not dumped - has not been modified since last dump\n",
1070 curDump->name, curDump->vid);
1072 curDump->hostAddr = 0;
1073 dparamsPtr->volumesNotDumped++;
1077 ErrorLog(1, taskId, dvcode, 0, "Volume %s (%u) failed\n",
1078 curDump->name, curDump->vid);
1085 /* check if we terminated while processing a volume */
1086 if (dparamsPtr->curVolume < nodePtr->arraySize) {
1087 TapeLog(2, taskId, 0, 0,
1088 "Terminated while processing Volume %s (%u)\n", curDump->name,
1092 /* print a summary of this pass */
1093 TapeLog(2, taskId, 0, 0, "End of pass %d: Volumes remaining = %d\n",
1095 nodePtr->arraySize - (dparamsPtr->volumesDumped +
1096 dparamsPtr->volumesFailed +
1097 dparamsPtr->volumesNotDumped));
1104 struct dumpNode *nodePtr = (struct dumpNode *)param;
1105 struct dumpRock dparams;
1106 struct butm_tapeInfo tapeInfo;
1112 /* for volume setup */
1114 int failedvolumes = 0;
1115 int dumpedvolumes = 0;
1116 int nodumpvolumes = 0;
1119 char finishedMsg1[128];
1120 char finishedMsg2[50];
1121 time_t startTime = 0;
1123 afs_int32 allocbufferSize;
1125 extern struct deviceSyncNode *deviceLatch;
1126 extern struct tapeConfig globalTapeConfig;
1128 afs_pthread_setname_self("dumper");
1129 taskId = nodePtr->taskID; /* Get task Id */
1130 setStatus(taskId, DRIVE_WAIT);
1131 EnterDeviceQueue(deviceLatch);
1132 clearStatus(taskId, DRIVE_WAIT);
1135 TapeLog(2, taskId, 0, 0, "Dump %s\n", nodePtr->dumpSetName);
1137 /* setup the dump parameters */
1138 memset(&dparams, 0, sizeof(dparams));
1139 dparams.node = nodePtr;
1140 dparams.tapeInfoPtr = &tapeInfo;
1141 dlqInit(&savedEntries);
1144 /* Instantiate the tape module */
1145 tapeInfo.structVersion = BUTM_MAJORVERSION;
1146 code = butm_file_Instantiate(&tapeInfo, &globalTapeConfig);
1148 ErrorLog(0, taskId, code, tapeInfo.error,
1149 "Can't initialize the tape module\n");
1154 /* check if abort requested while waiting on device latch */
1155 if (checkAbortByTaskId(taskId))
1156 ERROR_EXIT(TC_ABORTEDBYREQUEST);
1158 /* Are there volumes to dump */
1159 if (nodePtr->arraySize == 0) {
1160 TLog(taskId, "Dump (%s), no volumes to dump\n", nodePtr->dumpSetName);
1164 /* Allocate a buffer for the dumps. Leave room for header and vol-trailer.
1165 * dataSize is amount of data to read in each rx_Read() call.
1168 /* XBSA dumps have not header */
1169 dataSize = BufferSize;
1170 allocbufferSize = dataSize + sizeof(struct volumeHeader);
1172 tapeblocks = BufferSize / BUTM_BLOCKSIZE; /* # of 16K tapeblocks */
1173 dataSize = (tapeblocks * BUTM_BLKSIZE);
1175 BUTM_HDRSIZE + dataSize + sizeof(struct volumeHeader);
1178 bufferBlock = malloc(allocbufferSize);
1180 ErrorLog(0, taskId, TC_NOMEMORY, 0,
1181 "Can't allocate BUFFERSIZE for dumps\n");
1182 ERROR_EXIT(TC_NOMEMORY);
1185 /* Determine the dumpid of the most recent dump of this volumeset and dumplevel
1186 * Used when requesting a tape. Done now because once we create the dump, the
1187 * routine will then find the newly created dump.
1189 sprintf(strlevel, "%d", nodePtr->level);
1191 bcdb_FindLatestDump(nodePtr->volumeSetName, strlevel,
1194 if (code != BUDB_NODUMPNAME) {
1195 ErrorLog(0, taskId, code, 0, "Can't read backup database\n");
1198 memset(&dparams.lastDump, 0, sizeof(dparams.lastDump));
1201 code = createDump(&dparams); /* enter dump into database */
1203 ErrorLog(0, taskId, code, 0, "Can't create dump in database\n");
1207 TLog(taskId, "Dump %s (DumpID %u)\n", nodePtr->dumpSetName,
1208 dparams.databaseDumpId);
1211 /* mount the tape and write its label */
1212 code = getDumpTape(&dparams, autoQuery, nodePtr->doAppend);
1214 /* Create a dummy tape to satisfy backup databae */
1215 code = getXBSATape(&dparams);
1216 tapeInfo.position = 1;
1219 /* If didn't write the label, remove dump from the database */
1220 if (!dparams.wroteLabel) {
1221 i = bcdb_deleteDump(dparams.databaseDumpId, 0, 0, 0);
1222 if (i && (i != BUDB_NOENT))
1223 ErrorLog(1, taskId, i, 0,
1224 "Warning: Can't delete dump %u from database\n",
1225 dparams.databaseDumpId);
1227 dparams.databaseDumpId = 0;
1229 ERROR_EXIT(code); /* exit with code from getTape */
1232 startTime = time(0);
1233 for (pass = 1; pass <= maxpass; pass++) {
1234 lastPass = (pass == maxpass);
1235 code = dumpPass(&dparams, pass);
1239 /* if no failed volumes, we're done */
1240 if ((dparams.volumesDumped + dparams.volumesFailed +
1241 dparams.volumesNotDumped) == nodePtr->arraySize)
1246 * Log the error but ignore it since the dump is effectively done.
1247 * Scantape may assume another volume and ask for next tape.
1250 code = butm_WriteEOT(&tapeInfo);
1252 TapeLog(0, taskId, code, tapeInfo.error,
1253 "Warning: Can't write end-of-dump on tape\n");
1257 finishTape(&dparams.tape,
1258 dparams.tapeInfoPtr->kBytes +
1259 (dparams.tapeInfoPtr->nBytes ? 1 : 0));
1263 code = finishDump(&dparams.dump);
1267 action = dparams.curVolumeStatus;
1268 code = flushSavedEntries(action);
1279 unmountTape(taskId, &tapeInfo);
1283 dumpedvolumes = dparams.volumesDumped;
1284 nodumpvolumes = dparams.volumesNotDumped;
1285 failedvolumes = nodePtr->arraySize - (dumpedvolumes + nodumpvolumes);
1287 /* pass back the number of volumes we failed to dump */
1289 nodePtr->statusNodePtr->volsFailed = failedvolumes;
1292 lastPass = 1; /* In case we aborted */
1294 DUMPNAME(finishedMsg1, nodePtr->dumpSetName, dparams.databaseDumpId);
1295 sprintf(finishedMsg2, "%d volumes dumped", dumpedvolumes);
1296 if (failedvolumes) {
1297 sprintf(msg, ", %d failed", failedvolumes);
1298 strcat(finishedMsg2, msg);
1300 if (nodumpvolumes) {
1301 sprintf(msg, ", %d unchanged", nodumpvolumes);
1302 strcat(finishedMsg2, msg);
1305 if (code == TC_ABORTEDBYREQUEST) {
1306 ErrorLog(0, taskId, 0, 0, "%s: Aborted by request. %s\n",
1307 finishedMsg1, finishedMsg2);
1308 clearStatus(taskId, ABORT_REQUEST);
1309 setStatus(taskId, ABORT_DONE);
1311 ErrorLog(0, taskId, code, 0, "%s: Finished with errors. %s\n",
1312 finishedMsg1, finishedMsg2);
1313 setStatus(taskId, TASK_ERROR);
1315 TLog(taskId, "%s: Finished. %s\n", finishedMsg1, finishedMsg2);
1319 /* Record how long the dump took */
1320 if (centralLogIO && startTime) {
1322 afs_int32 hrs, min, sec, tmp;
1324 struct tm tmstart, tmend;
1326 localtime_r(&startTime, &tmstart);
1327 localtime_r(&endTime, &tmend);
1328 timediff = (int)endTime - (int)startTime;
1329 hrs = timediff / 3600;
1330 tmp = timediff % 3600;
1335 "%-5d %02d/%02d/%04d %02d:%02d:%02d "
1336 "%02d/%02d/%04d %02d:%02d:%02d " "%02d:%02d:%02d "
1337 "%s %d of %d volumes dumped (%lu KB)\n", taskId,
1338 tmstart.tm_mon + 1, tmstart.tm_mday, tmstart.tm_year + 1900,
1339 tmstart.tm_hour, tmstart.tm_min, tmstart.tm_sec,
1340 tmend.tm_mon + 1, tmend.tm_mday, tmend.tm_year + 1900,
1341 tmend.tm_hour, tmend.tm_min, tmend.tm_sec, hrs, min, sec,
1342 nodePtr->volumeSetName, dumpedvolumes,
1343 dumpedvolumes + failedvolumes,
1344 afs_printable_uint32_lu(dparams.tapeInfoPtr->kBytes + 1));
1346 fwrite(line, strlen(line), 1, centralLogIO);
1347 fflush(centralLogIO);
1350 setStatus(taskId, TASK_DONE);
1352 FreeNode(taskId); /* free the dump node */
1353 LeaveDeviceQueue(deviceLatch);
1354 return (void *)(intptr_t)(code);
1357 #define BELLTIME 60 /* 60 seconds before a bell rings */
1358 #define BELLCHAR 7 /* ascii for bell */
1361 * prompt the user to decide how to handle a failed volume dump. The
1362 * volume parameters describe the volume that failed
1364 * volumeName - name of volume
1365 * volumeId - volume id
1366 * taskId - for job contrl
1368 * character typed by user, one of r, o or a
1372 retryPrompt(char *volumeName, afs_int32 volumeId, afs_uint32 taskId)
1378 setStatus(taskId, OPR_WAIT);
1379 printf("\nDump of volume %s (%u) failed\n\n", volumeName, volumeId);
1381 printf("Please select action to be taken for this volume\n");
1384 printf("r - retry, try dumping this volume again\n");
1385 printf("o - omit, this volume from this dump\n");
1386 printf("a - abort, the entire dump\n");
1395 code = LWP_GetResponseKey(5, &ch); /* ch stores key pressed */
1397 break; /* input is available */
1399 if (checkAbortByTaskId(taskId)) {
1400 clearStatus(taskId, OPR_WAIT);
1402 ("This tape operation has been aborted by the coordinator\n");
1406 if (time(0) > start + BELLTIME)
1409 /* otherwise, we should beep again, check for abort and go back,
1410 * since the GetResponseKey() timed out.
1413 break; /* input is available */
1415 clearStatus(taskId, OPR_WAIT);
1416 if (ch != 'r' && ch != 'o' && ch != 'a') {
1417 printf("Please select one of the 3 options, r, o or a\n");
1424 /* For testing: it prints the tape label */
1426 printTapeLabel(struct butm_tapeLabel *tl)
1428 printf("Tape Label\n");
1429 printf(" structVersion = %d\n", tl->structVersion);
1430 printf(" creationTime = %u\n", tl->creationTime);
1431 printf(" expirationDate = %u\n", tl->expirationDate);
1432 printf(" AFSName = %s\n", tl->AFSName);
1433 printf(" cell = %s\n", tl->cell);
1434 printf(" dumpid = %d\n", tl->dumpid);
1435 printf(" useCount = %d\n", tl->useCount);
1436 printf(" comment = %s\n", tl->comment);
1437 printf(" pName = %s\n", tl->pName);
1438 printf(" size = %u\n", tl->size);
1439 printf(" dumpPath = %s\n", tl->dumpPath);
1444 * Create a tape structure to be satisfy the backup database
1445 * even though we don't really use a tape with XBSA.
1448 getXBSATape(struct dumpRock *dparamsPtr)
1450 struct dumpNode *nodePtr = dparamsPtr->node;
1451 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
1452 struct butm_tapeLabel *tapeLabelPtr = &dparamsPtr->tapeLabel;
1455 tc_MakeTapeName(dparamsPtr->tapeName, &nodePtr->tapeSetDesc,
1456 dparamsPtr->tapeSeq);
1458 GetNewLabel(tapeInfoPtr, "" /*pName */ , dparamsPtr->tapeName,
1460 strcpy(tapeLabelPtr->dumpPath, nodePtr->dumpName);
1461 tapeLabelPtr->dumpid = dparamsPtr->databaseDumpId;
1462 tapeLabelPtr->expirationDate =
1463 calcExpirationDate(nodePtr->tapeSetDesc.expType,
1464 nodePtr->tapeSetDesc.expDate, time(0));
1466 /* printTapeLabel(tapeLabelPtr); For testing */
1469 useTape(&dparamsPtr->tape, dparamsPtr->databaseDumpId,
1470 dparamsPtr->tapeName,
1471 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1472 tapeLabelPtr->useCount, tapeLabelPtr->creationTime,
1473 tapeLabelPtr->expirationDate, 0 /*tape position */ );
1478 * iterate until the desired tape (as specified by the dump structures)
1482 * 0 - assume the tape is there. Prompt if assumption false
1483 * 1 - prompt regardless
1487 getDumpTape(struct dumpRock *dparamsPtr, int interactiveFlag,
1490 struct dumpNode *nodePtr = dparamsPtr->node;
1491 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
1492 struct butm_tapeLabel *newTapeLabelPtr = &dparamsPtr->tapeLabel;
1493 char AFSTapeName[TC_MAXTAPENAMELEN];
1494 afs_int32 taskId = nodePtr->taskID;
1495 struct butm_tapeLabel oldTapeLabel;
1496 struct budb_dumpEntry dumpEntry;
1497 struct budb_tapeEntry tapeEntry;
1498 struct budb_volumeEntry volEntry;
1505 afs_int32 tapepos, lastpos;
1507 extern struct tapeConfig globalTapeConfig;
1509 askForTape = interactiveFlag;
1510 dparamsPtr->wroteLabel = 0;
1512 /* Keep prompting for a tape until we get it right */
1514 /* What the name of the tape would be if not appending to it */
1515 tc_MakeTapeName(AFSTapeName, &nodePtr->tapeSetDesc,
1516 dparamsPtr->tapeSeq);
1522 PromptForTape((doAppend ? APPENDOPCODE : WRITEOPCODE),
1523 AFSTapeName, dparamsPtr->databaseDumpId, taskId,
1531 /* open the tape device */
1532 code = butm_Mount(tapeInfoPtr, AFSTapeName);
1534 TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
1538 /* Read the tape label */
1539 code = butm_ReadLabel(tapeInfoPtr, &oldTapeLabel, 1); /* rewind */
1541 if (tapeInfoPtr->error) {
1542 ErrorLog(0, taskId, code, tapeInfoPtr->error,
1543 "Warning: Tape error while reading label (will proceed with dump)\n");
1545 memset(&oldTapeLabel, 0, sizeof(oldTapeLabel));
1548 /* Check if null tape. Prior 3.3, backup tapes have no dump id */
1549 if ((strcmp(oldTapeLabel.AFSName, "") == 0)
1550 && (oldTapeLabel.dumpid == 0)) {
1553 "Dump not found on tape. Proceeding with initial dump\n");
1556 } else if (doAppend) { /* appending */
1557 /* Check that we don't have a database dump tape */
1558 if (databaseTape(oldTapeLabel.AFSName)) {
1559 char gotName[BU_MAXTAPELEN + 32];
1561 /* label does not match */
1562 LABELNAME(gotName, &oldTapeLabel);
1563 TLog(taskId, "Can't append to database tape %s\n", gotName);
1567 /* Verify that the tape is of version 4 (AFS 3.3) or greater */
1568 if (oldTapeLabel.structVersion < TAPE_VERSION_4) {
1570 "Can't append: requires tape version %d or greater\n",
1575 /* Verify that the last tape of the dump set is in the drive.
1576 * volEntry will be zeroed if last dump has no volume entries.
1579 bcdb_FindLastTape(oldTapeLabel.dumpid, &dumpEntry, &tapeEntry,
1582 ErrorLog(0, taskId, code, 0,
1583 "Can't append: Can't find last volume of dumpId %u in database\n",
1584 oldTapeLabel.dumpid);
1585 printf("Please scan the dump in or choose another tape\n");
1589 (volEntry.position ? volEntry.position : tapeEntry.labelpos);
1591 if (strcmp(TNAME(&oldTapeLabel), tapeEntry.name)) {
1592 char expName[BU_MAXTAPELEN + 32], gotName[BU_MAXTAPELEN + 32];
1594 TAPENAME(expName, tapeEntry.name, oldTapeLabel.dumpid);
1595 LABELNAME(gotName, &oldTapeLabel);
1598 "Can't append: Last tape in dump-set is %s, label seen %s\n",
1603 /* After reading the tape label, we now know what it is */
1604 strcpy(AFSTapeName, oldTapeLabel.AFSName); /* the real name */
1605 strcpy(tapeInfoPtr->name, oldTapeLabel.AFSName); /* the real name */
1607 /* Position after last volume on the tape */
1608 code = butm_SeekEODump(tapeInfoPtr, lastpos);
1610 ErrorLog(0, taskId, code, tapeInfoPtr->error,
1611 "Can't append: Can't position to end of dump on tape %s\n",
1616 /* Track size of tape - set after seek since seek changes the value */
1617 tapeInfoPtr->kBytes = tapeEntry.useKBytes;
1618 } else { /* not appending */
1622 struct budb_dumpEntry de, de2;
1624 /* Check if tape name is not what expected - null tapes are acceptable
1625 * Don't do check if the tape has a user defined label.
1627 if (dump_namecheck && (strcmp(oldTapeLabel.pName, "") == 0)) {
1628 if (strcmp(oldTapeLabel.AFSName, "") && /* not null tape */
1629 strcmp(oldTapeLabel.AFSName, AFSTapeName)) { /* not expected name */
1630 TLog(taskId, "Tape label expected %s, label seen %s\n",
1631 AFSTapeName, oldTapeLabel.AFSName);
1635 /* Check that we don't have a database dump tape */
1636 if (databaseTape(oldTapeLabel.AFSName)) {
1637 /* label does not match */
1639 "Tape label expected %s, can't dump to database tape %s\n",
1640 AFSTapeName, oldTapeLabel.AFSName);
1645 /* Verify the tape has not expired - only check if not appending */
1646 if (!tapeExpired(&oldTapeLabel)) {
1647 TLog(taskId, "This tape has not expired\n");
1651 /* Given a tape dump with good data, verify we don't overwrite recent dumps
1652 * and also verify that the volume will be restorable - if not print warnings
1654 if (oldTapeLabel.dumpid) {
1655 /* Do not overwrite a tape that belongs to the dump's dumpset */
1657 (dparamsPtr->initialDumpId ? dparamsPtr->
1658 initialDumpId : dparamsPtr->databaseDumpId);
1659 if (oldTapeLabel.dumpid == tapeid) {
1660 ErrorLog(0, taskId, 0, 0,
1661 "Can't overwrite tape containing the dump in progress\n");
1665 /* Since the dumpset on this tape will be deleted from database, check if
1666 * any of the dump's parent-dumps are on this tape.
1668 for (dmp = nodePtr->parent; dmp; dmp = de.parent) {
1669 code = bcdb_FindDumpByID(dmp, &de);
1671 ErrorLog(0, taskId, 0, 0,
1672 "Warning: Can't find parent dump %u in backup database\n",
1677 tapeid = (de.initialDumpID ? de.initialDumpID : de.id);
1678 if (oldTapeLabel.dumpid == tapeid) {
1679 ErrorLog(0, taskId, 0, 0,
1680 "Can't overwrite the parent dump %s (DumpID %u)\n",
1686 /* Since the dumpset on this tape will be deleted from database, check if
1687 * any of the dumps in this dumpset are most-recent-dumps.
1689 for (dmp = oldTapeLabel.dumpid; dmp; dmp = de.appendedDumpID) {
1690 if (dmp == dparamsPtr->lastDump.id) {
1691 memcpy(&de, &dparamsPtr->lastDump, sizeof(de));
1692 memcpy(&de2, &dparamsPtr->lastDump, sizeof(de2));
1694 code = bcdb_FindDumpByID(dmp, &de);
1697 sprintf(strlevel, "%d", de.level);
1699 bcdb_FindLatestDump(de.volumeSetName, strlevel,
1705 /* If dump on the tape is the latest dump at this level */
1706 if (de.id == de2.id) {
1707 if (strcmp(DUMP_TAPE_NAME, de2.name) == 0) {
1708 ErrorLog(0, taskId, 0, 0,
1709 "Warning: Overwriting most recent dump %s (DumpID %u)\n",
1712 ErrorLog(0, taskId, 0, 0,
1713 "Warning: Overwriting most recent dump of the '%s' volumeset: %s (DumpID %u)\n",
1714 de.volumeSetName, de.name, de.id);
1718 } /* if (oldTapeLabel.dumpid) */
1719 } /* else not appending */
1722 * Now have the right tape. Create a new label for the tape
1723 * Appended labels have the dump's dumpId - labels at beginnings of
1724 * tape have the initial dump's dumpId.
1725 * Appended labels do not increment the useCount.
1726 * Labels at beginnings of tape use the most future expiration of the dump set.
1728 GetNewLabel(tapeInfoPtr, oldTapeLabel.pName, AFSTapeName,
1730 strcpy(newTapeLabelPtr->dumpPath, nodePtr->dumpName);
1731 newTapeLabelPtr->expirationDate =
1732 calcExpirationDate(nodePtr->tapeSetDesc.expType,
1733 nodePtr->tapeSetDesc.expDate, time(0));
1734 newTapeLabelPtr->dumpid = dparamsPtr->databaseDumpId;
1735 newTapeLabelPtr->useCount = oldTapeLabel.useCount;
1738 newTapeLabelPtr->useCount++;
1739 if (dparamsPtr->initialDumpId) {
1740 newTapeLabelPtr->dumpid = dparamsPtr->initialDumpId;
1741 expir = ExpirationDate(dparamsPtr->initialDumpId);
1742 if (expir > newTapeLabelPtr->expirationDate)
1743 newTapeLabelPtr->expirationDate = expir;
1747 /* write the label on the tape - rewind if not appending and vice-versa */
1748 code = butm_Create(tapeInfoPtr, newTapeLabelPtr, !doAppend);
1750 char gotName[BU_MAXTAPELEN + 32];
1752 LABELNAME(gotName, newTapeLabelPtr);
1753 TapeLog(0, taskId, code, tapeInfoPtr->error,
1754 "Can't label tape as %s\n", gotName);
1757 dparamsPtr->wroteLabel = 1; /* Remember we wrote the label */
1758 tapepos = tapeInfoPtr->position - 1;
1760 strcpy(dparamsPtr->tapeName, TNAME(newTapeLabelPtr));
1762 /* If appending, set dumpentry in the database as appended. */
1764 char gotName[BU_MAXTAPELEN + 32];
1766 nodePtr->tapeSetDesc.b = extractTapeSeq(AFSTapeName);
1767 dparamsPtr->dump.tapes.b = nodePtr->tapeSetDesc.b;
1768 dparamsPtr->initialDumpId = oldTapeLabel.dumpid;
1769 strcpy(nodePtr->tapeSetDesc.format, dumpEntry.tapes.format);
1772 bcdb_MakeDumpAppended(dparamsPtr->databaseDumpId,
1773 dparamsPtr->initialDumpId,
1774 nodePtr->tapeSetDesc.b);
1776 ErrorLog(2, taskId, code, 0,
1777 "Warning: Can't append dump %u to dump %u in database\n",
1778 dparamsPtr->databaseDumpId,
1779 dparamsPtr->initialDumpId);
1781 LABELNAME(gotName, &oldTapeLabel);
1782 TLog(taskId, "Appending dump %s (DumpID %u) to tape %s\n",
1783 nodePtr->dumpSetName, dparamsPtr->databaseDumpId, gotName);
1786 /* If not appending, delete overwritten dump from the database */
1788 if ((oldTapeLabel.structVersion >= TAPE_VERSION_3)
1789 && oldTapeLabel.dumpid) {
1790 code = bcdb_deleteDump(oldTapeLabel.dumpid, 0, 0, 0);
1791 if (code && (code != BUDB_NOENT))
1792 ErrorLog(0, taskId, code, 0,
1793 "Warning: Can't delete old dump %u from database\n",
1794 oldTapeLabel.dumpid);
1799 useTape(&dparamsPtr->tape, dparamsPtr->databaseDumpId,
1800 dparamsPtr->tapeName,
1801 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1802 newTapeLabelPtr->useCount, newTapeLabelPtr->creationTime,
1803 newTapeLabelPtr->expirationDate, tapepos);
1806 * The margin of space to check for end of tape is set to the
1807 * amount of space used to write an end-of-tape multiplied by 2.
1808 * The amount of space is size of a 16K volume trailer, a 16K File
1809 * End mark, its EOF marker, a 16K EODump marker, its EOF marker,
1810 * and up to two EOF markers done on close (3 16K blocks + 4 EOF
1813 tc_EndMargin = (3 * 16384 + 4 * globalTapeConfig.fileMarkSize) * 2;
1814 tc_KEndMargin = tc_EndMargin / 1024;
1818 unmountTape(taskId, tapeInfoPtr);
1826 makeVolumeHeader(struct volumeHeader *vhptr, struct dumpRock *dparamsPtr,
1829 struct dumpNode *nodePtr = dparamsPtr->node;
1830 struct tc_dumpDesc *curDump;
1833 curDump = &nodePtr->dumps[dparamsPtr->curVolume];
1835 memset(vhptr, 0, sizeof(*vhptr));
1836 strcpy(vhptr->volumeName, curDump->name);
1837 vhptr->volumeID = curDump->vid;
1838 vhptr->cloneDate = curDump->cloneDate;
1839 vhptr->server = curDump->hostAddr;
1840 vhptr->part = curDump->partition;
1841 vhptr->from = curDump->date;
1842 vhptr->frag = fragmentNumber;
1844 vhptr->magic = TC_VOLBEGINMAGIC;
1845 vhptr->dumpID = dparamsPtr->databaseDumpId; /* real dump id */
1846 vhptr->level = nodePtr->level;
1847 vhptr->parentID = nodePtr->parent;
1849 vhptr->versionflags = CUR_TAPE_VERSION;
1850 strcpy(vhptr->dumpSetName, nodePtr->dumpSetName);
1851 strcpy(vhptr->preamble, "H++NAME#");
1852 strcpy(vhptr->postamble, "T--NAME#");
1858 volumeHeader_hton(struct volumeHeader *hostPtr, struct volumeHeader *netPtr)
1860 struct volumeHeader volHdr;
1862 memset(&volHdr, 0, sizeof(volHdr));
1864 strcpy(volHdr.preamble, hostPtr->preamble);
1865 strcpy(volHdr.postamble, hostPtr->postamble);
1866 strcpy(volHdr.volumeName, hostPtr->volumeName);
1867 strcpy(volHdr.dumpSetName, hostPtr->dumpSetName);
1868 volHdr.volumeID = htonl(hostPtr->volumeID);
1869 volHdr.server = htonl(hostPtr->server);
1870 volHdr.part = htonl(hostPtr->part);
1871 volHdr.from = htonl(hostPtr->from);
1872 volHdr.frag = htonl(hostPtr->frag);
1873 volHdr.magic = htonl(hostPtr->magic);
1874 volHdr.contd = htonl(hostPtr->contd);
1875 volHdr.dumpID = htonl(hostPtr->dumpID);
1876 volHdr.level = htonl(hostPtr->level);
1877 volHdr.parentID = htonl(hostPtr->parentID);
1878 volHdr.endTime = htonl(hostPtr->endTime);
1879 volHdr.versionflags = htonl(hostPtr->versionflags);
1880 volHdr.cloneDate = htonl(hostPtr->cloneDate);
1882 memcpy(netPtr, &volHdr, sizeof(struct volumeHeader));
1886 /* database related routines */
1889 createDump(struct dumpRock *dparamsPtr)
1891 struct dumpNode *nodePtr = dparamsPtr->node;
1892 struct budb_dumpEntry *dumpPtr;
1895 dumpPtr = &dparamsPtr->dump;
1896 memset(dumpPtr, 0, sizeof(*dumpPtr));
1898 /* id filled in by database */
1899 dumpPtr->parent = nodePtr->parent;
1900 dumpPtr->level = nodePtr->level;
1904 if (xbsaType == XBSA_SERVER_TYPE_ADSM) {
1905 strcpy(dumpPtr->tapes.tapeServer, butxInfo.serverName);
1906 dumpPtr->flags = BUDB_DUMP_ADSM;
1908 if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1909 /* The current server (API) doesn't provide the function required
1910 * to specify a server at startup time. For that reason, we can't
1911 * be sure that the server name supplied by the user in the user-
1912 * defined configuration file is correct. We set a flag here so
1913 * we know at restore time that the servername info in the backup
1914 * database may be incorrect. We will not allow a server switch
1915 * at that time, even if the server at restore time supports
1918 dumpPtr->flags |= BUDB_DUMP_XBSA_NSS;
1922 strcpy(dumpPtr->volumeSetName, nodePtr->volumeSetName);
1923 strcpy(dumpPtr->dumpPath, nodePtr->dumpName);
1924 strcpy(dumpPtr->name, nodePtr->dumpSetName);
1925 dumpPtr->created = 0; /* let database assign it */
1926 dumpPtr->incTime = 0; /* not really used */
1927 dumpPtr->nVolumes = 0;
1928 dumpPtr->initialDumpID = 0;
1930 dumpPtr->tapes.id = groupId;
1931 dumpPtr->tapes.b = 1;
1932 dumpPtr->tapes.maxTapes = 0;
1933 strcpy(dumpPtr->tapes.format, nodePtr->tapeSetDesc.format);
1935 /* principal filled in by database */
1937 /* now call the database to create the entry */
1938 code = bcdb_CreateDump(dumpPtr);
1940 dparamsPtr->databaseDumpId = dumpPtr->id;
1947 * Initialize to a specific server. The first time, we remember the
1948 * server as the original server and go back to it each time we pass 0
1952 InitToServer(afs_int32 taskId, struct butx_transactionInfo * butxInfoP,
1955 static char origserver[BSA_MAX_DESC];
1956 static int init = 0;
1957 afs_int32 rc, code = 0;
1960 strcpy(origserver, "");
1965 server = origserver; /* return to original server */
1966 if (strcmp(server, "") == 0)
1967 return 0; /* No server, do nothing */
1968 if (strcmp(butxInfoP->serverName, server) == 0)
1969 return 0; /* same server, do nothing */
1970 if (strcmp(origserver, "") == 0)
1971 strcpy(origserver, server); /* remember original server */
1973 if (strcmp(butxInfoP->serverName, "") != 0) {
1974 /* If already connected to a server, disconnect from it.
1975 * Check to see if our server does not support switching.
1977 if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1978 ErrorLog(0, taskId, TC_BADTASK, 0,
1979 "This version of XBSA libraries does not support switching "
1980 "from server %s to server %s\n", butxInfoP->serverName,
1982 return (TC_BADTASK);
1985 rc = xbsa_Finalize(&butxInfo);
1986 if (rc != XBSA_SUCCESS) {
1987 ErrorLog(0, taskId, rc, 0,
1988 "InitToServer: Unable to terminate the connection to server %s\n",
1989 butxInfoP->serverName);
1994 /* initialize to the new server */
1995 rc = xbsa_Initialize(&butxInfo, xbsaObjectOwner, appObjectOwner,
1996 xbsaSecToken, server);
1997 if (rc != XBSA_SUCCESS) {
1998 ErrorLog(0, taskId, rc, 0,
1999 "InitToServer: Unable to initialize the XBSA library to server %s\n",
2013 DeleteDump(void *param)
2015 struct deleteDumpIf *ptr = (struct deleteDumpIf *)param;
2018 afs_int32 rc, code = 0;
2020 afs_int32 index, next, dbTime;
2022 struct budb_dumpEntry dumpEntry;
2023 char tapeName[BU_MAXTAPELEN];
2024 char dumpIdStr[XBSA_MAX_OSNAME];
2025 char volumeNameStr[XBSA_MAX_PATHNAME];
2028 int allnotfound = 1, onenotfound = 0;
2029 extern struct udbHandleS udbHandle;
2030 extern struct deviceSyncNode *deviceLatch;
2032 dumpid = ptr->dumpID;
2033 taskId = ptr->taskId; /* Get task Id */
2035 afs_pthread_setname_self("deletedump");
2036 setStatus(taskId, DRIVE_WAIT);
2037 EnterDeviceQueue(deviceLatch);
2038 clearStatus(taskId, DRIVE_WAIT);
2041 TapeLog(2, taskId, 0, 0, "Delete Dump %u\n", dumpid);
2043 vl.budb_volumeList_len = 0;
2044 vl.budb_volumeList_val = 0;
2047 /* Get the dump info for the dump we are deleting */
2048 rc = bcdb_FindDumpByID(dumpid, &dumpEntry);
2050 ErrorLog(0, taskId, rc, 0,
2051 "Unable to locate dump ID %u in database\n", dumpid);
2052 setStatus(taskId, TASK_ERROR);
2056 /* we must make sure that we are configured with the correct type of
2057 * XBSA server for this dump delete! Only those dumped to an ADSM server.
2059 if ((xbsaType == XBSA_SERVER_TYPE_ADSM)
2060 && !((dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA)))) {
2061 ErrorLog(0, taskId, TC_BADTASK, 0,
2062 "The dump %u requested for deletion is incompatible with this instance of butc\n",
2064 setStatus(taskId, TASK_ERROR);
2065 ERROR_EXIT(TC_BADTASK);
2068 /* Make sure we are connected to the correct server. If not, switch to it if appropriate */
2069 if ((strlen((char *)dumpEntry.tapes.tapeServer) != 0)
2070 && (strcmp((char *)dumpEntry.tapes.tapeServer, butxInfo.serverName) !=
2073 /* Check to see if the tapeServer name is trustworthy */
2074 if ((dumpEntry.flags & (BUDB_DUMP_XBSA_NSS | BUDB_DUMP_BUTA))
2075 && !forcemultiple) {
2076 /* The dump was made with a version of the XBSA interface
2077 * that didn't allow switching of servers, we can't be sure
2078 * that the servername in the backup database is correct. So,
2079 * we will check the servername and log it if they don't match;
2080 * but we will try to do the delete without switching servers.
2083 "The dump %d requested for deletion is on server %s "
2084 "but butc is connected to server %s "
2085 "(Attempting to delete the dump anyway)\n", dumpid,
2086 (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2089 "The dump %u requested for deletion is on server %s "
2090 "but butc is connected to server %s "
2091 "(switching servers)\n", dumpid,
2092 (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2094 rc = InitToServer(taskId, &butxInfo,
2095 (char *)dumpEntry.tapes.tapeServer);
2096 if (rc != XBSA_SUCCESS) {
2097 setStatus(taskId, TASK_ERROR);
2103 /* Start a new Transaction */
2104 rc = xbsa_BeginTrans(&butxInfo);
2105 if (rc != XBSA_SUCCESS) {
2106 ErrorLog(0, taskId, rc, 0, "Unable to create a new transaction\n");
2107 setStatus(taskId, TASK_ERROR);
2112 /* Query the backup database for list of volumes to delete */
2113 for (index = next = 0; index != -1; index = next) {
2114 rc = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client,
2115 UF_SINGLESERVER, BUDB_MAJORVERSION,
2116 BUDB_OP_DUMPID, tapeName, dumpid, 0,
2117 index, &next, &dbTime, &vl);
2119 if (rc == BUDB_ENDOFLIST)
2121 ErrorLog(0, taskId, rc, 0, "Can't find volume info for dump %d\n",
2123 setStatus(taskId, TASK_ERROR);
2127 /* Delete all volumes on the list */
2128 for (i = 0; i < vl.budb_volumeList_len; i++) {
2129 if (dumpEntry.flags & BUDB_DUMP_BUTA) {
2130 /* dump was from buta, use old buta style names */
2131 sprintf(dumpIdStr, "/%d", dumpid);
2132 strcpy(volumeNameStr, "/");
2133 strcat(volumeNameStr, (char *)vl.budb_volumeList_val[i].name);
2134 } else { /* BUDB_DUMP_ADSM */
2135 /* dump was from butc to ADSM, use butc names */
2136 strcpy(dumpIdStr, butcdumpIdStr);
2137 sprintf(volumeNameStr, "/%d", dumpid);
2138 strcat(volumeNameStr, "/");
2139 strcat(volumeNameStr, (char *)vl.budb_volumeList_val[i].name);
2142 rc = xbsa_DeleteObject(&butxInfo, dumpIdStr, volumeNameStr);
2143 if (rc != XBSA_SUCCESS) {
2144 ErrorLog(0, taskId, rc, 0,
2145 "Unable to delete the object %s of dump %s from the server\n",
2146 volumeNameStr, dumpIdStr);
2147 /* Don't exit if volume was not found */
2148 if (rc != BUTX_DELETENOVOL) {
2156 "Deleted volume %s (%u) in dumpID %u from the backup server\n",
2157 vl.budb_volumeList_val[i].name,
2158 vl.budb_volumeList_val[i].id, dumpid);
2162 /* free the memory allocated by RPC for this list */
2163 if (vl.budb_volumeList_val)
2164 free(vl.budb_volumeList_val);
2165 vl.budb_volumeList_len = 0;
2166 vl.budb_volumeList_val = 0;
2171 rc = xbsa_EndTrans(&butxInfo);
2172 if (rc != XBSA_SUCCESS) {
2173 ErrorLog(0, taskId, rc, 0,
2174 "Unable to terminate the current transaction\n");
2175 setStatus(taskId, TASK_ERROR);
2180 /* Switch back to the original server */
2181 rc = InitToServer(taskId, &butxInfo, NULL);
2183 if (vl.budb_volumeList_val)
2184 free(vl.budb_volumeList_val);
2186 setStatus(taskId, TASK_DONE);
2187 FreeNode(taskId); /* free the dump node */
2188 LeaveDeviceQueue(deviceLatch);
2190 /* If we don't find any dumps on the server, rather than returning
2191 * a success, return a failure.
2193 if (!code && onenotfound && allnotfound) {
2194 code = BUTX_DELETENOVOL;
2195 setStatus(taskId, TASK_ERROR);
2197 return (void *)(uintptr_t)(code);