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