rxgen-prototypes-fixes-20020310
[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 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
918             server.sin_len         = sizeof(struct sockaddr_in);
919 #endif
920             curDump->hostAddr      = HOSTADDR(&server);
921             curDump->partition     = vldbEntry.serverPartition[e];
922
923             /* Determine date from which to do an incremental dump
924              */
925             if (nodePtr->parent)
926             {
927                 tcode = bcdb_FindClone(nodePtr->parent, curDump->name, &curDump->date);
928                 if (tcode) curDump->date = 0;
929             }
930             else
931             {
932                 curDump->date = 0;    /* do a full dump */
933             }
934         } /*pass*/
935
936         if ( checkAbortByTaskId(taskId) ) 
937             ERROR_EXIT(TC_ABORTEDBYREQUEST);
938             
939         /* Establish connection to volume - UV_ routine expects 
940          * host address in network order 
941          */
942         if (CONF_XBSA) {
943            dvcode = xbsaDumpVolume(curDump, dparamsPtr);
944         } else {
945            dvcode = dumpVolume(curDump, dparamsPtr);
946         }
947         action = dparamsPtr->curVolumeStatus;
948
949         /* Flush volume and tape entries to the database */
950         tcode = flushSavedEntries(action);
951         if (tcode) ERROR_EXIT(tcode);
952
953         switch (action)
954         {
955            case DUMP_SUCCESS:
956                 TapeLog(1, taskId, 0, 0, "Volume %s (%u) successfully dumped\n",
957                         curDump->name, curDump->vid);
958                 if (dvcode)
959                     ErrorLog(1, taskId, dvcode, 0,
960                              "Warning: Termination processing error on volume %s (%u)\n",
961                              curDump->name, curDump->vid);
962
963                 curDump->hostAddr = 0;
964                 dparamsPtr->volumesDumped++;
965                 break;
966                 
967            case DUMP_PARTIAL:
968            case DUMP_NOTHING:
969                 if (action == DUMP_PARTIAL)
970                 {
971                     ErrorLog(1, taskId, dvcode, 0, 
972                              "Volume %s (%u) failed - partially dumped\n",
973                              curDump->name, curDump->vid);
974                 }
975                 else if (dvcode)
976                 {
977                     ErrorLog(0, taskId, dvcode, 0,
978                        "Volume %s (%u) failed\n", curDump->name, curDump->vid);
979                 }
980                 else
981                 {
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                 {
989                     if (!queryoperator)
990                         ch = 'o';
991                     else
992                         ch = retryPrompt(curDump->name, curDump->vid, taskId);
993
994                     switch ( ch )
995                     {
996                        case 'r':                                    /* retry */
997                            dparamsPtr->curVolume--;      /* redump this volume */
998                            continue;
999                            break;
1000                         case 'o':                                    /* omit */
1001                            ErrorLog(1, taskId, 0, 0, "Volume %s (%u) omitted\n",
1002                                     curDump->name, curDump->vid);
1003                            dparamsPtr->volumesFailed++;
1004                            break;
1005                         case 'a':                                    /* abort */
1006                            TapeLog(1, taskId, 0, 0, "Dump aborted\n");
1007                            ERROR_EXIT(TC_ABORTEDBYREQUEST);
1008                            break;
1009                         default:
1010                            ERROR_EXIT(TC_INTERNALERROR);
1011                            break;
1012                     }
1013                 }
1014                 break;
1015                 
1016            case DUMP_RETRY:
1017                 TapeLog(1, taskId, dvcode, 0, 
1018                         "Volume %s (%u) hit end-of-tape inferred - will retry on next tape\n",
1019                         curDump->name, curDump->vid);
1020
1021                 /* Get the next tape */
1022                 unmountTape(taskId, tapeInfoPtr);
1023
1024                 dparamsPtr->tapeSeq++;
1025                 tcode = getDumpTape(dparamsPtr, 1, 0);   /* interactive - no appends */
1026                 if (tcode) ERROR_EXIT(tcode);
1027
1028                 dparamsPtr->curVolume--;                 /* redump this volume */
1029                 continue;
1030                 break;
1031
1032            case DUMP_NORETRYEOT:
1033                 ErrorLog(1, taskId, 0, 0, "Volume %s (%u) failed - volume larger than tape\n",
1034                          curDump->name, curDump->vid);
1035
1036                 /* rewrite the label on the tape - rewind - no need to switch tapes */
1037                 tcode = butm_Create(tapeInfoPtr, &dparamsPtr->tapeLabel, 1);
1038                 if (tcode) {
1039                     ErrorLog(0, taskId, tcode, tapeInfoPtr->error, "Can't relabel tape\n");
1040                     
1041                     unmountTape(taskId, tapeInfoPtr);
1042                     tcode = getDumpTape(dparamsPtr, 1, 0);   /* interactive - no appends */
1043                     if (tcode) ERROR_EXIT(tcode);
1044                 }
1045                 else {              /* Record the tape in database */
1046                     tapepos = tapeInfoPtr->position;
1047                     tcode = useTape(&dparamsPtr->tape, 
1048                                     dparamsPtr->databaseDumpId,
1049                                     dparamsPtr->tapeName, 
1050                                     (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1051                                     dparamsPtr->tapeLabel.useCount, 
1052                                     dparamsPtr->tapeLabel.creationTime, 
1053                                     dparamsPtr->tapeLabel.expirationDate,
1054                                     tapepos);
1055                 }
1056
1057                 curDump->hostAddr = 0;
1058                 dparamsPtr->volumesFailed++;
1059                 break;
1060
1061            case DUMP_NODUMP:
1062                 TapeLog(1, taskId, dvcode, 0, 
1063                         "Volume %s (%u) not dumped - has not been modified since last dump\n",
1064                         curDump->name, curDump->vid);
1065
1066                 curDump->hostAddr = 0;
1067                 dparamsPtr->volumesNotDumped++;
1068                 break;
1069
1070            default:
1071                 ErrorLog(1, taskId, dvcode, 0, "Volume %s (%u) failed\n",
1072                          curDump->name, curDump->vid);
1073                 ERROR_EXIT(dvcode);
1074                 break;
1075         }
1076     } /*w*/
1077
1078 error_exit:
1079     /* check if we terminated while processing a volume */
1080     if (dparamsPtr->curVolume < nodePtr->arraySize)
1081     {
1082         TapeLog(2, taskId, 0, 0, "Terminated while processing Volume %s (%u)\n", 
1083                 curDump->name, curDump->vid);
1084     }
1085
1086     /* print a summary of this pass */
1087     TapeLog(2, taskId, 0, 0, "End of pass %d: Volumes remaining = %d\n", passNumber, 
1088             nodePtr->arraySize - (dparamsPtr->volumesDumped + dparamsPtr->volumesFailed + 
1089                                   dparamsPtr->volumesNotDumped));
1090     return(code);
1091 }
1092
1093 Dumper(nodePtr)
1094     struct dumpNode *nodePtr;
1095 {
1096     struct dumpRock      dparams;
1097     struct butm_tapeInfo tapeInfo;
1098     int pass;
1099     int action;
1100     afs_int32 taskId;
1101     afs_int32 code = 0;
1102
1103     /* for volume setup */
1104     struct tc_dumpDesc  *dumpDescPtr;
1105     int    i;
1106     int    failedvolumes = 0;
1107     int    dumpedvolumes = 0;
1108     int    nodumpvolumes = 0;
1109     char   strlevel[5];
1110     char   msg[20];
1111     char   finishedMsg1[50];
1112     char   finishedMsg2[50];
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     extern afs_int32 createDump();
1120
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, "Can't initialize the tape module\n");
1141           ERROR_EXIT(code);
1142        }
1143     }
1144
1145     /* check if abort requested while waiting on device latch */
1146     if ( checkAbortByTaskId(taskId) ) 
1147         ERROR_EXIT(TC_ABORTEDBYREQUEST);
1148
1149     /* Are there volumes to dump */
1150     if (nodePtr->arraySize == 0)
1151     {
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 = BUTM_HDRSIZE + dataSize + sizeof(struct volumeHeader);
1167     }
1168     bufferBlock = (char *)0;
1169     bufferBlock = malloc(allocbufferSize);
1170     if (!bufferBlock) {
1171         ErrorLog(0, taskId, TC_NOMEMORY, 0, "Can't allocate BUFFERSIZE for dumps\n");
1172         ERROR_EXIT(TC_NOMEMORY);
1173     }
1174
1175     /* Determine the dumpid of the most recent dump of this volumeset and dumplevel
1176      * Used when requesting a tape. Done now because once we create the dump, the
1177      * routine will then find the newly created dump.
1178      */
1179     sprintf(strlevel, "%d", nodePtr->level);
1180     code = bcdb_FindLatestDump(nodePtr->volumeSetName, strlevel, &dparams.lastDump);
1181     if (code) {
1182        if (code != BUDB_NODUMPNAME) {
1183           ErrorLog(0, taskId, code, 0, "Can't read backup database\n");
1184           ERROR_EXIT(code);
1185        }
1186        memset(&dparams.lastDump, 0, sizeof(dparams.lastDump));
1187     }
1188
1189     code = createDump(&dparams);                        /* enter dump into database */
1190     if (code) 
1191     {
1192         ErrorLog(0, taskId, code, 0, "Can't create dump in database\n");
1193         ERROR_EXIT(code);
1194     }
1195
1196     TLog(taskId, "Dump %s (DumpID %u)\n", nodePtr->dumpSetName, dparams.databaseDumpId);
1197
1198     if (!CONF_XBSA) {
1199        /* mount the tape and write its label */
1200        code = getDumpTape(&dparams, autoQuery, nodePtr->doAppend);
1201     } else {
1202        /* Create a dummy tape to satisfy backup databae */
1203        code = getXBSATape(&dparams);
1204        tapeInfo.position = 1;
1205     }
1206     if (code) {
1207        /* If didn't write the label, remove dump from the database */
1208        if ( !dparams.wroteLabel ) {
1209           i = bcdb_deleteDump(dparams.databaseDumpId, 0, 0, 0);
1210           if ( i && (i != BUDB_NOENT) )
1211              ErrorLog(1, taskId, i, 0, "Warning: Can't delete dump %u from database\n", 
1212                                        dparams.databaseDumpId);
1213           else 
1214              dparams.databaseDumpId = 0;
1215        }
1216        ERROR_EXIT(code);                        /* exit with code from getTape */
1217     }
1218         
1219     startTime = time(0);
1220     for (pass = 1; pass <= maxpass; pass++)
1221     {
1222         lastPass = (pass == maxpass);
1223         code = dumpPass(&dparams, pass);
1224         if (code) ERROR_EXIT(code);
1225
1226         /* if no failed volumes, we're done */
1227         if ((dparams.volumesDumped + dparams.volumesFailed + dparams.volumesNotDumped) == 
1228             nodePtr->arraySize) break;
1229     }
1230
1231     /* 
1232      * Log the error but ignore it since the dump is effectively done.
1233      * Scantape may assume another volume and ask for next tape.
1234      */
1235     if (!CONF_XBSA) {
1236        code = butm_WriteEOT(&tapeInfo);
1237        if (code) TapeLog(taskId, code, tapeInfo.error, 
1238                          "Warning: Can't write end-of-dump on tape\n");
1239     }
1240
1241     code = finishTape(&dparams.tape, dparams.tapeInfoPtr->kBytes + 
1242                                     (dparams.tapeInfoPtr->nBytes ? 1 : 0));
1243     if (code) ERROR_EXIT(code);
1244
1245     code = finishDump(&dparams.dump);
1246     if (code) ERROR_EXIT(code);
1247
1248     action = dparams.curVolumeStatus;
1249     code = flushSavedEntries(action);
1250     if (code) ERROR_EXIT(code);
1251
1252   error_exit:
1253     endTime = time(0);
1254     Bind(0);
1255     if (bufferBlock) free(bufferBlock);
1256
1257     if (!CONF_XBSA) {
1258        unmountTape(taskId, &tapeInfo);
1259     }
1260     waitDbWatcher();
1261
1262     dumpedvolumes = dparams.volumesDumped;
1263     nodumpvolumes = dparams.volumesNotDumped;
1264     failedvolumes = nodePtr->arraySize - (dumpedvolumes + nodumpvolumes);
1265
1266     /* pass back the number of volumes we failed to dump */
1267     lock_Status();
1268     nodePtr->statusNodePtr->volsFailed = failedvolumes;
1269     unlock_Status();
1270
1271     lastPass = 1; /* In case we aborted */
1272
1273     DUMPNAME(finishedMsg1, nodePtr->dumpSetName, dparams.databaseDumpId);
1274     sprintf(finishedMsg2, "%d volumes dumped", dumpedvolumes);
1275     if (failedvolumes)
1276     {
1277         sprintf(msg, ", %d failed", failedvolumes);
1278         strcat(finishedMsg2, msg);
1279     }
1280     if (nodumpvolumes)
1281     {
1282         sprintf(msg, ", %d unchanged", nodumpvolumes);
1283         strcat(finishedMsg2, msg);
1284     }
1285
1286     if (code == TC_ABORTEDBYREQUEST)
1287     {
1288         ErrorLog(0, taskId, 0, 0, 
1289                  "%s: Aborted by request. %s\n", finishedMsg1, finishedMsg2);
1290         clearStatus(taskId, ABORT_REQUEST);
1291         setStatus  (taskId, ABORT_DONE);
1292     }
1293     else if (code)
1294     {
1295         ErrorLog(0, taskId, code, 0, 
1296                  "%s: Finished with errors. %s\n", finishedMsg1, finishedMsg2);
1297         setStatus(taskId, TASK_ERROR);
1298     }
1299     else
1300     {
1301         TLog(taskId, "%s: Finished. %s\n", finishedMsg1, finishedMsg2);
1302     }
1303     lastPass = 0;
1304
1305     /* Record how long the dump took */
1306     if (centralLogIO && startTime) {
1307        long timediff;
1308        afs_int32 hrs, min, sec, tmp;
1309        char line[1024];
1310        struct tm tmstart, tmend;
1311
1312        localtime_r(&startTime, &tmstart);
1313        localtime_r(&endTime, &tmend);
1314        timediff = (int)endTime - (int)startTime;
1315        hrs = timediff / 3600;
1316        tmp = timediff % 3600;
1317        min = tmp / 60;
1318        sec = tmp % 60;
1319
1320        sprintf(line, "%-5d  %02d/%02d/%04d %02d:%02d:%02d  "
1321                            "%02d/%02d/%04d %02d:%02d:%02d  "
1322                            "%02d:%02d:%02d  "
1323                      "%s %d of %d volumes dumped (%ld KB)\n",
1324                taskId, tmstart.tm_mon+1, tmstart.tm_mday, tmstart.tm_year+1900,
1325                        tmstart.tm_hour, tmstart.tm_min, tmstart.tm_sec,
1326                        tmend.tm_mon+1, tmend.tm_mday, tmend.tm_year+1900,
1327                        tmend.tm_hour, tmend.tm_min, tmend.tm_sec,
1328                hrs, min, sec, nodePtr->volumeSetName, 
1329                dumpedvolumes, dumpedvolumes+failedvolumes,
1330                dparams.tapeInfoPtr->kBytes+1);
1331
1332        fwrite(line, strlen(line), 1, centralLogIO);
1333        fflush(centralLogIO);
1334     }
1335
1336     setStatus(taskId, TASK_DONE);
1337
1338     FreeNode(taskId);                     /* free the dump node */
1339     LeaveDeviceQueue(deviceLatch);
1340     return(code);
1341 }
1342
1343 #define BELLTIME 60     /* 60 seconds before a bell rings */
1344 #define BELLCHAR 7      /* ascii for bell */
1345
1346 /* retryPrompt
1347  *      prompt the user to decide how to handle a failed volume dump. The
1348  *      volume parameters describe the volume that failed
1349  * entry:
1350  *      volumeName - name of volume
1351  *      volumeId - volume id 
1352  *      taskId - for job contrl
1353  * fn return:
1354  *      character typed by user, one of r, o or a
1355  */
1356
1357 char
1358 retryPrompt(volumeName, volumeId, taskId)
1359      char *volumeName;
1360      afs_int32 volumeId;
1361      afs_uint32 taskId;
1362 {
1363     afs_int32 start;
1364     char ch;
1365     afs_int32 code = 0;
1366
1367     setStatus(taskId, OPR_WAIT);
1368     printf("\nDump of volume %s (%u) failed\n\n", volumeName, volumeId);
1369
1370     printf("Please select action to be taken for this volume\n");
1371
1372 again:    
1373     printf("r - retry, try dumping this volume again\n");
1374     printf("o - omit,  this volume from this dump\n");
1375     printf("a - abort, the entire dump\n");
1376
1377     while (1) {
1378         FFlushInput(stdin);
1379         putchar(BELLCHAR);
1380         fflush(stdout);
1381
1382         start = time(0);
1383         while(1) 
1384         {
1385 #ifdef AFS_PTHREAD_ENV
1386             code = GetResponseKey(5, &ch); /* ch stores key pressed */
1387 #else
1388             code = LWP_GetResponseKey(5, &ch); /* ch stores key pressed */
1389 #endif
1390             if (code == 1) 
1391                 break;                  /* input is available */
1392
1393             if ( checkAbortByTaskId(taskId) )
1394             {
1395                 clearStatus(taskId, OPR_WAIT);
1396                 printf("This tape operation has been aborted by the coordinator\n");
1397                 return 'a';
1398             }
1399
1400             if (time(0) > start + BELLTIME) break;
1401         }
1402         /* otherwise, we should beep again, check for abort and go back,
1403          * since the GetResponseKey() timed out.
1404          */
1405         if (code == 1) 
1406             break;                      /* input is available */
1407     }
1408     clearStatus(taskId, OPR_WAIT);
1409     if ( ch != 'r' && ch != 'o' && ch != 'a' )
1410     {
1411         printf("Please select one of the 3 options, r, o or a\n");
1412         goto again;
1413     }
1414
1415     return ch;
1416 }
1417
1418 /* For testing: it prints the tape label */
1419 printTapeLabel(tl)
1420    struct butm_tapeLabel *tl;
1421 {
1422    printf("Tape Label\n");
1423    printf("   structVersion  = %d\n", tl->structVersion);
1424    printf("   creationTime   = %u\n", tl->creationTime);
1425    printf("   expirationDate = %u\n", tl->expirationDate);
1426    printf("   AFSName        = %s\n", tl->AFSName);
1427    printf("   cell           = %s\n", tl->cell);
1428    printf("   dumpid         = %d\n", tl->dumpid);
1429    printf("   useCount       = %d\n", tl->useCount);
1430    printf("   comment        = %s\n", tl->comment);
1431    printf("   pName          = %s\n", tl->pName);
1432    printf("   size           = %u\n", tl->size);
1433    printf("   dumpPath       = %s\n", tl->dumpPath);
1434 }
1435
1436 /* getXBSATape
1437  *      Create a tape structure to be satisfy the backup database
1438  *      even though we don't really use a tape with XBSA.
1439  */
1440 getXBSATape(dparamsPtr)
1441     struct dumpRock *dparamsPtr;
1442 {
1443     struct dumpNode       *nodePtr      =  dparamsPtr->node;
1444     struct butm_tapeInfo  *tapeInfoPtr  =  dparamsPtr->tapeInfoPtr;
1445     struct butm_tapeLabel *tapeLabelPtr = &dparamsPtr->tapeLabel;
1446     afs_int32 code = 0;
1447
1448     tc_MakeTapeName(dparamsPtr->tapeName, &nodePtr->tapeSetDesc, dparamsPtr->tapeSeq);
1449
1450     GetNewLabel(tapeInfoPtr, ""/*pName*/, dparamsPtr->tapeName, tapeLabelPtr);
1451     strcpy(tapeLabelPtr->dumpPath, nodePtr->dumpName);
1452     tapeLabelPtr->dumpid         = dparamsPtr->databaseDumpId;
1453     tapeLabelPtr->expirationDate = calcExpirationDate(nodePtr->tapeSetDesc.expType,
1454                                                       nodePtr->tapeSetDesc.expDate,
1455                                                       time(0));
1456
1457     /* printTapeLabel(tapeLabelPtr); For testing */
1458
1459     code = useTape(&dparamsPtr->tape,
1460                    dparamsPtr->databaseDumpId,
1461                    dparamsPtr->tapeName,
1462                    (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1463                    tapeLabelPtr->useCount,
1464                    tapeLabelPtr->creationTime,
1465                    tapeLabelPtr->expirationDate,
1466                    0 /*tape position*/);
1467     return(code);
1468 }
1469
1470 /* getDumpTape
1471  *      iterate until the desired tape (as specified by the dump structures)
1472  *      is mounted.
1473  * entry:
1474  *      interactiveFlag
1475  *              0 - assume the tape is there. Prompt if assumption false
1476  *              1 - prompt regardless
1477  */
1478
1479 getDumpTape(dparamsPtr, interactiveFlag, append)
1480      struct dumpRock *dparamsPtr;
1481      int interactiveFlag;
1482      afs_int32 append;
1483 {
1484     struct dumpNode       *nodePtr         =  dparamsPtr->node;
1485     struct butm_tapeInfo  *tapeInfoPtr     =  dparamsPtr->tapeInfoPtr;
1486     struct butm_tapeLabel *newTapeLabelPtr = &dparamsPtr->tapeLabel;
1487     char                  AFSTapeName[TC_MAXTAPENAMELEN];
1488     afs_int32                 taskId           = nodePtr->taskID;
1489     struct butm_tapeLabel   oldTapeLabel;
1490     struct budb_dumpEntry   dumpEntry;
1491     struct budb_tapeEntry   tapeEntry;
1492     struct budb_volumeEntry volEntry;
1493     Date  oldTapeExpiration, expir;
1494     afs_int32 curTime;
1495     afs_int32 doAppend;
1496     afs_int32 code = 0;
1497     int   askForTape, opcode;
1498     int   tapecount = 1;
1499     char  strlevel[5];
1500     afs_int32 tapepos, lastpos;
1501
1502     extern struct tapeConfig globalTapeConfig;
1503     extern struct udbHandleS udbHandle;
1504
1505     askForTape = interactiveFlag;
1506     dparamsPtr->wroteLabel = 0;
1507
1508     /* Keep prompting for a tape until we get it right */
1509     while (1)
1510     {
1511         /* What the name of the tape would be if not appending to it */
1512         tc_MakeTapeName(AFSTapeName, &nodePtr->tapeSetDesc, dparamsPtr->tapeSeq);
1513
1514         doAppend = append;
1515
1516         if (askForTape)
1517         {
1518             code = PromptForTape((doAppend ? APPENDOPCODE : WRITEOPCODE), 
1519                                  AFSTapeName, dparamsPtr->databaseDumpId, taskId, tapecount);
1520             if (code) ERROR_EXIT(code);
1521         }
1522         askForTape = 1;
1523         tapecount++;
1524
1525         /* open the tape device */
1526         code = butm_Mount(tapeInfoPtr, AFSTapeName);
1527         if (code)
1528         {
1529             TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
1530             goto getNewTape;
1531         }
1532
1533         /* Read the tape label */
1534         code = butm_ReadLabel(tapeInfoPtr, &oldTapeLabel, 1);   /* rewind */
1535         if (code) {
1536            if (tapeInfoPtr->error) {
1537               ErrorLog(0, taskId, code, tapeInfoPtr->error, 
1538                        "Warning: Tape error while reading label (will proceed with dump)\n");
1539            }
1540            memset(&oldTapeLabel, 0, sizeof(oldTapeLabel));
1541         }
1542
1543         /* Check if null tape. Prior 3.3, backup tapes have no dump id */
1544         if ( (strcmp(oldTapeLabel.AFSName,"") == 0) && (oldTapeLabel.dumpid == 0) )
1545         {
1546             if (doAppend)
1547             {
1548                 TLog(taskId, "Dump not found on tape. Proceeding with initial dump\n");
1549                 doAppend = 0;
1550             }
1551         }
1552         else if (doAppend)                          /* appending */
1553         {
1554             /* Check that we don't have a database dump tape */
1555             if ( databaseTape(oldTapeLabel.AFSName) )
1556             {
1557                 char gotName[BU_MAXTAPELEN+32];
1558
1559                 /* label does not match */
1560                 LABELNAME(gotName, &oldTapeLabel);
1561                 TLog(taskId, "Can't append to database tape %s\n", gotName);
1562                 goto getNewTape;
1563             }
1564
1565             /* Verify that the tape is of version 4 (AFS 3.3) or greater */
1566             if (oldTapeLabel.structVersion < TAPE_VERSION_4)
1567             {
1568                 TLog(taskId, "Can't append: requires tape version %d or greater\n", 
1569                      TAPE_VERSION_4);
1570                 goto getNewTape;
1571             }
1572
1573             /* Verify that the last tape of the dump set is in the drive.
1574              * volEntry will be zeroed if last dump has no volume entries.
1575              */
1576             code = bcdb_FindLastTape(oldTapeLabel.dumpid, &dumpEntry, &tapeEntry, &volEntry);
1577             if (code) {
1578                ErrorLog(0, taskId, code, 0, 
1579                         "Can't append: Can't find last volume of dumpId %u in database\n",
1580                          oldTapeLabel.dumpid);
1581                printf("Please scan the dump in or choose another tape\n");
1582                goto getNewTape;
1583             }
1584             lastpos = (volEntry.position ? volEntry.position : tapeEntry.labelpos);
1585
1586             if (strcmp(TNAME(&oldTapeLabel),tapeEntry.name))
1587             {
1588                 char expName[BU_MAXTAPELEN+32], gotName[BU_MAXTAPELEN+32];
1589
1590                 TAPENAME(expName, tapeEntry.name, oldTapeLabel.dumpid);
1591                 LABELNAME(gotName, &oldTapeLabel);
1592
1593                 TLog(taskId, 
1594                      "Can't append: Last tape in dump-set is %s, label seen %s\n",
1595                      expName, gotName);
1596                 goto getNewTape;
1597             }
1598
1599             /* After reading the tape label, we now know what it is */
1600             strcpy (AFSTapeName,       oldTapeLabel.AFSName);    /* the real name */
1601             strcpy (tapeInfoPtr->name, oldTapeLabel.AFSName);    /* the real name */
1602
1603             /* Position after last volume on the tape */
1604             code = butm_SeekEODump(tapeInfoPtr, lastpos);
1605             if (code) 
1606             {
1607                 ErrorLog(0, taskId, code, tapeInfoPtr->error, 
1608                          "Can't append: Can't position to end of dump on tape %s\n", 
1609                          tapeEntry.name);
1610                 goto getNewTape;
1611             }
1612
1613             /* Track size of tape - set after seek since seek changes the value */
1614             tapeInfoPtr->kBytes = tapeEntry.useKBytes;
1615         }
1616         else                         /* not appending */
1617         {
1618             afs_uint32 tapeid;
1619             afs_uint32 dmp, parent;
1620             struct budb_dumpEntry de, de2;
1621
1622
1623             /* Check if tape name is not what expected - null tapes are acceptable
1624              * Don't do check if the tape has a user defined label.
1625              */
1626             if ( dump_namecheck && (strcmp(oldTapeLabel.pName,"")==0) )
1627             {
1628                 if ( strcmp(oldTapeLabel.AFSName,"") &&             /* not null tape */
1629                      strcmp(oldTapeLabel.AFSName,AFSTapeName) )     /* not expected name */
1630                 {
1631                     TLog(taskId, "Tape label expected %s, label seen %s\n", 
1632                          AFSTapeName, oldTapeLabel.AFSName);
1633                     goto getNewTape;
1634                 }
1635
1636                 /* Check that we don't have a database dump tape */
1637                 if ( databaseTape(oldTapeLabel.AFSName) )
1638                 {
1639                     /* label does not match */
1640                     TLog(taskId, "Tape label expected %s, can't dump to database tape %s\n", 
1641                          AFSTapeName, oldTapeLabel.AFSName);
1642                     goto getNewTape;
1643                 }
1644             }
1645
1646             /* Verify the tape has not expired - only check if not appending */
1647             if ( !tapeExpired(&oldTapeLabel) ) {
1648                 TLog(taskId, "This tape has not expired\n");
1649                 goto getNewTape;
1650             }
1651
1652             /* Given a tape dump with good data, verify we don't overwrite recent dumps
1653              * and also verify that the volume will be restorable - if not print warnings
1654              */
1655             if (oldTapeLabel.dumpid)
1656             {
1657                 /* Do not overwrite a tape that belongs to the dump's dumpset */
1658                 tapeid = (dparamsPtr->initialDumpId ? dparamsPtr->initialDumpId :
1659                                                       dparamsPtr->databaseDumpId);
1660                 if (oldTapeLabel.dumpid == tapeid)
1661                 {
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                 {
1672                     code = bcdb_FindDumpByID(dmp, &de);
1673                     if (code) {
1674                         ErrorLog(0, taskId, 0, 0, 
1675                                  "Warning: Can't find parent dump %u in backup database\n", 
1676                                  dmp);
1677                         break;
1678                     }
1679
1680                     tapeid = (de.initialDumpID ? de.initialDumpID : de.id);
1681                     if (oldTapeLabel.dumpid == tapeid) {
1682                         ErrorLog(0, taskId, 0, 0, 
1683                                  "Can't overwrite the parent dump %s (DumpID %u)\n",
1684                                  de.name, de.id);
1685                         goto getNewTape;
1686                     }
1687                 }
1688
1689                 /* Since the dumpset on this tape will be deleted from database, check if
1690                  * any of the dumps in this dumpset are most-recent-dumps.
1691                  */
1692                 for (dmp=oldTapeLabel.dumpid; dmp; dmp=de.appendedDumpID) {
1693                     if (dmp == dparamsPtr->lastDump.id) {
1694                         memcpy(&de, &dparamsPtr->lastDump, sizeof(de));
1695                         memcpy(&de2, &dparamsPtr->lastDump, sizeof(de2));
1696                     }
1697                     else {
1698                         code = bcdb_FindDumpByID(dmp, &de);
1699                         if (code) break;
1700                         sprintf(strlevel, "%d", de.level);
1701                         code = bcdb_FindLatestDump(de.volumeSetName, strlevel, &de2);
1702                         if (code) continue;
1703                     }
1704
1705                     /* If dump on the tape is the latest dump at this level */
1706                     if (de.id == de2.id) {
1707                        if (strcmp(DUMP_TAPE_NAME,de2.name) == 0) {
1708                           ErrorLog(0, taskId, 0, 0, 
1709                                    "Warning: Overwriting most recent dump %s (DumpID %u)\n",
1710                                    de.name, de.id);
1711                        }
1712                        else {
1713                           ErrorLog(0, taskId, 0, 0, 
1714                                    "Warning: Overwriting most recent dump of the '%s' volumeset: %s (DumpID %u)\n",
1715                                    de.volumeSetName, de.name, de.id);
1716                        }
1717                     }
1718                 }
1719             }       /* if (oldTapeLabel.dumpid) */
1720         }           /* else not appending */
1721         
1722         /*
1723          * Now have the right tape. Create a new label for the tape
1724          * Appended labels have the dump's dumpId - labels at beginnings of 
1725          *     tape have the initial dump's dumpId.
1726          * Appended labels do not increment the useCount.
1727          * Labels at beginnings of tape use the most future expiration of the dump set.
1728          */
1729         GetNewLabel(tapeInfoPtr, oldTapeLabel.pName, AFSTapeName, newTapeLabelPtr);
1730         strcpy(newTapeLabelPtr->dumpPath, nodePtr->dumpName);
1731         newTapeLabelPtr->expirationDate = 
1732           calcExpirationDate(nodePtr->tapeSetDesc.expType, nodePtr->tapeSetDesc.expDate, time(0));
1733         newTapeLabelPtr->dumpid   = dparamsPtr->databaseDumpId;
1734         newTapeLabelPtr->useCount = oldTapeLabel.useCount;
1735
1736         if (!doAppend) {
1737             newTapeLabelPtr->useCount++;
1738             if (dparamsPtr->initialDumpId) {
1739                 newTapeLabelPtr->dumpid = dparamsPtr->initialDumpId;
1740                 expir = ExpirationDate(dparamsPtr->initialDumpId);
1741                 if (expir > newTapeLabelPtr->expirationDate)
1742                     newTapeLabelPtr->expirationDate = expir;
1743             }
1744         }
1745
1746         /* write the label on the tape - rewind if not appending and vice-versa */
1747         code = butm_Create(tapeInfoPtr, newTapeLabelPtr, !doAppend);
1748         if (code)
1749         {
1750             char gotName[BU_MAXTAPELEN+32];
1751
1752             LABELNAME(gotName, newTapeLabelPtr);
1753             TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't label tape as %s\n", gotName);
1754             goto getNewTape;
1755         }
1756         dparamsPtr->wroteLabel = 1;                   /* Remember we wrote the label */
1757         tapepos = tapeInfoPtr->position-1;
1758
1759         strcpy(dparamsPtr->tapeName, TNAME(newTapeLabelPtr));
1760
1761         /* If appending, set dumpentry in the database as appended. */
1762         if (doAppend)
1763         {
1764             char gotName[BU_MAXTAPELEN+32];
1765
1766             nodePtr->tapeSetDesc.b = extractTapeSeq(AFSTapeName);
1767             dparamsPtr->dump.tapes.b = nodePtr->tapeSetDesc.b;
1768             dparamsPtr->initialDumpId = oldTapeLabel.dumpid;
1769             strcpy(nodePtr->tapeSetDesc.format, dumpEntry.tapes.format);
1770
1771             code = bcdb_MakeDumpAppended(dparamsPtr->databaseDumpId, 
1772                                          dparamsPtr->initialDumpId, nodePtr->tapeSetDesc.b);
1773             if (code)
1774                 ErrorLog(2, taskId, code, 0,
1775                          "Warning: Can't append dump %u to dump %u in database\n",
1776                          dparamsPtr->databaseDumpId, dparamsPtr->initialDumpId);
1777
1778             LABELNAME(gotName, &oldTapeLabel);
1779             TLog(taskId, "Appending dump %s (DumpID %u) to tape %s\n",
1780                  nodePtr->dumpSetName,  dparamsPtr->databaseDumpId, gotName);
1781         }
1782
1783         /* If not appending, delete overwritten dump from the database */
1784         else
1785         {
1786             if ( (oldTapeLabel.structVersion >= TAPE_VERSION_3) && oldTapeLabel.dumpid )
1787             {
1788                 code = bcdb_deleteDump(oldTapeLabel.dumpid, 0, 0, 0);
1789                 if ( code && (code != BUDB_NOENT) )
1790                     ErrorLog(0, taskId, code, 0, 
1791                              "Warning: Can't delete old dump %u from database\n",
1792                              oldTapeLabel.dumpid);
1793             }
1794         }
1795
1796         code = useTape(&dparamsPtr->tape, 
1797                         dparamsPtr->databaseDumpId,
1798                         dparamsPtr->tapeName,
1799                        (dparamsPtr->tapeSeq + dparamsPtr->dump.tapes.b),
1800                         newTapeLabelPtr->useCount, 
1801                         newTapeLabelPtr->creationTime, 
1802                         newTapeLabelPtr->expirationDate,
1803                         tapepos);
1804
1805         /*
1806          * The margin of space to check for end of tape is set to the
1807          * amount of space used to write an end-of-tape multiplied by 2.
1808          * The amount of space is size of a 16K volume trailer, a 16K File
1809          * End mark, its EOF marker, a 16K EODump marker, its EOF marker,
1810          * and up to two EOF markers done on close (3 16K blocks + 4 EOF
1811          * markers).
1812          */
1813         tc_EndMargin  = (3 * 16384 + 4 * globalTapeConfig.fileMarkSize) * 2;
1814         tc_KEndMargin = tc_EndMargin / 1024;
1815         break;
1816
1817       getNewTape:
1818         unmountTape(taskId, tapeInfoPtr);
1819     }
1820
1821 error_exit:
1822     return(code);
1823 }
1824
1825 makeVolumeHeader(vhptr, dparamsPtr, fragmentNumber)
1826      struct volumeHeader *vhptr;
1827      struct dumpRock *dparamsPtr;
1828      int fragmentNumber;
1829 {
1830     struct dumpNode *nodePtr = dparamsPtr->node;
1831     struct tc_dumpDesc *curDump;
1832     afs_int32 code = 0;
1833
1834     curDump = &nodePtr->dumps[dparamsPtr->curVolume];
1835
1836     memset(vhptr, 0, sizeof(*vhptr));
1837     strcpy(vhptr->volumeName, curDump->name);
1838     vhptr->volumeID = curDump->vid;
1839     vhptr->cloneDate = curDump->cloneDate;
1840     vhptr->server = curDump->hostAddr;
1841     vhptr->part = curDump->partition;
1842     vhptr->from = curDump->date;
1843     vhptr->frag = fragmentNumber;
1844     vhptr->contd = 0;
1845     vhptr->magic = TC_VOLBEGINMAGIC;
1846     vhptr->dumpID = dparamsPtr->databaseDumpId; /* real dump id */
1847     vhptr->level = nodePtr->level;
1848     vhptr->parentID = nodePtr->parent;
1849     vhptr->endTime = 0;
1850     vhptr->versionflags = CUR_TAPE_VERSION;
1851     strcpy(vhptr->dumpSetName, nodePtr->dumpSetName);
1852     strcpy(vhptr->preamble,"H++NAME#");
1853     strcpy(vhptr->postamble,"T--NAME#");
1854 error_exit:
1855     return (code);
1856 }
1857
1858 volumeHeader_hton(hostPtr, netPtr)
1859      struct volumeHeader *hostPtr, *netPtr;
1860 {
1861     struct volumeHeader volHdr;
1862
1863     strcpy(volHdr.preamble,    hostPtr->preamble);
1864     strcpy(volHdr.postamble,   hostPtr->postamble);
1865     strcpy(volHdr.volumeName,  hostPtr->volumeName);
1866     strcpy(volHdr.dumpSetName, hostPtr->dumpSetName);
1867     volHdr.volumeID     = htonl(hostPtr->volumeID);
1868     volHdr.server       = htonl(hostPtr->server);
1869     volHdr.part         = htonl(hostPtr->part);
1870     volHdr.from         = htonl(hostPtr->from);
1871     volHdr.frag         = htonl(hostPtr->frag);
1872     volHdr.magic        = htonl(hostPtr->magic);
1873     volHdr.contd        = htonl(hostPtr->contd);
1874     volHdr.dumpID       = htonl(hostPtr->dumpID);
1875     volHdr.level        = htonl(hostPtr->level);
1876     volHdr.parentID     = htonl(hostPtr->parentID);
1877     volHdr.endTime      = htonl(hostPtr->endTime);
1878     volHdr.versionflags = htonl(hostPtr->versionflags);
1879     volHdr.cloneDate    = htonl(hostPtr->cloneDate);
1880
1881     memcpy(netPtr, &volHdr, sizeof(struct volumeHeader));
1882 }
1883
1884 /* database related routines */
1885
1886 afs_int32
1887 createDump(dparamsPtr)
1888      struct dumpRock *dparamsPtr;
1889 {
1890     struct dumpNode *nodePtr = dparamsPtr->node;
1891     struct budb_dumpEntry *dumpPtr;
1892     afs_int32 code = 0;
1893
1894     dumpPtr = &dparamsPtr->dump;
1895     memset(dumpPtr, 0, sizeof(*dumpPtr));
1896
1897     /* id filled in by database */
1898     dumpPtr->parent = nodePtr->parent;
1899     dumpPtr->level  = nodePtr->level;
1900     dumpPtr->flags  = 0;
1901 #ifdef xbsa
1902     if (CONF_XBSA) {
1903        if (xbsaType == XBSA_SERVER_TYPE_ADSM) {
1904           strcpy(dumpPtr->tapes.tapeServer, butxInfo.serverName);
1905           dumpPtr->flags = BUDB_DUMP_ADSM;
1906        }
1907        if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1908           /* The current server (API) doesn't provide the function required
1909            * to specify a server at startup time.  For that reason, we can't
1910            * be sure that the server name supplied by the user in the user-
1911            * defined configuration file is correct.  We set a flag here so
1912            * we know at restore time that the servername info in the backup
1913            * database may be incorrect.  We will not allow a server switch
1914            * at that time, even if the server at restore time supports
1915            * multiple servers.
1916            */
1917           dumpPtr->flags |= BUDB_DUMP_XBSA_NSS;
1918        }
1919     }
1920 #endif
1921     strcpy(dumpPtr->volumeSetName, nodePtr->volumeSetName);
1922     strcpy(dumpPtr->dumpPath,      nodePtr->dumpName);
1923     strcpy(dumpPtr->name,          nodePtr->dumpSetName);
1924     dumpPtr->created       = 0;                     /* let database assign it */
1925     dumpPtr->incTime       = 0;                     /* not really used */
1926     dumpPtr->nVolumes      = 0;
1927     dumpPtr->initialDumpID = 0;
1928
1929     dumpPtr->tapes.id       = groupId;
1930     dumpPtr->tapes.b        = 1;
1931     dumpPtr->tapes.maxTapes = 0;
1932     strcpy(dumpPtr->tapes.format, nodePtr->tapeSetDesc.format);
1933
1934     /* principal filled in by database */
1935
1936     /* now call the database to create the entry */
1937     code = bcdb_CreateDump(dumpPtr);
1938     if (code == 0) dparamsPtr->databaseDumpId = dumpPtr->id;
1939
1940     return(code);
1941 }
1942
1943 #ifdef xbsa
1944 /* InitToServer:
1945  * Initialize to a specific server. The first time, we remember the
1946  * server as the original server and go back to it each time we pass 0
1947  * as the server.
1948  */
1949 afs_int32 InitToServer(afs_int32 taskId, struct butx_transactionInfo *butxInfoP, char *server)
1950 {
1951    static char origserver[BSA_MAX_DESC];
1952    static int  init=0;
1953    afs_int32 rc, code=0;
1954
1955    if (!init) {
1956       strcpy(origserver,"");
1957       init = 1;
1958    }
1959
1960    if (!server) server = origserver;               /* return to original server */
1961    if (strcmp(server,"") == 0) return 0;           /* No server, do nothing */
1962    if (strcmp(butxInfoP->serverName,server) == 0) return 0;   /* same server, do nothing */
1963    if (strcmp(origserver,"") == 0) strcpy(origserver, server);/* remember original server */
1964
1965    if (strcmp(butxInfoP->serverName,"") != 0) {
1966       /* If already connected to a server, disconnect from it.
1967        * Check to see if our server does not support switching.
1968        */
1969       if (!(butxInfo.serverType & XBSA_SERVER_FLAG_MULTIPLE)) {
1970          ErrorLog(0, taskId, TC_BADTASK, 0, 
1971                   "This version of XBSA libraries does not support switching "
1972                   "from server %s to server %s\n",
1973                   butxInfoP->serverName, server);
1974          return(TC_BADTASK);
1975       }
1976
1977       rc = xbsa_Finalize(&butxInfo);
1978       if (rc != XBSA_SUCCESS) {
1979          ErrorLog(0, taskId, rc, 0, 
1980                   "InitToServer: Unable to terminate the connection to server %s\n",
1981                   butxInfoP->serverName);
1982          ERROR_EXIT(rc);
1983       }
1984    }
1985
1986    /* initialize to the new server */
1987    rc = xbsa_Initialize(&butxInfo, xbsaObjectOwner, appObjectOwner, xbsaSecToken, server);
1988    if (rc != XBSA_SUCCESS) {
1989       ErrorLog(0, taskId, rc, 0,
1990                "InitToServer: Unable to initialize the XBSA library to server %s\n",
1991                server);
1992       ERROR_EXIT(rc);
1993    }
1994
1995   error_exit:
1996    return(code);
1997 }
1998
1999
2000 /* DeleteDump
2001  *
2002  */
2003 DeleteDump(ptr) 
2004    struct deleteDumpIf *ptr;
2005 {
2006     afs_int32 taskId;
2007     afs_int32 rc, code=0;
2008     afs_uint32 dumpid;
2009     afs_int32 index, next, dbTime;
2010     budb_volumeList vl;
2011     struct budb_dumpEntry dumpEntry;
2012     char tapeName[BU_MAXTAPELEN];
2013     char dumpIdStr[XBSA_MAX_OSNAME];
2014     char volumeNameStr[XBSA_MAX_PATHNAME];
2015     afs_int32 i;
2016     int intrans=0;
2017     int allnotfound=1, onenotfound=0;
2018     extern struct udbHandleS udbHandle;
2019     extern struct deviceSyncNode *deviceLatch;
2020
2021     setStatus(taskId, DRIVE_WAIT);
2022     EnterDeviceQueue(deviceLatch);
2023     clearStatus(taskId, DRIVE_WAIT);
2024
2025     dumpid = ptr->dumpID;
2026     taskId = ptr->taskId;                              /* Get task Id */
2027
2028     printf("\n\n");
2029     TapeLog(2, taskId, 0, 0, "Delete Dump %u\n", dumpid);
2030
2031     vl.budb_volumeList_len = 0;
2032     vl.budb_volumeList_val = 0;
2033     tapeName[0] = '\0';
2034
2035     /* Get the dump info for the dump we are deleting */
2036     rc = bcdb_FindDumpByID(dumpid, &dumpEntry);
2037     if (rc) {
2038        ErrorLog(0, taskId, rc, 0, "Unable to locate dump ID %u in database\n", dumpid);
2039        setStatus(taskId, TASK_ERROR);
2040        ERROR_EXIT(rc);
2041     }
2042
2043     /* we must make sure that we are configured with the correct type of
2044      * XBSA server for this dump delete! Only those dumped to an ADSM server.
2045      */
2046     if ( (xbsaType == XBSA_SERVER_TYPE_ADSM) &&
2047          !((dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) ) {
2048        ErrorLog(0, taskId, TC_BADTASK, 0, 
2049                 "The dump %u requested for deletion is incompatible with this instance of butc\n",
2050                 dumpid);
2051        setStatus(taskId, TASK_ERROR);
2052        ERROR_EXIT(TC_BADTASK);
2053     }
2054
2055     /* Make sure we are connected to the correct server. If not, switch to it if appropriate */
2056     if ( (strlen((char *)dumpEntry.tapes.tapeServer) != 0) &&
2057          (strcmp((char *)dumpEntry.tapes.tapeServer,butxInfo.serverName) != 0)) {
2058
2059        /* Check to see if the tapeServer name is trustworthy */
2060        if ((dumpEntry.flags & (BUDB_DUMP_XBSA_NSS | BUDB_DUMP_BUTA)) && !forcemultiple) {
2061           /* The dump was made with a version of the XBSA interface
2062            * that didn't allow switching of servers, we can't be sure
2063            * that the servername in the backup database is correct.  So,
2064            * we will check the servername and log it if they don't match;
2065            * but we will try to do the delete without switching servers.
2066            */
2067           TLog(taskId, "The dump %d requested for deletion is on server %s "
2068                        "but butc is connected to server %s "
2069                        "(Attempting to delete the dump anyway)\n",
2070                dumpid, (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2071        } else {
2072           TLog(taskId, "The dump %u requested for deletion is on server %s "
2073                        "but butc is connected to server %s "
2074                        "(switching servers)\n",
2075                dumpid, (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
2076
2077           rc = InitToServer(taskId, &butxInfo, (char *)dumpEntry.tapes.tapeServer);
2078           if (rc != XBSA_SUCCESS) {
2079              setStatus(taskId, TASK_ERROR);
2080              ERROR_EXIT(rc);
2081           }
2082        }
2083     }
2084
2085     /* Start a new Transaction */
2086     rc = xbsa_BeginTrans(&butxInfo);
2087     if (rc != XBSA_SUCCESS) {
2088        ErrorLog(0, taskId, rc, 0, "Unable to create a new transaction\n");
2089        setStatus(taskId, TASK_ERROR);
2090        ERROR_EXIT(rc);
2091     }
2092     intrans = 1;
2093
2094     /* Query the backup database for list of volumes to delete */
2095     for (index=next=0; index!=-1; index=next) {
2096        rc = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client,
2097                                    UF_SINGLESERVER, BUDB_MAJORVERSION,
2098                                    BUDB_OP_DUMPID,
2099                                    tapeName, dumpid,
2100                                    0,
2101                                    index, &next,
2102                                    &dbTime, &vl);
2103        if (rc) {
2104           if (rc == BUDB_ENDOFLIST) break;
2105           ErrorLog(0, taskId, rc, 0, "Can't find volume info for dump %d\n", dumpid);
2106           setStatus(taskId, TASK_ERROR);
2107           ERROR_EXIT(rc);
2108        }             
2109
2110        /* Delete all volumes on the list */
2111        for (i=0; i<vl.budb_volumeList_len; i++) {
2112           if (dumpEntry.flags & BUDB_DUMP_BUTA) {
2113              /* dump was from buta, use old buta style names */
2114              sprintf(dumpIdStr, "/%d", dumpid);
2115              strcpy(volumeNameStr, "/");
2116              strcat(volumeNameStr, (char *)vl.budb_volumeList_val[i].name);
2117           } else { /* BUDB_DUMP_ADSM */
2118              /* dump was from butc to ADSM, use butc names */
2119              strcpy(dumpIdStr, butcdumpIdStr);
2120              sprintf(volumeNameStr, "/%d", dumpid);
2121              strcat(volumeNameStr, "/");
2122              strcat(volumeNameStr, (char *)vl.budb_volumeList_val[i].name);
2123           }
2124
2125           rc = xbsa_DeleteObject(&butxInfo, dumpIdStr, volumeNameStr);
2126           if (rc != XBSA_SUCCESS) {
2127              ErrorLog(0, taskId, rc, 0, 
2128                       "Unable to delete the object %s of dump %s from the server\n",
2129                       volumeNameStr, dumpIdStr);
2130              /* Don't exit if volume was not found */
2131              if (rc != BUTX_DELETENOVOL) {
2132                allnotfound = 0;
2133                ERROR_EXIT(rc);
2134              }
2135              onenotfound = 1;
2136           } else {
2137              allnotfound = 0;
2138              TLog(0, "Deleted volume %s (%u) in dumpID %u from the backup server\n",
2139                   vl.budb_volumeList_val[i].name, vl.budb_volumeList_val[i].id, dumpid);
2140           }
2141        }
2142
2143         /* free the memory allocated by RPC for this list */
2144         if (vl.budb_volumeList_val) free(vl.budb_volumeList_val);
2145         vl.budb_volumeList_len = 0;
2146         vl.budb_volumeList_val = 0;
2147     }
2148
2149   error_exit:
2150     if (intrans) {
2151        rc = xbsa_EndTrans(&butxInfo);
2152        if (rc != XBSA_SUCCESS) {
2153           ErrorLog(0, taskId, rc, 0, "Unable to terminate the current transaction\n");
2154           setStatus(taskId, TASK_ERROR);
2155           ERROR_EXIT(rc);
2156        }
2157     }
2158
2159     /* Switch back to the original server */
2160     rc = InitToServer(taskId, &butxInfo, (char *)0);
2161
2162     if (vl.budb_volumeList_val) free(vl.budb_volumeList_val);
2163
2164     setStatus(taskId, TASK_DONE);
2165     FreeNode(taskId);                     /* free the dump node */
2166     LeaveDeviceQueue(deviceLatch);
2167
2168     /* If we don't find any dumps on the server, rather than returning
2169      * a success, return a failure.
2170      */
2171     if (!code && onenotfound && allnotfound) {
2172        code = BUTX_DELETENOVOL;
2173        setStatus(taskId, TASK_ERROR);
2174     }
2175     return(code);
2176 }
2177 #endif