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