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