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