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