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>
15 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <sys/socket.h>
31 #include <afs/tcdata.h>
32 #include <afs/bubasics.h>
33 #include <afs/budb_client.h>
34 #include <afs/vldbint.h>
35 #include <afs/ktime.h>
36 #include <afs/vlserver.h>
37 #include <afs/volser.h>
38 #include <afs/volint.h>
39 #include <afs/cellconfig.h>
41 #include "error_macros.h"
42 #include "butc_xbsa.h"
46 /* GLOBAL CONFIGURATION PARAMETERS */
47 extern int dump_namecheck;
48 extern int queryoperator;
50 extern int forcemultiple;
52 extern struct ubik_client *cstruct;
53 dlqlinkT savedEntries;
54 dlqlinkT entries_to_flush;
56 afs_int32 flushSavedEntries(), finishDump(), finishTape(), useTape(), addVolume();
58 extern struct rx_connection *UV_Bind();
60 extern char *globalCellName;
62 extern afs_int32 xbsaType;
63 extern afs_int32 groupId;
64 extern afs_int32 BufferSize;
65 extern afs_int32 statusSize;
66 extern FILE *centralLogIO;
69 char *butcdumpIdStr = "/backup_afs_volume_dumps";
70 extern struct butx_transactionInfo butxInfo;
71 extern char *xbsaObjectOwner;
72 extern char *appObjectOwner;
73 extern char *xbsaSecToken;
74 extern char *xbsalGName;
75 extern char *globalButcLog;
78 afs_int32 dataSize; /* Size of data to read on each rx_Read() call */
79 afs_int32 tapeblocks; /* Number of 16K tape datablocks in buffer (!CONF_XBSA) */
83 * Done 1) dump id generation
84 * Done xx) volume fragment number accounting !! I think.
85 * 2) check abort - check after subroutine calls
86 * Done 3) trailer anomaly
87 * 4) trailer damage indicator after partial dumps ( affects scandump )
88 * Done 5) Ensure mount failure logged
89 * 6) Ensure bucoord status calls work
93 * keep token timeout. If no user reponse (idle time > some period)
94 * and tokens about to time out, terminate dump. This provides at
95 * least something usable.
98 #define DUMPNAME(dumpname, name, dbDumpId) \
100 sprintf(dumpname, "%s", name); \
102 sprintf(dumpname, "%s (DumpId %u)", name, dbDumpId);
104 #if defined(AFS_NT40_ENV) || defined(AFS_DARWIN_ENV)
109 memcpy(tm, localtime(t), sizeof(struct tm));
117 int curVolume; /* index in dumpNode of volume */
118 int curVolumeStatus; /* more explicit dump state */
119 afs_uint32 curVolStartPos; /* Starting position of the current volume */
120 afs_uint32 databaseDumpId; /* real dump id, for db */
121 afs_uint32 initialDumpId; /* the initial dump, for appended dumps */
122 afs_int32 volumesDumped; /* # volumes successfully dumped */
123 afs_int32 volumesFailed; /* # volumes that failed to dump */
124 afs_int32 volumesNotDumped; /* # volumes that were not dumped (didn't fail) */
126 /* tape management */
127 char tapeName[TC_MAXTAPENAMELEN];
128 struct butm_tapeInfo *tapeInfoPtr;
129 struct butm_tapeLabel tapeLabel;
130 int wroteLabel; /* If the tape label is written */
132 /* database information */
133 struct budb_dumpEntry lastDump; /* the last dump of this volset */
134 struct budb_dumpEntry dump; /* current dump */
135 struct budb_tapeEntry tape; /* current tape, not used -VA*/
137 /* links to existing info */
138 struct dumpNode *node;
141 /* configuration variables */
142 #define HITEOT(code) ((code == BUTM_IO) || (code == BUTM_EOT) || (code == BUTM_IOCTL))
143 extern int autoQuery;
146 afs_int32 tc_EndMargin;
147 afs_int32 tc_KEndMargin;
150 /* compute the absolute expiration date */
152 calcExpirationDate(expType, expDate, createTime)
155 afs_int32 createTime;
157 struct ktime_date kd;
158 afs_int32 Add_RelDate_to_Time();
163 /* expiration date is relative to the creation time of the dump.
164 * This is the only case that requires any work
166 Int32To_ktimeRelDate(expDate, &kd);
167 return(Add_RelDate_to_Time(&kd, createTime));
180 afs_int32 curr_bserver = 0;
181 struct rx_connection *curr_fromconn = (struct rx_connection *)0;
183 struct rx_connection *Bind(server)
187 if (curr_bserver == server) /* Keep connection if have it */
188 return (curr_fromconn);
190 rx_DestroyConnection (curr_fromconn); /* Otherwise get rid of it */
191 curr_fromconn = (struct rx_connection *)0;
196 curr_fromconn = UV_Bind (server, AFSCONF_VOLUMEPORT); /* Establish new connection */
197 if (curr_fromconn) curr_bserver = server;
200 return (curr_fromconn);
204 * 1) save the chunksize or otherwise ensure tape space remaining is
205 * check frequently enough
206 * 2) This is called once. For partial dumps, need to
207 * ensure that the tape device is left in the correct state for
211 #define BIGCHUNK 102400
214 dumpVolume(curDump, dparamsPtr)
215 struct tc_dumpDesc *curDump;
216 struct dumpRock *dparamsPtr;
218 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
219 struct dumpNode *nodePtr = dparamsPtr->node;
220 afs_int32 taskId = nodePtr->taskID;
223 afs_int32 volumeFlags;
224 afs_int32 kRemaining;
225 afs_int32 rc, code = 0;
227 afs_uint32 volBytesRead;
228 afs_uint32 chunkSize;
229 afs_int32 bytesread; /* rx reads */
230 int endofvolume=0; /* Have we read all volume data */
233 struct volumeHeader hostVolumeHeader;
235 struct rx_call *fromcall = (struct rx_call *)0;
236 struct rx_connection *fromconn;
237 afs_int32 updatedate, fromtid = 0;
238 volEntries volumeInfo;
239 afs_int32 bytesWritten;
240 afs_uint32 statuscount=statusSize, tsize=0;
242 dparamsPtr->curVolumeStatus = DUMP_NOTHING;
244 fromconn = Bind(htonl(curDump->hostAddr)); /* get connection to the server */
246 /* Determine when the volume was last cloned and updated */
247 volumeInfo.volEntries_val = (volintInfo *)0;
248 volumeInfo.volEntries_len = 0;
249 rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid, &volumeInfo);
250 if (rc) ERROR_EXIT(rc);
251 updatedate = volumeInfo.volEntries_val[0].updateDate;
252 curDump->cloneDate = ((curDump->vtype == RWVOL) ?
253 time(0) : volumeInfo.volEntries_val[0].creationDate);
255 if (curDump->date >= curDump->cloneDate)
256 ERROR_EXIT(0); /* not recloned since last dump */
257 if (curDump->date > updatedate) {
258 dparamsPtr->curVolumeStatus = DUMP_NODUMP; /* not modified since last dump */
262 /* Start the volserver transaction and dump */
263 rc = AFSVolTransCreate (fromconn, curDump->vid, curDump->partition, ITBusy, &fromtid);
264 if (rc) ERROR_EXIT(rc);
265 fromcall = rx_NewCall(fromconn);
267 rc = StartAFSVolDump (fromcall, fromtid, curDump->date);
268 if (rc) ERROR_EXIT(rc);
270 dparamsPtr->curVolumeStatus = DUMP_PARTIAL;
271 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
273 /* buffer is place in bufferBlock to write volume data.
274 * butm_writeFileData() assumes the previous BUTM_HDRSIZE bytes
275 * is available to write the tape block header.
277 buffer = bufferBlock + BUTM_HDRSIZE;
279 /* Dump one volume fragment at a time until we dump the full volume.
280 * A volume with more than 1 fragment means the volume will 'span'
283 for (fragmentNumber=1; !endofvolume; fragmentNumber++) { /*frag*/
284 rc = butm_WriteFileBegin(tapeInfoPtr);
286 ErrorLog(1, taskId, rc, tapeInfoPtr->error, "Can't write FileBegin on tape\n");
289 indump = 1; /* first write to tape */
291 /* Create and Write the volume header */
292 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, fragmentNumber);
293 hostVolumeHeader.contd = ((fragmentNumber == 1) ? 0 : TC_VOLCONTD);
294 volumeHeader_hton(&hostVolumeHeader, buffer);
296 rc = butm_WriteFileData(tapeInfoPtr, buffer, 1, sizeof(hostVolumeHeader));
298 ErrorLog(1, taskId, rc, tapeInfoPtr->error, "Can't write VolumeHeader on tape\n");
302 bytesWritten = BUTM_BLOCKSIZE; /* Wrote one tapeblock */
303 tsize += bytesWritten;
305 /* Start reading volume data, rx_Read(), and dumping to the tape
306 * until we've dumped the entire volume (endofvolume == 1). We can
307 * exit this loop early if we find we are close to the end of the
308 * tape; in which case we dump the next fragment on the next tape.
313 while (!endofvolume && !fragmentvolume) { /*w*/
316 /* Check for abort in the middle of writing data */
317 if (volBytesRead >= chunkSize) {
318 chunkSize += BIGCHUNK;
319 if ( checkAbortByTaskId(taskId) )
320 ABORT_EXIT(TC_ABORTEDBYREQUEST);
322 /* set bytes dumped for backup */
324 nodePtr->statusNodePtr->nKBytes = tapeInfoPtr->kBytes;
328 /* Determine how much data to read in upcoming RX_Read() call */
330 /* Check if we are close to the EOT. There should at least be some
331 * data on the tape before it is switched. HACK: we have to split a
332 * volume across tapes because the volume trailer says the dump
333 * continues on the next tape (and not the filemark). This could
334 * result in a volume starting on one tape (no volume data dumped) and
335 * continued on the next tape. It'll work, just requires restore to
336 * switch tapes. This allows many small volumes (<16K) to be dumped.
338 kRemaining = butm_remainingKSpace(tapeInfoPtr);
339 if ( (kRemaining < tc_KEndMargin) &&
340 (volBytesRead || (tapeInfoPtr->position > (isafile?3:2))) ) {
345 /* Guess at how much data to read. So we don't write off end of tape */
346 if (kRemaining < (tapeblocks * 16)) {
347 if (kRemaining < 0) {
348 toread = BUTM_BLKSIZE;
350 toread = ((kRemaining/16) + 1) * BUTM_BLKSIZE;
351 if (toread > dataSize) toread = dataSize;
355 /* Read some volume data. */
356 if (fragmentvolume) {
359 bytesread = rx_Read(fromcall, buffer, toread);
360 volBytesRead += bytesread;
361 if (bytesread != toread) {
362 /* Make sure were at end of volume and not a communication error */
363 rc = rx_Error(fromcall);
364 if (rc) ERROR_EXIT(rc);
369 if (fragmentvolume || endofvolume) {
370 /* Create a volume trailer appending it to this data block */
371 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, fragmentNumber);
372 hostVolumeHeader.contd = (endofvolume ? 0 : TC_VOLCONTD);
373 hostVolumeHeader.magic = TC_VOLENDMAGIC;
374 hostVolumeHeader.endTime = (endofvolume ? time(0) : 0);
375 volumeHeader_hton(&hostVolumeHeader, &buffer[bytesread]);
376 bytesread += sizeof(hostVolumeHeader);
379 /* Write the datablock out */
380 /* full data buffer - write it to tape */
381 rc = butm_WriteFileData(tapeInfoPtr, buffer, tapeblocks, bytesread);
383 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
384 "Can't write VolumeData on tape\n");
387 bytesWritten = tapeblocks * BUTM_BLOCKSIZE;
388 tsize += bytesWritten;
390 /* Display a status line every statusSize or at end of volume */
392 ((tsize >= statuscount) || endofvolume || fragmentvolume) ) {
395 localtime_r(&t, &tm);
396 printf("%02d:%02d:%02d: Task %u: %u KB: %s: %u B\n",
397 tm.tm_hour, tm.tm_min, tm.tm_sec, taskId,
398 tapeInfoPtr->kBytes, hostVolumeHeader.volumeName, tsize);
399 statuscount = tsize + statusSize;
403 /* End the dump before recording it in BUDB as successfully dumped */
404 rc = butm_WriteFileEnd(tapeInfoPtr);
407 ErrorLog(1, taskId, rc, tapeInfoPtr->error, "Can't write FileEnd on tape\n");
411 /* Record in BUDB the volume fragment as succcessfully dumped */
412 volumeFlags = (( fragmentNumber == 1 ) ? BUDB_VOL_FIRSTFRAG : 0);
413 if (endofvolume) 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,
419 volBytesRead, (fragmentNumber-1), volumeFlags);
420 if (rc) ABORT_EXIT(rc);
422 /* If haven't finished dumping the volume, end this
423 * tape and get the next tape.
426 /* Write an EOT marker.
427 * Log the error but ignore it since the dump is effectively done.
428 * Scantape will detect continued volume and not read the EOT.
430 rc = butm_WriteEOT(tapeInfoPtr);
431 if (rc) TapeLog(1, taskId, rc, tapeInfoPtr->error,
432 "Warning: Can't write End-Of-Dump on tape\n");
434 /* Unmount the tape */
435 unmountTape(taskId, tapeInfoPtr);
437 /* Tell the database the tape is complete (and ok) */
438 rc = finishTape(&dparamsPtr->tape, dparamsPtr->tapeInfoPtr->kBytes +
439 (dparamsPtr->tapeInfoPtr->nBytes ? 1 : 0));
440 if (rc) ABORT_EXIT(rc);
442 /* get the next tape. Prompt, mount, and add it into the database */
443 dparamsPtr->tapeSeq++;
444 rc = getDumpTape(dparamsPtr, 1, 0); /* interactive - no append */
445 if (rc) ABORT_EXIT(rc);
447 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
451 dparamsPtr->curVolumeStatus = DUMP_SUCCESS;
455 * If we hit the end, see if this is the first volume on the tape or not.
456 * Also, mark the tape as finished if the tape contains other dumps.
459 ErrorLog(2, taskId, code, tapeInfoPtr->error,
460 "Warning: Dump (%s) hit end-of-tape inferred\n",
461 nodePtr->dumpSetName);
463 if (tapeInfoPtr->position == 2) {
464 dparamsPtr->curVolumeStatus = DUMP_NORETRYEOT;
466 dparamsPtr->curVolumeStatus = DUMP_RETRY;
467 rc = finishTape(&dparamsPtr->tape, dparamsPtr->tapeInfoPtr->kBytes +
468 (dparamsPtr->tapeInfoPtr->nBytes ? 1 : 0));
469 if (rc) ABORT_EXIT(rc);
474 * This is used when an error occurs part way into a volume dump. Clean
475 * the tape state by writing an FileEnd mark. Forgo this action if we hit
479 rc = butm_WriteFileEnd(tapeInfoPtr);
482 ErrorLog(1, taskId, rc, tapeInfoPtr->error, "Can't write FileEnd on tape\n");
487 rc = rx_EndCall (fromcall, 0);
488 if (!code) code = rc;
493 rc = AFSVolEndTrans (fromconn, fromtid, &rcode);
494 if (!code) code = (rc ? rc : rcode);
500 dparamsPtr->curVolumeStatus = DUMP_FAILED;
505 xbsaDumpVolume(curDump, dparamsPtr)
506 struct tc_dumpDesc *curDump;
507 struct dumpRock *dparamsPtr;
510 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
511 struct dumpNode *nodePtr = dparamsPtr->node;
512 char *buffer = bufferBlock;
513 afs_int32 taskId = nodePtr->taskID;
514 afs_int32 rc, code = 0;
516 afs_uint32 volBytesRead;
517 afs_uint32 chunkSize;
518 afs_int32 bytesread; /* rx reads */
519 int endofvolume=0; /* Have we read all volume data */
520 int begindump=0, indump=0; /* if dump transaction started; if dumping data */
521 struct volumeHeader hostVolumeHeader;
523 struct rx_call *fromcall = (struct rx_call *)0;
524 struct rx_connection *fromconn;
525 afs_int32 updatedate, fromtid = 0;
526 volEntries volumeInfo;
527 afs_int32 bytesWritten;
528 afs_uint32 statuscount=statusSize, tsize=0, esize;
531 char dumpIdStr[XBSA_MAX_OSNAME];
532 char volumeNameStr[XBSA_MAX_PATHNAME];
533 static char *dumpDescription = "AFS volume dump";
534 static char *objectDescription = "XBSA - butc";
536 dparamsPtr->curVolumeStatus = DUMP_NOTHING;
538 fromconn = Bind(htonl(curDump->hostAddr)); /* get connection to the server */
540 /* Determine when the volume was last cloned and updated */
541 volumeInfo.volEntries_val = (volintInfo *)0;
542 volumeInfo.volEntries_len = 0;
543 rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid, &volumeInfo);
544 if (rc) ERROR_EXIT(rc);
545 updatedate = volumeInfo.volEntries_val[0].updateDate;
546 curDump->cloneDate = ((curDump->vtype == RWVOL) ?
547 time(0) : volumeInfo.volEntries_val[0].creationDate);
549 /* Get the volume size (in KB) and increase by 25%. Then set as a hyper */
550 esize = volumeInfo.volEntries_val[0].size;
551 esize += (esize/4)+1;
553 if (curDump->date >= curDump->cloneDate)
554 ERROR_EXIT(0); /* not recloned since last dump */
555 if (curDump->date > updatedate) {
556 dparamsPtr->curVolumeStatus = DUMP_NODUMP; /* not modified since last dump */
560 /* Start a new XBSA Transaction */
561 rc = xbsa_BeginTrans(&butxInfo);
562 if (rc != XBSA_SUCCESS) {
563 ErrorLog(1, taskId, rc, 0, "Unable to create a new transaction\n");
566 begindump = 1; /* Will need to do an xbsa_EndTrans */
568 /* Start the volserver transaction and dump. Once started, the
569 * volume status is "partial dump". Also, the transaction with
570 * the volserver is idle until the first read. An idle transaction
571 * will time out in 600 seconds. After the first rx_Read,
572 * the transaction is not idle. See GCTrans().
574 rc = AFSVolTransCreate (fromconn, curDump->vid, curDump->partition, ITBusy, &fromtid);
575 if (rc) ERROR_EXIT(rc);
576 fromcall = rx_NewCall(fromconn);
578 rc = StartAFSVolDump (fromcall, fromtid, curDump->date);
579 if (rc) ERROR_EXIT(rc);
581 dparamsPtr->curVolumeStatus = DUMP_PARTIAL;
582 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
584 /* Tell XBSA what the name and size of volume to write */
585 strcpy(dumpIdStr, butcdumpIdStr); /* "backup_afs_volume_dumps" */
586 sprintf(volumeNameStr, "/%d", dparamsPtr->databaseDumpId);
587 strcat(volumeNameStr, "/");
588 strcat(volumeNameStr, curDump->name); /* <dumpid>/<volname> */
589 hset32(estSize, esize);
590 hshlft(estSize, 10); /* Multiply by 1024 so its in KB */
592 rc = xbsa_WriteObjectBegin(&butxInfo, dumpIdStr, volumeNameStr,
593 xbsalGName, estSize, dumpDescription,
595 if (rc != XBSA_SUCCESS) {
596 ErrorLog(1, taskId, rc, 0,
597 "Unable to begin writing of the fileset data to the server\n");
600 indump = 1; /* Will need to do an xbsa_WriteObjectEnd */
602 /* Create and Write the volume header */
603 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, 1);
604 hostVolumeHeader.contd = 0;
605 volumeHeader_hton(&hostVolumeHeader, buffer);
607 rc = xbsa_WriteObjectData(&butxInfo, buffer, sizeof(struct volumeHeader), &bytesWritten);
608 if (rc != XBSA_SUCCESS) {
609 ErrorLog(1, taskId, rc, 0, "Unable to write VolumeHeader data to the server\n");
612 /* There is a bug in the ADSM library where the bytesWritten is
613 * not filled in, so we set it as correct anyway.
615 bytesWritten = sizeof(struct volumeHeader);
616 if (bytesWritten != sizeof(struct volumeHeader)) {
617 ErrorLog(1, taskId, rc, 0,
618 "The size of VolumeHeader written (%d) does not equal its actual size (%d)\n",
619 bytesWritten, sizeof(struct volumeHeader));
620 ERROR_EXIT(TC_INTERNALERROR);
623 incSize(tapeInfoPtr, sizeof(struct volumeHeader)); /* Increment amount we've written */
624 tsize += bytesWritten;
626 /* Start reading volume data, rx_Read(), and dumping to the tape
627 * until we've dumped the entire volume (endofvolume == 1).
631 while (!endofvolume) { /*w*/
634 /* Check for abort in the middle of writing data */
635 if (volBytesRead >= chunkSize) {
636 chunkSize += BIGCHUNK;
637 if ( checkAbortByTaskId(taskId) )
638 ABORT_EXIT(TC_ABORTEDBYREQUEST);
640 /* set bytes dumped for backup */
642 nodePtr->statusNodePtr->nKBytes = tapeInfoPtr->kBytes;
646 /* Determine how much data to read in upcoming RX_Read() call */
649 /* Read some volume data. */
650 bytesread = rx_Read(fromcall, buffer, toread);
651 volBytesRead += bytesread;
652 if (bytesread != toread) {
655 /* Make sure were at end of volume and not a communication error */
656 rc = rx_Error(fromcall);
657 if (rc) ERROR_EXIT(rc);
661 /* Create a volume trailer appending it to this data block (if not XBSA) */
662 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, 1);
663 hostVolumeHeader.contd = 0;
664 hostVolumeHeader.magic = TC_VOLENDMAGIC;
665 hostVolumeHeader.endTime = time(0);
666 volumeHeader_hton(&hostVolumeHeader, &buffer[bytesread]);
667 bytesread += sizeof(hostVolumeHeader);
669 /* End the dump and transaction with the volserver. We end it now, before
670 * we make the XBSA call because if XBSA blocks, we could time out on the
671 * volserver (After last read, the transaction with the volserver is idle).
673 rc = rx_EndCall (fromcall, 0);
675 if (rc) ERROR_EXIT(rc);
677 rc = AFSVolEndTrans (fromconn, fromtid, &rcode);
679 if (rc) ERROR_EXIT(rc);
682 /* Write the datablock out */
683 rc = xbsa_WriteObjectData(&butxInfo, buffer, bytesread, &bytesWritten);
684 if (rc != XBSA_SUCCESS) {
685 ErrorLog(1, taskId, rc, 0, "Unable to write data to the server\n");
688 /* There is a bug in the ADSM library where the bytesWritten is
689 * not filled in, so we set it as correct anyway.
691 bytesWritten = bytesread;
692 if (bytesWritten != bytesread) {
693 ErrorLog(1, taskId, rc, 0,
694 "The size of data written (%d) does not equal size read (%d)\n",
695 bytesWritten, bytesread);
696 ERROR_EXIT(TC_INTERNALERROR);
699 incSize(tapeInfoPtr, bytesread); /* Increment amount we've written */
700 tsize += bytesWritten;
702 /* Display a status line every statusSize or at end of volume */
703 if ( statusSize && ((tsize >= statuscount) || endofvolume) ) {
706 localtime_r(&t, &tm);
707 printf("%02d:%02d:%02d: Task %u: %u KB: %s: %u B\n",
708 tm.tm_hour, tm.tm_min, tm.tm_sec, taskId,
709 tapeInfoPtr->kBytes, hostVolumeHeader.volumeName, tsize);
710 statuscount = tsize + statusSize;
714 /* End the XBSA transaction before recording it in BUDB as successfully dumped */
715 rc = xbsa_WriteObjectEnd(&butxInfo);
717 if (rc != XBSA_SUCCESS) {
718 ErrorLog(1, taskId, rc, 0,
719 "Unable to terminate writing of the volume data to the server");
722 rc = xbsa_EndTrans(&butxInfo);
724 tapeInfoPtr->position++;
725 if (rc != XBSA_SUCCESS) {
726 ErrorLog(1, taskId, rc, 0, "Unable to terminate the current transaction");
730 /* Record in BUDB the volume fragment as succcessfully dumped */
731 rc = addVolume(0, dparamsPtr->databaseDumpId, dparamsPtr->tapeName,
732 nodePtr->dumps[dparamsPtr->curVolume].name,
733 nodePtr->dumps[dparamsPtr->curVolume].vid,
734 nodePtr->dumps[dparamsPtr->curVolume].cloneDate,
735 dparamsPtr->curVolStartPos,
736 volBytesRead, 0/*frag0*/,
737 (BUDB_VOL_FIRSTFRAG | BUDB_VOL_LASTFRAG));
738 if (rc) ABORT_EXIT(rc);
740 dparamsPtr->curVolumeStatus = DUMP_SUCCESS;
743 /* Cleanup after an error occurs part way into a volume dump */
745 rc = rx_EndCall (fromcall, 0);
746 if (!code) code = rc;
751 rc = AFSVolEndTrans (fromconn, fromtid, &rcode);
752 if (!code) code = (rc ? rc : rcode);
755 /* If this dump failed, what happens to successive retries
756 * of the volume? How do they get recorded in the XBSA database
757 * (overwritten)? If not, we don't record this in the BUDB database
758 * so it will not be removed when we delete the dump. What to do?
759 * Also if the volume was never recorded in the DB (partial dump).
763 rc = xbsa_WriteObjectEnd(&butxInfo);
765 if (rc != XBSA_SUCCESS) {
766 ErrorLog(1, taskId, rc, 0,
767 "Unable to terminate writing of the volume data to the server");
769 tapeInfoPtr->position++;
773 /* End the XBSA Transaction */
774 rc = xbsa_EndTrans(&butxInfo);
776 if (rc != XBSA_SUCCESS) {
777 ErrorLog(1, taskId, rc, 0, "Unable to terminate the current transaction");
784 dparamsPtr->curVolumeStatus = DUMP_FAILED;
790 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.S_un.S_addr
792 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
796 * Go through the list of volumes to dump, dumping each one. The action
797 * taken when a volume dump fails, depends on the passNumber. At minimum,
798 * the failed volume is remembered.
800 * flushSavedEntries - inconsistent treatment for errors. What should
801 * be done for user aborts?
805 dumpPass(dparamsPtr, passNumber)
806 struct dumpRock *dparamsPtr;
809 struct dumpNode *nodePtr = dparamsPtr->node;
810 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
811 afs_int32 taskId = nodePtr->taskID;
812 struct tc_dumpDesc *curDump;
814 afs_int32 code = 0, tcode, dvcode;
817 struct vldbentry vldbEntry;
818 struct sockaddr_in server;
821 TapeLog(2, taskId, 0, 0, "Starting pass %d\n", passNumber);
823 /* while there are more volumes to dump */
824 for (dparamsPtr->curVolume = 0;
825 dparamsPtr->curVolume < nodePtr->arraySize;
826 dparamsPtr->curVolume++)
828 curDump = &nodePtr->dumps[dparamsPtr->curVolume];
829 if (curDump->hostAddr == 0) continue;
831 /* set name of current volume being dumped */
833 strcpy(nodePtr->statusNodePtr->volumeName, curDump->name);
836 /* Determine location of the volume.
837 * In case the volume moved has moved.
841 tcode = bc_GetEntryByID(cstruct, curDump->vid, curDump->vtype, &vldbEntry);
844 ErrorLog(0, taskId, tcode, 0,
845 "Volume %s (%u) failed - Can't find volume in VLDB\n",
846 curDump->name, curDump->vid);
847 curDump->hostAddr = 0;
848 dparamsPtr->volumesFailed++;
852 switch (curDump->vtype)
855 if ( !(vldbEntry.flags & BACK_EXISTS) )
857 ErrorLog(0, taskId, 0, 0,
858 "Volume %s (%u) failed - Backup volume no longer exists\n",
859 curDump->name, curDump->vid);
860 curDump->hostAddr = 0;
861 dparamsPtr->volumesFailed++;
864 /* Fall into RWVOL case */
867 for (e=0; e<vldbEntry.nServers; e++) /* Find the RW volume */
869 if (vldbEntry.serverFlags[e] & ITSRWVOL) break;
874 /* Try to use the server and partition we found the volume on
875 * Otherwise, use the first RO volume.
877 for (e=0; e<vldbEntry.nServers; e++) /* Find the RO volume */
879 if ( (curDump->hostAddr == vldbEntry.serverNumber[e] ) &&
880 (curDump->partition == vldbEntry.serverPartition[e]) )
884 if (e >= vldbEntry.nServers)
885 { /* Didn't find RO volume */
886 for (e=0; e<vldbEntry.nServers; e++) /* Find the first RO volume */
888 if (vldbEntry.serverFlags[e] & ITSROVOL) break;
894 ErrorLog(0, taskId, 0, 0,
895 "Volume %s (%u) failed - Unknown volume type\n",
896 curDump->name, curDump->vid);
897 curDump->hostAddr = 0;
902 if (e >=vldbEntry.nServers)
904 ErrorLog(0, taskId, 0, 0,
905 "Volume %s (%u) failed - Can't find volume entry in VLDB\n",
906 curDump->name, curDump->vid);
907 curDump->hostAddr = 0;
908 dparamsPtr->volumesFailed++;
912 /* Remember the server and partition the volume exists on */
913 memset(&server, 0, sizeof(server));
914 server.sin_addr.s_addr = vldbEntry.serverNumber[e];
916 server.sin_family = AF_INET;
917 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
918 server.sin_len = sizeof(struct sockaddr_in);
920 curDump->hostAddr = HOSTADDR(&server);
921 curDump->partition = vldbEntry.serverPartition[e];
923 /* Determine date from which to do an incremental dump
927 tcode = bcdb_FindClone(nodePtr->parent, curDump->name, &curDump->date);
928 if (tcode) curDump->date = 0;
932 curDump->date = 0; /* do a full dump */
936 if ( checkAbortByTaskId(taskId) )
937 ERROR_EXIT(TC_ABORTEDBYREQUEST);
939 /* Establish connection to volume - UV_ routine expects
940 * host address in network order
943 dvcode = xbsaDumpVolume(curDump, dparamsPtr);
945 dvcode = dumpVolume(curDump, dparamsPtr);
947 action = dparamsPtr->curVolumeStatus;
949 /* Flush volume and tape entries to the database */
950 tcode = flushSavedEntries(action);
951 if (tcode) ERROR_EXIT(tcode);
956 TapeLog(1, taskId, 0, 0, "Volume %s (%u) successfully dumped\n",
957 curDump->name, curDump->vid);
959 ErrorLog(1, taskId, dvcode, 0,
960 "Warning: Termination processing error on volume %s (%u)\n",
961 curDump->name, curDump->vid);
963 curDump->hostAddr = 0;
964 dparamsPtr->volumesDumped++;
969 if (action == DUMP_PARTIAL)
971 ErrorLog(1, taskId, dvcode, 0,
972 "Volume %s (%u) failed - partially dumped\n",
973 curDump->name, curDump->vid);
977 ErrorLog(0, taskId, dvcode, 0,
978 "Volume %s (%u) failed\n", curDump->name, curDump->vid);
982 ErrorLog(0, taskId, dvcode, 0,
983 "Volume %s (%u) not dumped - has not been re-cloned since last dump\n",
984 curDump->name, curDump->vid);
987 if (passNumber == maxpass)
992 ch = retryPrompt(curDump->name, curDump->vid, taskId);
996 case 'r': /* retry */
997 dparamsPtr->curVolume--; /* redump this volume */
1000 case 'o': /* omit */
1001 ErrorLog(1, taskId, 0, 0, "Volume %s (%u) omitted\n",
1002 curDump->name, curDump->vid);
1003 dparamsPtr->volumesFailed++;
1005 case 'a': /* abort */
1006 TapeLog(1, taskId, 0, 0, "Dump aborted\n");
1007 ERROR_EXIT(TC_ABORTEDBYREQUEST);
1010 ERROR_EXIT(TC_INTERNALERROR);
1017 TapeLog(1, taskId, dvcode, 0,
1018 "Volume %s (%u) hit end-of-tape inferred - will retry on next tape\n",
1019 curDump->name, curDump->vid);
1021 /* Get the next tape */
1022 unmountTape(taskId, tapeInfoPtr);
1024 dparamsPtr->tapeSeq++;
1025 tcode = getDumpTape(dparamsPtr, 1, 0); /* interactive - no appends */
1026 if (tcode) ERROR_EXIT(tcode);
1028 dparamsPtr->curVolume--; /* redump this volume */
1032 case DUMP_NORETRYEOT:
1033 ErrorLog(1, taskId, 0, 0, "Volume %s (%u) failed - volume larger than tape\n",
1034 curDump->name, curDump->vid);
1036 /* rewrite the label on the tape - rewind - no need to switch tapes */
1037 tcode = butm_Create(tapeInfoPtr, &dparamsPtr->tapeLabel, 1);
1039 ErrorLog(0, taskId, tcode, tapeInfoPtr->error, "Can't relabel tape\n");
1041 unmountTape(taskId, tapeInfoPtr);
1042 tcode = getDumpTape(dparamsPtr, 1, 0); /* interactive - no appends */
1043 if (tcode) ERROR_EXIT(tcode);
1045 else { /* Record the tape in database */
1046 tapepos = tapeInfoPtr->position;
1047 tcode = useTape(&dparamsPtr->tape,
1048 dparamsPtr->databaseDumpId,
1049 dparamsPtr->tapeName,
1050 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1051 dparamsPtr->tapeLabel.useCount,
1052 dparamsPtr->tapeLabel.creationTime,
1053 dparamsPtr->tapeLabel.expirationDate,
1057 curDump->hostAddr = 0;
1058 dparamsPtr->volumesFailed++;
1062 TapeLog(1, taskId, dvcode, 0,
1063 "Volume %s (%u) not dumped - has not been modified since last dump\n",
1064 curDump->name, curDump->vid);
1066 curDump->hostAddr = 0;
1067 dparamsPtr->volumesNotDumped++;
1071 ErrorLog(1, taskId, dvcode, 0, "Volume %s (%u) failed\n",
1072 curDump->name, curDump->vid);
1079 /* check if we terminated while processing a volume */
1080 if (dparamsPtr->curVolume < nodePtr->arraySize)
1082 TapeLog(2, taskId, 0, 0, "Terminated while processing Volume %s (%u)\n",
1083 curDump->name, curDump->vid);
1086 /* print a summary of this pass */
1087 TapeLog(2, taskId, 0, 0, "End of pass %d: Volumes remaining = %d\n", passNumber,
1088 nodePtr->arraySize - (dparamsPtr->volumesDumped + dparamsPtr->volumesFailed +
1089 dparamsPtr->volumesNotDumped));
1094 struct dumpNode *nodePtr;
1096 struct dumpRock dparams;
1097 struct butm_tapeInfo tapeInfo;
1103 /* for volume setup */
1104 struct tc_dumpDesc *dumpDescPtr;
1106 int failedvolumes = 0;
1107 int dumpedvolumes = 0;
1108 int nodumpvolumes = 0;
1111 char finishedMsg1[50];
1112 char finishedMsg2[50];
1115 afs_int32 allocbufferSize;
1117 extern struct deviceSyncNode *deviceLatch;
1118 extern struct tapeConfig globalTapeConfig;
1119 extern afs_int32 createDump();
1121 taskId = nodePtr->taskID; /* Get task Id */
1122 setStatus(taskId, DRIVE_WAIT);
1123 EnterDeviceQueue(deviceLatch);
1124 clearStatus(taskId, DRIVE_WAIT);
1127 TapeLog(2, taskId, 0, 0, "Dump %s\n", nodePtr->dumpSetName);
1129 /* setup the dump parameters */
1130 memset(&dparams, 0, sizeof(dparams));
1131 dparams.node = nodePtr;
1132 dparams.tapeInfoPtr = &tapeInfo;
1133 dlqInit(&savedEntries);
1136 /* Instantiate the tape module */
1137 tapeInfo.structVersion = BUTM_MAJORVERSION;
1138 code = butm_file_Instantiate(&tapeInfo, &globalTapeConfig);
1140 ErrorLog(0, taskId, code, tapeInfo.error, "Can't initialize the tape module\n");
1145 /* check if abort requested while waiting on device latch */
1146 if ( checkAbortByTaskId(taskId) )
1147 ERROR_EXIT(TC_ABORTEDBYREQUEST);
1149 /* Are there volumes to dump */
1150 if (nodePtr->arraySize == 0)
1152 TLog(taskId, "Dump (%s), no volumes to dump\n", nodePtr->dumpSetName);
1156 /* Allocate a buffer for the dumps. Leave room for header and vol-trailer.
1157 * dataSize is amount of data to read in each rx_Read() call.
1160 /* XBSA dumps have not header */
1161 dataSize = BufferSize;
1162 allocbufferSize = dataSize + sizeof(struct volumeHeader);
1164 tapeblocks = BufferSize / BUTM_BLOCKSIZE; /* # of 16K tapeblocks */
1165 dataSize = (tapeblocks * BUTM_BLKSIZE);
1166 allocbufferSize = BUTM_HDRSIZE + dataSize + sizeof(struct volumeHeader);
1169 bufferBlock = malloc(allocbufferSize);
1171 ErrorLog(0, taskId, TC_NOMEMORY, 0, "Can't allocate BUFFERSIZE for dumps\n");
1172 ERROR_EXIT(TC_NOMEMORY);
1175 /* Determine the dumpid of the most recent dump of this volumeset and dumplevel
1176 * Used when requesting a tape. Done now because once we create the dump, the
1177 * routine will then find the newly created dump.
1179 sprintf(strlevel, "%d", nodePtr->level);
1180 code = bcdb_FindLatestDump(nodePtr->volumeSetName, strlevel, &dparams.lastDump);
1182 if (code != BUDB_NODUMPNAME) {
1183 ErrorLog(0, taskId, code, 0, "Can't read backup database\n");
1186 memset(&dparams.lastDump, 0, sizeof(dparams.lastDump));
1189 code = createDump(&dparams); /* enter dump into database */
1192 ErrorLog(0, taskId, code, 0, "Can't create dump in database\n");
1196 TLog(taskId, "Dump %s (DumpID %u)\n", nodePtr->dumpSetName, dparams.databaseDumpId);
1199 /* mount the tape and write its label */
1200 code = getDumpTape(&dparams, autoQuery, nodePtr->doAppend);
1202 /* Create a dummy tape to satisfy backup databae */
1203 code = getXBSATape(&dparams);
1204 tapeInfo.position = 1;
1207 /* If didn't write the label, remove dump from the database */
1208 if ( !dparams.wroteLabel ) {
1209 i = bcdb_deleteDump(dparams.databaseDumpId, 0, 0, 0);
1210 if ( i && (i != BUDB_NOENT) )
1211 ErrorLog(1, taskId, i, 0, "Warning: Can't delete dump %u from database\n",
1212 dparams.databaseDumpId);
1214 dparams.databaseDumpId = 0;
1216 ERROR_EXIT(code); /* exit with code from getTape */
1219 startTime = time(0);
1220 for (pass = 1; pass <= maxpass; pass++)
1222 lastPass = (pass == maxpass);
1223 code = dumpPass(&dparams, pass);
1224 if (code) ERROR_EXIT(code);
1226 /* if no failed volumes, we're done */
1227 if ((dparams.volumesDumped + dparams.volumesFailed + dparams.volumesNotDumped) ==
1228 nodePtr->arraySize) break;
1232 * Log the error but ignore it since the dump is effectively done.
1233 * Scantape may assume another volume and ask for next tape.
1236 code = butm_WriteEOT(&tapeInfo);
1237 if (code) TapeLog(taskId, code, tapeInfo.error,
1238 "Warning: Can't write end-of-dump on tape\n");
1241 code = finishTape(&dparams.tape, dparams.tapeInfoPtr->kBytes +
1242 (dparams.tapeInfoPtr->nBytes ? 1 : 0));
1243 if (code) ERROR_EXIT(code);
1245 code = finishDump(&dparams.dump);
1246 if (code) ERROR_EXIT(code);
1248 action = dparams.curVolumeStatus;
1249 code = flushSavedEntries(action);
1250 if (code) ERROR_EXIT(code);
1255 if (bufferBlock) free(bufferBlock);
1258 unmountTape(taskId, &tapeInfo);
1262 dumpedvolumes = dparams.volumesDumped;
1263 nodumpvolumes = dparams.volumesNotDumped;
1264 failedvolumes = nodePtr->arraySize - (dumpedvolumes + nodumpvolumes);
1266 /* pass back the number of volumes we failed to dump */
1268 nodePtr->statusNodePtr->volsFailed = failedvolumes;
1271 lastPass = 1; /* In case we aborted */
1273 DUMPNAME(finishedMsg1, nodePtr->dumpSetName, dparams.databaseDumpId);
1274 sprintf(finishedMsg2, "%d volumes dumped", dumpedvolumes);
1277 sprintf(msg, ", %d failed", failedvolumes);
1278 strcat(finishedMsg2, msg);
1282 sprintf(msg, ", %d unchanged", nodumpvolumes);
1283 strcat(finishedMsg2, msg);
1286 if (code == TC_ABORTEDBYREQUEST)
1288 ErrorLog(0, taskId, 0, 0,
1289 "%s: Aborted by request. %s\n", finishedMsg1, finishedMsg2);
1290 clearStatus(taskId, ABORT_REQUEST);
1291 setStatus (taskId, ABORT_DONE);
1295 ErrorLog(0, taskId, code, 0,
1296 "%s: Finished with errors. %s\n", finishedMsg1, finishedMsg2);
1297 setStatus(taskId, TASK_ERROR);
1301 TLog(taskId, "%s: Finished. %s\n", finishedMsg1, finishedMsg2);
1305 /* Record how long the dump took */
1306 if (centralLogIO && startTime) {
1308 afs_int32 hrs, min, sec, tmp;
1310 struct tm tmstart, tmend;
1312 localtime_r(&startTime, &tmstart);
1313 localtime_r(&endTime, &tmend);
1314 timediff = (int)endTime - (int)startTime;
1315 hrs = timediff / 3600;
1316 tmp = timediff % 3600;
1320 sprintf(line, "%-5d %02d/%02d/%04d %02d:%02d:%02d "
1321 "%02d/%02d/%04d %02d:%02d:%02d "
1323 "%s %d of %d volumes dumped (%ld KB)\n",
1324 taskId, tmstart.tm_mon+1, tmstart.tm_mday, tmstart.tm_year+1900,
1325 tmstart.tm_hour, tmstart.tm_min, tmstart.tm_sec,
1326 tmend.tm_mon+1, tmend.tm_mday, tmend.tm_year+1900,
1327 tmend.tm_hour, tmend.tm_min, tmend.tm_sec,
1328 hrs, min, sec, nodePtr->volumeSetName,
1329 dumpedvolumes, dumpedvolumes+failedvolumes,
1330 dparams.tapeInfoPtr->kBytes+1);
1332 fwrite(line, strlen(line), 1, centralLogIO);
1333 fflush(centralLogIO);
1336 setStatus(taskId, TASK_DONE);
1338 FreeNode(taskId); /* free the dump node */
1339 LeaveDeviceQueue(deviceLatch);
1343 #define BELLTIME 60 /* 60 seconds before a bell rings */
1344 #define BELLCHAR 7 /* ascii for bell */
1347 * prompt the user to decide how to handle a failed volume dump. The
1348 * volume parameters describe the volume that failed
1350 * volumeName - name of volume
1351 * volumeId - volume id
1352 * taskId - for job contrl
1354 * character typed by user, one of r, o or a
1358 retryPrompt(volumeName, volumeId, taskId)
1367 setStatus(taskId, OPR_WAIT);
1368 printf("\nDump of volume %s (%u) failed\n\n", volumeName, volumeId);
1370 printf("Please select action to be taken for this volume\n");
1373 printf("r - retry, try dumping this volume again\n");
1374 printf("o - omit, this volume from this dump\n");
1375 printf("a - abort, the entire dump\n");
1385 #ifdef AFS_PTHREAD_ENV
1386 code = GetResponseKey(5, &ch); /* ch stores key pressed */
1388 code = LWP_GetResponseKey(5, &ch); /* ch stores key pressed */
1391 break; /* input is available */
1393 if ( checkAbortByTaskId(taskId) )
1395 clearStatus(taskId, OPR_WAIT);
1396 printf("This tape operation has been aborted by the coordinator\n");
1400 if (time(0) > start + BELLTIME) break;
1402 /* otherwise, we should beep again, check for abort and go back,
1403 * since the GetResponseKey() timed out.
1406 break; /* input is available */
1408 clearStatus(taskId, OPR_WAIT);
1409 if ( ch != 'r' && ch != 'o' && ch != 'a' )
1411 printf("Please select one of the 3 options, r, o or a\n");
1418 /* For testing: it prints the tape label */
1420 struct butm_tapeLabel *tl;
1422 printf("Tape Label\n");
1423 printf(" structVersion = %d\n", tl->structVersion);
1424 printf(" creationTime = %u\n", tl->creationTime);
1425 printf(" expirationDate = %u\n", tl->expirationDate);
1426 printf(" AFSName = %s\n", tl->AFSName);
1427 printf(" cell = %s\n", tl->cell);
1428 printf(" dumpid = %d\n", tl->dumpid);
1429 printf(" useCount = %d\n", tl->useCount);
1430 printf(" comment = %s\n", tl->comment);
1431 printf(" pName = %s\n", tl->pName);
1432 printf(" size = %u\n", tl->size);
1433 printf(" dumpPath = %s\n", tl->dumpPath);
1437 * Create a tape structure to be satisfy the backup database
1438 * even though we don't really use a tape with XBSA.
1440 getXBSATape(dparamsPtr)
1441 struct dumpRock *dparamsPtr;
1443 struct dumpNode *nodePtr = dparamsPtr->node;
1444 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
1445 struct butm_tapeLabel *tapeLabelPtr = &dparamsPtr->tapeLabel;
1448 tc_MakeTapeName(dparamsPtr->tapeName, &nodePtr->tapeSetDesc, dparamsPtr->tapeSeq);
1450 GetNewLabel(tapeInfoPtr, ""/*pName*/, dparamsPtr->tapeName, tapeLabelPtr);
1451 strcpy(tapeLabelPtr->dumpPath, nodePtr->dumpName);
1452 tapeLabelPtr->dumpid = dparamsPtr->databaseDumpId;
1453 tapeLabelPtr->expirationDate = calcExpirationDate(nodePtr->tapeSetDesc.expType,
1454 nodePtr->tapeSetDesc.expDate,
1457 /* printTapeLabel(tapeLabelPtr); For testing */
1459 code = useTape(&dparamsPtr->tape,
1460 dparamsPtr->databaseDumpId,
1461 dparamsPtr->tapeName,
1462 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1463 tapeLabelPtr->useCount,
1464 tapeLabelPtr->creationTime,
1465 tapeLabelPtr->expirationDate,
1466 0 /*tape position*/);
1471 * iterate until the desired tape (as specified by the dump structures)
1475 * 0 - assume the tape is there. Prompt if assumption false
1476 * 1 - prompt regardless
1479 getDumpTape(dparamsPtr, interactiveFlag, append)
1480 struct dumpRock *dparamsPtr;
1481 int interactiveFlag;
1484 struct dumpNode *nodePtr = dparamsPtr->node;
1485 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
1486 struct butm_tapeLabel *newTapeLabelPtr = &dparamsPtr->tapeLabel;
1487 char AFSTapeName[TC_MAXTAPENAMELEN];
1488 afs_int32 taskId = nodePtr->taskID;
1489 struct butm_tapeLabel oldTapeLabel;
1490 struct budb_dumpEntry dumpEntry;
1491 struct budb_tapeEntry tapeEntry;
1492 struct budb_volumeEntry volEntry;
1493 Date oldTapeExpiration, expir;
1497 int askForTape, opcode;
1500 afs_int32 tapepos, lastpos;
1502 extern struct tapeConfig globalTapeConfig;
1503 extern struct udbHandleS udbHandle;
1505 askForTape = interactiveFlag;
1506 dparamsPtr->wroteLabel = 0;
1508 /* Keep prompting for a tape until we get it right */
1511 /* What the name of the tape would be if not appending to it */
1512 tc_MakeTapeName(AFSTapeName, &nodePtr->tapeSetDesc, dparamsPtr->tapeSeq);
1518 code = PromptForTape((doAppend ? APPENDOPCODE : WRITEOPCODE),
1519 AFSTapeName, dparamsPtr->databaseDumpId, taskId, tapecount);
1520 if (code) ERROR_EXIT(code);
1525 /* open the tape device */
1526 code = butm_Mount(tapeInfoPtr, AFSTapeName);
1529 TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
1533 /* Read the tape label */
1534 code = butm_ReadLabel(tapeInfoPtr, &oldTapeLabel, 1); /* rewind */
1536 if (tapeInfoPtr->error) {
1537 ErrorLog(0, taskId, code, tapeInfoPtr->error,
1538 "Warning: Tape error while reading label (will proceed with dump)\n");
1540 memset(&oldTapeLabel, 0, sizeof(oldTapeLabel));
1543 /* Check if null tape. Prior 3.3, backup tapes have no dump id */
1544 if ( (strcmp(oldTapeLabel.AFSName,"") == 0) && (oldTapeLabel.dumpid == 0) )
1548 TLog(taskId, "Dump not found on tape. Proceeding with initial dump\n");
1552 else if (doAppend) /* appending */
1554 /* Check that we don't have a database dump tape */
1555 if ( databaseTape(oldTapeLabel.AFSName) )
1557 char gotName[BU_MAXTAPELEN+32];
1559 /* label does not match */
1560 LABELNAME(gotName, &oldTapeLabel);
1561 TLog(taskId, "Can't append to database tape %s\n", gotName);
1565 /* Verify that the tape is of version 4 (AFS 3.3) or greater */
1566 if (oldTapeLabel.structVersion < TAPE_VERSION_4)
1568 TLog(taskId, "Can't append: requires tape version %d or greater\n",
1573 /* Verify that the last tape of the dump set is in the drive.
1574 * volEntry will be zeroed if last dump has no volume entries.
1576 code = bcdb_FindLastTape(oldTapeLabel.dumpid, &dumpEntry, &tapeEntry, &volEntry);
1578 ErrorLog(0, taskId, code, 0,
1579 "Can't append: Can't find last volume of dumpId %u in database\n",
1580 oldTapeLabel.dumpid);
1581 printf("Please scan the dump in or choose another tape\n");
1584 lastpos = (volEntry.position ? volEntry.position : tapeEntry.labelpos);
1586 if (strcmp(TNAME(&oldTapeLabel),tapeEntry.name))
1588 char expName[BU_MAXTAPELEN+32], gotName[BU_MAXTAPELEN+32];
1590 TAPENAME(expName, tapeEntry.name, oldTapeLabel.dumpid);
1591 LABELNAME(gotName, &oldTapeLabel);
1594 "Can't append: Last tape in dump-set is %s, label seen %s\n",
1599 /* After reading the tape label, we now know what it is */
1600 strcpy (AFSTapeName, oldTapeLabel.AFSName); /* the real name */
1601 strcpy (tapeInfoPtr->name, oldTapeLabel.AFSName); /* the real name */
1603 /* Position after last volume on the tape */
1604 code = butm_SeekEODump(tapeInfoPtr, lastpos);
1607 ErrorLog(0, taskId, code, tapeInfoPtr->error,
1608 "Can't append: Can't position to end of dump on tape %s\n",
1613 /* Track size of tape - set after seek since seek changes the value */
1614 tapeInfoPtr->kBytes = tapeEntry.useKBytes;
1616 else /* not appending */
1619 afs_uint32 dmp, parent;
1620 struct budb_dumpEntry de, de2;
1623 /* Check if tape name is not what expected - null tapes are acceptable
1624 * Don't do check if the tape has a user defined label.
1626 if ( dump_namecheck && (strcmp(oldTapeLabel.pName,"")==0) )
1628 if ( strcmp(oldTapeLabel.AFSName,"") && /* not null tape */
1629 strcmp(oldTapeLabel.AFSName,AFSTapeName) ) /* not expected name */
1631 TLog(taskId, "Tape label expected %s, label seen %s\n",
1632 AFSTapeName, oldTapeLabel.AFSName);
1636 /* Check that we don't have a database dump tape */
1637 if ( databaseTape(oldTapeLabel.AFSName) )
1639 /* label does not match */
1640 TLog(taskId, "Tape label expected %s, can't dump to database tape %s\n",
1641 AFSTapeName, oldTapeLabel.AFSName);
1646 /* Verify the tape has not expired - only check if not appending */
1647 if ( !tapeExpired(&oldTapeLabel) ) {
1648 TLog(taskId, "This tape has not expired\n");
1652 /* Given a tape dump with good data, verify we don't overwrite recent dumps
1653 * and also verify that the volume will be restorable - if not print warnings
1655 if (oldTapeLabel.dumpid)
1657 /* Do not overwrite a tape that belongs to the dump's dumpset */
1658 tapeid = (dparamsPtr->initialDumpId ? dparamsPtr->initialDumpId :
1659 dparamsPtr->databaseDumpId);
1660 if (oldTapeLabel.dumpid == tapeid)
1662 ErrorLog(0, taskId, 0, 0,
1663 "Can't overwrite tape containing the dump in progress\n");
1667 /* Since the dumpset on this tape will be deleted from database, check if
1668 * any of the dump's parent-dumps are on this tape.
1670 for (dmp=nodePtr->parent; dmp; dmp=de.parent)
1672 code = bcdb_FindDumpByID(dmp, &de);
1674 ErrorLog(0, taskId, 0, 0,
1675 "Warning: Can't find parent dump %u in backup database\n",
1680 tapeid = (de.initialDumpID ? de.initialDumpID : de.id);
1681 if (oldTapeLabel.dumpid == tapeid) {
1682 ErrorLog(0, taskId, 0, 0,
1683 "Can't overwrite the parent dump %s (DumpID %u)\n",
1689 /* Since the dumpset on this tape will be deleted from database, check if
1690 * any of the dumps in this dumpset are most-recent-dumps.
1692 for (dmp=oldTapeLabel.dumpid; dmp; dmp=de.appendedDumpID) {
1693 if (dmp == dparamsPtr->lastDump.id) {
1694 memcpy(&de, &dparamsPtr->lastDump, sizeof(de));
1695 memcpy(&de2, &dparamsPtr->lastDump, sizeof(de2));
1698 code = bcdb_FindDumpByID(dmp, &de);
1700 sprintf(strlevel, "%d", de.level);
1701 code = bcdb_FindLatestDump(de.volumeSetName, strlevel, &de2);
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",
1713 ErrorLog(0, taskId, 0, 0,
1714 "Warning: Overwriting most recent dump of the '%s' volumeset: %s (DumpID %u)\n",
1715 de.volumeSetName, de.name, de.id);
1719 } /* if (oldTapeLabel.dumpid) */
1720 } /* else not appending */
1723 * Now have the right tape. Create a new label for the tape
1724 * Appended labels have the dump's dumpId - labels at beginnings of
1725 * tape have the initial dump's dumpId.
1726 * Appended labels do not increment the useCount.
1727 * Labels at beginnings of tape use the most future expiration of the dump set.
1729 GetNewLabel(tapeInfoPtr, oldTapeLabel.pName, AFSTapeName, newTapeLabelPtr);
1730 strcpy(newTapeLabelPtr->dumpPath, nodePtr->dumpName);
1731 newTapeLabelPtr->expirationDate =
1732 calcExpirationDate(nodePtr->tapeSetDesc.expType, nodePtr->tapeSetDesc.expDate, time(0));
1733 newTapeLabelPtr->dumpid = dparamsPtr->databaseDumpId;
1734 newTapeLabelPtr->useCount = oldTapeLabel.useCount;
1737 newTapeLabelPtr->useCount++;
1738 if (dparamsPtr->initialDumpId) {
1739 newTapeLabelPtr->dumpid = dparamsPtr->initialDumpId;
1740 expir = ExpirationDate(dparamsPtr->initialDumpId);
1741 if (expir > newTapeLabelPtr->expirationDate)
1742 newTapeLabelPtr->expirationDate = expir;
1746 /* write the label on the tape - rewind if not appending and vice-versa */
1747 code = butm_Create(tapeInfoPtr, newTapeLabelPtr, !doAppend);
1750 char gotName[BU_MAXTAPELEN+32];
1752 LABELNAME(gotName, newTapeLabelPtr);
1753 TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't label tape as %s\n", gotName);
1756 dparamsPtr->wroteLabel = 1; /* Remember we wrote the label */
1757 tapepos = tapeInfoPtr->position-1;
1759 strcpy(dparamsPtr->tapeName, TNAME(newTapeLabelPtr));
1761 /* 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);
1771 code = bcdb_MakeDumpAppended(dparamsPtr->databaseDumpId,
1772 dparamsPtr->initialDumpId, nodePtr->tapeSetDesc.b);
1774 ErrorLog(2, taskId, code, 0,
1775 "Warning: Can't append dump %u to dump %u in database\n",
1776 dparamsPtr->databaseDumpId, dparamsPtr->initialDumpId);
1778 LABELNAME(gotName, &oldTapeLabel);
1779 TLog(taskId, "Appending dump %s (DumpID %u) to tape %s\n",
1780 nodePtr->dumpSetName, dparamsPtr->databaseDumpId, gotName);
1783 /* If not appending, delete overwritten dump from the database */
1786 if ( (oldTapeLabel.structVersion >= TAPE_VERSION_3) && oldTapeLabel.dumpid )
1788 code = bcdb_deleteDump(oldTapeLabel.dumpid, 0, 0, 0);
1789 if ( code && (code != BUDB_NOENT) )
1790 ErrorLog(0, taskId, code, 0,
1791 "Warning: Can't delete old dump %u from database\n",
1792 oldTapeLabel.dumpid);
1796 code = useTape(&dparamsPtr->tape,
1797 dparamsPtr->databaseDumpId,
1798 dparamsPtr->tapeName,
1799 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1800 newTapeLabelPtr->useCount,
1801 newTapeLabelPtr->creationTime,
1802 newTapeLabelPtr->expirationDate,
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);
1825 makeVolumeHeader(vhptr, dparamsPtr, fragmentNumber)
1826 struct volumeHeader *vhptr;
1827 struct dumpRock *dparamsPtr;
1830 struct dumpNode *nodePtr = dparamsPtr->node;
1831 struct tc_dumpDesc *curDump;
1834 curDump = &nodePtr->dumps[dparamsPtr->curVolume];
1836 memset(vhptr, 0, sizeof(*vhptr));
1837 strcpy(vhptr->volumeName, curDump->name);
1838 vhptr->volumeID = curDump->vid;
1839 vhptr->cloneDate = curDump->cloneDate;
1840 vhptr->server = curDump->hostAddr;
1841 vhptr->part = curDump->partition;
1842 vhptr->from = curDump->date;
1843 vhptr->frag = fragmentNumber;
1845 vhptr->magic = TC_VOLBEGINMAGIC;
1846 vhptr->dumpID = dparamsPtr->databaseDumpId; /* real dump id */
1847 vhptr->level = nodePtr->level;
1848 vhptr->parentID = nodePtr->parent;
1850 vhptr->versionflags = CUR_TAPE_VERSION;
1851 strcpy(vhptr->dumpSetName, nodePtr->dumpSetName);
1852 strcpy(vhptr->preamble,"H++NAME#");
1853 strcpy(vhptr->postamble,"T--NAME#");
1858 volumeHeader_hton(hostPtr, netPtr)
1859 struct volumeHeader *hostPtr, *netPtr;
1861 struct volumeHeader volHdr;
1863 strcpy(volHdr.preamble, hostPtr->preamble);
1864 strcpy(volHdr.postamble, hostPtr->postamble);
1865 strcpy(volHdr.volumeName, hostPtr->volumeName);
1866 strcpy(volHdr.dumpSetName, hostPtr->dumpSetName);
1867 volHdr.volumeID = htonl(hostPtr->volumeID);
1868 volHdr.server = htonl(hostPtr->server);
1869 volHdr.part = htonl(hostPtr->part);
1870 volHdr.from = htonl(hostPtr->from);
1871 volHdr.frag = htonl(hostPtr->frag);
1872 volHdr.magic = htonl(hostPtr->magic);
1873 volHdr.contd = htonl(hostPtr->contd);
1874 volHdr.dumpID = htonl(hostPtr->dumpID);
1875 volHdr.level = htonl(hostPtr->level);
1876 volHdr.parentID = htonl(hostPtr->parentID);
1877 volHdr.endTime = htonl(hostPtr->endTime);
1878 volHdr.versionflags = htonl(hostPtr->versionflags);
1879 volHdr.cloneDate = htonl(hostPtr->cloneDate);
1881 memcpy(netPtr, &volHdr, sizeof(struct volumeHeader));
1884 /* database related routines */
1887 createDump(dparamsPtr)
1888 struct dumpRock *dparamsPtr;
1890 struct dumpNode *nodePtr = dparamsPtr->node;
1891 struct budb_dumpEntry *dumpPtr;
1894 dumpPtr = &dparamsPtr->dump;
1895 memset(dumpPtr, 0, sizeof(*dumpPtr));
1897 /* id filled in by database */
1898 dumpPtr->parent = nodePtr->parent;
1899 dumpPtr->level = nodePtr->level;
1903 if (xbsaType == XBSA_SERVER_TYPE_ADSM) {
1904 strcpy(dumpPtr->tapes.tapeServer, butxInfo.serverName);
1905 dumpPtr->flags = BUDB_DUMP_ADSM;
1907 if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1908 /* The current server (API) doesn't provide the function required
1909 * to specify a server at startup time. For that reason, we can't
1910 * be sure that the server name supplied by the user in the user-
1911 * defined configuration file is correct. We set a flag here so
1912 * we know at restore time that the servername info in the backup
1913 * database may be incorrect. We will not allow a server switch
1914 * at that time, even if the server at restore time supports
1917 dumpPtr->flags |= BUDB_DUMP_XBSA_NSS;
1921 strcpy(dumpPtr->volumeSetName, nodePtr->volumeSetName);
1922 strcpy(dumpPtr->dumpPath, nodePtr->dumpName);
1923 strcpy(dumpPtr->name, nodePtr->dumpSetName);
1924 dumpPtr->created = 0; /* let database assign it */
1925 dumpPtr->incTime = 0; /* not really used */
1926 dumpPtr->nVolumes = 0;
1927 dumpPtr->initialDumpID = 0;
1929 dumpPtr->tapes.id = groupId;
1930 dumpPtr->tapes.b = 1;
1931 dumpPtr->tapes.maxTapes = 0;
1932 strcpy(dumpPtr->tapes.format, nodePtr->tapeSetDesc.format);
1934 /* principal filled in by database */
1936 /* now call the database to create the entry */
1937 code = bcdb_CreateDump(dumpPtr);
1938 if (code == 0) dparamsPtr->databaseDumpId = dumpPtr->id;
1945 * Initialize to a specific server. The first time, we remember the
1946 * server as the original server and go back to it each time we pass 0
1949 afs_int32 InitToServer(afs_int32 taskId, struct butx_transactionInfo *butxInfoP, char *server)
1951 static char origserver[BSA_MAX_DESC];
1953 afs_int32 rc, code=0;
1956 strcpy(origserver,"");
1960 if (!server) server = origserver; /* return to original server */
1961 if (strcmp(server,"") == 0) return 0; /* No server, do nothing */
1962 if (strcmp(butxInfoP->serverName,server) == 0) return 0; /* same server, do nothing */
1963 if (strcmp(origserver,"") == 0) strcpy(origserver, server);/* remember original server */
1965 if (strcmp(butxInfoP->serverName,"") != 0) {
1966 /* If already connected to a server, disconnect from it.
1967 * Check to see if our server does not support switching.
1969 if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1970 ErrorLog(0, taskId, TC_BADTASK, 0,
1971 "This version of XBSA libraries does not support switching "
1972 "from server %s to server %s\n",
1973 butxInfoP->serverName, server);
1977 rc = xbsa_Finalize(&butxInfo);
1978 if (rc != XBSA_SUCCESS) {
1979 ErrorLog(0, taskId, rc, 0,
1980 "InitToServer: Unable to terminate the connection to server %s\n",
1981 butxInfoP->serverName);
1986 /* initialize to the new server */
1987 rc = xbsa_Initialize(&butxInfo, xbsaObjectOwner, appObjectOwner, xbsaSecToken, server);
1988 if (rc != XBSA_SUCCESS) {
1989 ErrorLog(0, taskId, rc, 0,
1990 "InitToServer: Unable to initialize the XBSA library to server %s\n",
2004 struct deleteDumpIf *ptr;
2007 afs_int32 rc, code=0;
2009 afs_int32 index, next, dbTime;
2011 struct budb_dumpEntry dumpEntry;
2012 char tapeName[BU_MAXTAPELEN];
2013 char dumpIdStr[XBSA_MAX_OSNAME];
2014 char volumeNameStr[XBSA_MAX_PATHNAME];
2017 int allnotfound=1, onenotfound=0;
2018 extern struct udbHandleS udbHandle;
2019 extern struct deviceSyncNode *deviceLatch;
2021 setStatus(taskId, DRIVE_WAIT);
2022 EnterDeviceQueue(deviceLatch);
2023 clearStatus(taskId, DRIVE_WAIT);
2025 dumpid = ptr->dumpID;
2026 taskId = ptr->taskId; /* Get task Id */
2029 TapeLog(2, taskId, 0, 0, "Delete Dump %u\n", dumpid);
2031 vl.budb_volumeList_len = 0;
2032 vl.budb_volumeList_val = 0;
2035 /* Get the dump info for the dump we are deleting */
2036 rc = bcdb_FindDumpByID(dumpid, &dumpEntry);
2038 ErrorLog(0, taskId, rc, 0, "Unable to locate dump ID %u in database\n", dumpid);
2039 setStatus(taskId, TASK_ERROR);
2043 /* we must make sure that we are configured with the correct type of
2044 * XBSA server for this dump delete! Only those dumped to an ADSM server.
2046 if ( (xbsaType == XBSA_SERVER_TYPE_ADSM) &&
2047 !((dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) ) {
2048 ErrorLog(0, taskId, TC_BADTASK, 0,
2049 "The dump %u requested for deletion is incompatible with this instance of butc\n",
2051 setStatus(taskId, TASK_ERROR);
2052 ERROR_EXIT(TC_BADTASK);
2055 /* Make sure we are connected to the correct server. If not, switch to it if appropriate */
2056 if ( (strlen((char *)dumpEntry.tapes.tapeServer) != 0) &&
2057 (strcmp((char *)dumpEntry.tapes.tapeServer,butxInfo.serverName) != 0)) {
2059 /* Check to see if the tapeServer name is trustworthy */
2060 if ((dumpEntry.flags & (BUDB_DUMP_XBSA_NSS | BUDB_DUMP_BUTA)) && !forcemultiple) {
2061 /* The dump was made with a version of the XBSA interface
2062 * that didn't allow switching of servers, we can't be sure
2063 * that the servername in the backup database is correct. So,
2064 * we will check the servername and log it if they don't match;
2065 * but we will try to do the delete without switching servers.
2067 TLog(taskId, "The dump %d requested for deletion is on server %s "
2068 "but butc is connected to server %s "
2069 "(Attempting to delete the dump anyway)\n",
2070 dumpid, (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2072 TLog(taskId, "The dump %u requested for deletion is on server %s "
2073 "but butc is connected to server %s "
2074 "(switching servers)\n",
2075 dumpid, (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2077 rc = InitToServer(taskId, &butxInfo, (char *)dumpEntry.tapes.tapeServer);
2078 if (rc != XBSA_SUCCESS) {
2079 setStatus(taskId, TASK_ERROR);
2085 /* Start a new Transaction */
2086 rc = xbsa_BeginTrans(&butxInfo);
2087 if (rc != XBSA_SUCCESS) {
2088 ErrorLog(0, taskId, rc, 0, "Unable to create a new transaction\n");
2089 setStatus(taskId, TASK_ERROR);
2094 /* Query the backup database for list of volumes to delete */
2095 for (index=next=0; index!=-1; index=next) {
2096 rc = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client,
2097 UF_SINGLESERVER, BUDB_MAJORVERSION,
2104 if (rc == BUDB_ENDOFLIST) break;
2105 ErrorLog(0, taskId, rc, 0, "Can't find volume info for dump %d\n", dumpid);
2106 setStatus(taskId, TASK_ERROR);
2110 /* Delete all volumes on the list */
2111 for (i=0; i<vl.budb_volumeList_len; i++) {
2112 if (dumpEntry.flags & BUDB_DUMP_BUTA) {
2113 /* dump was from buta, use old buta style names */
2114 sprintf(dumpIdStr, "/%d", dumpid);
2115 strcpy(volumeNameStr, "/");
2116 strcat(volumeNameStr, (char *)vl.budb_volumeList_val[i].name);
2117 } else { /* BUDB_DUMP_ADSM */
2118 /* dump was from butc to ADSM, use butc names */
2119 strcpy(dumpIdStr, butcdumpIdStr);
2120 sprintf(volumeNameStr, "/%d", dumpid);
2121 strcat(volumeNameStr, "/");
2122 strcat(volumeNameStr, (char *)vl.budb_volumeList_val[i].name);
2125 rc = xbsa_DeleteObject(&butxInfo, dumpIdStr, volumeNameStr);
2126 if (rc != XBSA_SUCCESS) {
2127 ErrorLog(0, taskId, rc, 0,
2128 "Unable to delete the object %s of dump %s from the server\n",
2129 volumeNameStr, dumpIdStr);
2130 /* Don't exit if volume was not found */
2131 if (rc != BUTX_DELETENOVOL) {
2138 TLog(0, "Deleted volume %s (%u) in dumpID %u from the backup server\n",
2139 vl.budb_volumeList_val[i].name, vl.budb_volumeList_val[i].id, dumpid);
2143 /* free the memory allocated by RPC for this list */
2144 if (vl.budb_volumeList_val) free(vl.budb_volumeList_val);
2145 vl.budb_volumeList_len = 0;
2146 vl.budb_volumeList_val = 0;
2151 rc = xbsa_EndTrans(&butxInfo);
2152 if (rc != XBSA_SUCCESS) {
2153 ErrorLog(0, taskId, rc, 0, "Unable to terminate the current transaction\n");
2154 setStatus(taskId, TASK_ERROR);
2159 /* Switch back to the original server */
2160 rc = InitToServer(taskId, &butxInfo, NULL);
2162 if (vl.budb_volumeList_val) free(vl.budb_volumeList_val);
2164 setStatus(taskId, TASK_DONE);
2165 FreeNode(taskId); /* free the dump node */
2166 LeaveDeviceQueue(deviceLatch);
2168 /* If we don't find any dumps on the server, rather than returning
2169 * a success, return a failure.
2171 if (!code && onenotfound && allnotfound) {
2172 code = BUTX_DELETENOVOL;
2173 setStatus(taskId, TASK_ERROR);