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