include-afsconfig-before-param-h-20010712
[openafs.git] / src / butc / lwps.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("$Header$");
14
15 #include <sys/types.h>
16 #ifdef AFS_NT40_ENV
17 #include <winsock2.h>
18 #include <conio.h>
19 #else
20 #include <sys/file.h>
21 #include <netinet/in.h>
22 #include <netdb.h>
23 #endif
24
25 #include <afs/procmgmt.h>
26 #include <rx/xdr.h>
27 #include <rx/rx.h>
28 #include <time.h>
29 #include <lwp.h>
30 #include <lock.h>
31 #include <afs/tcdata.h>
32 #include <afs/bubasics.h>                               /* PA */
33 #include <afs/budb_client.h>
34 #include <afs/volser.h>
35 #include <afs/com_err.h>
36 #include "error_macros.h"
37 #include <afs/afsutil.h>
38 #include <errno.h>
39 #include "butc_xbsa.h"
40
41 /* GLOBAL CONFIGURATION PARAMETERS */
42 extern int dump_namecheck;
43 extern int queryoperator;
44 extern int tapemounted;
45 extern char *opencallout;
46 extern char *closecallout;
47 extern char *whoami;
48 extern char *extractDumpName();
49 extern int  BufferSize;     /* Size in B stored for header info */
50 extern char *restoretofile;
51 FILE        *restoretofilefd;
52 extern int  forcemultiple;
53
54 /* XBSA Global Parameters */
55 afs_int32 xbsaType;
56 #ifdef xbsa
57 struct butx_transactionInfo butxInfo;
58 #endif
59
60 struct TapeBlock {               /* A 16KB tapeblock */
61     char mark[BUTM_HDRSIZE];     /* Header info */
62     char data[BUTM_BLKSIZE];     /* data */
63 } *bufferBlock;
64
65 afs_int32 dataSize;        /* Size of data to read on each xbsa_ReadObjectData() call (CONF_XBSA) */
66 afs_int32 tapeblocks;      /* Number of tape datablocks in buffer (!CONF_XBSA) */
67
68 /* notes
69  *      Need to re-write to:
70  *      1) non-interactive tape handling (optional)
71  *      2) compute tape and volume sizes for the database
72  *      3) compute and use tape id's for tape tracking (put on tape label)
73  *      4) status management
74  */
75
76 /* All the relevant info shared between Restorer and restoreVolume */
77 struct restoreParams 
78 {
79     struct dumpNode      *nodePtr;
80     afs_int32                frag;
81     char                 mntTapeName[BU_MAXTAPELEN];
82     afs_int32                tapeID;
83     struct butm_tapeInfo *tapeInfoPtr;
84 };
85
86 /* Abort checks are done after each  BIGCHUNK of data transfer */
87 #define BIGCHUNK 102400  
88
89 #define HEADER_CHECKS(vhptr, header)                                    \
90 {                                                                       \
91     afs_int32 magic, versionflags;                                              \
92                                                                         \
93     versionflags = ntohl(vhptr.versionflags);                           \
94     if ( versionflags == TAPE_VERSION_0 ||                              \
95              versionflags == TAPE_VERSION_1 ||                          \
96              versionflags == TAPE_VERSION_2 ||                          \
97              versionflags == TAPE_VERSION_3 ||                          \
98              versionflags == TAPE_VERSION_4 )       {                   \
99                                                                         \
100             magic = ntohl(vhptr.magic); /* another sanity check */      \
101             if (magic == TC_VOLBEGINMAGIC ||                            \
102                 magic == TC_VOLENDMAGIC ||                              \
103                 magic == TC_VOLCONTD )                 {                \
104                                                                         \
105                 bcopy (&vhptr, header, sizeof(struct volumeHeader));    \
106                 return (0);                                             \
107             } /* magic */                                               \
108         } /* versionflags */                                            \
109 }       
110
111 extern FILE *logIO;
112 extern FILE *ErrorlogIO;
113 extern FILE *centralLogIO;
114 extern FILE *lastLogIO;
115 extern afs_int32 lastPass;       /* Set true during last pass of dump */
116 extern int debugLevel;
117 extern int autoQuery;
118 extern struct tapeConfig globalTapeConfig;
119 extern struct deviceSyncNode *deviceLatch;
120 extern char globalCellName[];
121 struct timeval tp;
122 struct timezone tzp;
123
124 /* forward declaration */
125 afs_int32 readVolumeHeader(/*char *buffer,afs_int32 bufloc,(struct volumeHeader *)vhptr*/);
126
127 /* The on-disk volume header or trailer can differ in size from platform to platform */
128 struct TapeBlock tapeBlock;
129 char tapeVolumeHT[sizeof(struct volumeHeader) + 2*sizeof(char)];
130
131 void 
132 PrintLog(log, error1, error2, str, a,b,c,d,e,f,g,h,i,j)
133     FILE  *log;
134     afs_int32 error1, error2;
135     char  *str, *a, *b, *c, *d, *e, *f, *g, *h, *i, *j;
136 {
137     char *err1, *err2;
138
139     fprintf(log, str, a,b,c,d,e,f,g,h,i,j);
140     if (error1)
141     {
142         err2 = "vols";
143         switch (error1)
144         {
145             case VSALVAGE:   err1 = "Volume needs to be salvaged"; break;
146             case VNOVNODE:   err1 = "Bad vnode number quoted";     break;
147             case VNOVOL:     err1 = "Volume not attached, does not exist, or not on line"; break;
148             case VVOLEXISTS: err1 = "Volume already exists";       break;
149             case VNOSERVICE: err1 = "Volume is not in service";    break;
150             case VOFFLINE:   err1 = "Volume is off line";          break;
151             case VONLINE:    err1 = "Volume is already on line";   break;
152             case VDISKFULL:  err1 = "Partition is full";           break;
153             case VOVERQUOTA: err1 = "Volume max quota exceeded";   break;
154             case VBUSY:      err1 = "Volume temporarily unavailable";     break;
155             case VMOVED:     err1 = "Volume has moved to another server"; break;
156             default:
157                 err1 = (char *)error_message(error1);
158                 err2 = (char *)error_table_name(error1);
159                 break;
160         }
161         if (error1 == -1) fprintf(log, "     Possible communication failure");
162         else              fprintf(log, "     %s: %s", err2, err1);
163         if (error2)       fprintf(log, ": %s", error_message(error2));
164         fprintf(log, "\n");
165     }
166     fflush(log);
167 }
168
169 void
170 TapeLog(debug, task, error1, error2, str, a,b,c,d,e,f,g,h,i,j)
171     int   debug;
172     afs_int32 task, error1, error2;
173     char  *str, *a, *b, *c, *d, *e, *f, *g, *h, *i, *j;
174 {
175     time_t now;
176     char tbuffer[32], *timestr;
177
178     now = time(0);
179     timestr = afs_ctime(&now, tbuffer, sizeof(tbuffer));
180     timestr[24] = '\0';
181
182     fprintf(logIO, "%s: ", timestr);
183     if (task) fprintf(logIO, "Task %u: ", task);
184     PrintLog(logIO, error1, error2, str, a,b,c,d,e,f,g,h,i,j);
185
186     if (lastPass && lastLogIO) {
187        fprintf(lastLogIO, "%s: ", timestr);
188        if (task) fprintf(lastLogIO, "Task %u: ", task);
189        PrintLog(lastLogIO, error1, error2, str, a,b,c,d,e,f,g,h,i,j);
190     }
191
192     /* Now print to the screen if debug level requires */
193     if (debug <= debugLevel)
194         PrintLog(stdout, error1, error2, str, a,b,c,d,e,f,g,h,i,j);
195 }
196
197 void
198 TLog(task, str, a,b,c,d,e,f,g,h,i,j)
199     afs_int32 task;
200     char  *str, *a, *b, *c, *d, *e, *f, *g, *h, *i, *j;
201 {
202     /* Sends message to TapeLog and stdout */
203     TapeLog (0, task, 0,0, str, a,b,c,d,e,f,g,h,i,j);    
204 }
205
206 void 
207 ErrorLog(debug, task, error1, error2, str, a,b,c,d,e,f,g,h,i,j)
208     int   debug;
209     afs_int32 task, error1, error2;
210     char  *str, *a, *b, *c, *d, *e, *f, *g, *h, *i, *j;
211 {
212     time_t now;
213     char tbuffer[32], *timestr;
214
215     now = time(0);
216     timestr = afs_ctime(&now, tbuffer, sizeof(tbuffer));
217     timestr[24] = '\0';
218     fprintf(ErrorlogIO, "%s: ", timestr);
219
220     /* Print the time and task number */
221     if (task) fprintf(ErrorlogIO, "Task %u: ", task);
222     PrintLog(ErrorlogIO, error1, error2, str, a,b,c,d,e,f,g,h,i,j);
223
224     TapeLog(debug, task, error1, error2, str, a,b,c,d,e,f,g,h,i,j);
225 }
226
227 void
228 ELog(task, str, a,b,c,d,e,f,g,h,i,j)
229     afs_int32 task;
230     char  *str, *a, *b, *c, *d, *e, *f, *g, *h, *i, *j;
231 {
232     /* Sends message to ErrorLog, TapeLog and stdout */
233     ErrorLog (0, task, 0,0, str, a,b,c,d,e,f,g,h,i,j);    
234 }
235
236 /* first proc called by anybody who intends to use the device */
237 void EnterDeviceQueue(devLatch)
238 struct deviceSyncNode *devLatch;
239 {
240     ObtainWriteLock(&(devLatch->lock));
241     devLatch->flags = TC_DEVICEINUSE;
242 }
243
244 /* last proc called by anybody finishing using the device */
245 void LeaveDeviceQueue(devLatch)
246 struct deviceSyncNode *devLatch;
247 {
248     devLatch->flags = 0;
249     ReleaseWriteLock(&(devLatch->lock));
250 }
251
252 #define BELLTIME 60     /* 60 seconds before a bell rings */
253 #define BELLCHAR 7      /* ascii for bell */
254
255
256 #ifdef AFS_PTHREAD_ENV
257 #ifdef AFS_NT40_ENV
258 /* WaitForKeystroke : Wait until a key has been struck or time (secconds)
259  * runs out and return to caller. The NT version of this function will return
260  * immediately after a key has been pressed (doesn't wait for cr).
261  * Input:
262  *   seconds: wait for <seconds> seconds before returning. If seconds < 0,
263  *            wait infinitely.
264  * Return Value:
265  *    1:  Keyboard input available
266  *    0:  seconds elapsed. Timeout.
267  *
268  * STOLEN FROM LWP_WaitForKeystroke()
269  */
270 int WaitForKeystroke(int seconds)
271 {
272   time_t startTime, nowTime;
273   double timeleft = 1;
274   struct timeval twait;
275
276   time(&startTime); 
277   twait.tv_sec = 0;
278   twait.tv_usec = 250;
279   if (seconds >= 0)
280       timeleft = seconds; 
281
282   do {
283       /* check if we have a keystroke */
284       if (_kbhit()) return 1;
285       if (timeleft == 0) break;
286
287       /* sleep for  LWP_KEYSTROKE_DELAY ms and let other
288        * process run some*/
289       select(0, 0, 0, 0, &twait);
290
291       if (seconds > 0) { /* we only worry about elapsed time if 
292                            * not looping forever (seconds < 0) */
293           time(&nowTime);
294           timeleft = seconds - difftime(nowTime, startTime);
295       }
296   } while(timeleft > 0);
297   return 0;
298 }
299 #else /* AFS_NT40)ENV */
300 extern int WaitForKeystroke(int);
301 /*
302  *      STOLEN FROM LWP_WaitForKeystroke()
303  */
304 int WaitForKeystroke(int seconds)
305 {
306     fd_set rdfds;
307     int code;
308     struct timeval twait;
309     struct timeval *tp = NULL;
310
311 #ifdef AFS_LINUX20_ENV
312     if (stdin->_IO_read_ptr < stdin->_IO_read_end)
313         return 1;
314 #else
315     if (stdin->_cnt > 0)
316         return 1;
317 #endif
318     FD_ZERO(&rdfds);
319     FD_SET(fileno(stdin), &rdfds);
320
321     if (seconds>=0) {
322         twait.tv_sec = seconds;
323         twait.tv_usec = 0;
324         tp = &twait;
325     }
326     code = select(1+fileno(stdin), &rdfds, NULL, NULL, tp);
327     return (code == 1) ? 1 : 0;
328 }
329 #endif
330
331 /* GetResponseKey() - Waits for a specified period of time and
332  * returns a char when one has been typed by the user.
333  * Input:
334  *    seconds - how long to wait for a key press.
335  *    *key    - char entered by user
336  * Return Values: 
337  *    0 - Time ran out before the user typed a key.
338  *    1 - Valid char is being returned.
339  *
340  *    STOLEN FROM LWP_GetResponseKey();
341  */
342 int GetResponseKey(int seconds, char *key)
343 {
344   int rc;
345
346   if (key == NULL) return 0;     /* need space to store char */
347   fflush(stdin); /* flush all existing data and start anew */
348   
349   rc = WaitForKeystroke(seconds);
350   if (rc == 0) { /* time ran out */
351     *key = 0;
352     return rc;
353   }
354   
355   /* now read the char. */
356 #ifdef AFS_NT40_ENV
357   *key = getche(); /* get char and echo it to screen */
358 #else
359   *key = getchar();
360 #endif
361   return rc;
362 }
363 #endif /* AFS_PTHREAD_ENV
364
365 /* FFlushInput
366  *      flush all input
367  * notes:
368  *      only external clients are in recoverDb.c. Was static. PA
369  */
370 FFlushInput()
371 {
372   int w;
373
374   fflush(stdin);
375
376   while(1) {
377 #ifdef AFS_PTHREAD_ENV
378       w = WaitForKeystroke(0);
379 #else
380       w = LWP_WaitForKeystroke(0);
381 #endif /* AFS_PTHREAD_ENV */
382
383       if (w) {
384 #ifdef AFS_NT40_ENV
385          getche();
386 #else
387          getchar();
388 #endif /* AFS_NT40_ENV */
389       } else {
390          return;
391       }
392     } 
393 }
394
395 int callOutRoutine(taskId, tapePath, flag, name, dbDumpId, tapecount)
396    afs_int32  taskId;
397    char   *tapePath;
398    int    flag;
399    char   *name;
400    afs_uint32 dbDumpId;
401    int tapecount;
402 {
403   afs_int32 count;
404
405   afs_int32 code = 0;
406   int  pid;
407
408   char StapePath[256];
409   char ScallOut[256];
410   char Scount[10];
411   char Sopcode[16];
412   char Sdumpid[16];
413   char Stape[40];
414   char *callOut;
415
416   char *CO_argv[10];
417   char *CO_envp[1];
418
419
420   callOut = opencallout;
421   switch ( flag )
422     {
423     case READOPCODE: 
424       strcpy(Sopcode, "restore");
425       break;
426     case APPENDOPCODE:
427       strcpy(Sopcode, "appenddump");
428       break;
429     case WRITEOPCODE:
430       strcpy(Sopcode, "dump");
431       break;
432     case LABELOPCODE:
433       strcpy(Sopcode, "labeltape");
434       break;
435     case READLABELOPCODE:
436       strcpy(Sopcode, "readlabel");
437       break;
438     case SCANOPCODE:
439       strcpy(Sopcode, "scantape");
440       break;
441     case RESTOREDBOPCODE:
442       strcpy(Sopcode, "restoredb");
443       break;
444     case SAVEDBOPCODE:
445       strcpy(Sopcode, "savedb");
446       break;
447     case CLOSEOPCODE:
448       strcpy(Sopcode, "unmount");
449       callOut = closecallout;
450       break;
451     default:
452       strcpy(Sopcode, "unknown");
453       break;
454     }
455   
456   if (!callOut) /* no script to call */
457     return 0;
458   
459   strcpy(ScallOut, callOut);
460   CO_argv[0] = ScallOut;
461   
462   strcpy(StapePath, tapePath);
463   CO_argv[1] = StapePath;
464   
465   CO_argv[2] = Sopcode;
466   
467   if (flag == CLOSEOPCODE)
468     {
469       CO_argv[3] = (char *)0;
470     }
471   else
472     {
473       sprintf(Scount, "%d", tapecount);
474       CO_argv[3] = Scount;
475       
476       /* The tape label name - special case labeltape */
477       if ( !name || (strcmp(name,"") == 0) ) /* no label */
478         strcpy(Stape, "none");
479       else { /* labeltape */
480 #ifdef AFS_NT40_ENV
481         if (!strcmp(name, TC_NULLTAPENAME)) /* pass "<NULL>" instead of <NULL> */
482           strcpy(Stape, TC_QUOTEDNULLTAPENAME);
483         else
484 #endif
485           strcpy(Stape, name);
486       }
487       CO_argv[4] = Stape;
488       
489       /* The tape id */
490       if (!dbDumpId)
491         strcpy(Sdumpid, "none");
492       else
493         sprintf(Sdumpid, "%u", dbDumpId);
494       CO_argv[5] = Sdumpid;
495       
496       CO_argv[6] = (char *)0;
497     }
498
499       CO_envp[0] = (char *)0;
500
501       pid = spawnprocve(callOut, CO_argv, CO_envp, 2);
502       if (pid < 0) {
503         ErrorLog(0, taskId, errno, 0, "Call to %s outside routine %s failed\n", 
504                  Sopcode, callOut);
505         return 0; 
506       }
507
508       return(pid);
509 }
510
511 /*
512  * unmountTape
513  *     Unmounts a tape and prints a warning if it can't unmount it.
514  *     Regardless of error, the closecallout routine will be called
515  *     (unless a tape is not mounted in the first place).
516  */
517 unmountTape(taskId, tapeInfoPtr)
518     afs_int32 taskId;
519     struct butm_tapeInfo  *tapeInfoPtr;
520 {
521     afs_int32 code;
522     afs_int32          wcode;
523     int cpid, status, rcpid;
524
525     code = butm_Dismount(tapeInfoPtr);
526     if (code && (code !=  BUTM_NOMOUNT))
527         ErrorLog(0, taskId, code, (tapeInfoPtr)->error, "Warning: Can't close tape\n");
528
529     if (tapemounted && closecallout)
530     {
531         setStatus(taskId, CALL_WAIT);
532
533         cpid = callOutRoutine(taskId, globalTapeConfig.device, CLOSEOPCODE, "", 0, 1);
534         while (cpid)         /* Wait until return */
535         {
536             status = 0;
537             rcpid = waitpid(cpid, &status, WNOHANG);
538             if (rcpid > 0)
539             {
540                 tapemounted = 0;
541                 break;
542             }
543             if (rcpid == -1 && errno != EINTR) {
544                 tapemounted = 0;
545                 com_err(whoami, errno, "Error waiting for callout script to terminate.");
546                 break;
547             }
548 #ifdef AFS_PTHREAD_ENV
549             sleep(1);
550 #else
551             IOMGR_Sleep(1);
552 #endif
553
554             if ( checkAbortByTaskId(taskId) )
555             {
556                 TLog(taskId, "Callout routine has been aborted\n");
557                 if (kill(cpid, SIGKILL))        /* Cancel callout */
558                     ErrorLog(0, taskId, errno, 0, "Kill of callout process %d failed\n", cpid);
559                 break;
560             }
561         }
562     }
563     clearStatus(taskId, CALL_WAIT);
564 }
565
566 /* PrintPrompt
567  *      print out prompt to operator
568  * calledby:
569  *      PromptForTape only.
570  */
571
572 void static
573 PrintPrompt(flag, name, dumpid)
574     int flag;
575     char *name;
576 {
577     char           tapename[BU_MAXTAPELEN+32];
578     char           *dn;
579
580     TAPENAME(tapename, name, dumpid);
581
582     printf("******* OPERATOR ATTENTION *******\n");
583     printf("Device :  %s \n",globalTapeConfig.device);
584
585     switch ( flag )
586     {
587       case READOPCODE:                          /* mount for restore */
588         printf("Please put in tape %s for reading", tapename);
589         break;
590
591       case APPENDOPCODE:                        /* mount for dump (appends) */
592
593         dn = extractDumpName(name);
594
595         if (!dn || !dumpid)
596             printf("Please put in last tape of dump set for appending dump");
597         else
598             printf("Please put in last tape of dump set for appending dump %s (DumpID %u)",
599                    dn, dumpid);
600         break;
601
602       case WRITEOPCODE:                         /* mount for dump */
603         if (strcmp(name,"") == 0)
604             printf("Please put in tape for writing");
605
606         /* The name is what we are going to label the tape as */
607         else
608             printf("Please put in tape %s for writing", tapename);
609         break;
610
611       case LABELOPCODE:                         /* mount for labeltape */
612         printf("Please put in tape to be labelled as %s", tapename);
613         break;
614
615       case READLABELOPCODE:                     /* mount for readlabel */
616         printf("Please put in tape whose label is to be read");
617         break;
618
619       case SCANOPCODE:                          /* mount for scantape */
620         if (strcmp(name,"") == 0)
621             printf("Please put in tape to be scanned");
622         else
623             printf("Please put in tape %s for scanning", tapename);
624         break;
625
626       case RESTOREDBOPCODE:                     /* Mount for restoredb */
627         printf("Please insert a tape %s for the database restore", tapename);
628         break;
629
630       case SAVEDBOPCODE:                        /* Mount for savedb */
631         printf("Please insert a writeable tape %s for the database dump", tapename);
632         break;
633
634       default:
635         break;
636     }
637     printf(" and hit return when done\n");
638 }
639
640 /* PromptForTape
641  *      Prompt the operator to change the tape. 
642  *      Use to be a void routine but now returns an error. Some calls
643  *      don't use the error code.
644  * notes:
645  *      only external clients are in recoverDb.c. Was static PA
646  */
647 afs_int32 PromptForTape(flag, name, dbDumpId, taskId, tapecount)
648     int flag;
649     char *name;
650     afs_uint32 dbDumpId;              /* Specific dump ID - If non-zero */
651     afs_uint32 taskId;
652     int tapecount;
653 {
654     register afs_int32 code = 0;
655     afs_int32          wcode;
656     afs_int32          start = 0;
657     char           inchr;
658     int           CallOut;
659     int           cpid, status, rcpid;
660
661     if ( checkAbortByTaskId(taskId) )
662         ERROR_EXIT(TC_ABORTEDBYREQUEST);
663
664     if (dbDumpId)
665         TapeLog(2, taskId, 0, 0, "Prompt for tape %s (%u)\n", name, dbDumpId);
666     else
667         TapeLog(2, taskId, 0, 0, "Prompt for tape %s\n", name);
668
669     CallOut = (opencallout ? 1 : 0);
670     if (CallOut)
671     {
672         setStatus(taskId, CALL_WAIT);
673
674         cpid = callOutRoutine(taskId, globalTapeConfig.device, flag, name, dbDumpId, tapecount);
675         if (cpid == 0) CallOut = 0;              /* prompt at screen */
676
677         while (CallOut)                      
678         { /* Check if callout routine finished */
679             status = 0;
680             rcpid = waitpid(cpid, &status, WNOHANG);
681             if (rcpid > 0)
682             {
683                 if      (rcpid != cpid) 
684                   wcode = -1;
685                 else if (WIFEXITED(status))  
686                   wcode = WEXITSTATUS(status);
687                 else
688                   wcode = -1;
689
690                 if (wcode == 0) 
691                 {
692                     break;                               /* All done */
693                 }
694                 else if (wcode == 1)
695                 {
696                     ERROR_EXIT(TC_ABORTEDBYREQUEST);     /* Abort */
697                 }
698                 else if ( (flag == READOPCODE) && (wcode == 3) ) {
699                     ERROR_EXIT(TC_SKIPTAPE);             /* Restore: skip the tape */
700                 }
701                 else
702                 {
703                     TLog(taskId, "Callout routine has exited with code %d: will prompt\n", wcode);
704                     CallOut = 0;                         /* Switch to keyboard input */
705                     break;
706                 }
707             }
708             /* if waitpid experienced an error, we prompt */
709             if (rcpid == -1 && errno != EINTR) {
710               com_err(whoami, errno, "Error waiting for callout script to terminate.");
711               TLog(taskId, "Can't get exit status from callout script. will prompt\n", wcode);
712               CallOut = 0;
713               break;
714             }
715 #ifdef AFS_PTHREAD_ENV
716             sleep(1);
717 #else
718             IOMGR_Sleep(1);
719 #endif
720
721             if ( checkAbortByTaskId(taskId) )
722             {
723                 printf("This tape operation has been aborted by the coordinator.\n");
724
725                 if (kill(cpid, SIGKILL))        /* Cancel callout */
726                     ErrorLog(0, taskId, errno, 0, "Kill of callout process %d failed\n", cpid);
727
728                 ERROR_EXIT(TC_ABORTEDBYREQUEST);
729             }
730         }
731     }
732
733     if (!CallOut)
734     {
735         clearStatus(taskId, CALL_WAIT);
736         setStatus(taskId, OPR_WAIT);
737
738         PrintPrompt(flag, name, dbDumpId);
739
740         /* Loop until we get ok to go ahead (or abort) */
741         while(1) 
742         {
743             if ( time(0) > start+BELLTIME )
744             {
745                 start = time(0);
746                 FFlushInput(stdin);
747                 putchar(BELLCHAR);
748                 fflush(stdout);
749             }
750         
751 #ifdef AFS_PTHREAD_ENV
752             wcode = GetResponseKey(5, &inchr); /* inchr stores key read */
753 #else
754             wcode = LWP_GetResponseKey(5, &inchr); /* inchr stores key read */
755 #endif
756             if (wcode == 1) {             /* keyboard input is available */
757
758                 if ( (inchr == 'a') || (inchr == 'A') ) {
759                     printf("This tape operation has been aborted.\n");
760                     ERROR_EXIT(TC_ABORTEDBYREQUEST);               /* Abort command */
761                 } else if ( (flag == READOPCODE) && ((inchr == 's') || (inchr == 'S')) ) {
762                     printf("This tape will be skipped.\n");
763                     ERROR_EXIT(TC_SKIPTAPE);                       /* Restore: skip the tape */
764                 }
765                 break;                                             /* continue */
766             }
767
768             if ( checkAbortByTaskId(taskId) ) {
769                 printf("This tape operation has been aborted by the coordinator.\n");
770                 ERROR_EXIT(TC_ABORTEDBYREQUEST);
771             }
772         }
773
774     }
775
776     printf("Thanks, now proceeding with tape ");
777     switch (flag) 
778     {
779       case RESTOREDBOPCODE:
780       case READOPCODE:  
781         printf("reading");
782         break;
783
784       case APPENDOPCODE:
785         printf("append writing");
786         break;
787
788       case SAVEDBOPCODE:
789       case WRITEOPCODE:
790         printf("writing");
791         break;
792
793       case LABELOPCODE:
794         printf("labelling");
795         break;
796
797       case READLABELOPCODE:
798         printf("label reading");
799         break;
800
801       case SCANOPCODE:
802         printf("scanning");
803         break;
804         
805       default:
806         printf("unknown");
807         break;
808     }
809
810     printf(" operation.\n");
811     if (!CallOut)
812         printf("**********************************\n");
813
814     TapeLog(2, taskId, 0, 0, "Proceeding with tape operation\n");
815     tapemounted = 1;
816
817 error_exit:
818     clearStatus(taskId, (OPR_WAIT | CALL_WAIT));
819     return(code);
820 }
821
822
823 /* VolHeaderToHost
824  *      convert the fields in the tapeVolHeader into host byte order,
825  *      placing the converted copy of the structure into the hostVolHeader
826  * entry:
827  *      tapeVolHeader - points to volume header read from tape
828  *      hostVolHeader - pointer to struct for result
829  * exit:
830  *      hostVolHeader - information in host byte order
831  */
832
833 afs_int32
834 VolHeaderToHost(hostVolHeader, tapeVolHeader)
835      struct volumeHeader *hostVolHeader, *tapeVolHeader;
836 {
837     switch ( ntohl(tapeVolHeader->versionflags) )
838     {
839         case TAPE_VERSION_0:
840             /* sizes in bytes and fields in host order */
841             bcopy(hostVolHeader, tapeVolHeader, sizeof(struct volumeHeader));
842             break;
843
844         case TAPE_VERSION_1:
845         case TAPE_VERSION_2:
846         case TAPE_VERSION_3:                            /* for present */
847         case TAPE_VERSION_4:
848             /* sizes in K and fields in network order */
849             /* do the conversion field by field */
850
851             strcpy(hostVolHeader->preamble,    tapeVolHeader->preamble);
852             strcpy(hostVolHeader->postamble,   tapeVolHeader->postamble);
853             strcpy(hostVolHeader->volumeName,  tapeVolHeader->volumeName);
854             strcpy(hostVolHeader->dumpSetName, tapeVolHeader->dumpSetName);
855             hostVolHeader->volumeID     = ntohl(tapeVolHeader->volumeID);
856             hostVolHeader->server       = ntohl(tapeVolHeader->server);
857             hostVolHeader->part         = ntohl(tapeVolHeader->part);
858             hostVolHeader->from         = ntohl(tapeVolHeader->from);
859             hostVolHeader->frag         = ntohl(tapeVolHeader->frag);
860             hostVolHeader->magic        = ntohl(tapeVolHeader->magic);
861             hostVolHeader->contd        = ntohl(tapeVolHeader->contd);
862             hostVolHeader->dumpID       = ntohl(tapeVolHeader->dumpID);
863             hostVolHeader->level        = ntohl(tapeVolHeader->level);
864             hostVolHeader->parentID     = ntohl(tapeVolHeader->parentID);
865             hostVolHeader->endTime      = ntohl(tapeVolHeader->endTime);
866             hostVolHeader->versionflags = ntohl(tapeVolHeader->versionflags);
867             hostVolHeader->cloneDate    = ntohl(tapeVolHeader->cloneDate);
868             break;
869
870         default:
871             return(TC_BADVOLHEADER);
872     }
873     return(0);
874 }
875      
876 afs_int32
877 ReadVolHeader(taskId, tapeInfoPtr, volHeaderPtr)
878      afs_int32                 taskId;
879      struct butm_tapeInfo  *tapeInfoPtr;
880      struct volumeHeader   *volHeaderPtr;
881 {
882     afs_int32               code = 0;
883     afs_int32               nbytes;
884     struct volumeHeader volHead;
885
886     /* Read the volume header */
887     code = butm_ReadFileData(tapeInfoPtr, tapeBlock.data, sizeof(tapeVolumeHT), &nbytes);
888     if (code) {
889         ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't read volume header on tape\n");
890         ERROR_EXIT(code);
891     }
892
893     code = readVolumeHeader(tapeBlock.data, 0L, &volHead);
894     if (code) 
895     {
896         ErrorLog(0, taskId, code, 0, "Can't find volume header on tape block\n");
897         ERROR_EXIT(code);
898     }
899
900     code = VolHeaderToHost(volHeaderPtr, &volHead);
901     if (code) 
902     {
903         ErrorLog(0, taskId, code, 0, "Can't convert volume header\n");
904         ERROR_EXIT(code);
905     }
906
907 error_exit:
908     return code;
909 }
910
911 afs_int32 static
912 GetVolumeHead(taskId, tapeInfoPtr, position, volName, volId)
913     afs_int32                 taskId;
914     struct butm_tapeInfo  *tapeInfoPtr;
915     afs_int32                 position;
916     char                  *volName;
917     afs_int32                 volId;
918 {
919     afs_int32 code = 0;
920     struct volumeHeader   tapeVolHeader;
921
922     /* Position directly to the volume and read the header */
923     if (position) {
924         code = butm_Seek (tapeInfoPtr, position);
925         if (code) {
926             ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't seek to position %u on tape\n", 
927                      position);
928             ERROR_EXIT(code);
929         }
930
931         code = butm_ReadFileBegin(tapeInfoPtr);
932         if (code) {
933             ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't read FileBegin on tape\n");
934               ERROR_EXIT(code);
935         }
936
937         /* Read the volume header */
938         code = ReadVolHeader(taskId, tapeInfoPtr, &tapeVolHeader);
939         if (code) ERROR_EXIT(code);
940
941         /* Check if volume header matches */
942         if (strcmp(tapeVolHeader.volumeName,volName))   ERROR_EXIT(TC_BADVOLHEADER);
943         if (volId && (tapeVolHeader.volumeID != volId)) ERROR_EXIT(TC_BADVOLHEADER);
944         if (tapeVolHeader.magic != TC_VOLBEGINMAGIC)    ERROR_EXIT(TC_BADVOLHEADER);
945     }
946
947     /* Do a sequential search for the volume */
948     else {
949         while(1) {
950             code = butm_ReadFileBegin(tapeInfoPtr);
951             if (code) {
952                 ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't read FileBegin on tape\n");
953                 ERROR_EXIT(code);
954             }
955
956             code = ReadVolHeader(taskId, tapeInfoPtr, &tapeVolHeader);
957             if (code) ERROR_EXIT(TC_VOLUMENOTONTAPE);
958             
959             /* Test if we found the volume */
960             if ( ( strcmp(tapeVolHeader.volumeName,volName) == 0  ) &&
961                  ( !volId || (volId == tapeVolHeader.volumeID) ) )
962                 break;
963
964             /* skip to the next HW EOF marker */
965             code = SeekFile(tapeInfoPtr, 1);
966             if (code) {
967                 ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't seek to next EOF on tape\n");
968                 ERROR_EXIT(code);
969             }
970         }
971     }
972
973 error_exit:
974     return code;
975 }
976
977 afs_int32 GetRestoreTape(taskId, tapeInfoPtr, tname, tapeID, prompt)
978     afs_int32                taskId;
979     struct butm_tapeInfo *tapeInfoPtr;
980     char                 *tname;
981     afs_int32            tapeID;
982     int                  prompt;
983 {
984     struct butm_tapeLabel tapeLabel;
985     afs_int32             code = 0, rc;
986     int                   tapecount = 1;
987     struct budb_dumpEntry dumpEntry;
988
989     /* Make sure that the dump/tape is not a XBSA dump */
990     rc = bcdb_FindDumpByID(tapeID, &dumpEntry);
991     if (!rc && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
992        ErrorLog(0, taskId, 0, 0, "Volumes from dump %u are XBSA dumps (skipping)\n", tapeID);
993        ERROR_EXIT(TC_SKIPTAPE);
994     }
995
996     while (1)
997     {
998         if (prompt)
999         {
1000             code = PromptForTape (READOPCODE, tname, tapeID, taskId, tapecount);
1001             if (code) ERROR_EXIT(code);
1002         }
1003         prompt = 1;
1004         tapecount++;
1005
1006         code = butm_Mount (tapeInfoPtr, tname);
1007         if (code) 
1008         {
1009             TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
1010             goto newtape;
1011         }
1012
1013         code = butm_ReadLabel(tapeInfoPtr, &tapeLabel, 1);
1014         if (code) 
1015         {
1016             ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't read tape label\n");
1017             goto newtape;
1018         }
1019
1020         /* Now check the label to see if the tapename matches or tapeids match */
1021         if ( strcmp(TNAME(&tapeLabel),tname) ||
1022              ((tapeLabel.structVersion >= TAPE_VERSION_3) && 
1023               (tapeLabel.dumpid != tapeID)) )
1024         {
1025             char expectedName[BU_MAXTAPELEN+32], gotName[BU_MAXTAPELEN+32];
1026
1027             TAPENAME(expectedName, tname, tapeID);
1028             LABELNAME(gotName, &tapeLabel);
1029
1030             TapeLog(0, taskId, 0, 0, 
1031                     "Tape label expected %s, label seen %s\n", 
1032                     expectedName, gotName);
1033             goto newtape;
1034         }
1035         
1036         break;
1037
1038       newtape:
1039         unmountTape(taskId, tapeInfoPtr);
1040     }
1041
1042 error_exit:
1043     return code;
1044 }
1045
1046 afs_int32 xbsaRestoreVolumeData(call, rparamsPtr)
1047     register struct rx_call *call;
1048     struct restoreParams  *rparamsPtr;
1049 {
1050     afs_int32   code=0;
1051 #ifdef xbsa
1052     afs_int32   curChunk, rc;
1053     afs_uint32 totalWritten;
1054     afs_int32   headBytes, tailBytes, w;
1055     afs_int32   taskId;
1056     struct  volumeHeader volTrailer;
1057     afs_int32   vtsize=0;
1058     int     found;
1059     struct dumpNode       *nodePtr;
1060     struct tc_restoreDesc *Restore;
1061     afs_int32  bytesRead, tbuffersize, endData=0;
1062     char   *buffer = (char *)bufferBlock, tbuffer[256];
1063
1064     nodePtr     = rparamsPtr->nodePtr;
1065     Restore     = nodePtr->restores;
1066     taskId      = nodePtr->taskID;
1067
1068     /* Read the volume fragment one block at a time until
1069      * find a volume trailer
1070      */
1071     curChunk = BIGCHUNK+1;
1072     tbuffersize  = 0;
1073     totalWritten = 0;
1074
1075     while (!endData) {
1076        rc = xbsa_ReadObjectData(&butxInfo, buffer, dataSize, &bytesRead, &endData);
1077        if (restoretofile && (bytesRead > 0)) {
1078           fwrite(buffer, bytesRead, 1, restoretofilefd); /* Save to a file */
1079        }
1080        if (rc != XBSA_SUCCESS) {
1081           ErrorLog(0, taskId, rc, 0, "Unable to read volume data from the server\n");
1082           ERROR_EXIT(rc);
1083        }
1084          
1085        /* Periodically update status structure and check if should abort */
1086        curChunk += bytesRead;
1087        if (curChunk > BIGCHUNK) {
1088           curChunk = 0;
1089           lock_Status();
1090           nodePtr->statusNodePtr->nKBytes = totalWritten/1024;
1091           unlock_Status();
1092
1093           if ( checkAbortByTaskId(taskId) )
1094              ERROR_EXIT(TC_ABORTEDBYREQUEST);
1095        }
1096
1097        if (!endData && (bytesRead > 0)) {
1098           /* Fill tbuffer up with data from end of buffer and write
1099            * the remainder of buffer out.
1100            */
1101           if ((tbuffersize == 0) || (bytesRead >= sizeof(tbuffer))) {
1102              /* Write out contents of tbuffer */
1103              if (tbuffersize) {
1104                 w = rx_Write(call, tbuffer, tbuffersize);
1105                 if (w != tbuffersize) {
1106                    ErrorLog (0, taskId, -1, 0, "Error in RX write: Wrote %d bytes\n", w);
1107                    ERROR_EXIT(-1);
1108                 }
1109                 totalWritten += w;
1110              }
1111              /* fill tbuffer with end of buffer */
1112              bytesRead -= sizeof(tbuffer);
1113              bcopy(buffer+bytesRead, tbuffer, sizeof(tbuffer));
1114              tbuffersize = sizeof(tbuffer);
1115              /* Write out whatever is left over in buffer */
1116              if (bytesRead) {
1117                 w = rx_Write(call, buffer, bytesRead);
1118                 if (w != bytesRead) {
1119                    ErrorLog (0, taskId, -1, 0, "Error in RX data write: Wrote %d bytes\n", w);
1120                    ERROR_EXIT(-1);
1121                 }
1122                 totalWritten += w;
1123                 bytesRead = 0;
1124              }
1125           }
1126           else if ((tbuffersize + bytesRead) <= sizeof(tbuffer)) {
1127              /* Copy all of buffer into tbuffer (it will fit) */
1128              bcopy(buffer, tbuffer+tbuffersize, bytesRead);
1129              tbuffersize += bytesRead;
1130              bytesRead = 0;
1131           }
1132           else {
1133              /* We need to write some of tbuffer out and fill it with buffer */
1134              int towrite = bytesRead - (sizeof(tbuffer) - tbuffersize);
1135              w = rx_Write(call, tbuffer, towrite);
1136              if (w != towrite) {
1137                 ErrorLog (0, taskId, -1, 0, "Error in RX write: Wrote %d bytes\n", w);
1138                 ERROR_EXIT(-1);
1139              }
1140              totalWritten += w;
1141              tbuffersize  -= w;
1142             
1143              /* Move the data in tbuffer up */
1144              bcopy(tbuffer+towrite, tbuffer, tbuffersize);
1145
1146              /* Now copy buffer in */
1147              bcopy(buffer, tbuffer+tbuffersize, bytesRead);
1148              tbuffersize += bytesRead;
1149              bytesRead = 0;
1150           }
1151        }
1152     }
1153
1154     /* Pull the volume trailer from the last two buffers */
1155     found = FindVolTrailer2(tbuffer, tbuffersize, &headBytes,
1156                             buffer,  bytesRead,   &tailBytes,
1157                             &volTrailer);
1158
1159     if (!found) {
1160        ErrorLog(0, taskId, TC_MISSINGTRAILER, 0, "Missing volume trailer\n");
1161        ERROR_EXIT(TC_MISSINGTRAILER);
1162     }
1163
1164     /* Now rx_write the data in the last two blocks */
1165     if (headBytes) {
1166        w = rx_Write(call, tbuffer, headBytes);
1167        if (w != headBytes) {
1168           ErrorLog (0, taskId, -1, 0, "Error in RX trail1 write: Wrote %d bytes\n", w);
1169           ERROR_EXIT(-1);
1170        }
1171        totalWritten += w;
1172     }
1173     if (tailBytes) {
1174        w = rx_Write(call, buffer, tailBytes);
1175        if (w != tailBytes) {
1176           ErrorLog (0, taskId, -1, 0, "Error in RX trail2 write: Wrote %d bytes\n", w);
1177           ERROR_EXIT(-1);
1178        }
1179        totalWritten += w;
1180     }
1181
1182   error_exit:
1183 #endif /*xbsa*/
1184     return code;
1185 }
1186
1187 /* restoreVolume
1188  *      sends the contents of volume dump  to Rx Stream associated
1189  *      with <call>
1190  */
1191
1192 afs_int32 restoreVolumeData (call, rparamsPtr)
1193     register struct rx_call *call;
1194     struct restoreParams  *rparamsPtr;
1195 {
1196     afs_int32   curChunk;
1197     afs_uint32 totalWritten = 0;
1198     afs_uint32 tapeID;
1199     afs_int32   code, tcode;
1200     afs_int32   headBytes, tailBytes, w;
1201     afs_int32   taskId;
1202     afs_int32   nbytes;                   /* # bytes data in last tape block read */
1203     struct  volumeHeader tapeVolTrailer;
1204     int     found;
1205     int     moretoread;
1206     afs_int32   startRbuf, endRbuf, startWbuf, endWbuf, buf, pbuf, lastbuf;
1207     struct tc_restoreDesc *Restore;
1208     struct dumpNode       *nodePtr;
1209     struct butm_tapeInfo  *tapeInfoPtr;
1210     char   *origVolName;
1211     afs_int32  origVolID;
1212
1213     nodePtr     = rparamsPtr->nodePtr;
1214     taskId      = nodePtr->taskID;
1215     Restore     = nodePtr->restores;
1216     tapeInfoPtr = rparamsPtr->tapeInfoPtr;
1217     origVolName = Restore[rparamsPtr->frag].oldName;
1218     origVolID   = Restore[rparamsPtr->frag].origVid;
1219
1220     /* Read the volume one fragment at a time */
1221     while (rparamsPtr->frag < nodePtr->arraySize) {
1222         /*w*/
1223         curChunk = BIGCHUNK+1;         /* Check if should abort */
1224
1225         /* Read the volume fragment one block at a time until
1226          * find a volume trailer
1227          */
1228         moretoread = 1;
1229         startRbuf  = 0;
1230         endRbuf    = 0;
1231         startWbuf  = 0;
1232         while (moretoread) {
1233             /* Fill the circular buffer with tape blocks
1234              * Search for volume trailer in the process.
1235              */
1236             buf = startRbuf;
1237             do {
1238                 code = butm_ReadFileData(tapeInfoPtr, bufferBlock[buf].data,
1239                                          BUTM_BLKSIZE, &nbytes);
1240                 if (code) {
1241                     ErrorLog(0, taskId, code, tapeInfoPtr->error, 
1242                              "Can't read FileData on tape %s\n",
1243                              rparamsPtr->mntTapeName);
1244                     ERROR_EXIT(code);
1245                 }
1246                 curChunk += BUTM_BLKSIZE;
1247
1248                 /* Periodically update status structure and check if should abort */
1249                 if (curChunk > BIGCHUNK) {
1250                     curChunk = 0;
1251
1252                     lock_Status();
1253                     nodePtr->statusNodePtr->nKBytes = totalWritten/1024;
1254                     unlock_Status();
1255
1256                     if ( checkAbortByTaskId(taskId) )
1257                        ERROR_EXIT(TC_ABORTEDBYREQUEST);
1258                 }
1259
1260                 /* step to next block in buffer */
1261                 pbuf = buf;
1262                 buf  = ((buf+1) == tapeblocks) ? 0 : (buf+1);  
1263
1264                 /* If this is the end of the volume, the exit the loop */
1265                 if ( (nbytes != BUTM_BLKSIZE) ||
1266                      (FindVolTrailer(bufferBlock[pbuf].data,nbytes,&tailBytes, &tapeVolTrailer)) )
1267                     moretoread = 0;
1268
1269             } while (moretoread && (buf != endRbuf));
1270
1271             /* Write the buffer upto (but not including) the last read block
1272              * If volume is completely read, then leave the last two blocks.
1273              */
1274             lastbuf = endWbuf = pbuf;
1275             if (!moretoread && (endWbuf != startWbuf))
1276                 endWbuf = (endWbuf==0) ? (tapeblocks-1) : (endWbuf-1);
1277               
1278             for (buf = startWbuf; buf != endWbuf; buf = (((buf+1)==tapeblocks)?0:(buf+1))){
1279                 w = rx_Write(call,bufferBlock[buf].data,BUTM_BLKSIZE);
1280                 if (w != BUTM_BLKSIZE) {
1281                     ErrorLog (0, taskId, -1, 0, "Error in RX write\n");
1282                     ERROR_EXIT(-1);
1283                 }
1284                 totalWritten += BUTM_BLKSIZE;
1285             }
1286             
1287             /* Setup pointers to refill buffer */
1288             startRbuf = ((lastbuf+1) == tapeblocks) ? 0 : (lastbuf+1);
1289             endRbuf   = endWbuf;
1290             startWbuf = endWbuf;
1291         }
1292
1293         /* lastbuf is last block read and it has nbytes of data
1294          * startWbuf is the 2nd to last block read 
1295          * Seach for the volume trailer in these two blocks.
1296          */
1297         if (lastbuf == startWbuf)
1298             found = FindVolTrailer2((char *)0                , 0     , &headBytes,
1299                                     bufferBlock[lastbuf].data, nbytes, &tailBytes,
1300                                     &tapeVolTrailer);
1301         else
1302             found = FindVolTrailer2(bufferBlock[startWbuf].data, BUTM_BLKSIZE, &headBytes,
1303                                     bufferBlock[lastbuf].data  , nbytes      , &tailBytes,
1304                                     &tapeVolTrailer);
1305         if (!found) {
1306             ErrorLog(0, taskId, TC_MISSINGTRAILER, 0, 
1307                      "Missing volume trailer on tape %s\n", rparamsPtr->mntTapeName);
1308             ERROR_EXIT(TC_MISSINGTRAILER);
1309         }
1310
1311         /* Now rx_write the data in the last two blocks */
1312         if (headBytes) {
1313             w = rx_Write(call,bufferBlock[startWbuf].data,headBytes);
1314             if (w != headBytes) {
1315                 ErrorLog (0, taskId, -1, 0, "Error in RX write\n");
1316                 ERROR_EXIT(-1);
1317             }
1318             totalWritten += headBytes;
1319         }
1320         if (tailBytes) {
1321             w = rx_Write(call,bufferBlock[lastbuf].data,tailBytes);
1322             if (w != tailBytes) {
1323                 ErrorLog (0, taskId, -1, 0, "Error in RX write\n");
1324                 ERROR_EXIT(-1);
1325             }
1326             totalWritten += tailBytes;
1327         }
1328
1329         /* Exit the loop if the volume is not continued on next tape */
1330         if ( !tapeVolTrailer.contd )
1331            break;                    /* We've read the entire volume */
1332
1333         /* Volume is continued on next tape. 
1334          * Step to the next volume fragment and prompt for its tape.
1335          * When a volume has multiple frags, those frags are on different
1336          * tapes. So we know that we need to prompt for a tape.
1337          */
1338         rparamsPtr->frag++;
1339         if (rparamsPtr->frag >= nodePtr->arraySize)
1340            break;
1341
1342         unmountTape(taskId, tapeInfoPtr);
1343         strcpy(rparamsPtr->mntTapeName, Restore[rparamsPtr->frag].tapeName);
1344         rparamsPtr->tapeID = (Restore[rparamsPtr->frag].initialDumpId ?
1345                               Restore[rparamsPtr->frag].initialDumpId :
1346                               Restore[rparamsPtr->frag].dbDumpId);
1347         code = GetRestoreTape(taskId, tapeInfoPtr, rparamsPtr->mntTapeName,
1348                               rparamsPtr->tapeID, 1);
1349         if (code) ERROR_EXIT(code);
1350
1351         /* Position to the frag and read the volume header */
1352         code = GetVolumeHead(taskId, tapeInfoPtr, 
1353                              Restore[rparamsPtr->frag].position, 
1354                              origVolName, origVolID);
1355         if (code) {
1356             ErrorLog(0, taskId, code, 0, "Can't find volume %s (%u) on tape %s\n", 
1357                      origVolName, origVolID,
1358                      rparamsPtr->mntTapeName);
1359             ERROR_EXIT(TC_VOLUMENOTONTAPE);
1360         }
1361     } /*w*/
1362
1363 error_exit:
1364       return code;
1365 }
1366
1367 /* SkipTape
1368  *    Find all the volumes on a specific tape and mark them to skip.
1369  */
1370 SkipTape(Restore, size, index, tapename, tapeid, taskid)
1371   struct tc_restoreDesc *Restore;
1372   afs_int32  size, index, tapeid, taskid;
1373   char   *tapename;
1374 {
1375   afs_int32 i, tid;
1376
1377   for (i=index; i<size; i++) {
1378      if (Restore[i].flags & RDFLAG_SKIP) continue;
1379      tid = (Restore[i].initialDumpId ? Restore[i].initialDumpId : Restore[i].dbDumpId);
1380      if ( (strcmp(Restore[i].tapeName,tapename) == 0) && (tid == tapeid) ) {
1381         SkipVolume(Restore, size, i, Restore[i].origVid, taskid);
1382      }
1383   }
1384 }
1385
1386 /* SkipVolume
1387  *    Find all the entries for a volume and mark them to skip.
1388  */
1389 SkipVolume(Restore, size, index, volid, taskid)
1390   struct tc_restoreDesc *Restore;
1391      afs_int32 size, index, volid, taskid;
1392 {
1393   afs_int32 i;
1394   int report = 1;
1395
1396   for (i=index; i<size; i++) {
1397      if (Restore[i].flags & RDFLAG_SKIP) continue;
1398      if (Restore[i].origVid == volid) {
1399         Restore[i].flags |= RDFLAG_SKIP;
1400         if (report) {
1401            TLog(taskid, "Restore: Skipping %svolume %s (%u)\n",
1402                 ((Restore[i].dumpLevel == 0)?"":"remainder of "),
1403                 Restore[i].oldName, volid);
1404            report = 0;
1405         }
1406      }
1407   }
1408 }
1409
1410 xbsaRestoreVolume(taskId, restoreInfo, rparamsPtr)
1411     afs_uint32 taskId;
1412     struct tc_restoreDesc *restoreInfo;
1413     struct restoreParams *rparamsPtr;
1414 {
1415    afs_int32  code=0, rc;
1416 #ifdef xbsa
1417    afs_int32  newServer, newPart, newVolId;
1418    char       *newVolName;
1419    int        restoreflags, havetrans=0, startread=0;
1420    afs_int32  bytesRead, endData=0;
1421    afs_uint32 dumpID;
1422    struct budb_dumpEntry dumpEntry;
1423    char volumeNameStr[XBSA_MAX_PATHNAME], dumpIdStr[XBSA_MAX_OSNAME];
1424    struct volumeHeader volHeader, hostVolHeader;
1425
1426    if (restoretofile) {
1427       restoretofilefd = fopen(restoretofile, "w+");
1428    }
1429
1430    dumpID = restoreInfo->dbDumpId;
1431
1432    rc = bcdb_FindDumpByID(dumpID, &dumpEntry);
1433    if (rc) {
1434       ErrorLog(0, taskId, rc, 0, "Can't read database for dump %u\n", dumpID);
1435       ERROR_EXIT(rc);
1436    }
1437
1438    /* ADSM servers restore ADSM and BUTA dumps */
1439    if ( (xbsaType == XBSA_SERVER_TYPE_ADSM) &&
1440        !(dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA)) ) {
1441       ELog(taskId, "The dump requested by this restore operation for the "
1442                    "volumeset is incompatible with this instance of butc\n");
1443       /* Skip the entire dump (one dump per tape) */
1444       ERROR_EXIT(TC_SKIPTAPE);
1445    }
1446
1447    /* make sure we are connected to the correct server. */
1448    if ( (strlen((char *)dumpEntry.tapes.tapeServer) != 0) &&
1449         (strcmp((char *)dumpEntry.tapes.tapeServer,butxInfo.serverName) != 0) ) {
1450       if ((dumpEntry.flags & (BUDB_DUMP_XBSA_NSS | BUDB_DUMP_BUTA)) && !forcemultiple) {
1451          TLog(taskId, "Dump %d is on server %s but butc is connected "
1452                       "to server %s (attempting to restore)\n",
1453               dumpID, (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
1454       } else {
1455          TLog(taskId, "Dump %d is on server %s but butc is connected "
1456                       "to server %s (switching servers)\n",
1457               dumpID, (char *)dumpEntry.tapes.tapeServer, butxInfo.serverName);
1458
1459          rc = InitToServer(taskId, &butxInfo, (char *)dumpEntry.tapes.tapeServer);
1460          if (rc != XBSA_SUCCESS) ERROR_EXIT(TC_SKIPTAPE);
1461       }
1462    }
1463
1464    /* Start a transaction and query the server for the correct fileset dump */
1465    rc = xbsa_BeginTrans(&butxInfo);
1466    if (rc != XBSA_SUCCESS) {
1467       ELog(taskId, "Unable to create a new transaction\n");
1468       ERROR_EXIT(TC_SKIPTAPE);
1469    }
1470    havetrans = 1;
1471
1472    if (dumpEntry.flags & BUDB_DUMP_BUTA) {    /* old buta style names */
1473       sprintf(dumpIdStr, "/%d", dumpID);
1474       strcpy(volumeNameStr, "/");
1475       strcat(volumeNameStr, restoreInfo->oldName);
1476    } else {                                   /* new butc names */
1477       extern char *butcdumpIdStr;
1478       strcpy(dumpIdStr, butcdumpIdStr);
1479       sprintf(volumeNameStr, "/%d", dumpID);
1480       strcat(volumeNameStr, "/");
1481       strcat(volumeNameStr, restoreInfo->oldName);
1482    }
1483
1484    rc = xbsa_QueryObject(&butxInfo, dumpIdStr, volumeNameStr);
1485    if (rc != XBSA_SUCCESS) {
1486       ELog(taskId, "Unable to locate object (%s) of dump (%s) on the server\n",
1487            volumeNameStr, dumpIdStr);
1488       ERROR_EXIT(rc);
1489    }
1490
1491    rc = xbsa_EndTrans(&butxInfo);
1492    havetrans = 0;
1493    if (rc != XBSA_SUCCESS) {
1494      ELog(taskId, "Unable to terminate the current transaction\n");
1495      ERROR_EXIT(rc);
1496    }
1497
1498    if ( checkAbortByTaskId(taskId) )
1499       ERROR_EXIT(TC_ABORTEDBYREQUEST);
1500
1501    /* Now start a transaction on the volume to restore and read the
1502     * volumeheader. We do this before starting a transaction on
1503     * volserver to restore the volume because the XBSA server may take
1504     * a while to mount and seek to the volume causing the volserver to
1505     * time out.
1506     */
1507     rc = xbsa_BeginTrans(&butxInfo);
1508     if (rc != XBSA_SUCCESS) {
1509        ELog(taskId, "Unable to create a new transaction\n");
1510        ERROR_EXIT(TC_SKIPTAPE);
1511     }
1512     havetrans = 1;
1513
1514     rc = xbsa_ReadObjectBegin(&butxInfo, (char *)&volHeader,
1515                               sizeof(volHeader), &bytesRead, &endData);
1516     if (restoretofile && (bytesRead > 0)) {
1517        fwrite((char *)&volHeader, bytesRead, 1, restoretofilefd); /* Save to a file */
1518     }
1519     if (rc != XBSA_SUCCESS) {
1520        ELog(taskId, "Unable to begin reading of the volume from the server\n");
1521        ERROR_EXIT(rc);
1522     }
1523     startread = 1;
1524
1525     if ((bytesRead != sizeof(volHeader)) || endData) {
1526        ELog(taskId,"The size of data read (%d) does not equal the size of data requested (%d)\n",
1527             bytesRead, sizeof(volHeader));
1528        ERROR_EXIT(TC_BADVOLHEADER);
1529     }
1530
1531     /* convert and check the volume header */
1532     rc = VolHeaderToHost(&hostVolHeader, &volHeader);
1533     if (rc) {
1534         ErrorLog(0, taskId, code, 0, "Can't convert volume header\n");
1535         ERROR_EXIT(rc);
1536     }
1537
1538     if ( (strcmp(hostVolHeader.volumeName,restoreInfo->oldName) != 0)   ||
1539          (restoreInfo->origVid && (hostVolHeader.volumeID != restoreInfo->origVid)) ||
1540          (hostVolHeader.magic != TC_VOLBEGINMAGIC) )
1541        ERROR_EXIT(TC_BADVOLHEADER);
1542
1543    /* Set up prior restoring volume data */
1544    newVolName = restoreInfo->newName;
1545    newVolId   = restoreInfo->vid;
1546    newServer  = restoreInfo->hostAddr;
1547    newPart    = restoreInfo->partition;
1548    restoreflags = 0;
1549    if ( (restoreInfo->dumpLevel == 0) || (restoreInfo->flags & RDFLAG_FIRSTDUMP) )
1550       restoreflags |= RV_FULLRST;
1551    if (!(restoreInfo->flags & RDFLAG_LASTDUMP))
1552       restoreflags |= RV_OFFLINE;
1553
1554    if ( checkAbortByTaskId(taskId) )
1555       ERROR_EXIT(TC_ABORTEDBYREQUEST);
1556
1557    /* Start the restore of the volume data. This is the code we want to return */
1558    code = UV_RestoreVolume(htonl(newServer), newPart,
1559                            newVolId, newVolName,
1560                            restoreflags, 
1561                            xbsaRestoreVolumeData,
1562                            (char *)rparamsPtr);
1563   error_exit:
1564    if (startread) {
1565       rc = xbsa_ReadObjectEnd(&butxInfo);
1566       if (rc != XBSA_SUCCESS) {
1567          ELog(taskId, "Unable to terminate reading of the volume from the server\n");
1568          ERROR_EXIT(rc);
1569       }
1570    }
1571
1572    if (havetrans) {
1573       rc = xbsa_EndTrans(&butxInfo);
1574       if (rc != XBSA_SUCCESS) {
1575          ELog(taskId, "Unable to terminate the current transaction\n");
1576          if (!code) code = rc;
1577       }
1578    }
1579
1580    if (restoretofile && restoretofilefd) {
1581       fclose(restoretofilefd);
1582    }
1583 #endif
1584    return(code);
1585 }
1586
1587 restoreVolume(taskId, restoreInfo, rparamsPtr)
1588     afs_uint32 taskId;
1589     struct tc_restoreDesc *restoreInfo;
1590     struct restoreParams *rparamsPtr;
1591 {
1592    afs_int32  code=0, rc;
1593    afs_int32  newServer, newPart, newVolId;
1594    char       *newVolName;
1595    int        restoreflags;
1596    afs_uint32 tapeID;
1597    struct butm_tapeInfo *tapeInfoPtr = rparamsPtr->tapeInfoPtr;
1598    struct budb_dumpEntry dumpEntry;
1599
1600    /* Check if we need a tape and prompt for one if so */
1601    tapeID = (restoreInfo->initialDumpId ? restoreInfo->initialDumpId : restoreInfo->dbDumpId);
1602    if ( (rparamsPtr->frag == 0) ||
1603         (strcmp(restoreInfo->tapeName,rparamsPtr->mntTapeName) != 0) ||
1604         (tapeID != rparamsPtr->tapeID) ) {
1605       /* Unmount the previous tape */
1606       unmountTape(taskId, tapeInfoPtr);
1607
1608       /* Remember this new tape */
1609       strcpy(rparamsPtr->mntTapeName, restoreInfo->tapeName);
1610       rparamsPtr->tapeID = tapeID;
1611
1612       /* Mount a new tape */
1613       rc = GetRestoreTape(taskId, tapeInfoPtr, rparamsPtr->mntTapeName, 
1614                              rparamsPtr->tapeID, ((rparamsPtr->frag==0)?autoQuery:1));
1615       if (rc) ERROR_EXIT(rc);
1616    }
1617
1618    /* Seek to the correct spot and read the header information */
1619    rc = GetVolumeHead(taskId, tapeInfoPtr, restoreInfo->position, 
1620                       restoreInfo->oldName, restoreInfo->origVid);
1621    if (rc) ERROR_EXIT(rc);
1622
1623    /* Set up prior restoring volume data */
1624    newVolName = restoreInfo->newName;
1625    newVolId   = restoreInfo->vid;
1626    newServer  = restoreInfo->hostAddr;
1627    newPart    = restoreInfo->partition;
1628    restoreflags = 0;
1629    if ( (restoreInfo->dumpLevel == 0) || (restoreInfo->flags & RDFLAG_FIRSTDUMP) )
1630       restoreflags |= RV_FULLRST;
1631    if (!(restoreInfo->flags & RDFLAG_LASTDUMP))
1632       restoreflags |= RV_OFFLINE;
1633
1634    if ( checkAbortByTaskId(taskId) )
1635       ERROR_EXIT(TC_ABORTEDBYREQUEST);
1636
1637    /* Start the restore of the volume data. This is the code we
1638     * want to return.
1639     */
1640    code = UV_RestoreVolume(htonl(newServer), newPart,
1641                            newVolId, newVolName,
1642                            restoreflags, 
1643                            restoreVolumeData,
1644                            (char *)rparamsPtr);
1645
1646    /* Read the FileEnd marker for the volume and step to next FM */
1647    rc = butm_ReadFileEnd(tapeInfoPtr);
1648    if (rc) {
1649       ErrorLog(0, taskId, rc, tapeInfoPtr->error, "Can't read EOF on tape\n");
1650    }
1651
1652   error_exit:
1653    return(code);
1654 }
1655
1656 /* Restorer
1657  *      created as a LWP by the server stub, <newNode> is a pointer to all
1658  *      the parameters Restorer needs
1659  */
1660 Restorer (newNode)
1661     struct dumpNode *newNode;
1662 {
1663     afs_int32   code = 0, tcode, rc;
1664     afs_uint32 taskId;
1665     afs_uint32 dumpID;
1666     char    *newVolName;
1667     struct butm_tapeInfo   tapeInfo;
1668     struct tc_restoreDesc *Restore;
1669     struct tc_restoreDesc *RestoreDesc;
1670     struct restoreParams   rparams;
1671     int restoreflags;
1672     afs_int32 allocbufferSize;
1673     time_t startTime, endTime;
1674     afs_int32 goodrestore=0;
1675
1676     taskId = newNode->taskID;
1677     setStatus(taskId, DRIVE_WAIT);
1678     EnterDeviceQueue(deviceLatch);
1679     clearStatus(taskId, DRIVE_WAIT);
1680     
1681     printf("\n\n");
1682     TLog (taskId, "Restore\n");
1683
1684     bzero(&tapeInfo, sizeof(tapeInfo));
1685     if (!CONF_XBSA) {
1686        tapeInfo.structVersion = BUTM_MAJORVERSION;
1687        tcode = butm_file_Instantiate (&tapeInfo, &globalTapeConfig);
1688        if (tcode) {
1689           ErrorLog(0, taskId, tcode, tapeInfo.error,
1690                    "Can't initialize the tape module\n");
1691           ERROR_EXIT(tcode);
1692        }
1693     }
1694
1695     if ( checkAbortByTaskId(taskId) ) ERROR_EXIT(TC_ABORTEDBYREQUEST);
1696
1697     bzero(&rparams, sizeof(rparams));
1698     rparams.nodePtr     = newNode;
1699     rparams.tapeInfoPtr = &tapeInfo;
1700     Restore = newNode->restores;         /* Array of vol fragments to restore */
1701
1702     /* Allocate memory in which to restore the volumes data into */
1703     if (CONF_XBSA) {
1704        allocbufferSize = dataSize = BufferSize;
1705     } else {
1706        /* Must have at least two tape blocks */
1707        tapeblocks = BufferSize / BUTM_BLOCKSIZE;
1708        if (tapeblocks < 2) tapeblocks = 2;
1709        allocbufferSize = tapeblocks * BUTM_BLOCKSIZE;     /* This many full tapeblocks */
1710     }
1711     bufferBlock = (struct TapeBlock *)0;
1712     bufferBlock = (struct TapeBlock *) malloc(allocbufferSize);
1713     if (!bufferBlock) ERROR_EXIT(TC_NOMEMORY);
1714     bzero(bufferBlock, allocbufferSize);
1715
1716     startTime = time(0);
1717     for (rparams.frag=0; (rparams.frag < newNode->arraySize); rparams.frag++) {
1718         RestoreDesc = &Restore[rparams.frag];
1719
1720         /* Skip the volume if it was requested to */
1721         if (RestoreDesc->flags & RDFLAG_SKIP) {
1722            if (RestoreDesc->flags & RDFLAG_LASTDUMP) {
1723               /* If the volume was restored, should bring it online */
1724            }
1725            continue;
1726         }
1727
1728         newVolName = RestoreDesc->newName;
1729
1730         /* Make sure the server to restore to is good */
1731         if (!RestoreDesc->hostAddr) {
1732             ErrorLog(0, taskId, 0, 0, "Illegal host ID 0 for volume %s\n", newVolName);
1733             ERROR_EXIT(TC_INTERNALERROR);
1734         }
1735
1736         if ( checkAbortByTaskId(taskId) )
1737            ERROR_EXIT(TC_ABORTEDBYREQUEST);
1738
1739         TapeLog(1, taskId, 0, 0, "Restoring volume %s\n", newVolName);
1740         lock_Status();
1741         strncpy(newNode->statusNodePtr->volumeName, newVolName, BU_MAXNAMELEN);
1742         unlock_Status();
1743
1744         /* restoreVolume function takes care of all the related fragments
1745          * spanning various tapes. On return the complete volume has been
1746          * restored 
1747          */
1748         if (CONF_XBSA) {
1749            tcode = xbsaRestoreVolume(taskId, RestoreDesc, &rparams);
1750         } else {
1751            tcode = restoreVolume(taskId, RestoreDesc, &rparams);
1752         }
1753         if (tcode) {
1754             if (tcode == TC_ABORTEDBYREQUEST) {
1755                ERROR_EXIT(tcode);
1756             } else if (tcode == TC_SKIPTAPE) {
1757                afs_uint32 tapeID;
1758                tapeID = (RestoreDesc->initialDumpId ? RestoreDesc->initialDumpId :
1759                                                       RestoreDesc->dbDumpId);
1760                SkipTape(Restore, newNode->arraySize, rparams.frag,
1761                         RestoreDesc->tapeName, tapeID, taskId);
1762             } else {
1763                ErrorLog(0, taskId, tcode, 0, "Can't restore volume %s\n", newVolName);
1764                SkipVolume(Restore, newNode->arraySize, rparams.frag,
1765                           RestoreDesc->origVid, taskId);
1766             }
1767             rparams.frag--;
1768             continue;
1769         }
1770
1771         goodrestore++;
1772     }
1773
1774   error_exit:
1775     endTime = time(0);
1776     if (!CONF_XBSA) {
1777        unmountTape(taskId, &tapeInfo);
1778     } else {
1779 #ifdef xbsa
1780        rc = InitToServer(taskId, &butxInfo, 0);       /* Return to original server */
1781 #endif
1782     }
1783
1784     if (bufferBlock) free(bufferBlock);
1785
1786     if (code == TC_ABORTEDBYREQUEST)
1787     {
1788         ErrorLog(0, taskId, 0, 0, "Restore: Aborted by request\n");
1789         clearStatus(taskId, ABORT_REQUEST);
1790         setStatus  (taskId, ABORT_DONE);
1791     }
1792     else if (code)
1793     {
1794         TapeLog(0, taskId, code, 0, "Restore: Finished with errors\n");
1795         setStatus(taskId, TASK_ERROR);
1796     }
1797     else
1798     {
1799         TLog(taskId, "Restore: Finished\n");
1800     }
1801
1802     if (centralLogIO && startTime) {
1803        long timediff;
1804        afs_int32 hrs, min, sec, tmp;
1805        char line[1024];
1806        struct tm tmstart, tmend;
1807
1808        localtime_r(&startTime, &tmstart);
1809        localtime_r(&endTime, &tmend);
1810        timediff = (int)endTime - (int)startTime;
1811        hrs = timediff / 3600;
1812        tmp = timediff % 3600;
1813        min = tmp / 60;
1814        sec = tmp % 60;
1815
1816        sprintf(line, "%-5d  %02d/%02d/%04d %02d:%02d:%02d  "
1817                            "%02d/%02d/%04d %02d:%02d:%02d  "
1818                            "%02d:%02d:%02d  "
1819                      "%d of %d volume%s restored\n",
1820                taskId, tmstart.tm_mon+1, tmstart.tm_mday, tmstart.tm_year+1900,
1821                        tmstart.tm_hour, tmstart.tm_min, tmstart.tm_sec,
1822                        tmend.tm_mon+1, tmend.tm_mday, tmend.tm_year+1900,
1823                        tmend.tm_hour, tmend.tm_min, tmend.tm_sec,
1824                hrs, min, sec, 
1825                goodrestore, newNode->arraySize, ((newNode->arraySize>1)?"s":""));
1826
1827        fwrite(line, strlen(line), 1, centralLogIO);
1828        fflush(centralLogIO);
1829     }
1830
1831     setStatus(taskId, TASK_DONE);
1832
1833     FreeNode(taskId);
1834     LeaveDeviceQueue(deviceLatch);
1835     return(code);
1836
1837
1838 /* this is just scaffolding, creates new tape label with name <tapeName> */
1839
1840 GetNewLabel(tapeInfoPtr, pName, AFSName, tapeLabel)
1841     struct butm_tapeInfo  *tapeInfoPtr;
1842     char                  *pName, *AFSName;
1843     struct butm_tapeLabel *tapeLabel;
1844 {
1845     struct timeval tp;
1846     struct timezone tzp;
1847     afs_uint32 size;
1848
1849     bzero(tapeLabel,sizeof(struct butm_tapeLabel));
1850
1851     if (!CONF_XBSA) {
1852        butm_GetSize(tapeInfoPtr, &size);
1853        if ( !size ) size = globalTapeConfig.capacity;
1854     } else {
1855        size = 0; /* no tape size */
1856     }
1857     gettimeofday(&tp,&tzp);
1858
1859     tapeLabel->structVersion  = CUR_TAPE_VERSION;
1860     tapeLabel->creationTime   = tp.tv_sec;
1861     tapeLabel->size           = size;
1862     tapeLabel->expirationDate = 0;              /* 1970 sometime */
1863     tapeLabel->dumpPath[0]    = 0;              /* no path name  */
1864     tapeLabel->useCount       = 0;
1865     strcpy(tapeLabel->AFSName,          AFSName);
1866     strcpy(tapeLabel->pName,            pName);
1867     strcpy(tapeLabel->cell,             globalCellName);
1868     strcpy(tapeLabel->comment,          "AFS Backup Software");
1869     strcpy(tapeLabel->creator.name,     "AFS 3.6");
1870     strcpy(tapeLabel->creator.instance, "");
1871     strcpy(tapeLabel->creator.cell,     globalCellName);
1872 }
1873
1874 /* extracts trailer out of buffer, nbytes is set to total data in buffer - trailer size */
1875 afs_int32
1876 ExtractTrailer(buffer, size, nbytes, volTrailerPtr)
1877     char  *buffer;
1878     afs_int32 *nbytes;
1879     afs_int32 size;
1880     struct volumeHeader *volTrailerPtr;
1881 {   
1882     afs_int32  code = 0;
1883     afs_int32  startPos;
1884     struct volumeHeader tempTrailer;
1885
1886     for (startPos = 0;
1887          startPos <= (size - sizeof(struct volumeHeader) + sizeof(tempTrailer.pad)); 
1888          startPos++)    
1889     {
1890         code = readVolumeHeader(buffer, startPos, &tempTrailer);
1891         if (code == 0)
1892         {
1893             code = VolHeaderToHost(volTrailerPtr, &tempTrailer);
1894             if (code) break;
1895
1896             if (nbytes) *nbytes = startPos;
1897             return 1;                            /* saw the trailer */
1898         }
1899     }
1900
1901     if (nbytes) *nbytes = size/2;
1902     return 0;                                    /* did not see the trailer */
1903 }
1904
1905 int
1906 FindVolTrailer(buffer, size, dSize, volTrailerPtr)
1907     char                *buffer;
1908     afs_int32               size;
1909     struct volumeHeader *volTrailerPtr;
1910     afs_int32               *dSize; /* dataSize */
1911 {
1912     afs_int32 offset, s;
1913     afs_int32 code;
1914     int found;
1915     
1916     *dSize = size;
1917     if (!buffer) return 0;
1918
1919     s =  sizeof(struct volumeHeader) + sizeof(volTrailerPtr->pad);
1920     if (s > size) s = size;
1921
1922     found = ExtractTrailer((buffer+size-s),s,&offset,volTrailerPtr);
1923     if (found) *dSize -= (s - offset);
1924     return found;
1925 }
1926
1927 int
1928 FindVolTrailer2(buffera, sizea, dataSizea, bufferb, sizeb, dataSizeb, volTrailerPtr)
1929     char                *buffera;
1930     afs_int32               sizea;
1931     afs_int32               *dataSizea;
1932     char                *bufferb;
1933     afs_int32               sizeb;
1934     afs_int32               *dataSizeb;
1935     struct volumeHeader *volTrailerPtr;
1936 {
1937     afs_int32 offset, s;
1938     afs_int32 headB, tailB;
1939     int found=0;
1940
1941     if ( !buffera ) sizea = 0;
1942     if ( !bufferb ) sizeb = 0;
1943     *dataSizea = sizea;
1944     *dataSizeb = sizeb;
1945
1946     s =  sizeof(struct volumeHeader) + sizeof(volTrailerPtr->pad);
1947     if (sizeb >= s) {
1948         found = FindVolTrailer(bufferb, sizeb, dataSizeb, volTrailerPtr);
1949     } else {
1950         tailB = sizeb;
1951         headB = (s - sizeb);      /*(s > sizeb)*/
1952         if (headB > sizea) {
1953             headB = sizea;
1954             s = headB + tailB;
1955             if (!s) return 0;
1956         }
1957         
1958         bzero(tapeVolumeHT, sizeof(tapeVolumeHT));
1959         if (headB) bcopy (buffera + sizea - headB, tapeVolumeHT        , headB);
1960         if (tailB) bcopy (bufferb                , tapeVolumeHT + headB, tailB);
1961         if (ExtractTrailer (tapeVolumeHT, s, &offset, volTrailerPtr)) {
1962             found = 1;
1963             if (offset > headB) {
1964                 /* *dataSizea remains unchanged */
1965                 *dataSizeb = offset - headB;
1966             } else {
1967                 *dataSizea -= (headB - offset);   /*(headB >= offset)*/
1968                 *dataSizeb = 0;
1969             }
1970         }
1971     }
1972     return found;
1973 }
1974
1975 /* Returns true or false depending on whether the tape is expired or not */
1976
1977 Date ExpirationDate (dumpid)
1978     afs_int32 dumpid;
1979 {
1980     afs_int32                   code;
1981     Date                    expiration = 0;
1982     struct budb_dumpEntry   dumpEntry;
1983     struct budb_tapeEntry   tapeEntry;
1984     struct budb_volumeEntry volEntry;
1985   
1986     if (dumpid)
1987     {
1988         /*
1989          * Get the expiration date from DB if its there. The expiration of any tape
1990          * will be the most future expiration of any dump in the set. Can't use
1991          * bcdb_FindTape because dumpid here pertains to the initial dump id.
1992          */
1993         code = bcdb_FindLastTape(dumpid, &dumpEntry, &tapeEntry, &volEntry);
1994         if (!code) expiration = tapeEntry.expires;
1995     }
1996     return(expiration);
1997 }
1998
1999 int 
2000 tapeExpired (tapeLabelPtr)
2001     struct butm_tapeLabel *tapeLabelPtr;
2002 {
2003     Date   expiration;
2004     struct timeval          tp;
2005     struct timezone         tzp;
2006     
2007     expiration = ExpirationDate(tapeLabelPtr->dumpid);
2008     if (!expiration) expiration = tapeLabelPtr->expirationDate;
2009
2010     gettimeofday (&tp, &tzp);
2011     return ((expiration < tp.tv_sec) ? 1 : 0);
2012 }
2013
2014 /* updateTapeLabel
2015  *      given the label on the tape, delete any old information from the
2016  *      database. 
2017
2018  * Deletes all entries that match the volset.dumpnode
2019  *      and the dump path.
2020  */
2021
2022 updateTapeLabel(labelIfPtr, tapeInfoPtr, newLabelPtr)
2023      struct labelTapeIf *labelIfPtr;
2024      struct butm_tapeInfo *tapeInfoPtr;
2025      struct butm_tapeLabel *newLabelPtr;
2026 {
2027     struct butm_tapeLabel oldLabel;
2028     afs_int32 i, code = 0;
2029     afs_uint32 taskId;
2030     int tapeIsLabeled = 0;
2031     int interactiveFlag;
2032     extern afs_int32 BUDB_DeleteDump();
2033     int tapecount = 1;
2034
2035     interactiveFlag = autoQuery;
2036     taskId = labelIfPtr->taskId;
2037
2038     while (1)
2039     {
2040        if ( interactiveFlag )
2041        {
2042            code = PromptForTape(LABELOPCODE, TNAME(newLabelPtr), 0, 
2043                                 labelIfPtr->taskId, tapecount);
2044            if (code) ERROR_EXIT(code);
2045        }
2046        interactiveFlag = 1;
2047        tapecount++;
2048
2049        /* mount the tape */
2050        code = butm_Mount(tapeInfoPtr, newLabelPtr->AFSName);
2051        if (code)
2052        {
2053           TapeLog(0, taskId, code, tapeInfoPtr->error, "Can't open tape\n");
2054           goto newtape;
2055        }
2056
2057        code = butm_ReadLabel(tapeInfoPtr, &oldLabel, 1);     /* will rewind the tape */
2058        if (!code)
2059        {
2060           tapeIsLabeled = 1;
2061
2062           if ( (strcmp(newLabelPtr->AFSName,"") != 0) &&
2063                (strcmp(oldLabel.pName,"")       != 0) ) {
2064              /* We are setting the AFS name, yet tape 
2065               * has a permanent name (not allowed).
2066               */
2067              TLog(taskId, "Can't label. Tape has permanent label '%s'\n",
2068                   oldLabel.pName);
2069              goto newtape;
2070           }
2071
2072           if ( !tapeExpired(&oldLabel) )
2073           {
2074              if (!queryoperator)
2075              {
2076                  TLog(taskId, "This tape has not expired\n");
2077                  goto newtape;
2078              }
2079              if ( Ask("This tape has not expired - proceed") == 0 ) goto newtape;
2080           }
2081
2082           /* Keep the permanent name */
2083           if (strcmp(newLabelPtr->pName,"") == 0)
2084           {
2085               strcpy(newLabelPtr->pName, oldLabel.pName);
2086           }
2087           else if (strcmp(newLabelPtr->pName, TC_NULLTAPENAME) == 0)
2088           {
2089               strcpy(newLabelPtr->pName, "");
2090           }
2091        }
2092
2093        /* extract useful information from the old label */
2094        if ( tapeIsLabeled && oldLabel.structVersion >= TAPE_VERSION_3 )
2095        {
2096            newLabelPtr->dumpid = 0;
2097            newLabelPtr->useCount = oldLabel.useCount + 1;
2098        }
2099
2100        /* now write the new label */
2101        code = butm_Create(tapeInfoPtr, newLabelPtr, 1);    /* will rewind the tape */
2102        if (code)
2103        {
2104            ErrorLog(0, taskId, code, tapeInfoPtr->error, "Can't label tape\n");
2105            goto newtape;
2106        }
2107
2108        break;
2109
2110      newtape:
2111        unmountTape(taskId, tapeInfoPtr);
2112     }
2113
2114     /* delete obsolete information from the database */
2115     if ( tapeIsLabeled && oldLabel.structVersion >= TAPE_VERSION_3 )
2116     {
2117        /* delete based on dump id */
2118        if ( oldLabel.dumpid )
2119        {
2120           i = bcdb_deleteDump(oldLabel.dumpid, 0, 0, 0);
2121           if ( i && (i != BUDB_NOENT) )
2122              ErrorLog(0, taskId, i, 0, 
2123                       "Warning: Can't delete old dump %u from database\n", oldLabel.dumpid);
2124        }
2125     }
2126
2127 error_exit:
2128     unmountTape(taskId, tapeInfoPtr);
2129     return(code);
2130 }
2131
2132 /* Labeller
2133  *      LWP created by the server stub. Labels the tape with name and size
2134  *      specified by <label>
2135  */
2136
2137 Labeller (labelIfPtr)
2138      struct labelTapeIf *labelIfPtr;
2139 {
2140      struct tc_tapeLabel *label = &labelIfPtr->label;
2141      
2142      struct butm_tapeLabel newTapeLabel;
2143      struct butm_tapeInfo  tapeInfo;
2144      afs_uint32 taskId;
2145      afs_int32 code = 0;
2146
2147      taskId = labelIfPtr->taskId;
2148      setStatus(taskId, DRIVE_WAIT);
2149      EnterDeviceQueue(deviceLatch);
2150      clearStatus(taskId, DRIVE_WAIT);
2151
2152      printf("\n\n");
2153      TLog (taskId, "Labeltape\n");
2154      
2155      bzero(&tapeInfo, sizeof(tapeInfo));
2156      tapeInfo.structVersion = BUTM_MAJORVERSION;
2157      code = butm_file_Instantiate (&tapeInfo, &globalTapeConfig);
2158      if (code) 
2159      {
2160          ErrorLog(0, taskId, code, tapeInfo.error, "Can't initialize the tape module\n");
2161          ERROR_EXIT(code);
2162      }
2163
2164      GetNewLabel(&tapeInfo, label->pname, label->afsname, &newTapeLabel);
2165      if (label->size) newTapeLabel.size = label->size;
2166      else newTapeLabel.size = globalTapeConfig.capacity;
2167
2168      code = updateTapeLabel(labelIfPtr, &tapeInfo, &newTapeLabel);
2169      if (code) ERROR_EXIT(code);
2170
2171 error_exit:
2172      if (code == TC_ABORTEDBYREQUEST)
2173      {
2174          ErrorLog (0, taskId, 0, 0, "Labeltape: Aborted by request\n");
2175          clearStatus(taskId, ABORT_REQUEST);
2176          setStatus  (taskId, ABORT_DONE);
2177      }
2178      else if (code)
2179      {
2180          ErrorLog (0, taskId, code, 0, "Labeltape: Finished with errors\n");
2181          setStatus(taskId, TASK_ERROR);
2182      }
2183      else
2184      {
2185          TLog (taskId, "Labelled tape %s size %u Kbytes\n", 
2186                TNAME(&newTapeLabel), newTapeLabel.size);
2187      }
2188      setStatus(labelIfPtr->taskId, TASK_DONE);
2189
2190      free(labelIfPtr);
2191      LeaveDeviceQueue(deviceLatch);
2192      return(code);
2193 }
2194
2195 /* PrintTapeLabel
2196  *      print out the tape label.
2197  */
2198
2199 PrintTapeLabel(labelptr)
2200      struct butm_tapeLabel *labelptr;
2201 {
2202     char tapeName[BU_MAXTAPELEN+32];
2203
2204     printf("Tape label\n");
2205     printf("----------\n");
2206     TAPENAME(tapeName, labelptr->pName, labelptr->dumpid);
2207     printf("permanent tape name = %s\n", tapeName);
2208     TAPENAME(tapeName, labelptr->AFSName, labelptr->dumpid);
2209     printf("AFS tape name = %s\n", tapeName);
2210     printf("creationTime = %s", ctime(&labelptr->creationTime));
2211     if ( labelptr->expirationDate )
2212         printf("expirationDate = %s", cTIME(&labelptr->expirationDate));
2213     printf("cell = %s\n", labelptr->cell);
2214     printf("size = %u Kbytes\n", labelptr->size);
2215     printf("dump path = %s\n", labelptr->dumpPath);
2216
2217     if ( labelptr->structVersion >= TAPE_VERSION_3 )
2218     {
2219         printf("dump id = %u\n", labelptr->dumpid);
2220         printf("useCount = %d\n", labelptr->useCount);
2221     }
2222     printf("-- End of tape label --\n\n");
2223 }
2224
2225 /* ReadLabel 
2226  *      Read the label from a tape.
2227  *      Currently prints out a "detailed" summary of the label but passes
2228  *      back only selected fields.
2229  */
2230
2231 ReadLabel(label)
2232 struct tc_tapeLabel *label;
2233 {
2234     struct butm_tapeLabel newTapeLabel;
2235     struct butm_tapeInfo  tapeInfo;
2236     afs_uint32 taskId;
2237     Date  expir;
2238     afs_int32 code = 0;
2239     int interactiveFlag;
2240     int tapecount = 1;
2241
2242     EnterDeviceQueue(deviceLatch);
2243     taskId = allocTaskId();                     /* reqd for lower level rtns */
2244
2245     printf("\n\n");
2246     TLog (taskId, "Readlabel\n");
2247
2248     bzero(&tapeInfo,sizeof(tapeInfo));
2249     tapeInfo.structVersion = BUTM_MAJORVERSION;
2250     code = butm_file_Instantiate (&tapeInfo, &globalTapeConfig);
2251     if (code)
2252     {
2253         ErrorLog(0, taskId, code, tapeInfo.error, "Can't initialize the tape module\n");
2254         ERROR_EXIT(code);
2255     }
2256     bzero(&newTapeLabel,sizeof(newTapeLabel));
2257
2258     interactiveFlag = autoQuery;
2259
2260     while (1)
2261     {
2262        if ( interactiveFlag )
2263        {
2264            code = PromptForTape(READLABELOPCODE, "", 0, taskId, tapecount);
2265            if (code) ERROR_EXIT(code);
2266        }
2267        interactiveFlag = 1;
2268        tapecount++;
2269
2270        code = butm_Mount(&tapeInfo, "");
2271        if (code)
2272        {
2273           TapeLog(0, taskId, code, tapeInfo.error, "Can't open tape\n");
2274           goto newtape;
2275        }
2276        break;
2277
2278      newtape:
2279        unmountTape(taskId, &tapeInfo);
2280     }
2281     
2282     code = butm_ReadLabel(&tapeInfo, &newTapeLabel, 1);     /* will rewind the tape */
2283     if (code)
2284     {
2285         if (code == BUTM_NOLABEL)
2286         {
2287             printf("Tape is unlabelled\n");
2288             ERROR_EXIT(code);
2289         }
2290         ErrorLog(1, taskId, code, tapeInfo.error, "Can't read tape label\n");
2291         ERROR_EXIT(code);
2292     }
2293
2294     /* copy the fields to be passed to the caller */
2295     label->size   = newTapeLabel.size;
2296     label->tapeId = newTapeLabel.dumpid;
2297     strcpy(label->afsname, newTapeLabel.AFSName);
2298     strcpy(label->pname, newTapeLabel.pName);
2299     
2300
2301     expir = ExpirationDate(newTapeLabel.dumpid);
2302     if (expir) newTapeLabel.expirationDate = expir;
2303
2304     PrintTapeLabel(&newTapeLabel);
2305
2306 error_exit:
2307     unmountTape(taskId, &tapeInfo);
2308     
2309     if (code == TC_ABORTEDBYREQUEST)
2310         ErrorLog (0, taskId, 0, 0, "ReadLabel: Aborted by request\n");
2311     else if (code && (code != BUTM_NOLABEL))
2312         ErrorLog (0, taskId, code, 0, "ReadLabel: Finished with errors\n");
2313     else
2314         TLog (taskId, "ReadLabel: Finished\n");
2315
2316     LeaveDeviceQueue(deviceLatch);
2317     return(code);
2318 }
2319
2320 /* Function to read volume header and trailer structure from tape, taking
2321    into consideration, different word alignment rules.
2322 */
2323 afs_int32
2324 readVolumeHeader (buffer, bufloc, header)
2325      /*in*/ char *buffer;               /* buffer to read header from */
2326      /*in*/ afs_int32 bufloc;           /* header's location in buffer */
2327      /*out*/ struct volumeHeader *header;       /* header structure */
2328
2329 {
2330     struct volumeHeader vhptr, *tempvhptr;
2331     afs_int32 firstSplice = (afs_int32)&vhptr.pad - (afs_int32)&vhptr; 
2332     int padLen = sizeof(vhptr.pad);       /* pad to achieve 4 byte alignment */
2333     int nextSplice = sizeof(struct volumeHeader) - firstSplice - padLen;
2334
2335     /* Four cases are to be handled
2336
2337        Volume Header (byte alignment)
2338        -----------------------
2339                Tape   In Core
2340                ----   -------
2341        Case 1:  4       1
2342        Case 2:  4       4
2343        Case 3:  1       1
2344        Case 4:  1       4
2345        -----------------------
2346
2347        Case 2 and Case 3 are identical cases and handled the same way.
2348        Case 1 and Case 4 are separate cases. In one case the pad needs
2349        to be removed and in the other, it needs to be spliced in. The
2350        four cases are handled as follows
2351      */
2352     tempvhptr = (struct volumeHeader *)(buffer+bufloc);
2353     if ( (strncmp(tempvhptr->preamble,  "H++NAME#",8) == 0) &&
2354          (strncmp(tempvhptr->postamble, "T--NAME#",8) == 0) )
2355     {
2356         /* Handle Cases 2 & 3 */
2357         bcopy (buffer+bufloc, &vhptr, sizeof(struct volumeHeader));
2358         HEADER_CHECKS(vhptr, header);
2359         
2360         /* Handle Case 4 */
2361         bzero (&vhptr, sizeof(struct volumeHeader));
2362         bcopy (buffer+bufloc, &vhptr, firstSplice);
2363         bzero (&vhptr.pad, padLen);
2364         bcopy (buffer+bufloc+firstSplice, &vhptr.volumeID, nextSplice);
2365         HEADER_CHECKS(vhptr, header);
2366
2367         /* Handle Case 1 */
2368         bzero (&vhptr, sizeof(struct volumeHeader));
2369         bcopy (buffer+bufloc, &vhptr, firstSplice);
2370         bcopy (buffer+bufloc+firstSplice+padLen, &vhptr+firstSplice, nextSplice);
2371         HEADER_CHECKS(vhptr, header);
2372
2373     }
2374     return (TC_BADVOLHEADER);
2375 }