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