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