afsconfig-and-rcsid-all-around-20010705
[openafs.git] / src / butc / tcprocs.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 /* procedures invoked by the rpc stub */
11
12 #include <afs/param.h>
13 #include <afsconfig.h>
14
15 RCSID("$Header$");
16
17 #include <sys/types.h>
18 #include <errno.h>
19 #ifdef AFS_NT40_ENV
20 #include <winsock2.h>
21 #else
22 #include <sys/file.h>
23 #include <netinet/in.h>
24 #endif
25 #include <rx/xdr.h>
26 #include <rx/rx.h>
27 #include <afs/afsint.h>
28 #include <stdio.h>
29 #include <afs/procmgmt.h>
30 #include <afs/assert.h>
31 #include <afs/prs_fs.h>
32 #include <sys/stat.h>
33 #include <afs/nfs.h>
34 #include <lwp.h>
35 #include <lock.h>
36 #include <afs/auth.h>
37 #include <afs/cellconfig.h>
38 #include <afs/keys.h>
39 #include <ubik.h>
40 #include <afs/tcdata.h>
41 #include "error_macros.h"
42 #include "butc_xbsa.h"
43 extern afs_int32 xbsaType;
44
45 callPermitted(call)
46     struct rx_call *call;
47 {
48     afs_int32                         code;
49
50     struct rx_connection        *tconn;
51     rxkad_level                  level;
52     Date                         expiration;
53     char                         name[MAXKTCNAMELEN];
54     char                         inst[MAXKTCNAMELEN];
55     char                         celn[MAXKTCREALMLEN];
56     afs_int32                         kvno;
57
58     char                        *cell;
59     afs_int32                         flag;
60
61     /* before this code can be used, the rx connection, on the bucoord side, must */
62     /* be changed so that it will set up for token passing instead of using  a    */
63     /* simple rx connection that, below, returns a value of 0 from rx_SecurityClassOf */
64     return 1;
65 }
66
67 /* -------------------------
68  * butc - interface routines - alphabetic order
69  * -------------------------
70  */
71
72 STC_LabelTape(acid, label, taskId)
73      struct rx_call *acid;
74      struct tc_tapeLabel *label;
75      afs_uint32 *taskId;
76 {
77 #ifdef AFS_PTHREAD_ENV
78     pthread_t pid;
79     pthread_attr_t tattr;
80     AFS_SIGSET_DECL;
81 #else
82     PROCESS pid;
83 #endif
84     struct tc_tapeLabel *mylabel;
85     struct labelTapeIf *ptr;
86     statusP statusPtr;
87     afs_int32 code;
88
89      extern int Labeller();
90      extern statusP createStatusNode();
91      extern afs_int32 allocTaskId();
92
93 #ifdef xbsa
94     if (CONF_XBSA) return(TC_BADTASK);    /* LabelTape does not apply if XBSA */
95 #endif
96
97     if ( callPermitted(acid) == 0 )
98         return(TC_NOTPERMITTED);
99
100     ptr = (struct labelTapeIf *) malloc(sizeof(*ptr));
101     if (!ptr) ERROR_EXIT(TC_NOMEMORY);
102     bcopy(label, &ptr->label, sizeof(ptr->label));
103
104     /* set up the status node */
105     *taskId = allocTaskId();                    /* for bucoord */
106     ptr->taskId = *taskId;
107     
108     statusPtr = createStatusNode();
109     if ( !statusPtr ) ERROR_EXIT(TC_INTERNALERROR);
110
111     lock_Status();
112     statusPtr->taskId     = *taskId;
113     statusPtr->lastPolled = time(0);
114     statusPtr->flags     &= ~STARTING;         /* ok to examine */
115     strncpy(statusPtr->taskName, "Labeltape", sizeof(statusPtr->taskName));
116     unlock_Status();
117
118     /* create the LWP to do the real work behind the scenes */
119 #ifdef AFS_PTHREAD_ENV
120     code = pthread_attr_init(&tattr);
121     if (code) ERROR_EXIT(code);
122
123     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
124     if (code) ERROR_EXIT(code);
125
126     AFS_SIGSET_CLEAR();
127     code = pthread_create(&pid, &tattr, Labeller, ptr);
128     AFS_SIGSET_RESTORE();
129 #else
130     code = LWP_CreateProcess(Labeller, 32768, 1, ptr ,"labeller process", &pid);
131 #endif
132
133 error_exit:
134     if ( code )
135     {
136         if (statusPtr) deleteStatusNode(statusPtr);
137         if (ptr) free(ptr);
138     }
139
140     return(code);
141 }
142
143 /* STC_PerformDump
144  *      Tape coordinator server routine to do a dump
145  */
146
147 STC_PerformDump(rxCallId, tcdiPtr, tc_dumpArrayPtr, taskId)
148     struct rx_call *rxCallId;
149     struct tc_dumpInterface *tcdiPtr;
150     tc_dumpArray *tc_dumpArrayPtr;
151     afs_int32 *taskId;
152 {
153     struct dumpNode *newNode = 0;
154     statusP         statusPtr = 0;
155 #ifdef AFS_PTHREAD_ENV
156     pthread_t       pid;
157     pthread_attr_t  tattr;
158     AFS_SIGSET_DECL;
159 #else
160     PROCESS         pid;
161 #endif
162     afs_int32           code = 0;
163
164     extern statusP createStatusNode();
165     extern Dumper();
166
167     if ( callPermitted(rxCallId) == 0 )
168         return(TC_NOTPERMITTED);
169
170     /* should be verifying parameter validity */
171     *taskId = 0;
172
173     /* this creates a node in list, alots an id for it and prepares it for locking */
174     CreateNode(&newNode);
175
176     /*set up the parameters in the node, to be used by LWP */
177     strcpy(newNode->dumpSetName, tcdiPtr->dumpName);
178
179     newNode->dumpName = (char *) malloc(strlen(tcdiPtr->dumpPath)+1);
180     strcpy(newNode->dumpName, tcdiPtr->dumpPath);
181
182     newNode->volumeSetName = (char *) malloc(strlen(tcdiPtr->volumeSetName)+1);
183     strcpy(newNode->volumeSetName, tcdiPtr->volumeSetName);
184
185     CopyTapeSetDesc(&(newNode->tapeSetDesc), &tcdiPtr->tapeSet);
186
187     newNode->dumps = (struct tc_dumpDesc *)
188         malloc(sizeof(struct tc_dumpDesc) * tc_dumpArrayPtr->tc_dumpArray_len);
189     newNode->arraySize = tc_dumpArrayPtr->tc_dumpArray_len;
190     CopyDumpDesc(newNode->dumps, tc_dumpArrayPtr);
191
192     newNode->parent = tcdiPtr->parentDumpId;
193     newNode->level = tcdiPtr->dumpLevel;
194     newNode->doAppend = tcdiPtr->doAppend;
195 #ifdef xbsa
196     if (CONF_XBSA) newNode->doAppend = 0;    /* Append flag is ignored if talking to XBSA */
197 #endif
198
199     /* create the status node */
200     statusPtr = createStatusNode();
201     if ( !statusPtr ) ERROR_EXIT(TC_INTERNALERROR);
202
203     lock_Status();
204     statusPtr->taskId     = newNode->taskID;
205     statusPtr->lastPolled = time(0);
206     statusPtr->flags     &= ~STARTING;                     /* ok to examine */
207     strncpy(statusPtr->taskName, "Dump", sizeof(statusPtr->taskName));
208     unlock_Status();
209
210     newNode->statusNodePtr = statusPtr;
211
212     /* create the LWP to do the real work behind the scenes */
213 #ifdef AFS_PTHREAD_ENV
214     code = pthread_attr_init(&tattr);
215     if (code) ERROR_EXIT(code);
216
217     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
218     if (code) ERROR_EXIT(code);
219
220     AFS_SIGSET_CLEAR();
221     code = pthread_create(&pid, &tattr, Dumper, newNode);
222     AFS_SIGSET_RESTORE();
223 #else
224     code = LWP_CreateProcess(Dumper, 32768, 1, newNode, "dumper process", &pid);
225 #endif
226     if (code) ERROR_EXIT(code);
227
228     *taskId = newNode->taskID;
229
230 error_exit:
231     if (code)
232     {
233         if (statusPtr) deleteStatusNode(statusPtr);
234         FreeNode(newNode->taskID);      /*  failed to create LWP to do the dump. */
235     }
236
237     return(code);
238 }
239
240 STC_PerformRestore(acid, dumpSetName, arestores, taskID)
241      struct rx_call *acid;
242      char *dumpSetName;               /* not used */
243      tc_restoreArray *arestores;
244      afs_int32 *taskID;
245 {
246     struct dumpNode *newNode;
247     statusP statusPtr;
248     afs_int32 code = 0;
249 #ifdef AFS_PTHREAD_ENV
250     pthread_t pid;
251     pthread_attr_t tattr;
252     AFS_SIGSET_DECL;
253 #else
254     PROCESS pid;
255 #endif
256
257     extern int Restorer();
258     extern statusP createStatusNode();
259
260     if ( callPermitted(acid) == 0 )
261         return(TC_NOTPERMITTED);
262
263     /* should  verify parameter validity */
264
265     /* this creates a node in list, alots an id for it and prepares it for locking */
266     CreateNode(&newNode);
267
268     newNode->restores = (struct tc_restoreDesc *)
269         malloc (sizeof(struct tc_restoreDesc) * arestores->tc_restoreArray_len);
270     newNode->arraySize = arestores->tc_restoreArray_len;
271     CopyRestoreDesc(newNode->restores,arestores);
272     *taskID = newNode->taskID;
273
274     /* should log the intent */
275
276     /* create the status node */
277     statusPtr = createStatusNode();
278     if ( !statusPtr ) ERROR_EXIT(TC_INTERNALERROR);
279
280     lock_Status();
281     statusPtr->taskId     = newNode->taskID;
282     statusPtr->flags     &= ~STARTING;                     /* ok to examine */
283     statusPtr->lastPolled = time(0);
284     strncpy(statusPtr->taskName, "Restore", sizeof(statusPtr->taskName));
285     unlock_Status();
286
287     newNode->statusNodePtr = statusPtr;
288
289     /* create the LWP to do the real work behind the scenes */
290 #ifdef AFS_PTHREAD_ENV
291     code = pthread_attr_init(&tattr);
292     if (code) ERROR_EXIT(code);
293
294     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
295     if (code) ERROR_EXIT(code);
296
297     AFS_SIGSET_CLEAR();
298     code = pthread_create(&pid, &tattr, Restorer, newNode);
299     AFS_SIGSET_RESTORE();
300 #else
301     code = LWP_CreateProcess(Restorer, 65368, 1,newNode ,"restorer process", &pid);
302 #endif
303
304 error_exit:
305     if ( code )
306     {
307         if (statusPtr) deleteStatusNode(statusPtr);
308         FreeNode(newNode->taskID);                 /*  failed to create LWP to do the dump. */
309     }
310
311     return(code);
312 }
313
314 STC_ReadLabel(acid, label, taskId)
315      struct rx_call *acid;
316      struct tc_tapeLabel *label;
317      afs_uint32 *taskId;
318 {
319     afs_int32 code;
320
321     extern int ReadLabel();
322    
323 #ifdef xbsa
324     if (CONF_XBSA) return(TC_BADTASK);    /* ReadLabel does not apply if XBSA */
325 #endif
326
327     if ( callPermitted(acid) == 0 )
328         return(TC_NOTPERMITTED);
329
330     code = ReadLabel(label); /* Synchronous */
331     return code;
332 }
333
334 /* STC_RestoreDb
335  *      restore the backup database from tape
336  */
337
338 STC_RestoreDb(rxCall, taskId)
339      struct rx_call *rxCall;
340      afs_uint32 *taskId;
341 {
342 #ifdef AFS_PTHREAD_ENV
343     pthread_t pid;
344     pthread_attr_t tattr;
345     AFS_SIGSET_DECL;
346 #else
347     PROCESS pid;
348 #endif
349     statusP statusPtr;
350     afs_int32 code = 0;
351
352     extern afs_int32 restoreDbFromTape();
353     extern statusP createStatusNode();
354     extern afs_int32 allocTaskId();
355
356 #ifdef xbsa
357     if (CONF_XBSA) return(TC_BADTASK);    /* LabelTape does not apply if XBSA */
358 #endif
359
360     if ( callPermitted(rxCall) == 0 )
361         return(TC_NOTPERMITTED);
362
363     *taskId = allocTaskId();
364
365     /* create the status node */
366     statusPtr = createStatusNode();
367     if ( !statusPtr ) ERROR_EXIT(TC_INTERNALERROR);
368
369     lock_Status();
370     statusPtr->taskId     = *taskId;
371     statusPtr->flags     &= ~STARTING;                     /* ok to examine */
372     statusPtr->lastPolled = time(0);
373     strncpy(statusPtr->taskName, "RestoreDb", sizeof(statusPtr->taskName));
374     unlock_Status();
375
376 #ifdef AFS_PTHREAD_ENV
377     code = pthread_attr_init(&tattr);
378     if (code) ERROR_EXIT(code);
379
380     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
381     if (code) ERROR_EXIT(code);
382
383     AFS_SIGSET_CLEAR();
384     code = pthread_create(&pid, &tattr, restoreDbFromTape, (void *)*taskId);
385     AFS_SIGSET_RESTORE();
386 #else
387     code = LWP_CreateProcess(restoreDbFromTape, 32768, 1, *taskId, "Db restore", &pid);
388 #endif
389
390 error_exit:
391     if ( code )
392     {
393         if (statusPtr) deleteStatusNode(statusPtr);
394     }
395
396     return(code);
397 }
398
399 /* STC_SaveDb
400  *      restore the backup database from tape
401  */
402
403 STC_SaveDb(rxCall, archiveTime, taskId)
404      struct rx_call *rxCall;
405      Date   archiveTime;
406      afs_uint32 *taskId;
407 {
408 #ifdef AFS_PTHREAD_ENV
409     pthread_t pid;
410     pthread_attr_t tattr;
411     AFS_SIGSET_DECL;
412 #else
413     PROCESS pid;
414 #endif
415     statusP statusPtr;
416     afs_int32 code = 0;
417     struct saveDbIf *ptr;
418
419     extern afs_int32 saveDbToTape();
420     extern statusP createStatusNode();
421     extern afs_int32 allocTaskId();
422
423 #ifdef xbsa
424     if (CONF_XBSA) return(TC_BADTASK);    /* LabelTape does not apply if XBSA */
425 #endif
426
427     if ( callPermitted(rxCall) == 0 )
428         return(TC_NOTPERMITTED);
429
430     *taskId = allocTaskId();
431
432     ptr = (struct saveDbIf *) malloc(sizeof(struct saveDbIf));
433     if (!ptr) ERROR_EXIT(TC_NOMEMORY);
434     ptr->archiveTime = archiveTime;
435     ptr->taskId      = *taskId;
436
437     /* create the status node */
438     statusPtr = createStatusNode();
439     if ( !statusPtr ) ERROR_EXIT(TC_INTERNALERROR);
440
441     lock_Status();
442     statusPtr->taskId     = *taskId;
443     statusPtr->lastPolled = time(0);
444     statusPtr->flags     &= ~STARTING;                     /* ok to examine */
445     strncpy(statusPtr->taskName, "SaveDb", sizeof(statusPtr->taskName));
446     unlock_Status();
447
448     ptr->statusPtr = statusPtr;
449
450 #ifdef AFS_PTHREAD_ENV
451     code = pthread_attr_init(&tattr);
452     if (code) ERROR_EXIT(code);
453
454     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
455     if (code) ERROR_EXIT(code);
456
457     AFS_SIGSET_CLEAR();
458     code = pthread_create(&pid, &tattr, saveDbToTape, ptr);
459     AFS_SIGSET_RESTORE();
460 #else
461     code = LWP_CreateProcess(saveDbToTape, 32768, 1, ptr ,"Db save", &pid);
462 #endif
463
464 error_exit:
465     if ( code )
466     {
467         if (statusPtr) deleteStatusNode(statusPtr);
468         if (ptr) free(ptr);
469     }
470
471     return(code);
472 }
473         
474
475 /* STC_ScanDumps
476  *      read a dump (maybe more than one tape), and print out a summary
477  *      of its contents. If the flag is set, add to the database.
478  * entry:
479  *      addDbFlag - if set, the information will be added to the database
480  */
481
482 STC_ScanDumps(acid, addDbFlag, taskId)
483      struct rx_call *acid;
484      afs_int32 addDbFlag;
485      afs_uint32 *taskId;
486 {
487 #ifdef AFS_PTHREAD_ENV
488     pthread_t pid;
489     pthread_attr_t tattr;
490     AFS_SIGSET_DECL;
491 #else
492     PROCESS pid;
493 #endif
494     struct scanTapeIf *ptr;
495     statusP statusPtr;
496     afs_int32 code = 0;
497
498     extern afs_int32 ScanDumps();
499     extern afs_int32 allocTaskId();
500     extern statusP createStatusNode();
501
502 #ifdef xbsa
503     if (CONF_XBSA) return(TC_BADTASK);    /* ScanDumps does not apply if XBSA */
504 #endif
505
506     if ( callPermitted(acid) == 0 )
507         return(TC_NOTPERMITTED);
508
509     *taskId = allocTaskId();
510
511     ptr = (struct scanTapeIf *) malloc(sizeof(*ptr));
512     if (!ptr) ERROR_EXIT(TC_NOMEMORY);
513     ptr->addDbFlag = addDbFlag;
514     ptr->taskId = *taskId;
515
516     /* create the status node */
517     statusPtr = createStatusNode();
518     if ( !statusPtr ) ERROR_EXIT(TC_INTERNALERROR);
519
520     lock_Status();
521     statusPtr->taskId     = *taskId;
522     statusPtr->lastPolled = time(0);
523     statusPtr->flags     &= ~STARTING;                     /* ok to examine */
524     strncpy(statusPtr->taskName, "Scantape", sizeof(statusPtr->taskName));
525     unlock_Status();
526
527 #ifdef AFS_PTHREAD_ENV
528     code = pthread_attr_init(&tattr);
529     if (code) ERROR_EXIT(code);
530
531     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
532     if (code) ERROR_EXIT(code);
533
534     AFS_SIGSET_CLEAR();
535     code = pthread_create(&pid, &tattr, ScanDumps, ptr);
536     AFS_SIGSET_RESTORE();
537 #else
538     code = LWP_CreateProcess(ScanDumps,32768, 1, ptr, "scandump process",&pid);
539 #endif
540
541 error_exit:
542     if ( code )
543     {
544         if (statusPtr) deleteStatusNode(statusPtr);
545         if (ptr) free(ptr);
546     }
547
548     return code;
549 }
550
551 /* STC_TCInfo
552  *      return information about the tape coordinator. Currently this
553  *      is just the version number of the interface
554  */
555
556 STC_TCInfo(acid, tciptr)
557      struct rx_call *acid;
558      struct tc_tcInfo *tciptr;
559 {
560     if ( callPermitted(acid) == 0 )
561         return(TC_NOTPERMITTED);
562
563     tciptr->tcVersion = CUR_BUTC_VERSION;
564     return(0);
565 }
566
567 /* STC_DeleteDump
568  */
569 STC_DeleteDump(acid, dumpID, taskId)
570    struct rx_call *acid;
571    afs_int32 *taskId;
572 {
573    struct deleteDumpIf *ptr=0;
574    statusP statusPtr=0;
575    afs_int32 code = TC_BADTASK;   /* If not compiled -Dxbsa then fail */
576 #ifdef AFS_PTHREAD_ENV
577     pthread_t pid;
578     pthread_attr_t tattr;
579     AFS_SIGSET_DECL;
580 #else
581     PROCESS pid;
582 #endif
583     extern afs_int32 DeleteDump();
584     extern statusP createStatusNode();
585     extern afs_int32 allocTaskId();
586
587    *taskId = 0;
588    if (!CONF_XBSA) return(TC_BADTASK);   /* Only do if butc is started as XBSA */
589
590 #ifdef xbsa
591    code = 0;
592    if ( callPermitted(acid) == 0 )
593       return(TC_NOTPERMITTED);
594
595    ptr = (struct deleteDumpIf *) malloc(sizeof(*ptr));
596    if (!ptr) ERROR_EXIT(TC_NOMEMORY);
597
598    *taskId = allocTaskId();
599    ptr->dumpID = dumpID;
600    ptr->taskId = *taskId;
601
602    statusPtr = createStatusNode();
603    if (!statusPtr) ERROR_EXIT(TC_INTERNALERROR);
604
605    lock_Status();
606    statusPtr->taskId     = *taskId;
607    statusPtr->lastPolled = time(0);
608    statusPtr->flags     &= ~STARTING;
609    strncpy(statusPtr->taskName, "DeleteDump", sizeof(statusPtr->taskName));
610    unlock_Status();
611
612 #ifdef AFS_PTHREAD_ENV
613    code = pthread_attr_init(&tattr);
614    if (code) ERROR_EXIT(code);
615
616    code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
617    if (code) ERROR_EXIT(code);
618
619    AFS_SIGSET_CLEAR();
620    code = pthread_create(&pid, &tattr, DeleteDump, ptr);
621    AFS_SIGSET_RESTORE();
622 #else
623    code = LWP_CreateProcess(DeleteDump, 32768, 1, ptr, "deletedump process", &pid);
624 #endif
625
626 error_exit:
627    if (code) {
628       if (statusPtr) deleteStatusNode(statusPtr);
629       if (ptr) free(ptr);
630    }
631 #endif /* xbsa */
632
633    return(code);
634 }
635
636 /* -----------------------------
637  * misc. routines
638  * -----------------------------
639  */
640
641 static
642 CopyDumpDesc(toDump, fromDump)
643      struct tc_dumpDesc *toDump;
644      tc_dumpArray *fromDump;
645 {
646     struct tc_dumpDesc *toPtr, *fromPtr;
647     int i;
648
649     toPtr   = toDump;
650     fromPtr = fromDump->tc_dumpArray_val;
651     for(i = 0 ; i < fromDump->tc_dumpArray_len; i++)
652     {
653         toPtr->vid       = fromPtr->vid;
654         toPtr->vtype     = fromPtr->vtype;
655         toPtr->partition = fromPtr->partition;
656         toPtr->date      = fromPtr->date;
657         toPtr->cloneDate = fromPtr->cloneDate;
658         toPtr->hostAddr  = fromPtr->hostAddr;
659         strcpy(toPtr->name,fromPtr->name);
660         fromPtr++;
661         toPtr++;
662     }
663     return 0;
664 }
665
666
667 static CopyRestoreDesc(toRestore, fromRestore)
668 struct tc_restoreDesc *toRestore;
669 tc_restoreArray *fromRestore;
670 {
671     struct tc_restoreDesc *toPtr, *fromPtr;
672     int i;
673     
674     toPtr = toRestore ;
675     fromPtr = fromRestore->tc_restoreArray_val;
676     for(i = 0 ; i < fromRestore->tc_restoreArray_len ; i++){
677         toPtr->flags         = fromPtr->flags;
678         toPtr->position      = fromPtr->position;
679         strcpy(toPtr->tapeName,fromPtr->tapeName);
680         toPtr->dbDumpId      = fromPtr->dbDumpId;
681         toPtr->initialDumpId = fromPtr->initialDumpId;
682         toPtr->origVid       = fromPtr->origVid;
683         toPtr->vid           = fromPtr->vid;
684         toPtr->partition     = fromPtr->partition;
685         toPtr->dumpLevel     = fromPtr->dumpLevel;
686         toPtr->hostAddr      = fromPtr->hostAddr;
687         strcpy(toPtr->newName,fromPtr->newName);
688         strcpy(toPtr->oldName, fromPtr->oldName);
689         fromPtr++;
690         toPtr++;
691
692     }
693     return 0;
694 }
695
696 static CopyTapeSetDesc(toPtr,fromPtr)
697 struct tc_tapeSet *toPtr,*fromPtr;
698 {
699     
700     toPtr->id = fromPtr->id;
701     toPtr->maxTapes = fromPtr->maxTapes;
702     toPtr->a = fromPtr->a;
703     toPtr->b = fromPtr->b;
704     strcpy(toPtr->tapeServer,fromPtr->tapeServer);
705     strcpy(toPtr->format,fromPtr->format);
706
707     toPtr->expDate = fromPtr->expDate;
708     toPtr->expType = fromPtr->expType;
709     return 0;
710 }