Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / butc / dump.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14
15 #include <rx/xdr.h>
16 #include <rx/rx.h>
17 #include <lwp.h>
18 #include <lock.h>
19 #include <afs/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>
32
33 #include "butc_internal.h"
34 #include "error_macros.h"
35 #include "butc_xbsa.h"
36 #include "afs/butx.h"
37
38 /* GLOBAL CONFIGURATION PARAMETERS */
39 extern int dump_namecheck;
40 extern int queryoperator;
41 extern int isafile;
42 extern int forcemultiple;
43
44 extern struct ubik_client *cstruct;
45 dlqlinkT savedEntries;
46 dlqlinkT entries_to_flush;
47
48 extern afs_int32 groupId;
49 extern afs_int32 BufferSize;
50 extern afs_int32 statusSize;
51 extern FILE *centralLogIO;
52 afs_int32 lastPass = 0;
53 #ifdef xbsa
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;
62 #endif /*xbsa */
63
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) */
66
67 /* TBD
68 *
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
76 *
77 * notes
78 * pass 3:
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.
82 */
83
84 struct dumpRock {
85 /* status only */
86 int tapeSeq;
87 int curVolume; /* index in dumpNode of volume */
88 int curVolumeStatus; /* more explicit dump state */
89 afs_uint32 curVolStartPos; /* Starting position of the current volume */
90 afs_uint32 databaseDumpId; /* real dump id, for db */
91 afs_uint32 initialDumpId; /* the initial dump, for appended dumps */
92 afs_int32 volumesDumped; /* # volumes successfully dumped */
93 afs_int32 volumesFailed; /* # volumes that failed to dump */
94 afs_int32 volumesNotDumped; /* # volumes that were not dumped (didn't fail) */
95
96 /* tape management */
97 char tapeName[TC_MAXTAPENAMELEN];
98 struct butm_tapeInfo *tapeInfoPtr;
99 struct butm_tapeLabel tapeLabel;
100 int wroteLabel; /* If the tape label is written */
101
102 /* database information */
103 struct budb_dumpEntry lastDump; /* the last dump of this volset */
104 struct budb_dumpEntry dump; /* current dump */
105 struct budb_tapeEntry tape; /* current tape, not used -VA */
106
107 /* links to existing info */
108 struct dumpNode *node;
109 };
110
111 /* Forward declarations */
112
113 int makeVolumeHeader(struct volumeHeader *, struct dumpRock *, int);
114 int volumeHeader_hton(struct volumeHeader *, struct volumeHeader *);
115 char retryPrompt(char *, afs_int32, afs_uint32);
116 int getDumpTape(struct dumpRock *, int, afs_int32);
117 int getXBSATape(struct dumpRock *);
118 afs_int32 createDump(struct dumpRock *);
119
120 /* configuration variables */
121 #define HITEOT(code) ((code == BUTM_IO) || (code == BUTM_EOT) || (code == BUTM_IOCTL))
122 extern int autoQuery;
123 extern int maxpass;
124
125 afs_int32 tc_EndMargin;
126 afs_int32 tc_KEndMargin;
127 static char *bufferBlock;
128
129 /* compute the absolute expiration date */
130 afs_int32
131 calcExpirationDate(afs_int32 expType, afs_int32 expDate, afs_int32 createTime)
132 {
133 struct ktime_date kd;
134
135 switch (expType) {
136 case BC_REL_EXPDATE:
137 /* expiration date is relative to the creation time of the dump.
138 * This is the only case that requires any work
139 */
140 Int32To_ktimeRelDate(expDate, &kd);
141 return (Add_RelDate_to_Time(&kd, createTime));
142
143 case BC_ABS_EXPDATE:
144 return (expDate);
145
146 case BC_NO_EXPDATE:
147 default:
148 return (0);
149 }
150 }
151
152 afs_uint32 curr_bserver = 0;
153 struct rx_connection *curr_fromconn = (struct rx_connection *)0;
154
155 struct rx_connection *
156 Bind(afs_uint32 server)
157 {
158 if (curr_fromconn) {
159 if (curr_bserver == server) /* Keep connection if have it */
160 return (curr_fromconn);
161
162 rx_DestroyConnection(curr_fromconn); /* Otherwise get rid of it */
163 curr_fromconn = (struct rx_connection *)0;
164 curr_bserver = 0;
165 }
166
167 if (server) {
168 curr_fromconn = UV_Bind(server, AFSCONF_VOLUMEPORT); /* Establish new connection */
169 if (curr_fromconn)
170 curr_bserver = server;
171 }
172
173 return (curr_fromconn);
174 }
175
176 /* notes
177 * 1) save the chunksize or otherwise ensure tape space remaining is
178 * check frequently enough
179 * 2) This is called once. For partial dumps, need to
180 * ensure that the tape device is left in the correct state for
181 * further dumps.
182 *
183 */
184 #define BIGCHUNK 102400
185
186 afs_int32
187 dumpVolume(struct tc_dumpDesc * curDump, struct dumpRock * dparamsPtr)
188 {
189 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
190 struct dumpNode *nodePtr = dparamsPtr->node;
191 afs_int32 taskId = nodePtr->taskID;
192 char *buffer;
193 int fragmentNumber;
194 afs_int32 volumeFlags;
195 afs_int32 kRemaining;
196 afs_int32 rc, code = 0;
197 afs_int32 toread;
198 afs_uint32 volBytesRead;
199 afs_uint32 chunkSize;
200 afs_int32 bytesread; /* rx reads */
201 int endofvolume = 0; /* Have we read all volume data */
202 int indump = 0;
203 int fragmentvolume;
204 struct volumeHeader hostVolumeHeader;
205
206 struct rx_call *fromcall = (struct rx_call *)0;
207 struct rx_connection *fromconn;
208 afs_int32 updatedate, fromtid = 0;
209 volEntries volumeInfo;
210 afs_int32 bytesWritten;
211 afs_uint32 statuscount = statusSize, tsize = 0;
212
213 dparamsPtr->curVolumeStatus = DUMP_NOTHING;
214
215 fromconn = Bind(htonl(curDump->hostAddr)); /* get connection to the server */
216
217 /* Determine when the volume was last cloned and updated */
218 volumeInfo.volEntries_val = (volintInfo *) 0;
219 volumeInfo.volEntries_len = 0;
220 rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid,
221 &volumeInfo);
222 if (rc)
223 ERROR_EXIT(rc);
224 updatedate = volumeInfo.volEntries_val[0].updateDate;
225 curDump->cloneDate =
226 ((curDump->vtype ==
227 RWVOL) ? time(0) : volumeInfo.volEntries_val[0].creationDate);
228
229 if (curDump->date >= curDump->cloneDate)
230 ERROR_EXIT(0); /* not recloned since last dump */
231 if (curDump->date > updatedate) {
232 dparamsPtr->curVolumeStatus = DUMP_NODUMP; /* not modified since last dump */
233 ERROR_EXIT(0);
234 }
235
236 /* Start the volserver transaction and dump */
237 rc = AFSVolTransCreate(fromconn, curDump->vid, curDump->partition, ITBusy,
238 &fromtid);
239 if (rc)
240 ERROR_EXIT(rc);
241 fromcall = rx_NewCall(fromconn);
242
243 rc = StartAFSVolDump(fromcall, fromtid, curDump->date);
244 if (rc)
245 ERROR_EXIT(rc);
246
247 dparamsPtr->curVolumeStatus = DUMP_PARTIAL;
248 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
249
250 /* buffer is place in bufferBlock to write volume data.
251 * butm_writeFileData() assumes the previous BUTM_HDRSIZE bytes
252 * is available to write the tape block header.
253 */
254 buffer = bufferBlock + BUTM_HDRSIZE;
255
256 /* Dump one volume fragment at a time until we dump the full volume.
257 * A volume with more than 1 fragment means the volume will 'span'
258 * 2 or more tapes.
259 */
260 for (fragmentNumber = 1; !endofvolume; fragmentNumber++) { /*frag */
261 rc = butm_WriteFileBegin(tapeInfoPtr);
262 if (rc) {
263 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
264 "Can't write FileBegin on tape\n");
265 ERROR_EXIT(rc);
266 }
267 indump = 1; /* first write to tape */
268
269 /* Create and Write the volume header */
270 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, fragmentNumber);
271 hostVolumeHeader.contd = ((fragmentNumber == 1) ? 0 : TC_VOLCONTD);
272 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)buffer);
273
274 rc = butm_WriteFileData(tapeInfoPtr, buffer, 1,
275 sizeof(hostVolumeHeader));
276 if (rc) {
277 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
278 "Can't write VolumeHeader on tape\n");
279 ERROR_EXIT(rc);
280 }
281
282 bytesWritten = BUTM_BLOCKSIZE; /* Wrote one tapeblock */
283 tsize += bytesWritten;
284
285 /* Start reading volume data, rx_Read(), and dumping to the tape
286 * until we've dumped the entire volume (endofvolume == 1). We can
287 * exit this loop early if we find we are close to the end of the
288 * tape; in which case we dump the next fragment on the next tape.
289 */
290 volBytesRead = 0;
291 chunkSize = 0;
292 fragmentvolume = 0;
293 while (!endofvolume && !fragmentvolume) { /*w */
294 /* Check for abort in the middle of writing data */
295 if (volBytesRead >= chunkSize) {
296 chunkSize += BIGCHUNK;
297 if (checkAbortByTaskId(taskId))
298 ABORT_EXIT(TC_ABORTEDBYREQUEST);
299
300 /* set bytes dumped for backup */
301 lock_Status();
302 nodePtr->statusNodePtr->nKBytes = tapeInfoPtr->kBytes;
303 unlock_Status();
304 }
305
306 /* Determine how much data to read in upcoming RX_Read() call */
307 toread = dataSize;
308 /* Check if we are close to the EOT. There should at least be some
309 * data on the tape before it is switched. HACK: we have to split a
310 * volume across tapes because the volume trailer says the dump
311 * continues on the next tape (and not the filemark). This could
312 * result in a volume starting on one tape (no volume data dumped) and
313 * continued on the next tape. It'll work, just requires restore to
314 * switch tapes. This allows many small volumes (<16K) to be dumped.
315 */
316 kRemaining = butm_remainingKSpace(tapeInfoPtr);
317 if ((kRemaining < tc_KEndMargin)
318 && (volBytesRead
319 || (tapeInfoPtr->position > (isafile ? 3 : 2)))) {
320 fragmentvolume = 1;
321 }
322
323
324 /* Guess at how much data to read. So we don't write off end of tape */
325 if (kRemaining < (tapeblocks * 16)) {
326 if (kRemaining < 0) {
327 toread = BUTM_BLKSIZE;
328 } else {
329 toread = ((kRemaining / 16) + 1) * BUTM_BLKSIZE;
330 if (toread > dataSize)
331 toread = dataSize;
332 }
333 }
334
335 #ifdef xbsa
336 /* Set aside space for the trailing volume header when using large buffers. */
337 if (XBSAMAXBUFFER < toread + sizeof(hostVolumeHeader)) {
338 toread = XBSAMAXBUFFER - sizeof(hostVolumeHeader);
339 }
340 #endif
341
342 /* Read some volume data. */
343 if (fragmentvolume) {
344 bytesread = 0;
345 } else {
346 bytesread = rx_Read(fromcall, buffer, toread);
347 volBytesRead += bytesread;
348 if (bytesread != toread) {
349 /* Make sure were at end of volume and not a communication error */
350 rc = rx_Error(fromcall);
351 if (rc)
352 ERROR_EXIT(rc);
353 endofvolume = 1;
354 }
355 }
356
357 if (fragmentvolume || endofvolume) {
358 /* Create a volume trailer appending it to this data block */
359 makeVolumeHeader(&hostVolumeHeader, dparamsPtr,
360 fragmentNumber);
361 hostVolumeHeader.contd = (endofvolume ? 0 : TC_VOLCONTD);
362 hostVolumeHeader.magic = TC_VOLENDMAGIC;
363 hostVolumeHeader.endTime = (endofvolume ? time(0) : 0);
364 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)&buffer[bytesread]);
365 bytesread += sizeof(hostVolumeHeader);
366 }
367
368 /* Write the datablock out */
369 /* full data buffer - write it to tape */
370 rc = butm_WriteFileData(tapeInfoPtr, buffer, tapeblocks,
371 bytesread);
372 if (rc) {
373 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
374 "Can't write VolumeData on tape\n");
375 ERROR_EXIT(rc);
376 }
377 bytesWritten = tapeblocks * BUTM_BLOCKSIZE;
378 tsize += bytesWritten;
379
380 /* Display a status line every statusSize or at end of volume */
381 if (statusSize
382 && ((tsize >= statuscount) || endofvolume
383 || fragmentvolume)) {
384 time_t t = time(0);
385 struct tm tm;
386 localtime_r(&t, &tm);
387 printf("%02d:%02d:%02d: Task %u: %u KB: %s: %u B\n",
388 tm.tm_hour, tm.tm_min, tm.tm_sec, taskId,
389 tapeInfoPtr->kBytes, hostVolumeHeader.volumeName,
390 tsize);
391 statuscount = tsize + statusSize;
392 }
393 } /*w */
394
395 /* End the dump before recording it in BUDB as successfully dumped */
396 rc = butm_WriteFileEnd(tapeInfoPtr);
397 indump = 0;
398 if (rc) {
399 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
400 "Can't write FileEnd on tape\n");
401 ERROR_EXIT(rc);
402 }
403
404 /* Record in BUDB the volume fragment as succcessfully dumped */
405 volumeFlags = ((fragmentNumber == 1) ? BUDB_VOL_FIRSTFRAG : 0);
406 if (endofvolume)
407 volumeFlags |= BUDB_VOL_LASTFRAG;
408 rc = addVolume(0, dparamsPtr->databaseDumpId, dparamsPtr->tapeName,
409 nodePtr->dumps[dparamsPtr->curVolume].name,
410 nodePtr->dumps[dparamsPtr->curVolume].vid,
411 nodePtr->dumps[dparamsPtr->curVolume].cloneDate,
412 dparamsPtr->curVolStartPos, volBytesRead,
413 (fragmentNumber - 1), volumeFlags);
414 if (rc)
415 ABORT_EXIT(rc);
416
417 /* If haven't finished dumping the volume, end this
418 * tape and get the next tape.
419 */
420 if (!endofvolume) {
421 /* Write an EOT marker.
422 * Log the error but ignore it since the dump is effectively done.
423 * Scantape will detect continued volume and not read the EOT.
424 */
425 rc = butm_WriteEOT(tapeInfoPtr);
426 if (rc)
427 TapeLog(1, taskId, rc, tapeInfoPtr->error,
428 "Warning: Can't write End-Of-Dump on tape\n");
429
430 /* Unmount the tape */
431 unmountTape(taskId, tapeInfoPtr);
432
433 /* Tell the database the tape is complete (and ok) */
434 rc = finishTape(&dparamsPtr->tape,
435 dparamsPtr->tapeInfoPtr->kBytes +
436 (dparamsPtr->tapeInfoPtr->nBytes ? 1 : 0));
437 if (rc)
438 ABORT_EXIT(rc);
439
440 /* get the next tape. Prompt, mount, and add it into the database */
441 dparamsPtr->tapeSeq++;
442 rc = getDumpTape(dparamsPtr, 1, 0); /* interactive - no append */
443 if (rc)
444 ABORT_EXIT(rc);
445
446 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
447 }
448 } /*frag */
449
450 dparamsPtr->curVolumeStatus = DUMP_SUCCESS;
451
452 error_exit:
453 /*
454 * If we hit the end, see if this is the first volume on the tape or not.
455 * Also, mark the tape as finished if the tape contains other dumps.
456 */
457 if (!code)
458 code = rc;
459 if (HITEOT(code)) {
460 ErrorLog(2, taskId, code, tapeInfoPtr->error,
461 "Warning: Dump (%s) hit end-of-tape inferred\n",
462 nodePtr->dumpSetName);
463
464 if (tapeInfoPtr->position == 2) {
465 dparamsPtr->curVolumeStatus = DUMP_NORETRYEOT;
466 } else {
467 dparamsPtr->curVolumeStatus = DUMP_RETRY;
468 rc = finishTape(&dparamsPtr->tape,
469 dparamsPtr->tapeInfoPtr->kBytes +
470 (dparamsPtr->tapeInfoPtr->nBytes ? 1 : 0));
471 if (rc)
472 ABORT_EXIT(rc);
473 }
474 }
475
476 /*
477 * This is used when an error occurs part way into a volume dump. Clean
478 * the tape state by writing an FileEnd mark. Forgo this action if we hit
479 * the end of tape.
480 */
481 else if (indump) {
482 rc = butm_WriteFileEnd(tapeInfoPtr);
483 indump = 0;
484 if (rc) {
485 ErrorLog(1, taskId, rc, tapeInfoPtr->error,
486 "Can't write FileEnd on tape\n");
487 }
488 }
489
490 if (fromcall) {
491 rc = rx_EndCall(fromcall, 0);
492 if (!code)
493 code = rc;
494 }
495
496 if (fromtid) {
497 afs_int32 rcode;
498 rc = AFSVolEndTrans(fromconn, fromtid, &rcode);
499 if (!code)
500 code = (rc ? rc : rcode);
501 }
502
503 return (code);
504
505 abort_exit:
506 dparamsPtr->curVolumeStatus = DUMP_FAILED;
507 ERROR_EXIT(code);
508 }
509
510 afs_int32
511 xbsaDumpVolume(struct tc_dumpDesc * curDump, struct dumpRock * dparamsPtr)
512 {
513 #ifdef xbsa
514 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
515 struct dumpNode *nodePtr = dparamsPtr->node;
516 char *buffer = bufferBlock;
517 afs_int32 taskId = nodePtr->taskID;
518 afs_int32 rc, code = 0;
519 afs_int32 toread;
520 afs_uint32 volBytesRead;
521 afs_uint32 chunkSize;
522 afs_int32 bytesread; /* rx reads */
523 int endofvolume = 0; /* Have we read all volume data */
524 int begindump = 0, indump = 0; /* if dump transaction started; if dumping data */
525 struct volumeHeader hostVolumeHeader;
526
527 struct rx_call *fromcall = (struct rx_call *)0;
528 struct rx_connection *fromconn;
529 afs_int32 updatedate, fromtid = 0;
530 volEntries volumeInfo;
531 afs_int32 bytesWritten;
532 afs_uint32 statuscount = statusSize, tsize = 0, esize;
533 afs_hyper_t estSize;
534
535 char volumeNameStr[XBSA_MAX_PATHNAME];
536 static char *dumpDescription = "AFS volume dump";
537 static char *objectDescription = "XBSA - butc";
538
539 dparamsPtr->curVolumeStatus = DUMP_NOTHING;
540
541 fromconn = Bind(htonl(curDump->hostAddr)); /* get connection to the server */
542
543 /* Determine when the volume was last cloned and updated */
544 volumeInfo.volEntries_val = (volintInfo *) 0;
545 volumeInfo.volEntries_len = 0;
546 rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid,
547 &volumeInfo);
548 if (rc)
549 ERROR_EXIT(rc);
550 updatedate = volumeInfo.volEntries_val[0].updateDate;
551 curDump->cloneDate =
552 ((curDump->vtype ==
553 RWVOL) ? time(0) : volumeInfo.volEntries_val[0].creationDate);
554
555 /* Get the volume size (in KB) and increase by 25%. Then set as a hyper */
556 esize = volumeInfo.volEntries_val[0].size;
557 esize += (esize / 4) + 1;
558
559 if (curDump->date >= curDump->cloneDate)
560 ERROR_EXIT(0); /* not recloned since last dump */
561 if (curDump->date > updatedate) {
562 dparamsPtr->curVolumeStatus = DUMP_NODUMP; /* not modified since last dump */
563 ERROR_EXIT(0);
564 }
565
566 /* Start a new XBSA Transaction */
567 rc = xbsa_BeginTrans(&butxInfo);
568 if (rc != XBSA_SUCCESS) {
569 ErrorLog(1, taskId, rc, 0, "Unable to create a new transaction\n");
570 ERROR_EXIT(rc);
571 }
572 begindump = 1; /* Will need to do an xbsa_EndTrans */
573
574 /* Start the volserver transaction and dump. Once started, the
575 * volume status is "partial dump". Also, the transaction with
576 * the volserver is idle until the first read. An idle transaction
577 * will time out in 600 seconds. After the first rx_Read,
578 * the transaction is not idle. See GCTrans().
579 */
580 rc = AFSVolTransCreate(fromconn, curDump->vid, curDump->partition, ITBusy,
581 &fromtid);
582 if (rc)
583 ERROR_EXIT(rc);
584 fromcall = rx_NewCall(fromconn);
585
586 rc = StartAFSVolDump(fromcall, fromtid, curDump->date);
587 if (rc)
588 ERROR_EXIT(rc);
589
590 dparamsPtr->curVolumeStatus = DUMP_PARTIAL;
591 dparamsPtr->curVolStartPos = tapeInfoPtr->position;
592
593 /* Tell XBSA what the name and size of volume to write */
594 snprintf(volumeNameStr, sizeof(volumeNameStr), "/%d/%s",
595 dparamsPtr->databaseDumpId, curDump->name);
596 hset32(estSize, esize);
597 hshlft(estSize, 10); /* Multiply by 1024 so its in KB */
598
599 rc = xbsa_WriteObjectBegin(&butxInfo, butcdumpIdStr, volumeNameStr,
600 xbsalGName, estSize, dumpDescription,
601 objectDescription);
602 if (rc != XBSA_SUCCESS) {
603 ErrorLog(1, taskId, rc, 0,
604 "Unable to begin writing of the fileset data to the server\n");
605 ERROR_EXIT(rc);
606 }
607 indump = 1; /* Will need to do an xbsa_WriteObjectEnd */
608
609 /* Create and Write the volume header */
610 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, 1);
611 hostVolumeHeader.contd = 0;
612 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)buffer);
613
614 rc = xbsa_WriteObjectData(&butxInfo, buffer,
615 sizeof(struct volumeHeader), &bytesWritten);
616 if (rc != XBSA_SUCCESS) {
617 ErrorLog(1, taskId, rc, 0,
618 "Unable to write VolumeHeader data to the server\n");
619 ERROR_EXIT(rc);
620 }
621 /* There is a bug in the ADSM library where the bytesWritten is
622 * not filled in, so we set it as correct anyway.
623 */
624 bytesWritten = sizeof(struct volumeHeader);
625 if (bytesWritten != sizeof(struct volumeHeader)) {
626 ErrorLog(1, taskId, rc, 0,
627 "The size of VolumeHeader written (%d) does not equal its actual size (%" AFS_SIZET_FMT ")\n",
628 bytesWritten, sizeof(struct volumeHeader));
629 ERROR_EXIT(TC_INTERNALERROR);
630 }
631
632 incSize(tapeInfoPtr, sizeof(struct volumeHeader)); /* Increment amount we've written */
633 tsize += bytesWritten;
634
635 /* Start reading volume data, rx_Read(), and dumping to the tape
636 * until we've dumped the entire volume (endofvolume == 1).
637 */
638 volBytesRead = 0;
639 chunkSize = 0;
640 while (!endofvolume) { /*w */
641 /* Check for abort in the middle of writing data */
642 if (volBytesRead >= chunkSize) {
643 chunkSize += BIGCHUNK;
644 if (checkAbortByTaskId(taskId))
645 ABORT_EXIT(TC_ABORTEDBYREQUEST);
646
647 /* set bytes dumped for backup */
648 lock_Status();
649 nodePtr->statusNodePtr->nKBytes = tapeInfoPtr->kBytes;
650 unlock_Status();
651 }
652
653 /* Determine how much data to read in upcoming RX_Read() call */
654 toread = dataSize;
655
656 /* Read some volume data. */
657 bytesread = rx_Read(fromcall, buffer, toread);
658 volBytesRead += bytesread;
659 if (bytesread != toread) {
660 afs_int32 rcode;
661
662 /* Make sure were at end of volume and not a communication error */
663 rc = rx_Error(fromcall);
664 if (rc)
665 ERROR_EXIT(rc);
666
667 endofvolume = 1;
668
669 /* Create a volume trailer appending it to this data block (if not XBSA) */
670 makeVolumeHeader(&hostVolumeHeader, dparamsPtr, 1);
671 hostVolumeHeader.contd = 0;
672 hostVolumeHeader.magic = TC_VOLENDMAGIC;
673 hostVolumeHeader.endTime = time(0);
674 volumeHeader_hton(&hostVolumeHeader, (struct volumeHeader *)&buffer[bytesread]);
675 bytesread += sizeof(hostVolumeHeader);
676
677 /* End the dump and transaction with the volserver. We end it now, before
678 * we make the XBSA call because if XBSA blocks, we could time out on the
679 * volserver (After last read, the transaction with the volserver is idle).
680 */
681 rc = rx_EndCall(fromcall, 0);
682 fromcall = 0;
683 if (rc)
684 ERROR_EXIT(rc);
685
686 rc = AFSVolEndTrans(fromconn, fromtid, &rcode);
687 fromtid = 0;
688 if (rc)
689 ERROR_EXIT(rc);
690 }
691
692 /* Write the datablock out */
693 rc = xbsa_WriteObjectData(&butxInfo, buffer, bytesread,
694 &bytesWritten);
695 if (rc != XBSA_SUCCESS) {
696 ErrorLog(1, taskId, rc, 0,
697 "Unable to write data to the server\n");
698 ERROR_EXIT(rc);
699 }
700 /* There is a bug in the ADSM library where the bytesWritten is
701 * not filled in, so we set it as correct anyway.
702 */
703 bytesWritten = bytesread;
704 if (bytesWritten != bytesread) {
705 ErrorLog(1, taskId, rc, 0,
706 "The size of data written (%d) does not equal size read (%d)\n",
707 bytesWritten, bytesread);
708 ERROR_EXIT(TC_INTERNALERROR);
709 }
710
711 incSize(tapeInfoPtr, bytesread); /* Increment amount we've written */
712 tsize += bytesWritten;
713
714 /* Display a status line every statusSize or at end of volume */
715 if (statusSize && ((tsize >= statuscount) || endofvolume)) {
716 time_t t = time(0);
717 struct tm tm;
718 localtime_r(&t, &tm);
719 printf("%02d:%02d:%02d: Task %u: %u KB: %s: %u B\n", tm.tm_hour,
720 tm.tm_min, tm.tm_sec, taskId, tapeInfoPtr->kBytes,
721 hostVolumeHeader.volumeName, tsize);
722 statuscount = tsize + statusSize;
723 }
724 } /*w */
725
726 /* End the XBSA transaction before recording it in BUDB as successfully dumped */
727 rc = xbsa_WriteObjectEnd(&butxInfo);
728 indump = 0;
729 if (rc != XBSA_SUCCESS) {
730 ErrorLog(1, taskId, rc, 0,
731 "Unable to terminate writing of the volume data to the server");
732 ERROR_EXIT(rc);
733 }
734 rc = xbsa_EndTrans(&butxInfo);
735 begindump = 0;
736 tapeInfoPtr->position++;
737 if (rc != XBSA_SUCCESS) {
738 ErrorLog(1, taskId, rc, 0,
739 "Unable to terminate the current transaction");
740 ERROR_EXIT(rc);
741 }
742
743 /* Record in BUDB the volume fragment as succcessfully dumped */
744 rc = addVolume(0, dparamsPtr->databaseDumpId, dparamsPtr->tapeName,
745 nodePtr->dumps[dparamsPtr->curVolume].name,
746 nodePtr->dumps[dparamsPtr->curVolume].vid,
747 nodePtr->dumps[dparamsPtr->curVolume].cloneDate,
748 dparamsPtr->curVolStartPos, volBytesRead, 0 /*frag0 */ ,
749 (BUDB_VOL_FIRSTFRAG | BUDB_VOL_LASTFRAG));
750 if (rc)
751 ABORT_EXIT(rc);
752
753 dparamsPtr->curVolumeStatus = DUMP_SUCCESS;
754
755 error_exit:
756 /* Cleanup after an error occurs part way into a volume dump */
757 if (fromcall) {
758 rc = rx_EndCall(fromcall, 0);
759 if (!code)
760 code = rc;
761 }
762
763 if (fromtid) {
764 afs_int32 rcode;
765 rc = AFSVolEndTrans(fromconn, fromtid, &rcode);
766 if (!code)
767 code = (rc ? rc : rcode);
768 }
769
770 /* If this dump failed, what happens to successive retries
771 * of the volume? How do they get recorded in the XBSA database
772 * (overwritten)? If not, we don't record this in the BUDB database
773 * so it will not be removed when we delete the dump. What to do?
774 * Also if the volume was never recorded in the DB (partial dump).
775 */
776 if (indump) {
777 /* End the Write */
778 rc = xbsa_WriteObjectEnd(&butxInfo);
779 indump = 0;
780 if (rc != XBSA_SUCCESS) {
781 ErrorLog(1, taskId, rc, 0,
782 "Unable to terminate writing of the volume data to the server");
783 }
784 tapeInfoPtr->position++;
785 }
786
787 if (begindump) {
788 /* End the XBSA Transaction */
789 rc = xbsa_EndTrans(&butxInfo);
790 begindump = 0;
791 if (rc != XBSA_SUCCESS) {
792 ErrorLog(1, taskId, rc, 0,
793 "Unable to terminate the current transaction");
794 }
795 }
796
797 return (code);
798
799 abort_exit:
800 dparamsPtr->curVolumeStatus = DUMP_FAILED;
801 ERROR_EXIT(code);
802 #else
803 return 0;
804 #endif
805 }
806
807 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
808
809 /* dumpPass
810 * Go through the list of volumes to dump, dumping each one. The action
811 * taken when a volume dump fails, depends on the passNumber. At minimum,
812 * the failed volume is remembered.
813 * notes:
814 * flushSavedEntries - inconsistent treatment for errors. What should
815 * be done for user aborts?
816 */
817
818 afs_int32
819 dumpPass(struct dumpRock * dparamsPtr, int passNumber)
820 {
821 struct dumpNode *nodePtr = dparamsPtr->node;
822 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
823 afs_int32 taskId = nodePtr->taskID;
824 struct tc_dumpDesc *curDump;
825 int action, e;
826 afs_int32 code = 0, tcode, dvcode;
827 char ch;
828 struct vldbentry vldbEntry;
829 struct sockaddr_in server;
830 afs_int32 tapepos;
831
832 TapeLog(2, taskId, 0, 0, "Starting pass %d\n", passNumber);
833
834 /* while there are more volumes to dump */
835 for (dparamsPtr->curVolume = 0; dparamsPtr->curVolume < nodePtr->arraySize; dparamsPtr->curVolume++) { /*w */
836 curDump = &nodePtr->dumps[dparamsPtr->curVolume];
837 if (curDump->hostAddr == 0)
838 continue;
839
840 /* set name of current volume being dumped */
841 lock_Status();
842 strcpy(nodePtr->statusNodePtr->volumeName, curDump->name);
843 unlock_Status();
844
845 /* Determine location of the volume.
846 * In case the volume moved has moved.
847 */
848 if (passNumber > 1) { /*pass */
849 tcode =
850 bc_GetEntryByID(cstruct, curDump->vid, curDump->vtype,
851 &vldbEntry);
852 if (tcode) {
853 ErrorLog(0, taskId, tcode, 0,
854 "Volume %s (%u) failed - Can't find volume in VLDB\n",
855 curDump->name, curDump->vid);
856 curDump->hostAddr = 0;
857 dparamsPtr->volumesFailed++;
858 continue;
859 }
860
861 switch (curDump->vtype) {
862 case BACKVOL:
863 if (!(vldbEntry.flags & VLF_BACKEXISTS)) {
864 ErrorLog(0, taskId, 0, 0,
865 "Volume %s (%u) failed - Backup volume no longer exists\n",
866 curDump->name, curDump->vid);
867 curDump->hostAddr = 0;
868 dparamsPtr->volumesFailed++;
869 continue;
870 }
871 /* Fall into RWVOL case */
872
873 case RWVOL:
874 for (e = 0; e < vldbEntry.nServers; e++) { /* Find the RW volume */
875 if (vldbEntry.serverFlags[e] & VLSF_RWVOL)
876 break;
877 }
878 break;
879
880 case ROVOL:
881 /* Try to use the server and partition we found the volume on
882 * Otherwise, use the first RO volume.
883 */
884 for (e = 0; e < vldbEntry.nServers; e++) { /* Find the RO volume */
885 if ((curDump->hostAddr == vldbEntry.serverNumber[e])
886 && (curDump->partition ==
887 vldbEntry.serverPartition[e]))
888 break;
889 }
890
891 if (e >= vldbEntry.nServers) { /* Didn't find RO volume */
892 for (e = 0; e < vldbEntry.nServers; e++) { /* Find the first RO volume */
893 if (vldbEntry.serverFlags[e] & VLSF_ROVOL)
894 break;
895 }
896 }
897 break;
898
899 default:
900 ErrorLog(0, taskId, 0, 0,
901 "Volume %s (%u) failed - Unknown volume type\n",
902 curDump->name, curDump->vid);
903 curDump->hostAddr = 0;
904 continue;
905 }
906
907 if (e >= vldbEntry.nServers) {
908 ErrorLog(0, taskId, 0, 0,
909 "Volume %s (%u) failed - Can't find volume entry in VLDB\n",
910 curDump->name, curDump->vid);
911 curDump->hostAddr = 0;
912 dparamsPtr->volumesFailed++;
913 continue;
914 }
915
916 /* Remember the server and partition the volume exists on */
917 memset(&server, 0, sizeof(server));
918 server.sin_addr.s_addr = vldbEntry.serverNumber[e];
919 server.sin_port = 0;
920 server.sin_family = AF_INET;
921 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
922 server.sin_len = sizeof(struct sockaddr_in);
923 #endif
924 curDump->hostAddr = HOSTADDR(&server);
925 curDump->partition = vldbEntry.serverPartition[e];
926
927 /* Determine date from which to do an incremental dump
928 */
929 if (nodePtr->parent) {
930 tcode =
931 bcdb_FindClone(nodePtr->parent, curDump->name,
932 &curDump->date);
933 if (tcode)
934 curDump->date = 0;
935 } else {
936 curDump->date = 0; /* do a full dump */
937 }
938 }
939 /*pass */
940 if (checkAbortByTaskId(taskId))
941 ERROR_EXIT(TC_ABORTEDBYREQUEST);
942
943 /* Establish connection to volume - UV_ routine expects
944 * host address in network order
945 */
946 if (CONF_XBSA) {
947 dvcode = xbsaDumpVolume(curDump, dparamsPtr);
948 } else {
949 dvcode = dumpVolume(curDump, dparamsPtr);
950 }
951 action = dparamsPtr->curVolumeStatus;
952
953 /* Flush volume and tape entries to the database */
954 tcode = flushSavedEntries(action);
955 if (tcode)
956 ERROR_EXIT(tcode);
957
958 switch (action) {
959 case DUMP_SUCCESS:
960 TapeLog(1, taskId, 0, 0, "Volume %s (%u) successfully dumped\n",
961 curDump->name, curDump->vid);
962 if (dvcode)
963 ErrorLog(1, taskId, dvcode, 0,
964 "Warning: Termination processing error on volume %s (%u)\n",
965 curDump->name, curDump->vid);
966
967 curDump->hostAddr = 0;
968 dparamsPtr->volumesDumped++;
969 break;
970
971 case DUMP_PARTIAL:
972 case DUMP_NOTHING:
973 if (action == DUMP_PARTIAL) {
974 ErrorLog(1, taskId, dvcode, 0,
975 "Volume %s (%u) failed - partially dumped\n",
976 curDump->name, curDump->vid);
977 } else if (dvcode) {
978 ErrorLog(0, taskId, dvcode, 0, "Volume %s (%u) failed\n",
979 curDump->name, curDump->vid);
980 } else {
981 ErrorLog(0, taskId, dvcode, 0,
982 "Volume %s (%u) not dumped - has not been re-cloned since last dump\n",
983 curDump->name, curDump->vid);
984 }
985
986 if (passNumber == maxpass) {
987 if (!queryoperator)
988 ch = 'o';
989 else
990 ch = retryPrompt(curDump->name, curDump->vid, taskId);
991
992 switch (ch) {
993 case 'r': /* retry */
994 dparamsPtr->curVolume--; /* redump this volume */
995 continue;
996 case 'o': /* omit */
997 ErrorLog(1, taskId, 0, 0, "Volume %s (%u) omitted\n",
998 curDump->name, curDump->vid);
999 dparamsPtr->volumesFailed++;
1000 break;
1001 case 'a': /* abort */
1002 TapeLog(1, taskId, 0, 0, "Dump aborted\n");
1003 ERROR_EXIT(TC_ABORTEDBYREQUEST);
1004 break;
1005 default:
1006 ERROR_EXIT(TC_INTERNALERROR);
1007 break;
1008 }
1009 }
1010 break;
1011
1012 case DUMP_RETRY:
1013 TapeLog(1, taskId, dvcode, 0,
1014 "Volume %s (%u) hit end-of-tape inferred - will retry on next tape\n",
1015 curDump->name, curDump->vid);
1016
1017 /* Get the next tape */
1018 unmountTape(taskId, tapeInfoPtr);
1019
1020 dparamsPtr->tapeSeq++;
1021 tcode = getDumpTape(dparamsPtr, 1, 0); /* interactive - no appends */
1022 if (tcode)
1023 ERROR_EXIT(tcode);
1024
1025 dparamsPtr->curVolume--; /* redump this volume */
1026 continue;
1027
1028 case DUMP_NORETRYEOT:
1029 ErrorLog(1, taskId, 0, 0,
1030 "Volume %s (%u) failed - volume larger than tape\n",
1031 curDump->name, curDump->vid);
1032
1033 /* rewrite the label on the tape - rewind - no need to switch tapes */
1034 tcode = butm_Create(tapeInfoPtr, &dparamsPtr->tapeLabel, 1);
1035 if (tcode) {
1036 ErrorLog(0, taskId, tcode, tapeInfoPtr->error,
1037 "Can't relabel tape\n");
1038
1039 unmountTape(taskId, tapeInfoPtr);
1040 tcode = getDumpTape(dparamsPtr, 1, 0); /* interactive - no appends */
1041 if (tcode)
1042 ERROR_EXIT(tcode);
1043 } else { /* Record the tape in database */
1044 tapepos = tapeInfoPtr->position;
1045 tcode =
1046 useTape(&dparamsPtr->tape, dparamsPtr->databaseDumpId,
1047 dparamsPtr->tapeName,
1048 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1049 dparamsPtr->tapeLabel.useCount,
1050 dparamsPtr->tapeLabel.creationTime,
1051 dparamsPtr->tapeLabel.expirationDate, tapepos);
1052 }
1053
1054 curDump->hostAddr = 0;
1055 dparamsPtr->volumesFailed++;
1056 break;
1057
1058 case DUMP_NODUMP:
1059 TapeLog(1, taskId, dvcode, 0,
1060 "Volume %s (%u) not dumped - has not been modified since last dump\n",
1061 curDump->name, curDump->vid);
1062
1063 curDump->hostAddr = 0;
1064 dparamsPtr->volumesNotDumped++;
1065 break;
1066
1067 default:
1068 ErrorLog(1, taskId, dvcode, 0, "Volume %s (%u) failed\n",
1069 curDump->name, curDump->vid);
1070 ERROR_EXIT(dvcode);
1071 break;
1072 }
1073 } /*w */
1074
1075 error_exit:
1076 /* check if we terminated while processing a volume */
1077 if (dparamsPtr->curVolume < nodePtr->arraySize) {
1078 TapeLog(2, taskId, 0, 0,
1079 "Terminated while processing Volume %s (%u)\n", curDump->name,
1080 curDump->vid);
1081 }
1082
1083 /* print a summary of this pass */
1084 TapeLog(2, taskId, 0, 0, "End of pass %d: Volumes remaining = %d\n",
1085 passNumber,
1086 nodePtr->arraySize - (dparamsPtr->volumesDumped +
1087 dparamsPtr->volumesFailed +
1088 dparamsPtr->volumesNotDumped));
1089 return (code);
1090 }
1091
1092 void *
1093 Dumper(void *param)
1094 {
1095 struct dumpNode *nodePtr = (struct dumpNode *)param;
1096 struct dumpRock dparams;
1097 struct butm_tapeInfo tapeInfo;
1098 int pass;
1099 int action;
1100 afs_int32 taskId;
1101 afs_int32 code = 0;
1102
1103 /* for volume setup */
1104 int i;
1105 int failedvolumes = 0;
1106 int dumpedvolumes = 0;
1107 int nodumpvolumes = 0;
1108 char strlevel[5];
1109 char msg[128];
1110 char finishedMsg1[128];
1111 char finishedMsg2[128];
1112 time_t startTime = 0;
1113 time_t endTime = 0;
1114 afs_int32 allocbufferSize;
1115
1116 extern struct deviceSyncNode *deviceLatch;
1117 extern struct tapeConfig globalTapeConfig;
1118
1119 afs_pthread_setname_self("dumper");
1120 taskId = nodePtr->taskID; /* Get task Id */
1121 setStatus(taskId, DRIVE_WAIT);
1122 EnterDeviceQueue(deviceLatch);
1123 clearStatus(taskId, DRIVE_WAIT);
1124
1125 printf("\n\n");
1126 TapeLog(2, taskId, 0, 0, "Dump %s\n", nodePtr->dumpSetName);
1127
1128 /* setup the dump parameters */
1129 memset(&dparams, 0, sizeof(dparams));
1130 dparams.node = nodePtr;
1131 dparams.tapeInfoPtr = &tapeInfo;
1132 dlqInit(&savedEntries);
1133
1134 if (!CONF_XBSA) {
1135 /* Instantiate the tape module */
1136 tapeInfo.structVersion = BUTM_MAJORVERSION;
1137 code = butm_file_Instantiate(&tapeInfo, &globalTapeConfig);
1138 if (code) {
1139 ErrorLog(0, taskId, code, tapeInfo.error,
1140 "Can't initialize the tape module\n");
1141 ERROR_EXIT(code);
1142 }
1143 }
1144
1145 /* check if abort requested while waiting on device latch */
1146 if (checkAbortByTaskId(taskId))
1147 ERROR_EXIT(TC_ABORTEDBYREQUEST);
1148
1149 /* Are there volumes to dump */
1150 if (nodePtr->arraySize == 0) {
1151 TLog(taskId, "Dump (%s), no volumes to dump\n", nodePtr->dumpSetName);
1152 ERROR_EXIT(0);
1153 }
1154
1155 /* Allocate a buffer for the dumps. Leave room for header and vol-trailer.
1156 * dataSize is amount of data to read in each rx_Read() call.
1157 */
1158 if (CONF_XBSA) {
1159 /* XBSA dumps have not header */
1160 dataSize = BufferSize;
1161 allocbufferSize = dataSize + sizeof(struct volumeHeader);
1162 } else {
1163 tapeblocks = BufferSize / BUTM_BLOCKSIZE; /* # of 16K tapeblocks */
1164 dataSize = (tapeblocks * BUTM_BLKSIZE);
1165 allocbufferSize =
1166 BUTM_HDRSIZE + dataSize + sizeof(struct volumeHeader);
1167 }
1168 bufferBlock = NULL;
1169 bufferBlock = malloc(allocbufferSize);
1170 if (!bufferBlock) {
1171 ErrorLog(0, taskId, TC_NOMEMORY, 0,
1172 "Can't allocate BUFFERSIZE for dumps\n");
1173 ERROR_EXIT(TC_NOMEMORY);
1174 }
1175
1176 /* Determine the dumpid of the most recent dump of this volumeset and dumplevel
1177 * Used when requesting a tape. Done now because once we create the dump, the
1178 * routine will then find the newly created dump.
1179 */
1180 snprintf(strlevel, sizeof(strlevel), "%d", nodePtr->level);
1181 code =
1182 bcdb_FindLatestDump(nodePtr->volumeSetName, strlevel,
1183 &dparams.lastDump);
1184 if (code) {
1185 if (code != BUDB_NODUMPNAME) {
1186 ErrorLog(0, taskId, code, 0, "Can't read backup database\n");
1187 ERROR_EXIT(code);
1188 }
1189 memset(&dparams.lastDump, 0, sizeof(dparams.lastDump));
1190 }
1191
1192 code = createDump(&dparams); /* enter dump into database */
1193 if (code) {
1194 ErrorLog(0, taskId, code, 0, "Can't create dump in database\n");
1195 ERROR_EXIT(code);
1196 }
1197
1198 TLog(taskId, "Dump %s (DumpID %u)\n", nodePtr->dumpSetName,
1199 dparams.databaseDumpId);
1200
1201 if (!CONF_XBSA) {
1202 /* mount the tape and write its label */
1203 code = getDumpTape(&dparams, autoQuery, nodePtr->doAppend);
1204 } else {
1205 /* Create a dummy tape to satisfy backup databae */
1206 code = getXBSATape(&dparams);
1207 tapeInfo.position = 1;
1208 }
1209 if (code) {
1210 /* If didn't write the label, remove dump from the database */
1211 if (!dparams.wroteLabel) {
1212 i = bcdb_deleteDump(dparams.databaseDumpId, 0, 0, 0);
1213 if (i && (i != BUDB_NOENT))
1214 ErrorLog(1, taskId, i, 0,
1215 "Warning: Can't delete dump %u from database\n",
1216 dparams.databaseDumpId);
1217 else
1218 dparams.databaseDumpId = 0;
1219 }
1220 ERROR_EXIT(code); /* exit with code from getTape */
1221 }
1222
1223 startTime = time(0);
1224 for (pass = 1; pass <= maxpass; pass++) {
1225 lastPass = (pass == maxpass);
1226 code = dumpPass(&dparams, pass);
1227 if (code)
1228 ERROR_EXIT(code);
1229
1230 /* if no failed volumes, we're done */
1231 if ((dparams.volumesDumped + dparams.volumesFailed +
1232 dparams.volumesNotDumped) == nodePtr->arraySize)
1233 break;
1234 }
1235
1236 /*
1237 * Log the error but ignore it since the dump is effectively done.
1238 * Scantape may assume another volume and ask for next tape.
1239 */
1240 if (!CONF_XBSA) {
1241 code = butm_WriteEOT(&tapeInfo);
1242 if (code)
1243 TapeLog(0, taskId, code, tapeInfo.error,
1244 "Warning: Can't write end-of-dump on tape\n");
1245 }
1246
1247 code =
1248 finishTape(&dparams.tape,
1249 dparams.tapeInfoPtr->kBytes +
1250 (dparams.tapeInfoPtr->nBytes ? 1 : 0));
1251 if (code)
1252 ERROR_EXIT(code);
1253
1254 code = finishDump(&dparams.dump);
1255 if (code)
1256 ERROR_EXIT(code);
1257
1258 action = dparams.curVolumeStatus;
1259 code = flushSavedEntries(action);
1260 if (code)
1261 ERROR_EXIT(code);
1262
1263 error_exit:
1264 endTime = time(0);
1265 Bind(0);
1266 if (bufferBlock)
1267 free(bufferBlock);
1268
1269 if (!CONF_XBSA) {
1270 unmountTape(taskId, &tapeInfo);
1271 }
1272 waitDbWatcher();
1273
1274 dumpedvolumes = dparams.volumesDumped;
1275 nodumpvolumes = dparams.volumesNotDumped;
1276 failedvolumes = nodePtr->arraySize - (dumpedvolumes + nodumpvolumes);
1277
1278 /* pass back the number of volumes we failed to dump */
1279 lock_Status();
1280 nodePtr->statusNodePtr->volsFailed = failedvolumes;
1281 unlock_Status();
1282
1283 lastPass = 1; /* In case we aborted */
1284
1285 /* Format and log finished message. */
1286 snprintf(finishedMsg1, sizeof(finishedMsg1), "%s", nodePtr->dumpSetName);
1287 if (dparams.databaseDumpId != 0) {
1288 snprintf(msg, sizeof(msg), " (DumpId %u)", dparams.databaseDumpId);
1289 strlcat(finishedMsg1, msg, sizeof(finishedMsg1));
1290 }
1291 snprintf(finishedMsg2, sizeof(finishedMsg2),
1292 "%d volumes dumped", dumpedvolumes);
1293 if (failedvolumes) {
1294 snprintf(msg, sizeof(msg), ", %d failed", failedvolumes);
1295 strlcat(finishedMsg2, msg, sizeof(finishedMsg2));
1296 }
1297 if (nodumpvolumes) {
1298 snprintf(msg, sizeof(msg), ", %d unchanged", nodumpvolumes);
1299 strlcat(finishedMsg2, msg, sizeof(finishedMsg2));
1300 }
1301
1302 if (code == TC_ABORTEDBYREQUEST) {
1303 ErrorLog(0, taskId, 0, 0, "%s: Aborted by request. %s\n",
1304 finishedMsg1, finishedMsg2);
1305 clearStatus(taskId, ABORT_REQUEST);
1306 setStatus(taskId, ABORT_DONE);
1307 } else if (code) {
1308 ErrorLog(0, taskId, code, 0, "%s: Finished with errors. %s\n",
1309 finishedMsg1, finishedMsg2);
1310 setStatus(taskId, TASK_ERROR);
1311 } else {
1312 TLog(taskId, "%s: Finished. %s\n", finishedMsg1, finishedMsg2);
1313 }
1314 lastPass = 0;
1315
1316 /* Record how long the dump took */
1317 if (centralLogIO && startTime) {
1318 long timediff;
1319 afs_int32 hrs, min, sec, tmp;
1320 char *line = NULL;
1321 struct tm tmstart, tmend;
1322
1323 localtime_r(&startTime, &tmstart);
1324 localtime_r(&endTime, &tmend);
1325 timediff = (int)endTime - (int)startTime;
1326 hrs = timediff / 3600;
1327 tmp = timediff % 3600;
1328 min = tmp / 60;
1329 sec = tmp % 60;
1330
1331 code = asprintf(&line,
1332 "%-5d %02d/%02d/%04d %02d:%02d:%02d "
1333 "%02d/%02d/%04d %02d:%02d:%02d " "%02d:%02d:%02d "
1334 "%s %d of %d volumes dumped (%lu KB)\n", taskId,
1335 tmstart.tm_mon + 1, tmstart.tm_mday, tmstart.tm_year + 1900,
1336 tmstart.tm_hour, tmstart.tm_min, tmstart.tm_sec,
1337 tmend.tm_mon + 1, tmend.tm_mday, tmend.tm_year + 1900,
1338 tmend.tm_hour, tmend.tm_min, tmend.tm_sec, hrs, min, sec,
1339 nodePtr->volumeSetName, dumpedvolumes,
1340 dumpedvolumes + failedvolumes,
1341 afs_printable_uint32_lu(dparams.tapeInfoPtr->kBytes + 1));
1342 if (code < 0)
1343 line = NULL;
1344 if (line != NULL) {
1345 fwrite(line, strlen(line), 1, centralLogIO);
1346 fflush(centralLogIO);
1347 }
1348 free(line);
1349 }
1350
1351 setStatus(taskId, TASK_DONE);
1352
1353 FreeNode(taskId); /* free the dump node */
1354 LeaveDeviceQueue(deviceLatch);
1355 return (void *)(intptr_t)(code);
1356 }
1357
1358 #define BELLTIME 60 /* 60 seconds before a bell rings */
1359 #define BELLCHAR 7 /* ascii for bell */
1360
1361 /* retryPrompt
1362 * prompt the user to decide how to handle a failed volume dump. The
1363 * volume parameters describe the volume that failed
1364 * entry:
1365 * volumeName - name of volume
1366 * volumeId - volume id
1367 * taskId - for job contrl
1368 * fn return:
1369 * character typed by user, one of r, o or a
1370 */
1371
1372 char
1373 retryPrompt(char *volumeName, afs_int32 volumeId, afs_uint32 taskId)
1374 {
1375 afs_int32 start;
1376 char ch;
1377 afs_int32 code = 0;
1378
1379 setStatus(taskId, OPR_WAIT);
1380 printf("\nDump of volume %s (%u) failed\n\n", volumeName, volumeId);
1381
1382 printf("Please select action to be taken for this volume\n");
1383
1384 again:
1385 printf("r - retry, try dumping this volume again\n");
1386 printf("o - omit, this volume from this dump\n");
1387 printf("a - abort, the entire dump\n");
1388
1389 while (1) {
1390 FFlushInput();
1391 putchar(BELLCHAR);
1392 fflush(stdout);
1393
1394 start = time(0);
1395 while (1) {
1396 code = LWP_GetResponseKey(5, &ch); /* ch stores key pressed */
1397 if (code == 1)
1398 break; /* input is available */
1399
1400 if (checkAbortByTaskId(taskId)) {
1401 clearStatus(taskId, OPR_WAIT);
1402 printf
1403 ("This tape operation has been aborted by the coordinator\n");
1404 return 'a';
1405 }
1406
1407 if (time(0) > start + BELLTIME)
1408 break;
1409 }
1410 /* otherwise, we should beep again, check for abort and go back,
1411 * since the GetResponseKey() timed out.
1412 */
1413 if (code == 1)
1414 break; /* input is available */
1415 }
1416 clearStatus(taskId, OPR_WAIT);
1417 if (ch != 'r' && ch != 'o' && ch != 'a') {
1418 printf("Please select one of the 3 options, r, o or a\n");
1419 goto again;
1420 }
1421
1422 return ch;
1423 }
1424
1425 /* For testing: it prints the tape label */
1426 int
1427 printTapeLabel(struct butm_tapeLabel *tl)
1428 {
1429 printf("Tape Label\n");
1430 printf(" structVersion = %d\n", tl->structVersion);
1431 printf(" creationTime = %u\n", tl->creationTime);
1432 printf(" expirationDate = %u\n", tl->expirationDate);
1433 printf(" AFSName = %s\n", tl->AFSName);
1434 printf(" cell = %s\n", tl->cell);
1435 printf(" dumpid = %d\n", tl->dumpid);
1436 printf(" useCount = %d\n", tl->useCount);
1437 printf(" comment = %s\n", tl->comment);
1438 printf(" pName = %s\n", tl->pName);
1439 printf(" size = %u\n", tl->size);
1440 printf(" dumpPath = %s\n", tl->dumpPath);
1441 return 0;
1442 }
1443
1444 /* getXBSATape
1445 * Create a tape structure to be satisfy the backup database
1446 * even though we don't really use a tape with XBSA.
1447 */
1448 int
1449 getXBSATape(struct dumpRock *dparamsPtr)
1450 {
1451 struct dumpNode *nodePtr = dparamsPtr->node;
1452 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
1453 struct butm_tapeLabel *tapeLabelPtr = &dparamsPtr->tapeLabel;
1454 afs_int32 code = 0;
1455
1456 tc_MakeTapeName(dparamsPtr->tapeName, &nodePtr->tapeSetDesc,
1457 dparamsPtr->tapeSeq);
1458
1459 GetNewLabel(tapeInfoPtr, "" /*pName */ , dparamsPtr->tapeName,
1460 tapeLabelPtr);
1461 strcpy(tapeLabelPtr->dumpPath, nodePtr->dumpName);
1462 tapeLabelPtr->dumpid = dparamsPtr->databaseDumpId;
1463 tapeLabelPtr->expirationDate =
1464 calcExpirationDate(nodePtr->tapeSetDesc.expType,
1465 nodePtr->tapeSetDesc.expDate, time(0));
1466
1467 /* printTapeLabel(tapeLabelPtr); For testing */
1468
1469 code =
1470 useTape(&dparamsPtr->tape, dparamsPtr->databaseDumpId,
1471 dparamsPtr->tapeName,
1472 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1473 tapeLabelPtr->useCount, tapeLabelPtr->creationTime,
1474 tapeLabelPtr->expirationDate, 0 /*tape position */ );
1475 return (code);
1476 }
1477
1478 /* getDumpTape
1479 * iterate until the desired tape (as specified by the dump structures)
1480 * is mounted.
1481 * entry:
1482 * interactiveFlag
1483 * 0 - assume the tape is there. Prompt if assumption false
1484 * 1 - prompt regardless
1485 */
1486
1487 int
1488 getDumpTape(struct dumpRock *dparamsPtr, int interactiveFlag,
1489 afs_int32 append)
1490 {
1491 struct dumpNode *nodePtr = dparamsPtr->node;
1492 struct butm_tapeInfo *tapeInfoPtr = dparamsPtr->tapeInfoPtr;
1493 struct butm_tapeLabel *newTapeLabelPtr = &dparamsPtr->tapeLabel;
1494 char AFSTapeName[TC_MAXTAPENAMELEN];
1495 afs_int32 taskId = nodePtr->taskID;
1496 struct butm_tapeLabel oldTapeLabel;
1497 struct budb_dumpEntry dumpEntry;
1498 struct budb_tapeEntry tapeEntry;
1499 struct budb_volumeEntry volEntry;
1500 Date expir;
1501 afs_int32 doAppend;
1502 afs_int32 code = 0;
1503 int askForTape;
1504 int tapecount = 1;
1505 char strlevel[5];
1506 afs_int32 tapepos, lastpos;
1507
1508 extern struct tapeConfig globalTapeConfig;
1509
1510 askForTape = interactiveFlag;
1511 dparamsPtr->wroteLabel = 0;
1512
1513 /* Keep prompting for a tape until we get it right */
1514 while (1) {
1515 /* What the name of the tape would be if not appending to it */
1516 tc_MakeTapeName(AFSTapeName, &nodePtr->tapeSetDesc,
1517 dparamsPtr->tapeSeq);
1518
1519 doAppend = append;
1520
1521 if (askForTape) {
1522 code =
1523 PromptForTape((doAppend ? APPENDOPCODE : WRITEOPCODE),
1524 AFSTapeName, dparamsPtr->databaseDumpId, taskId,
1525 tapecount);
1526 if (code)
1527 ERROR_EXIT(code);
1528 }
1529 askForTape = 1;
1530 tapecount++;
1531
1532 /* open the tape device */
1533 code = butm_Mount(tapeInfoPtr, AFSTapeName);
1534 if (code) {
1535 TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
1536 goto getNewTape;
1537 }
1538
1539 /* Read the tape label */
1540 code = butm_ReadLabel(tapeInfoPtr, &oldTapeLabel, 1); /* rewind */
1541 if (code) {
1542 if (tapeInfoPtr->error) {
1543 ErrorLog(0, taskId, code, tapeInfoPtr->error,
1544 "Warning: Tape error while reading label (will proceed with dump)\n");
1545 }
1546 memset(&oldTapeLabel, 0, sizeof(oldTapeLabel));
1547 }
1548
1549 /* Check if null tape. Prior 3.3, backup tapes have no dump id */
1550 if ((strcmp(oldTapeLabel.AFSName, "") == 0)
1551 && (oldTapeLabel.dumpid == 0)) {
1552 if (doAppend) {
1553 TLog(taskId,
1554 "Dump not found on tape. Proceeding with initial dump\n");
1555 doAppend = 0;
1556 }
1557 } else if (doAppend) { /* appending */
1558 /* Check that we don't have a database dump tape */
1559 if (databaseTape(oldTapeLabel.AFSName)) {
1560 char gotName[BU_MAXTAPELEN + 32];
1561
1562 /* label does not match */
1563 LABELNAME(gotName, &oldTapeLabel);
1564 TLog(taskId, "Can't append to database tape %s\n", gotName);
1565 goto getNewTape;
1566 }
1567
1568 /* Verify that the tape is of version 4 (AFS 3.3) or greater */
1569 if (oldTapeLabel.structVersion < TAPE_VERSION_4) {
1570 TLog(taskId,
1571 "Can't append: requires tape version %d or greater\n",
1572 TAPE_VERSION_4);
1573 goto getNewTape;
1574 }
1575
1576 /* Verify that the last tape of the dump set is in the drive.
1577 * volEntry will be zeroed if last dump has no volume entries.
1578 */
1579 code =
1580 bcdb_FindLastTape(oldTapeLabel.dumpid, &dumpEntry, &tapeEntry,
1581 &volEntry);
1582 if (code) {
1583 ErrorLog(0, taskId, code, 0,
1584 "Can't append: Can't find last volume of dumpId %u in database\n",
1585 oldTapeLabel.dumpid);
1586 printf("Please scan the dump in or choose another tape\n");
1587 goto getNewTape;
1588 }
1589 lastpos =
1590 (volEntry.position ? volEntry.position : tapeEntry.labelpos);
1591
1592 if (strcmp(TNAME(&oldTapeLabel), tapeEntry.name)) {
1593 char expName[BU_MAXTAPELEN + 32], gotName[BU_MAXTAPELEN + 32];
1594
1595 TAPENAME(expName, tapeEntry.name, oldTapeLabel.dumpid);
1596 LABELNAME(gotName, &oldTapeLabel);
1597
1598 TLog(taskId,
1599 "Can't append: Last tape in dump-set is %s, label seen %s\n",
1600 expName, gotName);
1601 goto getNewTape;
1602 }
1603
1604 /* After reading the tape label, we now know what it is */
1605 strcpy(AFSTapeName, oldTapeLabel.AFSName); /* the real name */
1606 strcpy(tapeInfoPtr->name, oldTapeLabel.AFSName); /* the real name */
1607
1608 /* Position after last volume on the tape */
1609 code = butm_SeekEODump(tapeInfoPtr, lastpos);
1610 if (code) {
1611 ErrorLog(0, taskId, code, tapeInfoPtr->error,
1612 "Can't append: Can't position to end of dump on tape %s\n",
1613 tapeEntry.name);
1614 goto getNewTape;
1615 }
1616
1617 /* Track size of tape - set after seek since seek changes the value */
1618 tapeInfoPtr->kBytes = tapeEntry.useKBytes;
1619 } else { /* not appending */
1620
1621 afs_uint32 tapeid;
1622 afs_uint32 dmp;
1623 struct budb_dumpEntry de, de2;
1624
1625 /* Check if tape name is not what expected - null tapes are acceptable
1626 * Don't do check if the tape has a user defined label.
1627 */
1628 if (dump_namecheck && (strcmp(oldTapeLabel.pName, "") == 0)) {
1629 if (strcmp(oldTapeLabel.AFSName, "") && /* not null tape */
1630 strcmp(oldTapeLabel.AFSName, AFSTapeName)) { /* not expected name */
1631 TLog(taskId, "Tape label expected %s, label seen %s\n",
1632 AFSTapeName, oldTapeLabel.AFSName);
1633 goto getNewTape;
1634 }
1635
1636 /* Check that we don't have a database dump tape */
1637 if (databaseTape(oldTapeLabel.AFSName)) {
1638 /* label does not match */
1639 TLog(taskId,
1640 "Tape label expected %s, can't dump to database tape %s\n",
1641 AFSTapeName, oldTapeLabel.AFSName);
1642 goto getNewTape;
1643 }
1644 }
1645
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");
1649 goto getNewTape;
1650 }
1651
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
1654 */
1655 if (oldTapeLabel.dumpid) {
1656 /* Do not overwrite a tape that belongs to the dump's dumpset */
1657 tapeid =
1658 (dparamsPtr->initialDumpId ? dparamsPtr->
1659 initialDumpId : dparamsPtr->databaseDumpId);
1660 if (oldTapeLabel.dumpid == tapeid) {
1661 ErrorLog(0, taskId, 0, 0,
1662 "Can't overwrite tape containing the dump in progress\n");
1663 goto getNewTape;
1664 }
1665
1666 /* Since the dumpset on this tape will be deleted from database, check if
1667 * any of the dump's parent-dumps are on this tape.
1668 */
1669 for (dmp = nodePtr->parent; dmp; dmp = de.parent) {
1670 code = bcdb_FindDumpByID(dmp, &de);
1671 if (code) {
1672 ErrorLog(0, taskId, 0, 0,
1673 "Warning: Can't find parent dump %u in backup database\n",
1674 dmp);
1675 break;
1676 }
1677
1678 tapeid = (de.initialDumpID ? de.initialDumpID : de.id);
1679 if (oldTapeLabel.dumpid == tapeid) {
1680 ErrorLog(0, taskId, 0, 0,
1681 "Can't overwrite the parent dump %s (DumpID %u)\n",
1682 de.name, de.id);
1683 goto getNewTape;
1684 }
1685 }
1686
1687 /* Since the dumpset on this tape will be deleted from database, check if
1688 * any of the dumps in this dumpset are most-recent-dumps.
1689 */
1690 for (dmp = oldTapeLabel.dumpid; dmp; dmp = de.appendedDumpID) {
1691 if (dmp == dparamsPtr->lastDump.id) {
1692 memcpy(&de, &dparamsPtr->lastDump, sizeof(de));
1693 memcpy(&de2, &dparamsPtr->lastDump, sizeof(de2));
1694 } else {
1695 code = bcdb_FindDumpByID(dmp, &de);
1696 if (code)
1697 break;
1698 snprintf(strlevel, sizeof(strlevel), "%d", de.level);
1699 code =
1700 bcdb_FindLatestDump(de.volumeSetName, strlevel,
1701 &de2);
1702 if (code)
1703 continue;
1704 }
1705
1706 /* If dump on the tape is the latest dump at this level */
1707 if (de.id == de2.id) {
1708 if (strcmp(DUMP_TAPE_NAME, de2.name) == 0) {
1709 ErrorLog(0, taskId, 0, 0,
1710 "Warning: Overwriting most recent dump %s (DumpID %u)\n",
1711 de.name, de.id);
1712 } else {
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);
1716 }
1717 }
1718 }
1719 } /* if (oldTapeLabel.dumpid) */
1720 } /* else not appending */
1721
1722 /*
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.
1728 */
1729 GetNewLabel(tapeInfoPtr, oldTapeLabel.pName, AFSTapeName,
1730 newTapeLabelPtr);
1731 strcpy(newTapeLabelPtr->dumpPath, nodePtr->dumpName);
1732 newTapeLabelPtr->expirationDate =
1733 calcExpirationDate(nodePtr->tapeSetDesc.expType,
1734 nodePtr->tapeSetDesc.expDate, time(0));
1735 newTapeLabelPtr->dumpid = dparamsPtr->databaseDumpId;
1736 newTapeLabelPtr->useCount = oldTapeLabel.useCount;
1737
1738 if (!doAppend) {
1739 newTapeLabelPtr->useCount++;
1740 if (dparamsPtr->initialDumpId) {
1741 newTapeLabelPtr->dumpid = dparamsPtr->initialDumpId;
1742 expir = ExpirationDate(dparamsPtr->initialDumpId);
1743 if (expir > newTapeLabelPtr->expirationDate)
1744 newTapeLabelPtr->expirationDate = expir;
1745 }
1746 }
1747
1748 /* write the label on the tape - rewind if not appending and vice-versa */
1749 code = butm_Create(tapeInfoPtr, newTapeLabelPtr, !doAppend);
1750 if (code) {
1751 char gotName[BU_MAXTAPELEN + 32];
1752
1753 LABELNAME(gotName, newTapeLabelPtr);
1754 TapeLog(0, taskId, code, tapeInfoPtr->error,
1755 "Can't label tape as %s\n", gotName);
1756 goto getNewTape;
1757 }
1758 dparamsPtr->wroteLabel = 1; /* Remember we wrote the label */
1759 tapepos = tapeInfoPtr->position - 1;
1760
1761 strcpy(dparamsPtr->tapeName, TNAME(newTapeLabelPtr));
1762
1763 /* If appending, set dumpentry in the database as appended. */
1764 if (doAppend) {
1765 char gotName[BU_MAXTAPELEN + 32];
1766
1767 nodePtr->tapeSetDesc.b = extractTapeSeq(AFSTapeName);
1768 dparamsPtr->dump.tapes.b = nodePtr->tapeSetDesc.b;
1769 dparamsPtr->initialDumpId = oldTapeLabel.dumpid;
1770 strcpy(nodePtr->tapeSetDesc.format, dumpEntry.tapes.format);
1771
1772 code =
1773 bcdb_MakeDumpAppended(dparamsPtr->databaseDumpId,
1774 dparamsPtr->initialDumpId,
1775 nodePtr->tapeSetDesc.b);
1776 if (code)
1777 ErrorLog(2, taskId, code, 0,
1778 "Warning: Can't append dump %u to dump %u in database\n",
1779 dparamsPtr->databaseDumpId,
1780 dparamsPtr->initialDumpId);
1781
1782 LABELNAME(gotName, &oldTapeLabel);
1783 TLog(taskId, "Appending dump %s (DumpID %u) to tape %s\n",
1784 nodePtr->dumpSetName, dparamsPtr->databaseDumpId, gotName);
1785 }
1786
1787 /* If not appending, delete overwritten dump from the database */
1788 else {
1789 if ((oldTapeLabel.structVersion >= TAPE_VERSION_3)
1790 && oldTapeLabel.dumpid) {
1791 code = bcdb_deleteDump(oldTapeLabel.dumpid, 0, 0, 0);
1792 if (code && (code != BUDB_NOENT))
1793 ErrorLog(0, taskId, code, 0,
1794 "Warning: Can't delete old dump %u from database\n",
1795 oldTapeLabel.dumpid);
1796 }
1797 }
1798
1799 code =
1800 useTape(&dparamsPtr->tape, dparamsPtr->databaseDumpId,
1801 dparamsPtr->tapeName,
1802 (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1803 newTapeLabelPtr->useCount, newTapeLabelPtr->creationTime,
1804 newTapeLabelPtr->expirationDate, tapepos);
1805
1806 /*
1807 * The margin of space to check for end of tape is set to the
1808 * amount of space used to write an end-of-tape multiplied by 2.
1809 * The amount of space is size of a 16K volume trailer, a 16K File
1810 * End mark, its EOF marker, a 16K EODump marker, its EOF marker,
1811 * and up to two EOF markers done on close (3 16K blocks + 4 EOF
1812 * markers).
1813 */
1814 tc_EndMargin = (3 * 16384 + 4 * globalTapeConfig.fileMarkSize) * 2;
1815 tc_KEndMargin = tc_EndMargin / 1024;
1816 break;
1817
1818 getNewTape:
1819 unmountTape(taskId, tapeInfoPtr);
1820 }
1821
1822 error_exit:
1823 return (code);
1824 }
1825
1826 int
1827 makeVolumeHeader(struct volumeHeader *vhptr, struct dumpRock *dparamsPtr,
1828 int fragmentNumber)
1829 {
1830 struct dumpNode *nodePtr = dparamsPtr->node;
1831 struct tc_dumpDesc *curDump;
1832 afs_int32 code = 0;
1833
1834 curDump = &nodePtr->dumps[dparamsPtr->curVolume];
1835
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;
1844 vhptr->contd = 0;
1845 vhptr->magic = TC_VOLBEGINMAGIC;
1846 vhptr->dumpID = dparamsPtr->databaseDumpId; /* real dump id */
1847 vhptr->level = nodePtr->level;
1848 vhptr->parentID = nodePtr->parent;
1849 vhptr->endTime = 0;
1850 vhptr->versionflags = CUR_TAPE_VERSION;
1851 strcpy(vhptr->dumpSetName, nodePtr->dumpSetName);
1852 strcpy(vhptr->preamble, "H++NAME#");
1853 strcpy(vhptr->postamble, "T--NAME#");
1854
1855 return (code);
1856 }
1857
1858 int
1859 volumeHeader_hton(struct volumeHeader *hostPtr, struct volumeHeader *netPtr)
1860 {
1861 struct volumeHeader volHdr;
1862
1863 memset(&volHdr, 0, sizeof(volHdr));
1864
1865 strcpy(volHdr.preamble, hostPtr->preamble);
1866 strcpy(volHdr.postamble, hostPtr->postamble);
1867 strcpy(volHdr.volumeName, hostPtr->volumeName);
1868 strcpy(volHdr.dumpSetName, hostPtr->dumpSetName);
1869 volHdr.volumeID = htonl(hostPtr->volumeID);
1870 volHdr.server = htonl(hostPtr->server);
1871 volHdr.part = htonl(hostPtr->part);
1872 volHdr.from = htonl(hostPtr->from);
1873 volHdr.frag = htonl(hostPtr->frag);
1874 volHdr.magic = htonl(hostPtr->magic);
1875 volHdr.contd = htonl(hostPtr->contd);
1876 volHdr.dumpID = htonl(hostPtr->dumpID);
1877 volHdr.level = htonl(hostPtr->level);
1878 volHdr.parentID = htonl(hostPtr->parentID);
1879 volHdr.endTime = htonl(hostPtr->endTime);
1880 volHdr.versionflags = htonl(hostPtr->versionflags);
1881 volHdr.cloneDate = htonl(hostPtr->cloneDate);
1882
1883 memcpy(netPtr, &volHdr, sizeof(struct volumeHeader));
1884 return 0;
1885 }
1886
1887 /* database related routines */
1888
1889 afs_int32
1890 createDump(struct dumpRock *dparamsPtr)
1891 {
1892 struct dumpNode *nodePtr = dparamsPtr->node;
1893 struct budb_dumpEntry *dumpPtr;
1894 afs_int32 code = 0;
1895
1896 dumpPtr = &dparamsPtr->dump;
1897 memset(dumpPtr, 0, sizeof(*dumpPtr));
1898
1899 /* id filled in by database */
1900 dumpPtr->parent = nodePtr->parent;
1901 dumpPtr->level = nodePtr->level;
1902 dumpPtr->flags = 0;
1903 #ifdef xbsa
1904 if (CONF_XBSA) {
1905 if (xbsaType == XBSA_SERVER_TYPE_ADSM) {
1906 strcpy(dumpPtr->tapes.tapeServer, butxInfo.serverName);
1907 dumpPtr->flags = BUDB_DUMP_ADSM;
1908 }
1909 if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1910 /* The current server (API) doesn't provide the function required
1911 * to specify a server at startup time. For that reason, we can't
1912 * be sure that the server name supplied by the user in the user-
1913 * defined configuration file is correct. We set a flag here so
1914 * we know at restore time that the servername info in the backup
1915 * database may be incorrect. We will not allow a server switch
1916 * at that time, even if the server at restore time supports
1917 * multiple servers.
1918 */
1919 dumpPtr->flags |= BUDB_DUMP_XBSA_NSS;
1920 }
1921 }
1922 #endif
1923 strcpy(dumpPtr->volumeSetName, nodePtr->volumeSetName);
1924 strcpy(dumpPtr->dumpPath, nodePtr->dumpName);
1925 strcpy(dumpPtr->name, nodePtr->dumpSetName);
1926 dumpPtr->created = 0; /* let database assign it */
1927 dumpPtr->incTime = 0; /* not really used */
1928 dumpPtr->nVolumes = 0;
1929 dumpPtr->initialDumpID = 0;
1930
1931 dumpPtr->tapes.id = groupId;
1932 dumpPtr->tapes.b = 1;
1933 dumpPtr->tapes.maxTapes = 0;
1934 strcpy(dumpPtr->tapes.format, nodePtr->tapeSetDesc.format);
1935
1936 /* principal filled in by database */
1937
1938 /* now call the database to create the entry */
1939 code = bcdb_CreateDump(dumpPtr);
1940 if (code == 0)
1941 dparamsPtr->databaseDumpId = dumpPtr->id;
1942
1943 return (code);
1944 }
1945
1946 #ifdef xbsa
1947 /* InitToServer:
1948 * Initialize to a specific server. The first time, we remember the
1949 * server as the original server and go back to it each time we pass 0
1950 * as the server.
1951 */
1952 afs_int32
1953 InitToServer(afs_int32 taskId, struct butx_transactionInfo * butxInfoP,
1954 char *server)
1955 {
1956 static char origserver[BSA_MAX_DESC];
1957 static int init = 0;
1958 afs_int32 rc, code = 0;
1959
1960 if (!init) {
1961 strcpy(origserver, "");
1962 init = 1;
1963 }
1964
1965 if (!server)
1966 server = origserver; /* return to original server */
1967 if (strcmp(server, "") == 0)
1968 return 0; /* No server, do nothing */
1969 if (strcmp(butxInfoP->serverName, server) == 0)
1970 return 0; /* same server, do nothing */
1971 if (strcmp(origserver, "") == 0)
1972 strcpy(origserver, server); /* remember original server */
1973
1974 if (strcmp(butxInfoP->serverName, "") != 0) {
1975 /* If already connected to a server, disconnect from it.
1976 * Check to see if our server does not support switching.
1977 */
1978 if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1979 ErrorLog(0, taskId, TC_BADTASK, 0,
1980 "This version of XBSA libraries does not support switching "
1981 "from server %s to server %s\n", butxInfoP->serverName,
1982 server);
1983 return (TC_BADTASK);
1984 }
1985
1986 rc = xbsa_Finalize(&butxInfo);
1987 if (rc != XBSA_SUCCESS) {
1988 ErrorLog(0, taskId, rc, 0,
1989 "InitToServer: Unable to terminate the connection to server %s\n",
1990 butxInfoP->serverName);
1991 ERROR_EXIT(rc);
1992 }
1993 }
1994
1995 /* initialize to the new server */
1996 rc = xbsa_Initialize(&butxInfo, xbsaObjectOwner, appObjectOwner,
1997 xbsaSecToken, server);
1998 if (rc != XBSA_SUCCESS) {
1999 ErrorLog(0, taskId, rc, 0,
2000 "InitToServer: Unable to initialize the XBSA library to server %s\n",
2001 server);
2002 ERROR_EXIT(rc);
2003 }
2004
2005 error_exit:
2006 return (code);
2007 }
2008
2009
2010 /* DeleteDump
2011 *
2012 */
2013 void *
2014 DeleteDump(void *param)
2015 {
2016 struct deleteDumpIf *ptr = (struct deleteDumpIf *)param;
2017
2018 afs_int32 taskId;
2019 afs_int32 rc, code = 0;
2020 afs_uint32 dumpid;
2021 afs_int32 index, next, dbTime;
2022 budb_volumeList vl;
2023 struct budb_dumpEntry dumpEntry;
2024 char tapeName[BU_MAXTAPELEN];
2025 char dumpIdStr[XBSA_MAX_OSNAME];
2026 char volumeNameStr[XBSA_MAX_PATHNAME];
2027 afs_int32 i;
2028 int intrans = 0;
2029 int allnotfound = 1, onenotfound = 0;
2030 extern struct udbHandleS udbHandle;
2031 extern struct deviceSyncNode *deviceLatch;
2032
2033 dumpid = ptr->dumpID;
2034 taskId = ptr->taskId; /* Get task Id */
2035
2036 afs_pthread_setname_self("deletedump");
2037 setStatus(taskId, DRIVE_WAIT);
2038 EnterDeviceQueue(deviceLatch);
2039 clearStatus(taskId, DRIVE_WAIT);
2040
2041 printf("\n\n");
2042 TapeLog(2, taskId, 0, 0, "Delete Dump %u\n", dumpid);
2043
2044 vl.budb_volumeList_len = 0;
2045 vl.budb_volumeList_val = 0;
2046 tapeName[0] = '\0';
2047
2048 /* Get the dump info for the dump we are deleting */
2049 rc = bcdb_FindDumpByID(dumpid, &dumpEntry);
2050 if (rc) {
2051 ErrorLog(0, taskId, rc, 0,
2052 "Unable to locate dump ID %u in database\n", dumpid);
2053 setStatus(taskId, TASK_ERROR);
2054 ERROR_EXIT(rc);
2055 }
2056
2057 /* we must make sure that we are configured with the correct type of
2058 * XBSA server for this dump delete! Only those dumped to an ADSM server.
2059 */
2060 if ((xbsaType == XBSA_SERVER_TYPE_ADSM)
2061 && !((dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA)))) {
2062 ErrorLog(0, taskId, TC_BADTASK, 0,
2063 "The dump %u requested for deletion is incompatible with this instance of butc\n",
2064 dumpid);
2065 setStatus(taskId, TASK_ERROR);
2066 ERROR_EXIT(TC_BADTASK);
2067 }
2068
2069 /* Make sure we are connected to the correct server. If not, switch to it if appropriate */
2070 if ((strlen((char *)dumpEntry.tapes.tapeServer) != 0)
2071 && (strcmp((char *)dumpEntry.tapes.tapeServer, butxInfo.serverName) !=
2072 0)) {
2073
2074 /* Check to see if the tapeServer name is trustworthy */
2075 if ((dumpEntry.flags & (BUDB_DUMP_XBSA_NSS | BUDB_DUMP_BUTA))
2076 && !forcemultiple) {
2077 /* The dump was made with a version of the XBSA interface
2078 * that didn't allow switching of servers, we can't be sure
2079 * that the servername in the backup database is correct. So,
2080 * we will check the servername and log it if they don't match;
2081 * but we will try to do the delete without switching servers.
2082 */
2083 TLog(taskId,
2084 "The dump %d requested for deletion is on server %s "
2085 "but butc is connected to server %s "
2086 "(Attempting to delete the dump anyway)\n", dumpid,
2087 (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2088 } else {
2089 TLog(taskId,
2090 "The dump %u requested for deletion is on server %s "
2091 "but butc is connected to server %s "
2092 "(switching servers)\n", dumpid,
2093 (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2094
2095 rc = InitToServer(taskId, &butxInfo,
2096 (char *)dumpEntry.tapes.tapeServer);
2097 if (rc != XBSA_SUCCESS) {
2098 setStatus(taskId, TASK_ERROR);
2099 ERROR_EXIT(rc);
2100 }
2101 }
2102 }
2103
2104 /* Start a new Transaction */
2105 rc = xbsa_BeginTrans(&butxInfo);
2106 if (rc != XBSA_SUCCESS) {
2107 ErrorLog(0, taskId, rc, 0, "Unable to create a new transaction\n");
2108 setStatus(taskId, TASK_ERROR);
2109 ERROR_EXIT(rc);
2110 }
2111 intrans = 1;
2112
2113 /* Query the backup database for list of volumes to delete */
2114 for (index = next = 0; index != -1; index = next) {
2115 rc = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client,
2116 UF_SINGLESERVER, BUDB_MAJORVERSION,
2117 BUDB_OP_DUMPID, tapeName, dumpid, 0,
2118 index, &next, &dbTime, &vl);
2119 if (rc) {
2120 if (rc == BUDB_ENDOFLIST)
2121 break;
2122 ErrorLog(0, taskId, rc, 0, "Can't find volume info for dump %d\n",
2123 dumpid);
2124 setStatus(taskId, TASK_ERROR);
2125 ERROR_EXIT(rc);
2126 }
2127
2128 /* Delete all volumes on the list */
2129 for (i = 0; i < vl.budb_volumeList_len; i++) {
2130 if (dumpEntry.flags & BUDB_DUMP_BUTA) {
2131 /* dump was from buta, use old buta style names */
2132 snprintf(dumpIdStr, sizeof(dumpIdStr), "/%d", dumpid);
2133 snprintf(volumeNameStr, sizeof(volumeNameStr), "/%s",
2134 (char *)vl.budb_volumeList_val[i].name);
2135 } else { /* BUDB_DUMP_ADSM */
2136 /* dump was from butc to ADSM, use butc names */
2137 snprintf(dumpIdStr, sizeof(dumpIdStr), "%s", butcdumpIdStr);
2138 snprintf(volumeNameStr, sizeof(volumeNameStr), "/%d/%s",
2139 dumpid, (char *)vl.budb_volumeList_val[i].name);
2140 }
2141
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) {
2149 allnotfound = 0;
2150 ERROR_EXIT(rc);
2151 }
2152 onenotfound = 1;
2153 } else {
2154 allnotfound = 0;
2155 TLog(0,
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);
2159 }
2160 }
2161
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;
2167 }
2168
2169 error_exit:
2170 if (intrans) {
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);
2176 ERROR_EXIT(rc);
2177 }
2178 }
2179
2180 /* Switch back to the original server */
2181 rc = InitToServer(taskId, &butxInfo, NULL);
2182
2183 if (vl.budb_volumeList_val)
2184 free(vl.budb_volumeList_val);
2185
2186 setStatus(taskId, TASK_DONE);
2187 FreeNode(taskId); /* free the dump node */
2188 LeaveDeviceQueue(deviceLatch);
2189
2190 /* If we don't find any dumps on the server, rather than returning
2191 * a success, return a failure.
2192 */
2193 if (!code && onenotfound && allnotfound) {
2194 code = BUTX_DELETENOVOL;
2195 setStatus(taskId, TASK_ERROR);
2196 }
2197 return (void *)(uintptr_t)(code);
2198 }
2199 #endif