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