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