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