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