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