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