libadmin-vsprocs-use-volint-prototyping-20020702
[openafs.git] / src / libadmin / vos / vsprocs.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 /*
11  * This file is a reimplementation of volser/vsproc.s.  Every attempt
12  * has been made to keep it as similar as possible to aid comprehension
13  * of the code.  For most functions, two parameters have been added
14  * the cell handle, and a status variable.  For those functions that
15  * require one, a server handle may also be added.
16  *
17  * Other changes were made to provide thread safe functions and
18  * eliminate the practice of reporting errors to STDOUT.
19  */
20
21 #include <afsconfig.h>
22 #include <afs/param.h>
23
24 RCSID("$Header$");
25
26 #include "vsprocs.h"
27 #include "vosutils.h"
28 #include "lockprocs.h"
29 #include "../adminutil/afs_AdminInternal.h"
30 #include <afs/afs_AdminErrors.h>
31 #include "afs_vosAdmin.h"
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #else
35 #ifdef HAVE_STRINGS_H
36 #include <strings.h>
37 #endif
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #ifdef AFS_NT40_ENV
43 #include <io.h>
44 #endif
45
46 static afs_int32 GroupEntries();
47
48 struct release {
49   afs_int32 time;
50   afs_int32 vldbEntryIndex;
51 };
52
53 static struct rx_connection *UV_Bind(
54   afs_cell_handle_p cellHandle,
55   afs_int32 aserver,
56   afs_int32 port)
57 {
58     return rx_GetCachedConnection(htonl(aserver), htons(port), VOLSERVICE_ID,
59                                   cellHandle->tokens->afs_sc[cellHandle->tokens->sc_index],
60                                   cellHandle->tokens->sc_index);
61 }
62
63
64 /* if <okvol> is allright(indicated by beibg able to
65  * start a transaction, delete the <delvol> */
66 static afs_int32 CheckAndDeleteVolume(
67   struct rx_connection *aconn,
68   afs_int32 apart,
69   afs_int32 okvol,
70   afs_int32 delvol)
71 {
72     afs_int32 error,code,tid,rcode;
73
74     error = 0;
75     code = 0;
76
77     if(okvol == 0) {
78         code = AFSVolTransCreate(aconn, delvol, apart, ITOffline,&tid);
79         if(!error && code) error = code;
80         code = AFSVolDeleteVolume(aconn,tid);
81         if(!error && code) error = code;
82         code = AFSVolEndTrans(aconn,tid, &rcode);
83         if(!code) code = rcode;
84         if(!error && code) error = code;
85         return error;
86     }
87     else {
88         code = AFSVolTransCreate(aconn, okvol, apart, ITOffline,&tid);
89         if(!code) {
90             code = AFSVolEndTrans(aconn,tid, &rcode);
91             if(!code) code = rcode;
92             if(!error && code) error = code;
93             code = AFSVolTransCreate(aconn, delvol, apart, ITOffline,&tid);
94             if(!error && code) error = code;
95             code = AFSVolDeleteVolume(aconn,tid);
96             if(!error && code) error = code;
97             code = AFSVolEndTrans(aconn,tid, &rcode);
98             if(!code) code = rcode;
99             if(!error && code) error = code;
100         }
101         else 
102             error = code;
103         return error;
104     }
105 }
106
107 /* forcibly remove a volume.  Very dangerous call */
108 int UV_NukeVolume(
109   afs_cell_handle_p cellHandle,
110   struct rx_connection *server,
111   unsigned int partition,
112   unsigned int volumeId,
113   afs_status_p st)
114 {
115     int rc = 0;
116     afs_status_t tst = 0;
117
118     tst = AFSVolNukeVolume(server, partition, volumeId);
119
120     if (!tst) {
121         rc = 1;
122     }
123
124     if (st != NULL) {
125         *st = tst;
126     }
127     return rc;
128 }
129
130 /* create a volume, given a server, partition number, volume name --> sends
131 * back new vol id in <anewid>*/
132 int UV_CreateVolume(
133   afs_cell_handle_p cellHandle,
134   struct rx_connection *server,
135   unsigned int partition,
136   const char *volumeName,
137   unsigned int quota,
138   unsigned int *volumeId,
139   afs_status_p st)
140 {
141     int rc = 0;
142     afs_status_t tst = 0;
143     afs_int32 tid = 0;
144     afs_int32 rcode;
145     struct nvldbentry entry;
146     struct volintInfo tstatus;
147
148     memset (&tstatus, 0, sizeof(tstatus));
149     tstatus.dayUse = -1;
150     tstatus.maxquota = quota;
151
152     /* next the next 3 available ids from the VLDB */
153     tst = ubik_Call(VL_GetNewVolumeId, cellHandle->vos, 0, 3, volumeId);
154     if (tst) {
155         goto fail_UV_CreateVolume;
156     }
157
158     tst = AFSVolCreateVolume(server, partition, volumeName, volser_RW, 0,
159                              volumeId, &tid);
160     if (tst) {
161         goto fail_UV_CreateVolume;
162     }
163
164     AFSVolSetInfo(server, tid, &tstatus);
165
166     tst = AFSVolSetFlags(server, tid, 0);
167     if (tst) {
168         goto fail_UV_CreateVolume;
169     }
170
171     /* set up the vldb entry for this volume */
172     strncpy(entry.name, volumeName,VOLSER_OLDMAXVOLNAME);
173     entry.nServers = 1;
174     entry.serverNumber[0] = ntohl(rx_HostOf(rx_PeerOf(server)));
175     entry.serverPartition[0] = partition;
176     entry.flags = RW_EXISTS;
177     entry.serverFlags[0] = ITSRWVOL;
178     entry.volumeId[RWVOL] = *volumeId;
179     entry.volumeId[ROVOL] = *volumeId + 1;
180     entry.volumeId[BACKVOL] = *volumeId + 2;
181     entry.cloneId = 0;
182
183     if (!VLDB_CreateEntry(cellHandle, &entry, &tst)) {
184         AFSVolDeleteVolume(server,tid);
185         goto fail_UV_CreateVolume;
186     }
187
188     tst = AFSVolEndTrans(server, tid, &rcode);
189     tid = 0;
190     if (tst) {
191         goto fail_UV_CreateVolume;
192     }
193     rc = 1;
194
195 fail_UV_CreateVolume:
196
197     if (tid != 0) {
198         AFSVolEndTrans(server, tid, &rcode);
199     }
200
201     if (st != NULL) {
202         *st = tst;
203     }
204     return rc;
205 }
206
207
208 /* Delete the volume <volid>on <aserver> <apart>
209  * the physical entry gets removed from the vldb only if the ref count 
210  * becomes zero
211  */
212 int UV_DeleteVolume(
213   afs_cell_handle_p cellHandle,
214   struct rx_connection *server,
215   unsigned int partition,
216   unsigned int volumeId,
217   afs_status_p st)
218 {
219     int rc = 0;
220     afs_status_t tst = 0;
221     afs_status_t temp = 0;
222     int serverAddr = ntohl(rx_HostOf(rx_PeerOf(server)));
223
224     afs_int32 ttid = 0;
225     afs_int32 rcode;
226     struct nvldbentry entry;
227     int islocked = 0;
228     afs_int32 avoltype = -1, vtype;
229     int notondisk = 0, notinvldb = 0;
230
231     /* Find and read the VLDB entry for this volume */
232     tst = ubik_Call(VL_SetLock, cellHandle->vos, 0, volumeId, avoltype,
233                     VLOP_DELETE);
234     if (tst) {
235         if (tst != VL_NOENT) {
236             goto fail_UV_DeleteVolume;
237         }
238         notinvldb = 1;
239     } else {
240        islocked = 1;
241
242        if (!VLDB_GetEntryByID(cellHandle, volumeId, avoltype, &entry, &tst)) {
243             goto fail_UV_DeleteVolume;
244        }
245
246     }
247
248     /* Whether volume is in the VLDB or not. Delete the volume on disk */
249     tst = AFSVolTransCreate(server, volumeId, partition, ITOffline, &ttid);
250     if (tst) {
251        if (tst == VNOVOL) {
252           notondisk = 1;
253        } else {
254            goto fail_UV_DeleteVolume;
255        }
256     }
257     else {
258        tst = AFSVolDeleteVolume(server, ttid);
259        if (tst) {
260            goto fail_UV_DeleteVolume;
261        }
262        tst = AFSVolEndTrans(server, ttid, &rcode);
263        tst = (tst ? tst : rcode);
264        ttid = 0;
265        if (tst) {
266            goto fail_UV_DeleteVolume;
267        }
268     }
269
270     if (notinvldb) {
271         goto fail_UV_DeleteVolume;
272     }
273
274     if (volumeId == entry.volumeId[BACKVOL]) {
275         if ( !(entry.flags & BACK_EXISTS) || 
276              !Lp_Match(cellHandle, &entry, serverAddr, partition, &tst)) {
277            notinvldb = 2;
278            goto fail_UV_DeleteVolume;
279         }
280
281         entry.flags &= ~BACK_EXISTS;
282         vtype = BACKVOL;
283     }
284
285     else if (volumeId == entry.volumeId[ROVOL]) {
286         if (!Lp_ROMatch(cellHandle, &entry, serverAddr, partition, &tst)) {
287            notinvldb = 2;
288            goto fail_UV_DeleteVolume;
289         }
290         
291         Lp_SetROValue(cellHandle, &entry, serverAddr, partition, 0, 0);
292         entry.nServers--;
293         if (!Lp_ROMatch(cellHandle, &entry, 0, 0, &tst)) {
294             entry.flags &= ~RO_EXISTS;
295         }
296         vtype = ROVOL;
297     }
298
299     else if (volumeId == entry.volumeId[RWVOL]) {
300         if (!(entry.flags & RW_EXISTS) || 
301             !Lp_Match(cellHandle, &entry, serverAddr, partition, &tst)) {
302            notinvldb = 2;
303            goto fail_UV_DeleteVolume;
304         }
305         
306         /* Delete backup if it exists */
307         tst = AFSVolTransCreate(server, entry.volumeId[BACKVOL], partition,
308                                  ITOffline, &ttid);
309         if (!tst) {
310            tst = AFSVolDeleteVolume(server, ttid);
311            if (tst) {
312               goto fail_UV_DeleteVolume;
313            }
314            tst = AFSVolEndTrans(server, ttid, &rcode);
315            ttid = 0;
316            tst = (tst ? tst : rcode);
317            if (tst) {
318               goto fail_UV_DeleteVolume;
319            }
320         }
321
322         Lp_SetRWValue(cellHandle, &entry, serverAddr, partition, 0L, 0L);
323         entry.nServers--;
324         entry.flags &= ~(BACK_EXISTS | RW_EXISTS);
325         vtype = RWVOL;
326
327     }
328
329     else {
330        notinvldb = 2;         /* Not found on this server and partition */
331        goto fail_UV_DeleteVolume;
332     }
333
334     if ((entry.nServers <= 0) || !(entry.flags & (RO_EXISTS | RW_EXISTS))) {
335        tst = ubik_Call(VL_DeleteEntry, cellHandle->vos, 0, volumeId, vtype);
336        if (tst) {
337           goto fail_UV_DeleteVolume;
338        }
339     } else {
340        if (!VLDB_ReplaceEntry(cellHandle, volumeId, vtype, &entry,
341                         (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP), 
342                         &tst)) {
343           goto fail_UV_DeleteVolume;
344        }
345     }
346     islocked = 0;
347     rc = 1;
348
349 fail_UV_DeleteVolume:
350
351     if (notondisk && notinvldb) {
352        if (!tst) tst = ADMVOSVOLUMENOEXIST;
353     }
354
355     if (ttid) {
356         temp = AFSVolEndTrans(server, ttid, &rcode);
357         temp = (temp ? temp : rcode);
358         if (temp) {
359             if (!tst) tst = temp;
360         }
361     }
362
363     if (islocked) {
364         temp = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0, volumeId, -1, 
365                          (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
366         if (temp) {
367             if (!tst) tst = temp;
368         }
369     }
370
371     if (st != NULL) {
372         *st = tst;
373     }
374     return rc;
375 }
376
377 #define ONERR(ec, es, ep) if (ec) { fprintf(STDERR, (es), (ep)); error = (ec); goto mfail; }
378
379 /* Move volume <afromvol> on <afromserver> <afrompart> to <atoserver>
380  * <atopart>. The operation is almost idempotent 
381  */
382
383 int UV_MoveVolume(
384   afs_cell_handle_p cellHandle,
385   afs_int32 afromvol,
386   afs_int32 afromserver,
387   afs_int32 afrompart,
388   afs_int32 atoserver,
389   afs_int32 atopart,
390   afs_status_p st)
391 {
392     int rc = 0;
393     afs_status_t tst = 0;
394     afs_status_t etst = 0;
395     struct rx_connection *toconn, *fromconn ;
396     afs_int32                fromtid, totid, clonetid;
397     char                 vname[64];
398     char                 *volName = 0;
399     char                 tmpName[VOLSER_MAXVOLNAME +1];
400     afs_int32                rcode;
401     afs_int32                fromDate;
402     struct restoreCookie cookie;
403     afs_int32                newVol, volid, backupId;
404     struct volser_status tstatus;
405     struct destServer    destination;
406
407     struct nvldbentry    entry;
408     int                  islocked, pntg;
409     afs_int32                error;
410     int                  same;
411     afs_int32            store_flags;
412
413 #ifdef  ENABLE_BUGFIX_1165
414     volEntries volumeInfo;
415     struct volintInfo *infop = 0;
416 #endif
417
418     islocked = 0;
419     fromconn = (struct rx_connection *)0;
420     toconn   = (struct rx_connection *)0;
421     fromtid  = 0;
422     totid    = 0;
423     clonetid = 0;
424     error    = 0;
425     volid    = 0;
426     pntg     = 0;
427     backupId = 0;
428     newVol   = 0;
429
430     if (!VLDB_GetEntryByID(cellHandle, afromvol, -1, &entry, &tst)) {
431         goto fail_UV_MoveVolume;
432     }
433
434     if (entry.volumeId[RWVOL] != afromvol)
435     {
436         tst = ADMVOSVOLUMEMOVERWONLY;
437         goto fail_UV_MoveVolume;
438     }
439
440     tst = ubik_Call(VL_SetLock, cellHandle->vos, 0,afromvol, RWVOL, VLOP_MOVE);
441     if (tst) {
442         goto fail_UV_MoveVolume;
443     }
444     islocked = 1;
445
446     if (!VLDB_GetEntryByID(cellHandle, afromvol, RWVOL, &entry, &tst)) {
447         goto fail_UV_MoveVolume;
448     }
449
450     backupId = entry.volumeId[BACKVOL];
451
452     if ( !Lp_Match(cellHandle, &entry, afromserver, afrompart, &tst) )
453     {
454         /* the from server and partition do not exist in the vldb entry corresponding to volid */
455         if ( !Lp_Match(cellHandle, &entry, atoserver, atopart, &tst) ) 
456         {
457             /* the to server and partition do not exist in the vldb entry corresponding to volid */
458             tst = ubik_Call(VL_ReleaseLock, cellHandle->vos, 0, afromvol, -1,
459                               (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
460             if (tst) {
461                 goto fail_UV_MoveVolume;
462             }
463             tst = VOLSERVOLMOVED;
464             goto fail_UV_MoveVolume;
465         }
466
467         /* delete the volume afromvol on src_server */
468         /* from-info does not exist but to-info does =>
469          * we have already done the move, but the volume
470          * may still be existing physically on from fileserver
471          */
472         fromconn = UV_Bind(cellHandle, afromserver, AFSCONF_VOLUMEPORT);
473         fromtid = 0;
474         pntg = 1;
475
476         tst = AFSVolTransCreate(fromconn, afromvol, afrompart, ITOffline, &fromtid);
477         if (!tst) 
478         {   /* volume exists - delete it */
479             tst = AFSVolSetFlags(fromconn, fromtid, VTDeleteOnSalvage | VTOutOfService);
480             if  (tst) {
481                 goto fail_UV_MoveVolume;
482             }
483
484             tst = AFSVolDeleteVolume(fromconn,fromtid);
485             if  (tst) {
486                 goto fail_UV_MoveVolume;
487             }
488             
489             tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
490             fromtid = 0;
491             if (!tst) tst = rcode;
492             if  (tst) {
493                 goto fail_UV_MoveVolume;
494             }
495         }
496
497         /*delete the backup volume now */
498         fromtid = 0;
499         tst = AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline, &fromtid);
500         if (!tst) 
501         {   /* backup volume exists - delete it */
502             tst = AFSVolSetFlags(fromconn, fromtid, VTDeleteOnSalvage | VTOutOfService);
503             if  (tst) {
504                 goto fail_UV_MoveVolume;
505             }
506
507             tst = AFSVolDeleteVolume(fromconn,fromtid);
508             if  (tst) {
509                 goto fail_UV_MoveVolume;
510             }
511
512             tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
513             fromtid = 0;
514             if (!tst) tst = rcode;
515             if  (tst) {
516                 goto fail_UV_MoveVolume;
517             }
518         }
519
520         fromtid = 0;
521         error = 0;
522         goto fail_UV_MoveVolume;
523     }
524
525     /* From-info matches the vldb info about volid,
526      * its ok start the move operation, the backup volume 
527      * on the old site is deleted in the process 
528      */
529     if (afrompart == atopart) 
530     {
531         if (!VLDB_IsSameAddrs(cellHandle, afromserver, atoserver, &same, &tst)) {
532             goto fail_UV_MoveVolume;
533         }
534         if (same) {
535             tst = VOLSERVOLMOVED;
536             goto fail_UV_MoveVolume;
537         }
538     }
539
540     pntg = 1;
541     toconn   = UV_Bind(cellHandle, atoserver,   AFSCONF_VOLUMEPORT); /* get connections to the servers */
542     fromconn = UV_Bind(cellHandle, afromserver, AFSCONF_VOLUMEPORT);
543     fromtid = totid = 0;        /* initialize to uncreated */
544
545     /* ***
546      * clone the read/write volume locally.
547      * ***/
548
549     tst = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
550     if (tst) {
551         goto fail_UV_MoveVolume;
552     }
553
554     /* Get a clone id */
555     newVol = 0;
556     tst = ubik_Call (VL_GetNewVolumeId, cellHandle->vos, 0, 1, &newVol);
557     if (tst) {
558         goto fail_UV_MoveVolume;
559     }
560
561     /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
562     strcpy(vname, "move-clone-temp");
563     tst = AFSVolClone(fromconn, fromtid, 0,readonlyVolume, vname, &newVol);
564     if (tst) {
565         goto fail_UV_MoveVolume;
566     }
567
568     /* lookup the name of the volume we just cloned */
569     volid = afromvol;
570     tst = AFSVolGetName(fromconn, fromtid, &volName);
571     if (tst) {
572         goto fail_UV_MoveVolume;
573     }
574
575     rcode = 0;
576     tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
577     fromtid = 0;
578     if (!tst) tst = rcode;
579     if (tst) {
580         goto fail_UV_MoveVolume;
581     }
582
583     /* ***
584      * Create the destination volume
585      * ***/
586
587     tst = AFSVolTransCreate (fromconn, newVol, afrompart, ITOffline, &clonetid);
588     if (tst) {
589         goto fail_UV_MoveVolume;
590     }
591     tst = AFSVolSetFlags (fromconn, clonetid, VTDeleteOnSalvage|VTOutOfService); /*redundant */
592     if (tst) {
593         goto fail_UV_MoveVolume;
594     }
595
596     /* remember time from which we've dumped the volume */
597     tst = AFSVolGetStatus (fromconn, clonetid, &tstatus);
598     if (tst) {
599         goto fail_UV_MoveVolume;
600     }
601
602     fromDate = tstatus.creationDate-CLOCKSKEW;
603
604 #ifdef  ENABLE_BUGFIX_1165
605     /*
606      * Get the internal volume state from the source volume. We'll use such info (i.e. dayUse)
607      * to copy it to the new volume (via AFSSetInfo later on) so that when we move volumes we
608      * don't use this information...
609      */
610     volumeInfo.volEntries_val = (volintInfo *)0;/*this hints the stub to allocate space*/
611     volumeInfo.volEntries_len = 0;
612     tst = AFSVolListOneVolume(fromconn, afrompart, afromvol, &volumeInfo);
613     if (tst) {
614         goto fail_UV_MoveVolume;
615     }
616
617     infop = (volintInfo *) volumeInfo.volEntries_val;
618     infop->maxquota = -1;                       /* Else it will replace the default quota */
619 #endif
620
621     /* create a volume on the target machine */
622     volid = afromvol;
623     tst = AFSVolTransCreate (toconn, volid, atopart, ITOffline, &totid);
624     if (!tst) 
625     {   /*delete the existing volume */
626
627         tst = AFSVolDeleteVolume(toconn, totid);
628         if (tst) {
629             goto fail_UV_MoveVolume;
630         }
631         
632         tst = AFSVolEndTrans(toconn, totid, &rcode);
633         totid = 0;
634         if (!tst) tst = rcode;
635         if (tst) {
636             goto fail_UV_MoveVolume;
637         }
638
639     }
640
641     tst = AFSVolCreateVolume (toconn, atopart, volName, volser_RW, volid, &volid, &totid);
642     if (tst) {
643         goto fail_UV_MoveVolume;
644     }
645
646     strncpy(tmpName, volName, VOLSER_OLDMAXVOLNAME);
647     free(volName);
648     volName = (char *) 0;
649
650     tst = AFSVolSetFlags (toconn, totid, (VTDeleteOnSalvage | VTOutOfService));
651     if (tst) {
652         goto fail_UV_MoveVolume;
653     }
654
655     /***
656      * Now dump the clone to the new volume
657      ***/
658
659     destination.destHost = atoserver;
660     destination.destPort = AFSCONF_VOLUMEPORT;
661     destination.destSSID = 1;
662
663     /* Copy the clone to the new volume */
664     strncpy(cookie.name,tmpName,VOLSER_OLDMAXVOLNAME);
665     cookie.type   = RWVOL;
666     cookie.parent = entry.volumeId[RWVOL];
667     cookie.clone  = 0;
668     tst = AFSVolForward(fromconn, clonetid, 0, &destination, totid, &cookie);
669     if (tst) {
670         goto fail_UV_MoveVolume;
671     }
672
673     tst = AFSVolEndTrans(fromconn, clonetid, &rcode);
674     if (!tst) tst = rcode;
675     clonetid = 0;
676     if (tst) {
677         goto fail_UV_MoveVolume;
678     }
679
680     /* ***
681      * reattach to the main-line volume, and incrementally dump it.
682      * ***/
683
684     tst = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
685     if (tst) {
686         goto fail_UV_MoveVolume;
687     }
688
689     /* now do the incremental */
690     tst = AFSVolForward(fromconn, fromtid, fromDate, &destination, totid,&cookie);
691     if (tst) {
692         goto fail_UV_MoveVolume;
693     }
694
695     /* now adjust the flags so that the new volume becomes official */
696     tst = AFSVolSetFlags(fromconn, fromtid, VTOutOfService);
697     if (tst) {
698         goto fail_UV_MoveVolume;
699     }
700
701     tst = AFSVolSetFlags(toconn, totid, 0);
702     if (tst) {
703         goto fail_UV_MoveVolume;
704     }
705
706 #ifdef  ENABLE_BUGFIX_1165
707     tst = AFSVolSetInfo(toconn, totid, infop);
708     if (tst) {
709         goto fail_UV_MoveVolume;
710     }
711 #endif
712
713     /* put new volume online */
714     tst = AFSVolEndTrans(toconn, totid, &rcode);
715     totid = 0;
716     if (!tst) tst = rcode;
717     if (tst) {
718         goto fail_UV_MoveVolume;
719     }
720
721     Lp_SetRWValue(cellHandle, &entry, afromserver, afrompart,
722                   atoserver, atopart);
723     store_flags = entry.flags;
724     entry.flags &= ~BACK_EXISTS;
725
726     if (!VLDB_ReplaceEntry(cellHandle, afromvol, -1, &entry, 
727                            (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP),
728                            &tst)) {
729         goto fail_UV_MoveVolume;
730     }
731     entry.flags = store_flags;
732     islocked=0;
733
734     if (atoserver != afromserver) 
735     {
736         /* set forwarding pointer for moved volumes */
737         tst = AFSVolSetForwarding(fromconn, fromtid, htonl(atoserver));
738         if (tst) {
739             goto fail_UV_MoveVolume;
740         }
741     }
742
743     tst = AFSVolDeleteVolume(fromconn,fromtid); /* zap original volume */
744     if (tst) {
745         goto fail_UV_MoveVolume;
746     }
747     
748     tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
749     fromtid = 0;
750     if (!tst) tst = rcode;
751     if (tst) {
752         goto fail_UV_MoveVolume;
753     }
754
755     /* Delete the backup volume on the original site */
756     tst = AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline, &fromtid);
757     if (!tst) 
758     {
759         tst = AFSVolSetFlags(fromconn, fromtid, VTDeleteOnSalvage | VTOutOfService);
760         if (tst) {
761             goto fail_UV_MoveVolume;
762         }
763
764         tst = AFSVolDeleteVolume(fromconn,fromtid);
765         if (tst) {
766             goto fail_UV_MoveVolume;
767         }
768         
769         tst = AFSVolEndTrans(fromconn,fromtid, &rcode);
770         fromtid = 0;
771         if (!tst) tst = rcode;
772         if (tst) {
773             goto fail_UV_MoveVolume;
774         }
775
776     }
777     else tst = 0;               /* no backup volume? that's okay */
778
779     fromtid = 0;
780
781     tst = AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline, &clonetid);
782     if (tst) {
783         goto fail_UV_MoveVolume;
784     }
785
786     /* now delete the clone */
787
788     tst = AFSVolDeleteVolume(fromconn, clonetid);
789     if (tst) {
790         goto fail_UV_MoveVolume;
791     }
792     
793     tst = AFSVolEndTrans(fromconn, clonetid, &rcode);
794     if (!tst) tst = rcode;
795     clonetid = 0;
796     if (tst) {
797         goto fail_UV_MoveVolume;
798     }
799
800     /* fall through */
801     /* END OF MOVE */
802
803     /* normal cleanup code */
804
805     if (islocked)
806     {
807         etst = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0, afromvol, -1, 
808                           (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
809         if (etst) 
810         {
811             if (!tst) tst = etst;
812         }
813     }
814     
815     if (fromtid) 
816     {
817         etst = AFSVolEndTrans(fromconn, fromtid, &rcode);
818         if (etst || rcode)
819         {
820             if (!tst) tst = (etst ? etst : rcode);
821         }
822     }
823
824     if (clonetid) 
825     {
826         etst = AFSVolEndTrans(fromconn, clonetid, &rcode);
827         if (etst || rcode) 
828         {
829             if (!tst) tst = (etst ? etst : rcode);
830         }
831     }
832
833     if (totid) 
834     {
835         etst = AFSVolEndTrans(toconn, totid, &rcode);
836         if (etst) 
837         {
838             if (!tst) tst = (etst ? etst : rcode);
839         }
840     }
841     if (volName) free(volName);
842 #ifdef  ENABLE_BUGFIX_1165
843     if (infop)   free(infop);
844 #endif
845     if (fromconn) rx_ReleaseCachedConnection(fromconn);
846     if (toconn)   rx_ReleaseCachedConnection(toconn);
847
848     rc = 1;
849     if (st != NULL) {
850         *st = tst;
851     }
852     return rc;
853
854     /* come here only when the sky falls */
855
856 fail_UV_MoveVolume:
857
858     /* unlock VLDB entry */
859     if (islocked)
860         ubik_Call(VL_ReleaseLock, cellHandle->vos, 0, afromvol, -1,
861                   (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
862
863     if (clonetid) AFSVolEndTrans(fromconn, clonetid, &rcode);
864     if (totid)    AFSVolEndTrans(toconn, totid, &rcode);
865     if (fromtid)
866     {  /* put it on-line */
867         AFSVolSetFlags(fromconn,fromtid,0);
868         AFSVolEndTrans(fromconn, fromtid, &rcode);
869     }
870
871     if (!VLDB_GetEntryByID (cellHandle, afromvol, -1, &entry, &tst)) {
872         goto done;
873     }
874
875     /* Delete either the volume on the source location or the target location. 
876      * If the vldb entry still points to the source location, then we know the
877      * volume move didn't finish so we remove the volume from the target 
878      * location. Otherwise, we remove the volume from the source location.
879      */
880     if (Lp_Match(cellHandle, &entry, afromserver,afrompart,&tst)) {  /* didn't move - delete target volume */
881
882         if (volid && toconn) {
883             tst=AFSVolTransCreate(toconn,volid,atopart, ITOffline,&totid);
884             if (!tst) {
885                 AFSVolSetFlags(toconn,totid, VTDeleteOnSalvage | VTOutOfService);
886                 AFSVolDeleteVolume(toconn,totid);
887                 AFSVolEndTrans(toconn,totid,&rcode);
888             }
889         }
890
891         /* put source volume on-line */
892         if (fromconn) {
893             tst=AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
894             if (!tst) {
895                 AFSVolSetFlags(fromconn,fromtid,0);
896                 AFSVolEndTrans(fromconn,fromtid,&rcode);
897             }
898         }
899     }
900     else {      /* yep, move complete */
901         /* delete backup volume */
902         if (fromconn) {
903             tst=AFSVolTransCreate (fromconn,backupId,afrompart, ITOffline,&fromtid);
904             if (!tst) {
905                 AFSVolSetFlags(fromconn,fromtid, VTDeleteOnSalvage | VTOutOfService);
906                 AFSVolDeleteVolume(fromconn,fromtid);
907                 AFSVolEndTrans(fromconn,fromtid,&rcode);
908             }
909
910             /* delete source volume */
911             tst=AFSVolTransCreate (fromconn, afromvol, afrompart, ITBusy, &fromtid);
912             if (!tst) {
913                 AFSVolSetFlags(fromconn,fromtid, VTDeleteOnSalvage | VTOutOfService);
914                 if (atoserver != afromserver)
915                     AFSVolSetForwarding(fromconn,fromtid,htonl(atoserver));
916                 AFSVolDeleteVolume(fromconn,fromtid);
917                 AFSVolEndTrans(fromconn,fromtid,&rcode);
918             }
919         }
920     }
921
922     /* common cleanup - delete local clone */
923     if (newVol) {
924         tst = AFSVolTransCreate (fromconn, newVol, afrompart, ITOffline, &clonetid);
925         if (!tst) {
926             AFSVolDeleteVolume(fromconn,clonetid);
927             AFSVolEndTrans(fromconn,clonetid,&rcode);
928         }
929     }
930
931     /* unlock VLDB entry */
932     ubik_Call (VL_ReleaseLock, cellHandle->vos, 0, afromvol, -1,
933                (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
934
935 done:   /* routine cleanup */
936     if (volName)  free(volName);
937 #ifdef  ENABLE_BUGFIX_1165
938     if (infop)    free(infop);
939 #endif
940     if (fromconn) rx_ReleaseCachedConnection(fromconn);
941     if (toconn)   rx_ReleaseCachedConnection(toconn);
942
943     if (st != NULL) {
944         *st = tst;
945     }
946     return rc;
947 }
948
949 /* Make a new backup of volume <avolid> on <aserver> and <apart> 
950  * if one already exists, update it 
951  */
952
953 int UV_BackupVolume(
954   afs_cell_handle_p cellHandle,
955   afs_int32 aserver,
956   afs_int32 apart,
957   afs_int32 avolid,
958   afs_status_p st)
959 {
960     int rc = 0;
961     afs_status_t tst = 0, temp = 0;
962     afs_int32 ttid = 0, btid = 0;
963     afs_int32 backupID;
964     afs_int32 rcode = 0;
965     char vname[VOLSER_MAXVOLNAME +1];
966     struct nvldbentry entry;
967     int vldblocked = 0, vldbmod = 0, backexists = 1;
968     struct rx_connection *aconn = UV_Bind(cellHandle, aserver,
969                                           AFSCONF_VOLUMEPORT);
970
971
972     /* the calls to VLDB will succeed only if avolid is a RW volume,
973      * since we are following the RW hash chain for searching */
974     if(!VLDB_GetEntryByID(cellHandle, avolid, RWVOL, &entry, &tst)) {
975         goto fail_UV_BackupVolume;
976     }
977
978     /* These operations require the VLDB be locked since it means the VLDB
979      * will change or the vldb is already locked.
980      */
981     if (!(entry.flags & BACK_EXISTS)   || /* backup volume doesnt exist */
982          (entry.flags & VLOP_ALLOPERS) || /* vldb lock already held */
983          (entry.volumeId[BACKVOL] == INVALID_BID)) {
984         /* no assigned backup volume id */
985
986         tst = ubik_Call(VL_SetLock, cellHandle->vos, 0, avolid, RWVOL,
987                         VLOP_BACKUP);
988         if (tst) {
989             goto fail_UV_BackupVolume;
990         }
991         vldblocked = 1;
992
993         /* Reread the vldb entry */
994         if (!VLDB_GetEntryByID(cellHandle, avolid, RWVOL, &entry, &tst)) {
995             goto fail_UV_BackupVolume;
996         }
997     }
998
999     if (!ISNAMEVALID(entry.name)) {
1000         tst = VOLSERBADNAME;
1001         goto fail_UV_BackupVolume;
1002     }
1003
1004     backupID = entry.volumeId[BACKVOL];
1005     if (backupID == INVALID_BID) {
1006         /* Get a backup volume id from the VLDB and update the vldb
1007          * entry with it. 
1008          */
1009         tst = ubik_Call(VL_GetNewVolumeId, cellHandle->vos, 0, 1, &backupID);
1010         if (tst) {
1011             goto fail_UV_BackupVolume;
1012         }
1013         entry.volumeId[BACKVOL] = backupID;
1014         vldbmod = 1;
1015     }
1016
1017     /* Test to see if the backup volume exists by trying to create
1018      * a transaction on the backup volume. We've assumed the backup exists.
1019      */
1020     tst = AFSVolTransCreate(aconn, backupID, apart, ITOffline, &btid);
1021     if (tst) {
1022         if (tst != VNOVOL) {
1023             goto fail_UV_BackupVolume;
1024         }
1025         backexists = 0;                 /* backup volume does not exist */
1026     }
1027     if (btid) {
1028         tst = AFSVolEndTrans(aconn, btid, &rcode);
1029         btid = 0;
1030         if (tst || rcode) {
1031             tst = (tst ? tst : rcode);
1032             goto fail_UV_BackupVolume;
1033         }
1034     }
1035
1036     /* Now go ahead and try to clone the RW volume.
1037      * First start a transaction on the RW volume 
1038      */
1039     tst = AFSVolTransCreate(aconn, avolid, apart, ITBusy, &ttid);
1040     if (tst) {
1041         goto fail_UV_BackupVolume;
1042     }
1043
1044     /* Clone or reclone the volume, depending on whether the backup 
1045      * volume exists or not
1046      */
1047     if (backexists) {
1048         tst = AFSVolReClone(aconn, ttid, backupID);
1049         if (tst) {
1050             goto fail_UV_BackupVolume;
1051         }
1052     }
1053     else {
1054        strcpy(vname, entry.name);
1055        strcat(vname,".backup");
1056
1057        tst = AFSVolClone(aconn, ttid, 0,backupVolume, vname, &backupID);
1058        if (tst) {
1059             goto fail_UV_BackupVolume;
1060        }
1061     }
1062
1063     /* End transaction on the RW volume */
1064     tst = AFSVolEndTrans(aconn, ttid, &rcode);
1065     ttid = 0;
1066     if (tst || rcode) {
1067         tst = (tst ? tst : rcode);
1068         goto fail_UV_BackupVolume;
1069     }
1070
1071     /* Mork vldb as backup exists */
1072     if (!(entry.flags & BACK_EXISTS)) {
1073         entry.flags |= BACK_EXISTS;
1074         vldbmod = 1;
1075     }
1076
1077     /* Now go back to the backup volume and bring it on line */
1078     tst = AFSVolTransCreate(aconn, backupID, apart, ITOffline, &btid);
1079     if (tst) {
1080         goto fail_UV_BackupVolume;
1081     }
1082
1083     tst = AFSVolSetFlags(aconn, btid, 0);
1084     if (tst) {
1085         goto fail_UV_BackupVolume;
1086     }
1087
1088     tst = AFSVolEndTrans(aconn, btid, &rcode);
1089     btid = 0;
1090     if (tst || rcode) {
1091         tst = (tst ? tst : rcode);
1092         goto fail_UV_BackupVolume;
1093     }
1094     rc = 1;
1095
1096     /* Will update the vldb below */
1097
1098 fail_UV_BackupVolume:
1099
1100     if (ttid) {
1101        temp =  AFSVolEndTrans(aconn, ttid, &rcode);
1102        if (temp || rcode) {
1103           if (!tst)
1104              tst = (temp ? temp : rcode);
1105        }
1106     }
1107
1108     if (btid) {
1109        temp = AFSVolEndTrans(aconn, btid, &rcode);
1110        if (temp || rcode) {
1111           if (!tst)
1112              tst = (temp ? temp : rcode);
1113        }
1114     }
1115
1116     /* Now update the vldb - if modified */
1117     if (vldblocked) {
1118         if (vldbmod) {
1119             if(!VLDB_ReplaceEntry(cellHandle, avolid, RWVOL, &entry,
1120             (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP), &temp)) {
1121                 if (!tst) {
1122                     tst = temp;
1123                 }
1124             }
1125         } else {
1126             temp = ubik_Call(VL_ReleaseLock, cellHandle->vos, 0, avolid, RWVOL, 
1127             (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP));
1128             if (temp) {
1129                 if (!tst) {
1130                     tst = temp;
1131                 }
1132             }
1133         }
1134     }
1135
1136     if (aconn) {
1137        rx_ReleaseCachedConnection(aconn);
1138     }
1139
1140     if (st != NULL) {
1141         *st = tst;
1142     }
1143     return rc;
1144 }
1145
1146 static int DelVol (
1147   struct rx_connection *conn,
1148   afs_int32 vid,
1149   afs_int32 part,
1150   afs_int32 flags)
1151 {
1152     afs_int32 acode, ccode, rcode, tid;
1153     ccode = rcode = tid = 0;
1154
1155     acode = AFSVolTransCreate(conn, vid, part, flags, &tid);
1156     if (!acode) {              /* It really was there */
1157         acode = AFSVolDeleteVolume(conn, tid);
1158         ccode = AFSVolEndTrans(conn, tid, &rcode);
1159         if (!ccode) ccode = rcode;
1160     }
1161
1162     return acode;
1163 }
1164
1165 #define ONERROR(ec, ep, es) if (ec) { fprintf(STDERR, (es), (ep)); error = (ec); goto rfail; }
1166 #define ERROREXIT(ec) { error = (ec); goto rfail; }
1167
1168 #if 0 /* doesn't appear to be used, why compile it */
1169 static int CloneVol (
1170   afs_cell_handle_p cellHandle,
1171   struct rx_connection *conn,
1172   afs_int32 rwvid,
1173   afs_int32 part,
1174   afs_int32 *rovidp,
1175   int nottemp,
1176   struct nvldbentry *entry,
1177   afs_int32 *vidCreateDate,
1178   afs_status_p st)
1179 {
1180     int rc = 0;
1181     afs_status_t tst = 0, etst = 0;
1182     afs_int32 rcode = 0, tid = 0;
1183     struct volser_status volstatus;
1184     char vname[64];
1185
1186     /* Begin transaction on RW volume marking it busy (clients will wait) */
1187     tst = AFSVolTransCreate(conn, rwvid, part, ITBusy, &tid);
1188     if (tst) {
1189         goto fail_CloneVol;
1190     }
1191
1192     /* Get the RO volume id. Allocate a new one if need to */
1193     *rovidp = entry->volumeId[ROVOL];
1194     if (*rovidp == INVALID_BID) {
1195         tst = ubik_Call(VL_GetNewVolumeId, cellHandle->vos, 0, 1, rovidp);
1196         if (tst) {
1197             goto fail_CloneVol;
1198         }
1199
1200         entry->volumeId[ROVOL] = *rovidp;
1201     }
1202
1203     /* If we are creating the ro clone, what are we calling it.
1204     * Depends on whether its a temporary clone or not.
1205     */
1206     if (nottemp) {
1207         strcpy(vname, entry->name);
1208         strcat(vname, ".readonly");
1209     } else {
1210         strcpy(vname, "readonly-clone-temp"); /* Should be unique? */
1211     }
1212
1213     /* Create the new clone. If it exists, then reclone it */
1214     tst = AFSVolClone(conn, tid, 0, readonlyVolume, vname, rovidp);
1215     if (tst == VVOLEXISTS) {
1216         tst = AFSVolReClone(conn, tid, *rovidp);
1217         if (tst) {
1218             goto fail_CloneVol;
1219         }
1220     }
1221     if (tst) {
1222         goto fail_CloneVol;
1223     }
1224
1225     /* Bring the volume back on-line as soon as possible */
1226     if (nottemp) {
1227         afs_int32 fromtid=0;
1228
1229         /* Now bring the RO clone on-line */
1230         tst = AFSVolTransCreate(conn, *rovidp, part, ITOffline, &fromtid);
1231         if (tst) {
1232             goto fail_CloneVol;
1233         }
1234
1235         tst = AFSVolSetFlags(conn, fromtid, 0);
1236         if (tst) {
1237             goto fail_CloneVol;
1238         }
1239
1240         tst = AFSVolEndTrans(conn, fromtid, &rcode);
1241         fromtid = 0;
1242         if (!tst) tst = rcode;
1243         if (tst) {
1244             goto fail_CloneVol;
1245         }
1246     }
1247
1248     /* Get the time the RW was created for return information */
1249     tst = AFSVolGetStatus(conn, tid, &volstatus);
1250     if (tst) {
1251         goto fail_CloneVol;
1252     }
1253     *vidCreateDate = volstatus.creationDate;
1254     rc = 1;
1255
1256 fail_CloneVol:
1257
1258     if (tid) {
1259         tst = AFSVolEndTrans(conn, tid, &rcode);
1260         tid = 0;
1261         if (!tst) tst = rcode;
1262         if (tst) {
1263             rc = 0;
1264             goto fail_CloneVol;
1265         }
1266     }
1267
1268     if (st != NULL) {
1269         *st = tst;
1270     }
1271     return rc;
1272 }
1273 #endif
1274
1275 /* Get a "transaction" on this replica.  Create the volume 
1276  * if necessary.  Return the time from which a dump should
1277  * be made (0 if it's a new volume)
1278  */
1279 static int GetTrans (
1280   afs_cell_handle_p cellHandle,
1281   struct nvldbentry *vldbEntryPtr,
1282   afs_int32 index,
1283   struct rx_connection **connPtr,
1284   afs_int32 *transPtr,
1285   afs_int32 *timePtr,
1286   afs_status_p st)
1287 {
1288     int rc = 0;
1289     afs_status_t tst = 0, etst = 0;
1290     afs_int32 volid;
1291     struct volser_status tstatus;
1292     int rcode;
1293
1294     *connPtr  = (struct rx_connection *)0;
1295     *timePtr  = 0;
1296     *transPtr = 0;
1297
1298     /* get connection to the replication site */
1299     *connPtr = UV_Bind(cellHandle, vldbEntryPtr->serverNumber[index],
1300                        AFSCONF_VOLUMEPORT);
1301     if (!*connPtr) {
1302         /* server is down */
1303         tst = -1;
1304         goto fail_GetTrans;
1305     }
1306
1307     volid = vldbEntryPtr->volumeId[ROVOL];
1308     if (volid) {
1309         tst = AFSVolTransCreate(*connPtr, volid,
1310                                 vldbEntryPtr->serverPartition[index],
1311                                 ITOffline, transPtr);
1312     }
1313
1314     /* If the volume does not exist, create it */
1315     if (!volid || tst) {
1316         char volname[64];
1317
1318         if (volid && (tst != VNOVOL)){
1319             goto fail_GetTrans;
1320         }
1321
1322         strcpy(volname, vldbEntryPtr->name);
1323         strcat(volname, ".readonly");
1324
1325         tst = AFSVolCreateVolume(*connPtr,
1326                                  vldbEntryPtr->serverPartition[index], 
1327                                  volname, volser_RO,
1328                                  vldbEntryPtr->volumeId[RWVOL], &volid,
1329                                  transPtr);
1330         if (tst) {
1331             goto fail_GetTrans;
1332         }
1333         vldbEntryPtr->volumeId[ROVOL] = volid;
1334
1335         /* The following is a bit redundant, since create sets these flags by default */
1336         tst = AFSVolSetFlags(*connPtr, *transPtr,
1337                              VTDeleteOnSalvage | VTOutOfService);
1338         if (tst) {
1339             goto fail_GetTrans;
1340         }
1341     }
1342
1343     /* Otherwise, the transaction did succeed, so get the creation date of the
1344     * latest RO volume on the replication site 
1345     */
1346     else {
1347         tst  = AFSVolGetStatus(*connPtr, *transPtr, &tstatus);
1348         if (tst) {
1349             goto fail_GetTrans;
1350         }
1351         *timePtr = tstatus.creationDate-CLOCKSKEW;
1352     }
1353     rc = 1;
1354
1355 fail_GetTrans:
1356
1357     if ((rc == 0) && (*transPtr)) {
1358         etst = AFSVolEndTrans(*connPtr, *transPtr, &rcode);
1359         *transPtr = 0;
1360         if (!etst) etst = rcode;
1361     }
1362
1363     if (st != NULL) {
1364         *st = tst;
1365     }
1366     return rc;
1367 }
1368
1369 static int SimulateForwardMultiple(
1370   struct rx_connection *fromconn,
1371   afs_int32 fromtid,
1372   afs_int32 fromdate,
1373   manyDests *tr,
1374   afs_int32 flags,
1375   void *cookie,
1376   manyResults *results)
1377 {
1378     int i;
1379
1380     for (i=0; i<tr->manyDests_len; i++) {
1381         results->manyResults_val[i] = 
1382             AFSVolForward(fromconn, fromtid, fromdate,
1383                           &(tr->manyDests_val[i].server),
1384                           tr->manyDests_val[i].trans, cookie);
1385     }
1386     return 0;
1387 }
1388
1389
1390 /* VolumeExists()
1391  *      Determine if a volume exists on a server and partition.
1392  *      Try creating a transaction on the volume. If we can,
1393  *      the volume exists, if not, then return the error code.
1394  *      Some error codes mean the volume is unavailable but
1395  *      still exists - so we catch these error codes.
1396  */
1397 static afs_int32 VolumeExists(
1398   afs_cell_handle_p cellHandle,
1399   afs_int32 server,
1400   afs_int32 partition,
1401   afs_int32 volumeid,
1402   afs_status_p st)
1403 {
1404    int rc = 0;
1405    afs_status_t tst = 0;
1406    struct rx_connection *conn=(struct rx_connection *)0;
1407    volEntries           volumeInfo;
1408  
1409    conn = UV_Bind(cellHandle, server, AFSCONF_VOLUMEPORT);
1410    if (conn) {
1411       volumeInfo.volEntries_val = (volintInfo *)0;
1412       volumeInfo.volEntries_len = 0;
1413       tst = AFSVolListOneVolume(conn, partition, volumeid, &volumeInfo);
1414       if (volumeInfo.volEntries_val)
1415          free(volumeInfo.volEntries_val);
1416       if (tst == VOLSERILLEGAL_PARTITION) {
1417          tst = ENODEV;
1418       }
1419       rx_ReleaseCachedConnection(conn);
1420    }
1421    rc = 1;
1422
1423    if (st != NULL) {
1424        *st = tst;
1425    } 
1426    return rc;
1427 }
1428
1429 /* release volume <afromvol> on <afromserver> <afrompart> to all the
1430  * sites if forceflag is 1.If its 0 complete the release if the previous
1431  * release aborted else start a new release */
1432 int UV_ReleaseVolume(
1433   afs_cell_handle_p cellHandle,
1434   afs_int32 afromvol,
1435   afs_int32 afromserver,
1436   afs_int32 afrompart,
1437   int forceflag,
1438   afs_status_p st)
1439 {
1440     int rc = 0;
1441     afs_status_t tst = 0, etst = 0;
1442
1443   char vname[64];
1444   afs_int32 rcode;
1445   afs_int32 cloneVolId, roVolId;
1446   struct replica *replicas=0;
1447   struct nvldbentry entry;
1448   int i, volcount, m, fullrelease, vldbindex;
1449   int failure;
1450   struct restoreCookie cookie;
1451   struct rx_connection **toconns=0;
1452   struct release *times=0;
1453   int nservers = 0;
1454   struct rx_connection *fromconn = (struct rx_connection *)0;
1455   int islocked = 0;
1456   afs_int32 clonetid=0, onlinetid;
1457   afs_int32 fromtid=0;
1458   afs_uint32 fromdate=0, thisdate;
1459   int s;
1460   manyDests tr;
1461   manyResults results;
1462   int rwindex, roindex, roclone, roexists;
1463   afs_int32 rwcrdate = 0;
1464   struct rtime {
1465     int     validtime;
1466     afs_uint32 time;
1467   } remembertime[NMAXNSERVERS];
1468   int releasecount = 0;
1469   struct volser_status volstatus;
1470
1471   memset((char *)remembertime, 0, sizeof(remembertime));
1472   memset((char *)&results, 0, sizeof(results));
1473
1474   tst = ubik_Call(VL_SetLock, cellHandle->vos, 0, afromvol, RWVOL, VLOP_RELEASE);
1475   if ((tst) && (tst != VL_RERELEASE)) {
1476       goto fail_UV_ReleaseVolume;
1477   }
1478   islocked = 1;
1479
1480   /* Get the vldb entry in readable format */
1481   if (!VLDB_GetEntryByID (cellHandle, afromvol, RWVOL, &entry, &tst)) {
1482       goto fail_UV_ReleaseVolume;
1483   }  
1484
1485   if (!ISNAMEVALID(entry.name)) {
1486       tst = VOLSERBADNAME;
1487       goto fail_UV_ReleaseVolume;
1488   }  
1489
1490   if (entry.volumeId[RWVOL] != afromvol) {
1491       tst = ADMVOSVOLUMERELEASERWONLY;
1492       goto fail_UV_ReleaseVolume;
1493   }  
1494
1495   if (entry.nServers <= 1) {
1496       tst = ADMVOSVOLUMENOREPLICAS;
1497       goto fail_UV_ReleaseVolume;
1498   }  
1499
1500   if (strlen(entry.name) > (VOLSER_OLDMAXVOLNAME - 10)) {
1501       tst = VOLSERBADNAME;
1502       goto fail_UV_ReleaseVolume;
1503   }  
1504
1505   /* roclone is true if one of the RO volumes is on the same
1506    * partition as the RW volume. In this case, we make the RO volume
1507    * on the same partition a clone instead of a complete copy.
1508    */
1509   
1510   roindex = Lp_ROMatch(cellHandle, &entry, afromserver, afrompart, &tst) - 1;
1511   roclone = ((roindex == -1) ? 0 : 1);
1512   rwindex = Lp_GetRwIndex(cellHandle, &entry, 0);
1513   if (rwindex < 0) {
1514       tst = VOLSERNOVOL;
1515       goto fail_UV_ReleaseVolume;
1516   }  
1517
1518   /* Make sure we have a RO volume id to work with */
1519   if (entry.volumeId[ROVOL] == INVALID_BID) {
1520       /* need to get a new RO volume id */
1521       tst = ubik_Call(VL_GetNewVolumeId, cellHandle->vos, 0, 1, &roVolId);
1522       if (tst) {
1523           goto fail_UV_ReleaseVolume;
1524       }
1525
1526       entry.volumeId[ROVOL] = roVolId;
1527       if (!VLDB_ReplaceEntry(cellHandle, afromvol, RWVOL, &entry, 0, &tst)) {
1528           goto fail_UV_ReleaseVolume;
1529       }
1530   }
1531
1532   /* Will we be completing a previously unfinished release. -force overrides */
1533   for (fullrelease=1, i=0; (fullrelease && (i<entry.nServers)); i++) {
1534      if (entry.serverFlags[i] & NEW_REPSITE)
1535         fullrelease = 0;
1536   }
1537   if (forceflag && !fullrelease)
1538     fullrelease = 1;
1539
1540   /* Determine which volume id to use and see if it exists */
1541   cloneVolId = ((fullrelease || (entry.cloneId == 0)) ? entry.volumeId[ROVOL] : entry.cloneId);
1542   VolumeExists(cellHandle, afromserver, afrompart, cloneVolId, &tst);
1543   roexists = ((tst == ENODEV) ? 0 : 1);
1544   if (!roexists && !fullrelease)
1545      fullrelease = 1;      /* Do a full release if RO clone does not exist */
1546
1547   fromconn = UV_Bind(cellHandle, afromserver, AFSCONF_VOLUMEPORT);
1548   if (!fromconn) {
1549       tst = -1;
1550       goto fail_UV_ReleaseVolume;
1551   }
1552   
1553   if (fullrelease) {
1554      /* If the RO clone exists, then if the clone is a temporary
1555       * clone, delete it. Or if the RO clone is marked RO_DONTUSE
1556       * (it was recently added), then also delete it. We do not
1557       * want to "reclone" a temporary RO clone.
1558       */
1559      if ( roexists && 
1560          (!roclone || (entry.serverFlags[roindex] & RO_DONTUSE)) ) {
1561         tst = DelVol(fromconn, cloneVolId, afrompart, ITOffline);
1562         if (tst && (tst != VNOVOL)) {
1563             goto fail_UV_ReleaseVolume;
1564         }
1565         roexists = 0;
1566      }
1567
1568      /* Mark all the ROs in the VLDB entry as RO_DONTUSE. We don't
1569       * write this entry out to the vlserver until after the first
1570       * RO volume is released (temp RO clones don't count).
1571       */
1572      for (i=0; i<entry.nServers; i++) {
1573         entry.serverFlags[i] &= ~NEW_REPSITE;
1574         entry.serverFlags[i] |=  RO_DONTUSE;
1575      }
1576      entry.serverFlags[rwindex] |=  NEW_REPSITE;
1577      entry.serverFlags[rwindex] &= ~RO_DONTUSE;
1578       
1579      /* Begin transaction on RW and mark it busy while we clone it */
1580      tst = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &clonetid);
1581      if (tst) {
1582          goto fail_UV_ReleaseVolume;
1583      }
1584
1585      /* Clone or reclone the volume */
1586      if (roexists) {
1587         tst = AFSVolReClone(fromconn, clonetid, cloneVolId);
1588         if (tst) {
1589             goto fail_UV_ReleaseVolume;
1590         }
1591      } else {
1592         if (roclone) {
1593            strcpy(vname, entry.name);
1594            strcat(vname, ".readonly");
1595         } else {
1596            strcpy(vname, "readonly-clone-temp");
1597         }
1598         tst = AFSVolClone(fromconn, clonetid, 0, readonlyVolume, vname, &cloneVolId);
1599         if (tst) {
1600             goto fail_UV_ReleaseVolume;
1601         }
1602      }
1603
1604      /* Get the time the RW was created for future information */
1605      tst = AFSVolGetStatus(fromconn, clonetid, &volstatus);
1606      if (tst) {
1607          goto fail_UV_ReleaseVolume;
1608      }
1609      rwcrdate = volstatus.creationDate;
1610
1611      /* End the transaction on the RW volume */
1612      tst = AFSVolEndTrans(fromconn, clonetid, &rcode);
1613      clonetid = 0;
1614      tst = (tst ? tst : rcode);
1615      if (tst) {
1616          goto fail_UV_ReleaseVolume;
1617      }
1618
1619      /* Remember clone volume ID in case we fail or are interrupted */
1620      entry.cloneId = cloneVolId;
1621
1622      if (roclone) {
1623         /* Bring the RO clone online - though not if it's a temporary clone */
1624         tst = AFSVolTransCreate(fromconn, cloneVolId, afrompart, ITOffline, &onlinetid);
1625         if (tst) {
1626             goto fail_UV_ReleaseVolume;
1627         }
1628
1629         etst = AFSVolSetFlags(fromconn, onlinetid, 0);
1630
1631         tst = AFSVolEndTrans(fromconn, onlinetid, &rcode);
1632         tst = (tst ? tst : rcode);
1633         if (tst) {
1634             goto fail_UV_ReleaseVolume;
1635         }
1636         if (etst) {
1637             tst = etst;
1638             goto fail_UV_ReleaseVolume;
1639         }
1640
1641         /* Sleep so that a client searching for an online volume won't
1642          * find the clone offline and then the next RO offline while the 
1643          * release brings the clone online and the next RO offline (race).
1644          * There is a fix in the 3.4 client that does not need this sleep
1645          * anymore, but we don't know what clients we have.
1646          */
1647         if (entry.nServers > 2)
1648            sleep(5);
1649
1650         /* Mark the RO clone in the VLDB as a good site (already released)*/
1651         entry.serverFlags[roindex] |=  NEW_REPSITE;
1652         entry.serverFlags[roindex] &= ~RO_DONTUSE;
1653         entry.flags                |=  RO_EXISTS;
1654
1655         releasecount++;
1656
1657         /* Write out the VLDB entry only if the clone is not a temporary
1658          * clone. If we did this to a temporary clone then we would end
1659          * up marking all the ROs as "old release" making the ROs
1660          * temporarily unavailable.
1661          */
1662         if (!VLDB_ReplaceEntry(cellHandle, afromvol, RWVOL, &entry, 0, &tst)) {
1663             goto fail_UV_ReleaseVolume;
1664         }
1665      }
1666   }  
1667
1668   /* Now we will release from the clone to the remaining RO replicas.
1669    * The first 2 ROs (counting the non-temporary RO clone) are released
1670    * individually: releasecount. This is to reduce the race condition
1671    * of clients trying to find an on-line RO volume. The remaining ROs
1672    * are released in parallel but no more than half the number of ROs
1673    * (rounded up) at a time: nservers.
1674    */
1675
1676   strcpy(vname, entry.name);
1677   strcat(vname, ".readonly");
1678   memset(&cookie, 0, sizeof(cookie));
1679   strncpy(cookie.name, vname, VOLSER_OLDMAXVOLNAME);
1680   cookie.type   = ROVOL;
1681   cookie.parent = entry.volumeId[RWVOL];
1682   cookie.clone  = 0;
1683
1684   nservers = entry.nServers/2;           /* how many to do at once, excluding clone */
1685   replicas   = (struct replica *)        malloc (sizeof(struct replica)*nservers+1);
1686   times      = (struct release *)        malloc (sizeof(struct release)*nservers+1);
1687   toconns    = (struct rx_connection **) malloc (sizeof(struct rx_connection *)*nservers+1);
1688   results.manyResults_val = (afs_int32 *)    malloc (sizeof(afs_int32)*nservers+1);
1689   if ( !replicas || !times || !! !results.manyResults_val || !toconns ) {
1690       tst = ADMNOMEM;
1691       goto fail_UV_ReleaseVolume;
1692   } 
1693
1694   memset(replicas, 0, (sizeof(struct replica)*nservers+1));
1695   memset(times, 0, (sizeof(struct release)*nservers+1));
1696   memset(toconns, 0, (sizeof(struct rx_connection *)*nservers+1));
1697   memset(results.manyResults_val, 0, (sizeof(afs_int32)*nservers+1));
1698
1699   /* Create a transaction on the cloned volume */
1700   tst = AFSVolTransCreate(fromconn, cloneVolId, afrompart, ITBusy, &fromtid);
1701   if (tst) {
1702       goto fail_UV_ReleaseVolume;
1703   } 
1704
1705   /* For each index in the VLDB */
1706   for (vldbindex=0; vldbindex<entry.nServers; ) {
1707
1708      /* Get a transaction on the replicas. Pick replacas which have an old release. */
1709      for (volcount=0; ((volcount<nservers) && (vldbindex<entry.nServers)); vldbindex++) {
1710         /* The first two RO volumes will be released individually.
1711          * The rest are then released in parallel. This is a hack
1712          * for clients not recognizing right away when a RO volume
1713          * comes back on-line.
1714          */
1715         if ((volcount == 1) && (releasecount < 2))
1716            break;
1717
1718         if (vldbindex == roindex) continue;              /* the clone    */
1719         if ( (entry.serverFlags[vldbindex] & NEW_REPSITE) &&
1720             !(entry.serverFlags[vldbindex] & RO_DONTUSE) ) continue;
1721         if (!(entry.serverFlags[vldbindex] & ITSROVOL)) continue;  /* not a RO vol */
1722
1723
1724         /* Get a Transaction on this replica. Get a new connection if
1725          * necessary.  Create the volume if necessary.  Return the
1726          * time from which the dump should be made (0 if it's a new
1727          * volume).  Each volume might have a different time. 
1728          */
1729         replicas[volcount].server.destHost = entry.serverNumber[vldbindex];
1730         replicas[volcount].server.destPort = AFSCONF_VOLUMEPORT;
1731         replicas[volcount].server.destSSID = 1;
1732         times[volcount].vldbEntryIndex = vldbindex;
1733         
1734         if(!GetTrans(cellHandle, &entry, vldbindex, &(toconns[volcount]), 
1735                         &(replicas[volcount].trans), &(times[volcount].time),
1736                         &tst)) {
1737             continue;
1738         }
1739
1740         /* Thisdate is the date from which we want to pick up all changes */
1741         if (forceflag || !fullrelease || (rwcrdate > times[volcount].time)) {
1742            /* If the forceflag is set, then we want to do a full dump.
1743             * If it's not a full release, we can't be sure that the creation
1744             *  date is good (so we also do a full dump).
1745             * If the RW volume was replaced (its creation date is newer than
1746             *  the last release), then we can't be sure what has changed (so
1747             *  we do a full dump).
1748             */
1749            thisdate = 0;
1750         } else if (remembertime[vldbindex].validtime) {
1751            /* Trans was prev ended. Use the time from the prev trans
1752             * because, prev trans may have created the volume. In which
1753             * case time[volcount].time would be now instead of 0.
1754             */
1755            thisdate  = (remembertime[vldbindex].time < times[volcount].time) ? 
1756                         remembertime[vldbindex].time : times[volcount].time;
1757         } else {
1758            thisdate = times[volcount].time;
1759         }         
1760         remembertime[vldbindex].validtime = 1;
1761         remembertime[vldbindex].time = thisdate;
1762
1763         if (volcount == 0) {
1764            fromdate = thisdate;
1765         } else {
1766            /* Include this volume if it is within 15 minutes of the earliest */
1767            if (((fromdate>thisdate)?(fromdate-thisdate):(thisdate-fromdate)) > 900) {
1768               AFSVolEndTrans(toconns[volcount], replicas[volcount].trans, &rcode);
1769               replicas[volcount].trans = 0;
1770               break;
1771            }
1772            if (thisdate < fromdate)
1773               fromdate = thisdate;
1774         }
1775         volcount++;
1776      }
1777      if (!volcount) continue;
1778
1779      /* Release the ones we have collected */
1780      tr.manyDests_val = &(replicas[0]);
1781      tr.manyDests_len = results.manyResults_len = volcount;
1782      tst = AFSVolForwardMultiple(fromconn, fromtid, fromdate, &tr, 0/*spare*/, &cookie, &results);
1783      if (tst == RXGEN_OPCODE) {               /* RPC Interface Mismatch */
1784         tst = SimulateForwardMultiple(fromconn, fromtid, fromdate, &tr, 0/*spare*/, &cookie, &results);
1785         nservers = 1;
1786      }
1787
1788      if (tst) {
1789         goto fail_UV_ReleaseVolume;
1790      } else {
1791         for (m=0; m<volcount; m++) {
1792            if (results.manyResults_val[m]) {
1793               continue;
1794            }
1795             
1796            tst = AFSVolSetIdsTypes(toconns[m], replicas[m].trans, 
1797                                     vname, ROVOL, entry.volumeId[RWVOL], 0, 0);
1798            if (tst) {
1799               continue;
1800            }
1801
1802            /* have to clear dest. flags to ensure new vol goes online:
1803             * because the restore (forwarded) operation copied
1804             * the V_inService(=0) flag over to the destination. 
1805             */
1806            tst = AFSVolSetFlags(toconns[m], replicas[m].trans, 0);
1807            if (tst) {
1808               continue;
1809            }
1810
1811            entry.serverFlags[times[m].vldbEntryIndex] |=  NEW_REPSITE;
1812            entry.serverFlags[times[m].vldbEntryIndex] &= ~RO_DONTUSE;
1813            entry.flags                                |=  RO_EXISTS;
1814            releasecount++;
1815         }
1816      }   
1817
1818      /* End the transactions and destroy the connections */
1819      for (s=0; s<volcount; s++) {
1820         if (replicas[s].trans)
1821            tst = AFSVolEndTrans(toconns[s], replicas[s].trans, &rcode);
1822         replicas[s].trans = 0;
1823         if (!tst) tst = rcode;
1824         if (tst) {
1825            if ((s == 0) || (tst != ENOENT)) {
1826            } else {
1827               if (times[s].vldbEntryIndex < vldbindex)
1828                  vldbindex = times[s].vldbEntryIndex;
1829            }
1830         }
1831            
1832         if (toconns[s])
1833            rx_ReleaseCachedConnection(toconns[s]);
1834         toconns[s] = 0;
1835      }
1836    
1837      if (!VLDB_ReplaceEntry(cellHandle, afromvol, RWVOL, &entry, 0, &tst)) {
1838          goto fail_UV_ReleaseVolume;
1839      }
1840   } /* for each index in the vldb */
1841
1842   /* End the transaction on the cloned volume */
1843   tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
1844   fromtid = 0;
1845   
1846   /* Figure out if any volume were not released and say so */
1847   for (failure=0, i=0; i<entry.nServers; i++) {
1848      if (!(entry.serverFlags[i] & NEW_REPSITE))
1849         failure++;
1850   }
1851   if (failure) {
1852      if (!VLDB_ReplaceEntry(cellHandle, afromvol, RWVOL, &entry,
1853                             LOCKREL_TIMESTAMP, &tst)) {
1854          goto fail_UV_ReleaseVolume;
1855      }
1856
1857      tst = VOLSERBADRELEASE;
1858      goto fail_UV_ReleaseVolume;
1859   }
1860
1861   /* All the ROs were release successfully. Remove the temporary clone */
1862   if (!roclone) {
1863       tst = DelVol (fromconn, cloneVolId, afrompart, ITOffline);
1864       if (tst) {
1865           goto fail_UV_ReleaseVolume;
1866       }
1867   }
1868   entry.cloneId = 0;
1869
1870   for (i=0; i<entry.nServers; i++)
1871      entry.serverFlags[i] &= ~NEW_REPSITE;
1872
1873   /* Update the VLDB */
1874   if (!VLDB_ReplaceEntry(cellHandle, afromvol, RWVOL, &entry,
1875                            LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP,
1876                            &tst)) {
1877       goto fail_UV_ReleaseVolume;
1878   }
1879   rc = 1;
1880
1881 fail_UV_ReleaseVolume:
1882
1883   if (clonetid) {
1884       tst = AFSVolEndTrans(fromconn, clonetid, &rcode);
1885       clonetid = 0;
1886       if (tst) {
1887           rc = 0;
1888       }
1889   }
1890   if (fromtid) {
1891       tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
1892       fromtid = 0;
1893       if (tst) {
1894           rc = 0;
1895       }
1896   }
1897   for (i=0; i<nservers; i++) {
1898       if (replicas && replicas[i].trans) {
1899           tst = AFSVolEndTrans(toconns[i], replicas[i].trans, &rcode);
1900           replicas[i].trans = 0;
1901           if (tst) {
1902               rc = 0;
1903           }
1904       }
1905       if (toconns && toconns[i]) {
1906           rx_ReleaseCachedConnection(toconns[i]);
1907           toconns[i] = 0;
1908       }
1909   }
1910   if (islocked) {
1911       tst = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0, afromvol, RWVOL, 
1912                         LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
1913       if (tst) {
1914           rc = 0;
1915       }
1916   }
1917
1918   if (fromconn)                rx_ReleaseCachedConnection(fromconn);
1919   if (results.manyResults_val) free (results.manyResults_val);
1920   if (replicas)                free (replicas);
1921   if (toconns)                 free (toconns);
1922   if (times)                   free (times);
1923
1924   if (st != NULL) {
1925       *st = tst;
1926   }
1927   return rc;
1928 }
1929
1930 static int ReceiveFile(
1931     register int fd,
1932     register struct rx_call *call,
1933     register struct stat *status)
1934 {
1935     register char *buffer = (char*) 0;
1936     register int blockSize;
1937     afs_int32  bytesread, nbytes, bytesleft, w;
1938     fd_set out;
1939     afs_int32 error = 0;
1940  
1941 #ifdef AFS_NT40_ENV
1942     blockSize = 4096;
1943 #else
1944     if(fd != 1) {
1945 #ifdef  AFS_AIX_ENV
1946     struct statfs tstatfs;
1947  
1948 /* Unfortunately in AIX valuable fields such as st_blksize are gone from the sta
1949 t structure!! */
1950     fstatfs(fd, &tstatfs);
1951     blockSize = tstatfs.f_bsize;
1952 #else
1953     blockSize = status->st_blksize;
1954 #endif
1955     }
1956     else{
1957         blockSize = 4096;
1958     }
1959 #endif
1960     nbytes = blockSize;
1961     buffer = (char *)malloc(blockSize);
1962     if (!buffer) {
1963         return ADMNOMEM;
1964     }
1965     bytesread = 1;
1966     while(!error && (bytesread > 0)) {
1967         bytesread = rx_Read(call, buffer, nbytes);
1968         bytesleft = bytesread;
1969         while (!error && (bytesleft > 0)) {
1970             FD_ZERO(&out);
1971             FD_SET(fd, &out);
1972 #ifndef AFS_NT40_ENV  /* NT csn't select on non-socket fd's */
1973             select(fd+1, 0, &out, 0, 0);      /* don't timeout if write bl
1974 ocks */
1975 #endif
1976             w = write(fd, &buffer[bytesread-bytesleft], bytesleft);
1977             if (w < 0) {
1978                 error = ADMVOSDUMPFILEWRITEFAIL;
1979             }
1980             else {
1981                 bytesleft -= w;
1982             }
1983         }
1984     }
1985     if (buffer) free(buffer);
1986     if(fd !=1)
1987         if (!error) fstat(fd, status);
1988     return error;
1989 }
1990
1991
1992 static afs_int32 DumpFunction(
1993   struct rx_call *call,
1994   const char *filename)
1995 {
1996     int fd;
1997     struct stat status;
1998     afs_int32 error,code;
1999  
2000     error = 0;
2001     fd = -1;
2002  
2003     fd = open(filename,O_CREAT|O_TRUNC|O_WRONLY, 0666);
2004     if (fd < 0 || fstat(fd, &status) < 0){
2005         error = VOLSERBADOP;
2006         goto dffail;
2007     }
2008     code = ReceiveFile(fd,call,&status);
2009     if(code) {
2010         error = code;
2011         goto dffail;
2012     }
2013   dffail:
2014     if (fd >= 0) code = close(fd);
2015     else code = 0;
2016     if(code){
2017         if(!error) error = code;
2018     }
2019     return error;
2020 }
2021
2022
2023 /*dump the volume <afromvol> on <afromserver> and
2024 * <afrompart> to <afilename> starting from <fromdate>.
2025 * DumpFunction does the real work behind the scenes after
2026 * extracting parameters from the rock  */
2027 int UV_DumpVolume(
2028   afs_cell_handle_p cellHandle,
2029   afs_int32 afromvol,
2030   afs_int32 afromserver,
2031   afs_int32 afrompart,
2032   afs_int32 fromdate,
2033   const char *filename,
2034   afs_status_p st)
2035 {
2036     int rc = 0;
2037     afs_status_t tst = 0;
2038     afs_status_t etst = 0;
2039     struct rx_connection  *fromconn;
2040     struct rx_call *fromcall;
2041     afs_int32 fromtid;
2042     afs_int32 rxError;
2043     afs_int32 rcode;
2044     
2045     struct nvldbentry entry;
2046     afs_int32 error;
2047     int islocked;
2048
2049     islocked = 0;
2050     error = 0;
2051     rxError = 0;
2052     fromcall = (struct rx_call *)0;
2053     fromconn = (struct rx_connection *)0;
2054     fromtid = 0;
2055     fromcall = (struct rx_call *)0;
2056     
2057     islocked = 0;
2058     if (!VLDB_GetEntryByID(cellHandle, afromvol, -1, &entry, &tst)) {
2059         goto fail_UV_DumpVolume;
2060     }
2061
2062     /* get connections to the servers */
2063     fromconn = UV_Bind(cellHandle, afromserver, AFSCONF_VOLUMEPORT);
2064     tst = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
2065     if (tst) {
2066         goto fail_UV_DumpVolume;
2067     }
2068     fromcall = rx_NewCall(fromconn);
2069     tst = StartAFSVolDump(fromcall,fromtid, fromdate);
2070     if(tst) {
2071         goto fail_UV_DumpVolume;
2072     }
2073     if ((tst = DumpFunction(fromcall,filename))) {
2074         goto fail_UV_DumpVolume;
2075     }
2076     tst = rx_EndCall(fromcall,rxError);
2077     fromcall = (struct rx_call *)0;
2078     if (tst) {
2079         goto fail_UV_DumpVolume;
2080     }
2081     tst = AFSVolEndTrans(fromconn, fromtid, &rcode);
2082     fromtid = 0;
2083     if (!tst) tst = rcode;
2084     if (tst){
2085         goto fail_UV_DumpVolume;
2086     }
2087     rc = 1;
2088
2089 fail_UV_DumpVolume: 
2090
2091     if(islocked) {
2092         etst = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0, afromvol, -1, LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
2093         if(etst) {
2094             if(!tst) tst = etst;
2095         }
2096     }
2097
2098     if(fromcall) {
2099         etst = rx_EndCall(fromcall,rxError);
2100         if(etst){
2101             if(!tst) tst = etst;
2102         }       
2103     }
2104
2105     if (fromtid) {
2106         etst = AFSVolEndTrans(fromconn, fromtid, &rcode);
2107         if(!tst) tst = etst;
2108         if(rcode) {
2109             if(!tst) tst = rcode;
2110         }
2111     }
2112
2113     if (fromconn) {
2114         rx_ReleaseCachedConnection(fromconn);
2115     }
2116
2117     if (st != NULL) {
2118         *st = tst;
2119     }
2120     return rc;
2121 }
2122
2123 int SendFile(
2124   register int fd,
2125   register struct rx_call *call,
2126   register struct stat *status)
2127 {
2128     char *buffer = (char*) 0;
2129     int blockSize;
2130     fd_set in;
2131     afs_int32 error = 0;
2132     int done = 0;
2133     int nbytes;
2134  
2135 #ifdef AFS_NT40_ENV
2136     blockSize = 4096;
2137 #else
2138     if(fd != 0){
2139 #ifdef  AFS_AIX_ENV
2140     struct statfs tstatfs;
2141  
2142 /* Unfortunately in AIX valuable fields such as st_blksize are gone from the sta
2143 t structure!! */
2144     fstatfs(fd, &tstatfs);
2145     blockSize = tstatfs.f_bsize;
2146 #else
2147     blockSize = status->st_blksize;
2148 #endif
2149     }
2150     else{
2151         blockSize = 4096;
2152     }
2153 #endif
2154     buffer = (char *)malloc(blockSize);
2155     if (!buffer) {
2156         return ADMNOMEM;
2157     }
2158  
2159     while (!error && !done) {
2160         FD_ZERO(&in);
2161         FD_SET(fd, &in);
2162 #ifndef AFS_NT40_ENV  /* NT csn't select on non-socket fd's */
2163         select(fd+1, &in, 0, 0, 0);      /* don't timeout if read blocks */
2164 #endif
2165         nbytes = read(fd, buffer, blockSize);
2166         if (nbytes < 0) {
2167             error = ADMVOSRESTOREFILEREADFAIL;
2168             break;
2169         }
2170         if(nbytes == 0){
2171             done = 1;
2172             break;
2173         }
2174         if (rx_Write(call, buffer, nbytes) != nbytes){
2175             error = ADMVOSRESTOREFILEWRITEFAIL;
2176             break;
2177         }
2178     }
2179     if (buffer) free(buffer);
2180     return error;
2181 }
2182
2183 static afs_int32 WriteData(
2184   struct rx_call *call,
2185   const char *filename)
2186 {
2187     int fd;
2188     struct stat status;
2189     afs_int32 error,code;
2190  
2191     error = 0;
2192     fd = -1;
2193  
2194     fd = open(filename,0);
2195     if (fd < 0 || fstat(fd, &status) < 0){
2196         fprintf(STDERR,"Could access file '%s'\n",filename);
2197         error = ADMVOSRESTOREFILEOPENFAIL;
2198         goto fail_WriteData;
2199     }
2200     code = SendFile(fd,call,&status);
2201     if(code) {
2202         error = code;
2203         goto fail_WriteData;
2204     }
2205
2206 fail_WriteData:
2207
2208     if(fd >= 0) code = close(fd);
2209     else code = 0;
2210     if(code){
2211         if(!error) error = ADMVOSRESTOREFILECLOSEFAIL;
2212     }
2213     return error;
2214 }
2215
2216 /*
2217  * Restore a volume <tovolid> <tovolname> on <toserver> <topart> from
2218  * the dump file <afilename>. WriteData does all the real work
2219  * after extracting params from the rock 
2220  */
2221 int UV_RestoreVolume(
2222   afs_cell_handle_p cellHandle,
2223   afs_int32 toserver,
2224   afs_int32 topart,
2225   afs_int32 tovolid,
2226   const char *tovolname,
2227   int flags,
2228   const char *dumpFile,
2229   afs_status_p st)
2230 {
2231     int rc = 0;
2232     afs_status_t tst = 0;
2233     afs_status_t etst = 0;
2234     struct rx_connection *toconn,*tempconn;
2235     struct rx_call *tocall;
2236     afs_int32 totid, rcode;
2237     afs_int32 rxError = 0;
2238     struct volser_status tstatus;
2239     char partName[10];
2240     afs_int32 pvolid;
2241     afs_int32 temptid;
2242     int success;
2243     struct nvldbentry entry;
2244     afs_int32 error;
2245     int islocked;
2246     struct restoreCookie cookie;
2247     int reuseID;
2248     afs_int32 newDate, volflag;
2249     int index, same;
2250
2251
2252     memset(&cookie, 0, sizeof(cookie));
2253     islocked  = 0;
2254     success = 0;
2255     error = 0;
2256     reuseID = 1;
2257     tocall = (struct rx_call *)0;
2258     toconn = (struct rx_connection *)0;
2259     tempconn = (struct rx_connection *)0;
2260     totid = 0;
2261     temptid = 0;
2262
2263     pvolid = tovolid;
2264     toconn = UV_Bind(cellHandle, toserver, AFSCONF_VOLUMEPORT);
2265     if(pvolid == 0) {/*alot a new id if needed */
2266         VLDB_GetEntryByName(cellHandle, tovolname, &entry, &tst);
2267         if(tst == VL_NOENT) {
2268             tst = ubik_Call(VL_GetNewVolumeId, cellHandle->vos, 0, 1, &pvolid);
2269             if(tst) {
2270                 goto fail_UV_RestoreVolume;
2271             }
2272             reuseID = 0;
2273         }
2274         else{
2275             pvolid = entry.volumeId[RWVOL];
2276         }
2277     }
2278
2279     /* 
2280      * at this point we have a volume id to use/reuse for the
2281      * volume to be restored
2282      */
2283     if(strlen(tovolname) > (VOLSER_OLDMAXVOLNAME - 1)) {
2284         tst = ADMVOSRESTOREVOLUMENAMETOOBIG;
2285         goto fail_UV_RestoreVolume;
2286     }
2287
2288     if (!vos_PartitionIdToName(topart, partName, &tst)) {
2289         goto fail_UV_RestoreVolume;
2290     }
2291     /*what should the volume be restored as ? rw or ro or bk ?
2292       right now the default is rw always */
2293     tst = AFSVolCreateVolume(toconn, topart, tovolname, volser_RW, 0,&pvolid, &totid);
2294     if (tst){
2295         if (flags & RV_FULLRST) { /* full restore: delete then create anew */
2296             tst = AFSVolTransCreate(toconn, pvolid, topart, ITOffline, &totid);
2297             if (tst) {
2298                 goto fail_UV_RestoreVolume;
2299             }
2300             tst = AFSVolSetFlags(toconn, totid, VTDeleteOnSalvage | VTOutOfService);
2301             if (tst){
2302                 goto fail_UV_RestoreVolume;
2303             }
2304             tst = AFSVolDeleteVolume(toconn,totid);
2305             if (tst){
2306                 goto fail_UV_RestoreVolume;
2307             }
2308             tst = AFSVolEndTrans(toconn, totid, &rcode);
2309             totid = 0;
2310             if (!tst) tst = rcode;
2311             if (tst) {
2312                 goto fail_UV_RestoreVolume;
2313             }
2314             tst = AFSVolCreateVolume(toconn, topart, tovolname, volser_RW, 0,&pvolid, &totid);
2315             if (tst){
2316                 goto fail_UV_RestoreVolume;}
2317         } else {
2318             tst = AFSVolTransCreate(toconn, pvolid, topart, ITOffline, &totid);
2319             if (tst) {
2320                 goto fail_UV_RestoreVolume;
2321             }
2322         }
2323     }
2324     cookie.parent = pvolid;
2325     cookie.type = RWVOL;
2326     cookie.clone = 0;
2327     strncpy(cookie.name,tovolname,VOLSER_OLDMAXVOLNAME);
2328
2329     tocall = rx_NewCall(toconn);
2330     tst = StartAFSVolRestore(tocall,totid, 1,&cookie);
2331     if(tst) {
2332         goto fail_UV_RestoreVolume;
2333     }
2334     tst = WriteData(tocall, dumpFile);
2335     if(tst) {
2336         goto fail_UV_RestoreVolume;
2337     }
2338     tst = rx_EndCall(tocall,rxError);
2339     tocall = (struct rx_call *) 0;
2340     if (tst) {
2341         goto fail_UV_RestoreVolume;
2342     }
2343     tst = AFSVolGetStatus(toconn,totid, &tstatus);
2344     if(tst) {
2345         goto fail_UV_RestoreVolume;
2346     }
2347     tst = AFSVolSetIdsTypes(toconn,totid, tovolname, RWVOL, pvolid,0,0);
2348     if(tst) {
2349         goto fail_UV_RestoreVolume;
2350     }
2351     newDate = time(0);
2352     tst = AFSVolSetDate(toconn,totid, newDate);
2353     if(tst) {
2354         goto fail_UV_RestoreVolume;
2355     }
2356
2357     volflag = ((flags & RV_OFFLINE) ? VTOutOfService : 0); /* off or on-line */
2358     tst = AFSVolSetFlags(toconn, totid, volflag);
2359     if (tst){
2360         goto fail_UV_RestoreVolume;
2361     }
2362    
2363 /* It isn't handled right in fail_UV_RestoreVolume */
2364     tst = AFSVolEndTrans(toconn, totid, &rcode);
2365     totid = 0;
2366     if(!tst) tst = rcode;
2367     if(tst) {
2368         goto fail_UV_RestoreVolume;
2369     }
2370
2371     success = 1;
2372     if (success && (!reuseID || (flags & RV_FULLRST))) {
2373         /* Volume was restored on the file server, update the 
2374          * VLDB to reflect the change.
2375          */
2376         VLDB_GetEntryByID(cellHandle, pvolid, RWVOL, &entry, &tst);
2377         if(tst && tst != VL_NOENT && tst != VL_ENTDELETED) {
2378             goto fail_UV_RestoreVolume;
2379         }
2380         if(tst == VL_NOENT) { /* it doesnot exist already */
2381             /*make the vldb return this indication specifically*/
2382             strcpy(entry.name, tovolname);
2383             entry.nServers = 1;
2384             entry.serverNumber[0] = toserver;/*should be indirect */
2385             entry.serverPartition[0] = topart;
2386             entry.serverFlags[0] = ITSRWVOL;
2387             entry.flags = RW_EXISTS;
2388             if(tstatus.cloneID != 0){
2389                 entry.volumeId[ROVOL] = tstatus.cloneID;/*this should come from status info on the volume if non zero */
2390             }
2391             else
2392                 entry.volumeId[ROVOL] = INVALID_BID;
2393             entry.volumeId[RWVOL] = pvolid;
2394             entry.cloneId = 0;
2395             if(tstatus.backupID != 0){
2396                 entry.volumeId[BACKVOL] = tstatus.backupID;
2397                 /*this should come from status info on the volume if non zero */
2398             }
2399             else 
2400                 entry.volumeId[BACKVOL] = INVALID_BID;
2401             if (!VLDB_CreateEntry(cellHandle, &entry, &tst)) {
2402                 goto fail_UV_RestoreVolume;
2403             }
2404             islocked = 0;
2405         }
2406         else {  /*update the existing entry */
2407             tst = ubik_Call(VL_SetLock,cellHandle->vos, 0, pvolid, RWVOL, VLOP_RESTORE);
2408             if(tst) {
2409                 goto fail_UV_RestoreVolume;
2410             }
2411             islocked = 1;
2412             strcpy(entry.name, tovolname);
2413
2414             /* Update the vlentry with the new information */
2415             index = Lp_GetRwIndex(cellHandle, &entry, 0);
2416             if (index == -1) {
2417                /* Add the rw site for the volume being restored */
2418                entry.serverNumber[entry.nServers]    = toserver;
2419                entry.serverPartition[entry.nServers] = topart;
2420                entry.serverFlags[entry.nServers]     = ITSRWVOL;
2421                entry.nServers++;
2422             } else {
2423                /* This volume should be deleted on the old site
2424                 * if its different from new site.
2425                 */
2426                VLDB_IsSameAddrs(cellHandle, toserver, entry.serverNumber[index], &same, &tst);
2427                if ( (!tst && !same) || (entry.serverPartition[index] != topart) ) {
2428                   tempconn = UV_Bind(cellHandle, entry.serverNumber[index], AFSCONF_VOLUMEPORT);
2429                   tst = AFSVolTransCreate(tempconn, pvolid, entry.serverPartition[index], ITOffline, &temptid);
2430                   if (!tst){
2431                      tst = AFSVolSetFlags(tempconn, temptid, VTDeleteOnSalvage | VTOutOfService);
2432                      if(tst) {
2433                         goto fail_UV_RestoreVolume;
2434                      }
2435                      tst = AFSVolDeleteVolume(tempconn,temptid);
2436                      if(tst){
2437                         goto fail_UV_RestoreVolume;
2438                      }
2439                      tst = AFSVolEndTrans(tempconn, temptid, &rcode);
2440                      temptid = 0;
2441                      if(!tst) tst = rcode;
2442                      if(tst){
2443                         goto fail_UV_RestoreVolume;
2444                      }
2445                      vos_PartitionIdToName(entry.serverPartition[index],partName, &tst);
2446                   }
2447                }
2448                entry.serverNumber[index]    = toserver;
2449                entry.serverPartition[index] = topart;
2450             }
2451
2452             entry.flags |= RW_EXISTS;
2453             if (!VLDB_ReplaceEntry(cellHandle, pvolid, RWVOL, &entry,
2454                                    LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP, &tst)) {
2455                 goto fail_UV_RestoreVolume;
2456             }
2457             islocked = 0;
2458         }
2459     }
2460     rc = 1;
2461
2462 fail_UV_RestoreVolume:
2463
2464     if (tocall) {
2465         etst = rx_EndCall(tocall, rxError);
2466         if (!tst) tst = etst;
2467     }
2468     if(islocked) {
2469         etst = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0, pvolid, RWVOL, LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
2470         if(etst) {
2471             if(!tst) tst = etst;
2472         }
2473     }
2474     if(totid) {
2475         etst = AFSVolEndTrans(toconn, totid, &rcode);
2476         if(!etst) etst = rcode;
2477         if(etst) {
2478             if(!tst) tst = etst;
2479         }
2480     }
2481     if(temptid) {
2482         etst = AFSVolEndTrans(toconn, temptid, &rcode);
2483         if(!etst) etst = rcode;
2484         if(etst) {
2485             if(!tst) tst = etst;
2486         }
2487     }
2488
2489     if(tempconn) rx_ReleaseCachedConnection(tempconn);
2490     if(toconn) rx_ReleaseCachedConnection(toconn);
2491
2492     if (st != NULL) {
2493         *st = tst;
2494     }
2495     return rc;
2496 }
2497
2498 /*adds <server> and <part> as a readonly replication site for <volid>
2499 *in vldb */
2500 int UV_AddSite(
2501   afs_cell_handle_p cellHandle,
2502   afs_int32 server,
2503   afs_int32 part,
2504   afs_int32 volid,
2505   afs_status_p st)
2506 {
2507     int rc = 0;
2508     afs_status_t tst = 0;
2509     int j, nro=0, islocked=0;
2510     struct nvldbentry entry;
2511     int same = 0;
2512
2513     tst = ubik_Call(VL_SetLock, cellHandle->vos, 0,volid,RWVOL, VLOP_ADDSITE);
2514     if (tst) {
2515         goto fail_UV_AddSite;
2516     }
2517     islocked = 1;
2518
2519     if (!VLDB_GetEntryByID(cellHandle, volid, RWVOL, &entry, &tst)) {
2520         goto fail_UV_AddSite;
2521     }
2522     if (!ISNAMEVALID(entry.name)){
2523         tst = VOLSERBADOP;
2524         goto fail_UV_AddSite;
2525     }
2526
2527     /* See if it's too many entries */
2528     if (entry.nServers >= NMAXNSERVERS){
2529        tst = VOLSERBADOP;
2530        goto fail_UV_AddSite;
2531     }
2532
2533     /* See if it's on the same server */
2534     for (j=0; j < entry.nServers; j++) {
2535        if (entry.serverFlags[j] & ITSROVOL) {
2536           nro++;
2537           if (!VLDB_IsSameAddrs(cellHandle, server, entry.serverNumber[j], &same, &tst)) {
2538               goto fail_UV_AddSite;
2539           }  
2540           if (same) {
2541              tst =  VOLSERBADOP;
2542              goto fail_UV_AddSite;
2543           }
2544        }
2545     }
2546
2547     /* See if it's too many RO sites - leave one for the RW */
2548     if (nro >= NMAXNSERVERS-1){
2549        tst = VOLSERBADOP;
2550        goto fail_UV_AddSite;
2551     }
2552
2553     entry.serverNumber[entry.nServers] = server;
2554     entry.serverPartition[entry.nServers] = part;
2555     entry.serverFlags[entry.nServers] = (ITSROVOL | RO_DONTUSE);
2556     entry.nServers++;
2557         
2558     if (!VLDB_ReplaceEntry(cellHandle, volid, RWVOL, &entry,
2559                            LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP,
2560                            &tst)) {
2561        goto fail_UV_AddSite;
2562     }
2563     islocked = 0;
2564     rc = 1;
2565
2566 fail_UV_AddSite:
2567
2568     if (islocked) {
2569        tst = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0, volid, RWVOL,
2570                        LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
2571     }
2572
2573     if (st != NULL) {
2574         *st = tst;
2575     }
2576     return rc;
2577 }
2578
2579 /*removes <server> <part> as read only site for <volid> from the vldb */
2580 int UV_RemoveSite(
2581   afs_cell_handle_p cellHandle,
2582   afs_int32 server,
2583   afs_int32 part,
2584   afs_int32 volid,
2585   afs_status_p st)
2586 {
2587     int rc = 0;
2588     afs_status_t tst = 0;
2589     struct nvldbentry entry;
2590     int islocked = 0;
2591
2592     tst = ubik_Call(VL_SetLock, cellHandle->vos, 0,volid, RWVOL, VLOP_ADDSITE);
2593     if(tst) {
2594         goto fail_UV_RemoveSite;
2595     }
2596     islocked = 1;
2597
2598     if (!VLDB_GetEntryByID(cellHandle, volid, RWVOL, &entry, &tst)) {
2599         goto fail_UV_RemoveSite;
2600     }
2601     if (!Lp_ROMatch(cellHandle, &entry, server, part, &tst)) {
2602         /*this site doesnot exist  */
2603         goto fail_UV_RemoveSite;
2604     }
2605     else { /*remove the rep site */
2606         Lp_SetROValue(cellHandle, &entry, server, part, 0, 0);
2607         entry.nServers--;
2608         if((entry.nServers == 1) && (entry.flags & RW_EXISTS))
2609             entry.flags &= ~RO_EXISTS;
2610         if(entry.nServers < 1) { /*this is the last ref */
2611             tst = ubik_Call(VL_DeleteEntry, cellHandle->vos, 0,volid, ROVOL);
2612             if(tst) {
2613                 goto fail_UV_RemoveSite;
2614             }
2615         }
2616        if (!VLDB_ReplaceEntry(cellHandle, volid, RWVOL, &entry,
2617                         (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP), 
2618                         &tst)) {
2619            goto fail_UV_RemoveSite;
2620        }
2621     }
2622     rc = 1;
2623
2624 fail_UV_RemoveSite:
2625
2626     if (islocked) {
2627         afs_status_t t;
2628         t = ubik_Call(VL_ReleaseLock, cellHandle->vos, 0, volid, RWVOL, LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
2629         if (tst == 0) {
2630             tst = t;
2631         }
2632     }
2633
2634     if (st != NULL) {
2635         *st = tst;
2636     }
2637     return rc;
2638 }
2639
2640 /*list all the partitions on <aserver> */
2641 int UV_ListPartitions(
2642   struct rx_connection *server,
2643   struct partList *ptrPartList,
2644   afs_int32 *cntp,
2645   afs_status_p st)
2646 {
2647     int rc = 0;
2648     afs_status_t tst = 0;
2649     struct pIDs partIds;
2650     struct partEntries partEnts;
2651     register int i, j=0;
2652
2653     *cntp = 0;
2654
2655     partEnts.partEntries_len = 0;
2656     partEnts.partEntries_val = (afs_int32 *)0;
2657     /* this is available only on new servers */
2658     tst = AFSVolXListPartitions(server, &partEnts); 
2659
2660     /* next, try old interface */
2661     if (tst == RXGEN_OPCODE) 
2662     {
2663         for(i = 0; i < 26; i++)                         
2664             partIds.partIds[i]  = -1;
2665         tst = AFSVolListPartitions(server, &partIds);
2666         if (!tst) {
2667             for (i = 0;i < 26; i++) {
2668                 if((partIds.partIds[i]) != -1) {
2669                     ptrPartList->partId[j] = partIds.partIds[i];
2670                     ptrPartList->partFlags[j] = PARTVALID;
2671                     j++;
2672                 } else
2673                     ptrPartList->partFlags[i] = 0;
2674             }
2675             *cntp = j;
2676         } else {
2677             goto fail_UV_ListPartitions;
2678         }
2679     }
2680     else if (!tst) 
2681     {
2682         *cntp = partEnts.partEntries_len;
2683         if (*cntp > VOLMAXPARTS) {
2684             *cntp = VOLMAXPARTS;
2685         }
2686         for (i = 0;i < *cntp; i++) {
2687             ptrPartList->partId[i] = partEnts.partEntries_val[i];
2688             ptrPartList->partFlags[i] = PARTVALID;
2689         }
2690         free(partEnts.partEntries_val);
2691     } else {
2692         goto fail_UV_ListPartitions;
2693     }
2694     rc = 1;
2695
2696 fail_UV_ListPartitions:
2697
2698     if (st != NULL) {
2699         *st = tst;
2700     }
2701     return rc;
2702 }
2703
2704 /*------------------------------------------------------------------------
2705  * EXPORTED UV_XListVolumes
2706  *
2707  * Description:
2708  *      List the extended information for all the volumes on a particular
2709  *      File Server and partition.  We may either return the volume's ID
2710  *      or all of its extended information.
2711  *
2712  * Arguments:
2713  *      a_serverID         : Address of the File Server for which we want
2714  *                              extended volume info.
2715  *      a_partID           : Partition for which we want the extended
2716  *                              volume info.
2717  *      a_all              : If non-zero, fetch ALL the volume info,
2718  *                              otherwise just the volume ID.
2719  *      a_resultPP         : Ptr to the address of the area containing
2720  *                              the returned volume info.
2721  *      a_numEntsInResultP : Ptr for the value we set for the number of
2722  *                              entries returned.
2723  *
2724  * Returns:
2725  *      0 on success,
2726  *      Otherise, the return value of AFSVolXListVolumes.
2727  *
2728  * Environment:
2729  *      This routine is closely related to UV_ListVolumes, which returns
2730  *      only the standard level of detail on AFS volumes. It is a
2731  *      heavyweight operation, zipping through all the volume entries for
2732  *      a given server/partition.
2733  *
2734  * Side Effects:
2735  *      As advertised.
2736  *------------------------------------------------------------------------*/
2737
2738 int UV_XListVolumes(
2739   struct rx_connection *server,
2740   afs_int32 a_partID,
2741   int a_all,
2742   struct volintXInfo **a_resultPP,
2743   afs_int32 *a_numEntsInResultP,
2744   afs_status_p st)
2745 {
2746     int rc = 0;
2747     afs_status_t tst = 0;
2748
2749     volXEntries volumeXInfo;            /*Area for returned extended vol info*/
2750
2751     /*
2752      * Set up our error code and the area for returned extended volume info.
2753      * We set the val field to a null pointer as a hint for the stub to
2754      * allocate space.
2755      */
2756     *a_numEntsInResultP = 0;
2757     *a_resultPP = (volintXInfo *)0;
2758     volumeXInfo.volXEntries_val = (volintXInfo *)0;
2759     volumeXInfo.volXEntries_len = 0;
2760
2761     /*
2762      * Bind to the Volume Server port on the File Server machine in question,
2763      * then go for it.
2764      */
2765     tst = AFSVolXListVolumes(server, a_partID, a_all, &volumeXInfo);
2766     if (tst) {
2767         goto fail_UV_XListVolumes;
2768     } else {
2769         /*
2770          * We got the info; pull out the pointer to where the results lie
2771          * and how many entries are there.
2772          */
2773         *a_resultPP = volumeXInfo.volXEntries_val;
2774         *a_numEntsInResultP = volumeXInfo.volXEntries_len;
2775     }
2776     rc = 1;
2777
2778 fail_UV_XListVolumes:
2779
2780     if (st != NULL) {
2781         *st = tst;
2782     }
2783     return rc;
2784 }
2785
2786 /*------------------------------------------------------------------------
2787  * EXPORTED UV_XListOneVolume
2788  *
2789  * Description:
2790  *      List the extended information for a volume on a particular File
2791  *      Server and partition.
2792  *
2793  * Arguments:
2794  *      server     : a handle to the server where the volume resides.
2795  *      a_partID           : Partition for which we want the extended
2796  *                              volume info.
2797  *      a_volID            : Volume ID for which we want the info.
2798  *      a_resultPP         : Ptr to the address of the area containing
2799  *                              the returned volume info.
2800  *
2801  * Returns:
2802  *      0 on success,
2803  *      Otherise, the return value of AFSVolXListOneVolume.
2804  *
2805  * Environment:
2806  *      This routine is closely related to UV_ListOneVolume, which returns
2807  *      only the standard level of detail on the chosen AFS volume.
2808  *
2809  * Side Effects:
2810  *      As advertised.
2811  *------------------------------------------------------------------------*/
2812
2813 int UV_XListOneVolume(
2814   struct rx_connection *server,
2815   afs_int32 a_partID,
2816   afs_int32 a_volID,
2817   struct volintXInfo **a_resultPP,
2818   afs_status_p st)
2819 {
2820     int rc = 0;
2821     afs_status_t tst = 0;
2822     volXEntries volumeXInfo;            /*Area for returned info*/
2823
2824     /*
2825      * Set the area we're in which we are returning
2826      * the info.  Setting the val field to a null pointer tells the stub
2827      * to allocate space for us.
2828      */
2829     *a_resultPP = (volintXInfo *)0;
2830     volumeXInfo.volXEntries_val = (volintXInfo *)0;
2831     volumeXInfo.volXEntries_len = 0;
2832
2833     tst = AFSVolXListOneVolume(server, a_partID, a_volID, &volumeXInfo);
2834
2835     if (tst) {
2836         goto fail_UV_XListOneVolume;
2837     }
2838     else {
2839         /*
2840          * We got the info; pull out the pointer to where the results lie.
2841          */
2842         *a_resultPP = volumeXInfo.volXEntries_val;
2843     }
2844     rc = 1;
2845
2846 fail_UV_XListOneVolume:
2847
2848     if (st != NULL) {
2849         *st = tst;
2850     }
2851     return rc;    
2852
2853 } /*UV_XListOneVolume*/
2854
2855 /*sync vldb with all the entries on <myQueue> on <aserver> and <apart>*/
2856 static afs_int32 ProcessEntries(
2857   afs_cell_handle_p cellHandle,
2858   struct qHead *myQueue,
2859   struct rx_connection *server,
2860   afs_int32 apart,
2861   afs_int32 force)
2862 {
2863     struct aqueue elem;
2864     int success, temp,temp1,temp2;
2865     afs_int32 vcode, maxVolid = 0;
2866     struct nvldbentry entry;
2867     int noError = 1, error, same;
2868     int totalC,totalU,totalCE,totalUE,totalG;
2869     int counter;
2870     int aserver = ntohl(rx_HostOf(rx_PeerOf(server)));
2871     afs_status_t tst;
2872
2873     totalC = totalU = totalCE = totalUE = totalG = 0;
2874     counter = 0;
2875
2876     /* get the next  available id's from the vldb server */
2877     vcode = ubik_Call(VL_GetNewVolumeId,cellHandle->vos, 0, 0, &maxVolid);
2878     if(vcode) {
2879         return (vcode);
2880     }
2881     totalG = myQueue->count;
2882     if(totalG == 0) return 0;
2883     while (1) {
2884         Lp_QEnumerate(myQueue, &success, &elem, 0);
2885         if (!success) break;
2886         counter++;
2887
2888         if(!elem.isValid[RWVOL] && !elem.isValid[ROVOL] && !elem.isValid[BACKVOL]){ /*something is wrong with elem */
2889             noError = 0;
2890             continue;
2891         }
2892         if(maxVolid <= elem.ids[RWVOL]){
2893             temp1 = maxVolid;
2894             temp2 = elem.ids[RWVOL] - maxVolid + 1 ;
2895             maxVolid = 0;
2896             vcode = ubik_Call(VL_GetNewVolumeId,cellHandle->vos, 0, temp2, &maxVolid);
2897             maxVolid += temp2;
2898             
2899             
2900         }
2901         if(maxVolid <= elem.ids[ROVOL]){
2902             
2903             temp1 = maxVolid;
2904             temp2 = elem.ids[ROVOL] - maxVolid + 1;
2905             maxVolid = 0;
2906             vcode = ubik_Call(VL_GetNewVolumeId,cellHandle->vos, 0, temp2, &maxVolid);
2907             maxVolid += temp2;
2908             
2909         }
2910         if(maxVolid <= elem.ids[BACKVOL]){
2911             temp1 = maxVolid;
2912             temp2 = elem.ids[BACKVOL] - temp1 + 1;
2913             maxVolid = 0;
2914             vcode = ubik_Call(VL_GetNewVolumeId,cellHandle->vos, 0, temp2, &maxVolid);
2915             maxVolid += temp2;
2916             
2917         }
2918         VLDB_GetEntryByID(cellHandle, elem.ids[RWVOL], RWVOL, &entry, &tst);
2919         if(tst && (tst != VL_NOENT)){
2920             noError = 0;
2921             totalCE++;
2922         }
2923         else if(tst && (tst == VL_NOENT)) { /*entry doesnot exist */
2924             /*set up a vldb entry for elem */
2925             memset(&entry, 0, sizeof(entry));
2926             strncpy(entry.name,elem.name,VOLSER_OLDMAXVOLNAME);
2927             if(elem.isValid[RWVOL]) { /*rw exists */
2928                 entry.flags |= RW_EXISTS;
2929                 entry.serverFlags[entry.nServers] = ITSRWVOL;
2930                 entry.serverNumber[entry.nServers] = aserver;
2931                 entry.serverPartition[entry.nServers] = apart;
2932                 entry.nServers += 1;
2933                 entry.volumeId[RWVOL] = elem.ids[RWVOL];
2934                 entry.volumeId[ROVOL] = elem.ids[ROVOL];
2935                 entry.volumeId[BACKVOL] = elem.ids[BACKVOL];
2936             }
2937             if(elem.isValid[ROVOL]) { /*ro volume exists */
2938                 entry.flags |= RO_EXISTS;
2939                 entry.serverFlags[entry.nServers] = ITSROVOL;
2940                 entry.serverNumber[entry.nServers] = aserver;
2941                 entry.serverPartition[entry.nServers] = apart;
2942                 entry.nServers += 1;
2943                 entry.volumeId[RWVOL] = elem.ids[RWVOL];
2944                 entry.volumeId[ROVOL] = elem.ids[ROVOL];
2945                 
2946             }
2947             if(elem.isValid[BACKVOL]) { /*backup volume exists */
2948                 entry.flags |= BACK_EXISTS;
2949                 if(! (entry.flags & RW_EXISTS)) {/*this helps to check for a stray backup if parent moves */
2950                     entry.serverFlags[entry.nServers] = ITSRWVOL;
2951                     entry.serverNumber[entry.nServers] = aserver;
2952                     entry.serverPartition[entry.nServers] = apart;
2953                     entry.nServers += 1;
2954                 }
2955                 
2956                 entry.volumeId[RWVOL] = elem.ids[RWVOL];
2957                 entry.volumeId[BACKVOL] = elem.ids[BACKVOL];
2958                 
2959             }
2960             VLDB_CreateEntry(cellHandle, &entry, &tst);
2961             if(tst) {
2962                 noError = 0;
2963                 totalCE++;
2964             }
2965             else
2966                 totalC++;
2967         }
2968         else { /* Update the existing entry */
2969             strncpy(entry.name,elem.name,VOLSER_OLDMAXVOLNAME); /*the name Could have changed */
2970
2971             if (elem.isValid[RWVOL]) { /* A RW volume */
2972                temp = Lp_GetRwIndex(cellHandle, &entry, 0);
2973                if (temp == -1) {
2974                   /* A RW index is not found in the VLDB entry - will add it */
2975
2976                   entry.flags                          |= RW_EXISTS;
2977                   entry.serverNumber[entry.nServers]    = aserver;
2978                   entry.serverPartition[entry.nServers] = apart;
2979                   entry.serverFlags[entry.nServers]     = ITSRWVOL;
2980                   entry.nServers++;
2981                } else {
2982                   /* A RW index is found in the VLDB entry.
2983                    * Verify that the volume location matches the VLDB location.
2984                    * Fix the VLDB entry if it is not correct.
2985                    */
2986
2987                   error = VLDB_IsSameAddrs(cellHandle, aserver, entry.serverNumber[temp], &same, &tst);
2988                   if (!error) {
2989                      continue;
2990                   }
2991                   if (!same || (apart != entry.serverPartition[temp])) {
2992                      /* VLDB says volume is in another place. Fix the VLDB entry */
2993                      entry.serverNumber[temp]    = aserver;
2994                      entry.serverPartition[temp] = apart;
2995
2996                   }
2997                   entry.flags |= RW_EXISTS;
2998                }
2999                if ((elem.ids[BACKVOL] != 0) && elem.isValid[BACKVOL])
3000                   entry.volumeId[BACKVOL] = elem.ids[BACKVOL];
3001                if ((elem.ids[ROVOL] != 0) && elem.isValid[ROVOL])
3002                   entry.volumeId[ROVOL] = elem.ids[ROVOL];
3003             }
3004
3005             if (elem.isValid[ROVOL]) {
3006                 /*tackle a ro volume */
3007                 
3008                     if(!Lp_ROMatch(cellHandle, &entry, aserver, apart, 0)){
3009                     /*add this site */
3010                         if(elem.ids[ROVOL] > entry.volumeId[ROVOL]) {
3011                             /*there is a conflict of ids, keep the later volume */
3012                             /*delete all the ro volumes listed in vldb entry since they 
3013                              are older */
3014                             
3015                             int j,count, rwsite;
3016
3017
3018                             count = entry.nServers;
3019                             rwsite = -1;
3020                             for(j = 0 ; j < count; j++){
3021                                 if(entry.serverFlags[j] & ITSROVOL){
3022
3023                                     /*delete the site */
3024                                     entry.serverNumber[j] = 0;
3025                                     entry.serverPartition[j] = 0;
3026                                     entry.serverFlags[j] = 0;
3027                                  
3028                                 }
3029                                 else if( entry.serverFlags[j] & ITSRWVOL) 
3030                                     rwsite = j;
3031                             }
3032                             entry.nServers = 0;
3033                             if(rwsite != -1) {
3034                                 entry.serverNumber[entry.nServers] = entry.serverNumber[rwsite];
3035                                 entry.serverPartition[entry.nServers] = entry.serverPartition[rwsite];
3036                                 entry.serverFlags[entry.nServers] = entry.serverFlags[rwsite];
3037                                 entry.nServers++;
3038                             }
3039                             entry.serverNumber[entry.nServers] = aserver;
3040                             entry.serverPartition[entry.nServers] = apart;
3041                             entry.serverFlags[entry.nServers] = ITSROVOL;
3042                             entry.nServers++;
3043                             entry.volumeId[ROVOL] = elem.ids[ROVOL];
3044                             entry.flags |= RO_EXISTS;
3045                             
3046                         }
3047                         else if (elem.ids[ROVOL] < entry.volumeId[ROVOL] ){
3048                             if(!(entry.flags & RO_EXISTS)) {
3049                                 entry.volumeId[ROVOL] = elem.ids[ROVOL];
3050                                 entry.serverNumber[entry.nServers] = aserver;
3051                                 entry.serverPartition[entry.nServers] = apart;
3052                                 entry.serverFlags[entry.nServers] = ITSROVOL;
3053                                 entry.nServers++;
3054                                 entry.flags |= RO_EXISTS;
3055                             }
3056                                 
3057                         }
3058                        
3059                         else if (elem.ids[ROVOL] == entry.volumeId[ROVOL]){
3060                             entry.serverNumber[entry.nServers] = aserver;
3061                             entry.serverPartition[entry.nServers] = apart;
3062                             entry.serverFlags[entry.nServers] = ITSROVOL;
3063                             entry.nServers++;
3064                             entry.flags |= RO_EXISTS;
3065                             entry.volumeId[ROVOL] = elem.ids[ROVOL];
3066                         }
3067                     }
3068                     if(entry.volumeId[ROVOL] == INVALID_BID)
3069                         entry.volumeId[ROVOL] = elem.ids[ROVOL];
3070             }
3071
3072             if (elem.isValid[BACKVOL]) {
3073                 temp = Lp_GetRwIndex(cellHandle, &entry, 0);
3074                 if(temp != -1) { /*check if existing backup site matches with the given arguments */
3075                     error = VLDB_IsSameAddrs(cellHandle, aserver, entry.serverNumber[temp], &same, &tst);
3076                     if (!error) {
3077                         continue;
3078                     }
3079                 }
3080                 else {
3081                     /*tackle the backup volume */
3082                     entry.volumeId[BACKVOL] = elem.ids[BACKVOL];
3083                     entry.flags  |= BACK_EXISTS;
3084                 }
3085                 if(entry.volumeId[BACKVOL] == INVALID_BID) 
3086                     entry.volumeId[BACKVOL] = elem.ids[BACKVOL];
3087            }
3088             
3089            VLDB_ReplaceEntry(cellHandle, elem.ids[RWVOL], RWVOL,
3090                            &entry,
3091                            LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP,
3092                            &tst);
3093            if (tst) {
3094               noError = 0;
3095               totalUE++;
3096
3097               vcode = ubik_Call(VL_ReleaseLock,cellHandle->vos, 0,
3098                                 elem.ids[RWVOL], RWVOL,
3099                                 LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
3100               if(vcode) {
3101                  noError = 0;
3102               }
3103            }
3104         } /* else update the existing entry */
3105
3106     } /* End of while(1) */
3107
3108     if(noError) return 0;
3109     else return VOLSERBADOP;
3110 }
3111
3112 /*synchronise vldb with the file server <aserver> and <apart>(if flags=1).
3113 *else synchronise with all the valid partitions on <aserver>
3114 */
3115 int UV_SyncVldb(
3116   afs_cell_handle_p cellHandle,
3117   struct rx_connection *server,
3118   afs_int32 apart,
3119   int flags,
3120   int force,
3121   afs_status_p st)
3122 {
3123     int rc = 0;
3124     afs_status_t tst = 0;
3125     afs_int32 count;
3126     int i;
3127     volEntries volumeInfo;
3128     volintInfo *pntr;
3129     struct qHead myQueue;
3130     struct partList PartList;
3131     int noError = 1;
3132     afs_int32 cnt;
3133     char pname[10];
3134
3135     /*this hints the stub to allocate space*/
3136     volumeInfo.volEntries_val = (volintInfo *)0;
3137     volumeInfo.volEntries_len = 0;
3138
3139     if(!flags) {/*generate all the valid partitions */
3140         UV_ListPartitions(server, &PartList, &cnt, &tst);
3141         if(tst) {
3142             goto fail_UV_SyncVldb;
3143         }
3144     }  else {
3145         PartList.partId[0] = apart;
3146         cnt = 1;
3147     }
3148
3149     for(i = 0;i < cnt; i++) {
3150             apart = PartList.partId[i];
3151             /*this hints the stub to allocate space*/
3152             volumeInfo.volEntries_val = (volintInfo *)0;
3153             volumeInfo.volEntries_len = 0;
3154             tst = AFSVolListVolumes(server, apart, 1, &volumeInfo);
3155             if(tst) {
3156                 goto fail_UV_SyncVldb;
3157             }
3158             count = volumeInfo.volEntries_len;
3159             pntr = volumeInfo.volEntries_val;
3160  
3161             if (!vos_PartitionIdToName(apart,pname,&tst)) {
3162                 goto fail_UV_SyncVldb;
3163             }
3164             /*collect all vol entries by their parentid */
3165             tst = GroupEntries(server, pntr, count, &myQueue, apart);
3166             if(tst) {
3167                 noError = 0;
3168                 if (volumeInfo.volEntries_val) {
3169                     /*free the space allocated by the stub */
3170                     free(volumeInfo.volEntries_val);
3171                     volumeInfo.volEntries_val = 0;
3172                 }
3173                 continue;
3174             }
3175             tst = ProcessEntries(cellHandle, &myQueue, server, apart, force);
3176             if(tst) {
3177                 tst = VOLSERFAILEDOP;
3178                 if (volumeInfo.volEntries_val) {
3179                     /*free the space allocated by the stub */
3180                     free(volumeInfo.volEntries_val);
3181                     volumeInfo.volEntries_val = 0;
3182                 }
3183                 continue;
3184             }
3185             if(noError) tst = 0;
3186             else tst = VOLSERFAILEDOP;
3187     }/* thru all partitions */
3188     rc = 1;
3189
3190 fail_UV_SyncVldb:
3191
3192     if (volumeInfo.volEntries_val) free(volumeInfo.volEntries_val);
3193
3194     if (tst != 0) {
3195         rc = 0;
3196     }
3197
3198     if (st != NULL) {
3199         *st = tst;
3200     }
3201     return rc;
3202 }
3203
3204 static afs_int32 CheckVldbRWBK(
3205   afs_cell_handle_p cellHandle,
3206   struct nvldbentry *entry,
3207   afs_int32 *modified,
3208   afs_status_p st)
3209 {
3210    int rc = 0;
3211    afs_status_t tst = 0;
3212    int modentry = 0;
3213    int idx;
3214  
3215    if (modified) *modified = 0;
3216    idx = Lp_GetRwIndex(cellHandle, entry, 0);
3217  
3218    /* Check to see if the RW volume exists and set the RW_EXISTS
3219     * flag accordingly.
3220     */
3221    if (idx == -1) {                          /* Did not find a RW entry */
3222       if (entry->flags & RW_EXISTS) {        /* ... yet entry says RW exists */
3223          entry->flags &= ~RW_EXISTS;         /* ... so say RW does not exist */
3224          modentry++;
3225       }
3226    } else {
3227       if (VolumeExists(cellHandle,
3228                           entry->serverNumber[idx],
3229                           entry->serverPartition[idx],
3230                           entry->volumeId[RWVOL], &tst)) {
3231          if (!(entry->flags & RW_EXISTS)) {     /* ... yet entry says RW does no
3232 t exist */
3233             entry->flags |= RW_EXISTS;          /* ... so say RW does exist */
3234             modentry++;
3235          }
3236       }
3237       else if (tst == ENODEV) {                /* RW volume does not exist */
3238          if (entry->flags & RW_EXISTS) {        /* ... yet entry says RW exists
3239 */
3240             entry->flags &= ~RW_EXISTS;         /* ... so say RW does not exist
3241 */
3242             modentry++;
3243          }
3244       }
3245       else {
3246          /* If VLDB says it didn't exist, then ignore error */
3247          if (entry->flags & RW_EXISTS) {
3248              goto fail_CheckVldbRWBK;
3249          }
3250       }
3251    }
3252  
3253    /* Check to see if the BK volume exists and set the BACK_EXISTS
3254     * flag accordingly. idx already ponts to the RW entry.
3255     */
3256    if (idx == -1) {                         /* Did not find a RW entry */
3257       if (entry->flags & BACK_EXISTS) {     /* ... yet entry says BK exists */
3258          entry->flags &= ~BACK_EXISTS;      /* ... so say BK does not exist */
3259          modentry++;
3260       }
3261    }
3262    else {                                            /* Found a RW entry */
3263       if (VolumeExists(cellHandle, entry->serverNumber[idx],
3264                           entry->serverPartition[idx],
3265                           entry->volumeId[BACKVOL], &tst)) {
3266          if (!(entry->flags & BACK_EXISTS)) {    /* ... yet entry says BK does n
3267 ot exist */
3268             entry->flags |= BACK_EXISTS;         /* ... so say BK does exist */
3269             modentry++;
3270          }
3271       }
3272       else if (tst == ENODEV) {                 /* BK volume does not exist */
3273          if (entry->flags & BACK_EXISTS) {       /* ... yet entry says BK exists
3274  */
3275             entry->flags &= ~BACK_EXISTS;        /* ... so say BK does not exist
3276  */
3277             modentry++;
3278          }
3279       }
3280       else {
3281          /* If VLDB says it didn't exist, then ignore error */
3282          if (entry->flags & BACK_EXISTS) {
3283              goto fail_CheckVldbRWBK;
3284          }
3285       }
3286    }
3287  
3288    /* If there is an idx but the BK and RW volumes no
3289     * longer exist, then remove the RW entry.
3290     */
3291    if ((idx != -1) && !(entry->flags & RW_EXISTS) &&
3292                       !(entry->flags & BACK_EXISTS)) {
3293       Lp_SetRWValue(cellHandle, entry, entry->serverNumber[idx],
3294                     entry->serverPartition[idx], 0L, 0L);
3295       entry->nServers--;
3296       modentry++;
3297    }
3298    rc = 1;
3299  
3300  fail_CheckVldbRWBK:
3301
3302    if (modified) *modified = modentry;
3303
3304    if (st != NULL) {
3305       *st = tst;
3306    } 
3307    return rc;
3308 }
3309
3310
3311 static int CheckVldbRO(
3312   afs_cell_handle_p cellHandle,
3313   struct nvldbentry *entry,
3314   afs_int32 *modified,
3315   afs_status_p st)
3316 {
3317    int rc = 0;
3318    afs_status_t tst = 0;
3319    int idx;
3320    int foundro = 0, modentry = 0;
3321  
3322    if (modified) *modified = 0;
3323  
3324    /* Check to see if the RO volumes exist and set the RO_EXISTS
3325     * flag accordingly.
3326     */
3327    for (idx=0; idx < entry->nServers; idx++) {
3328       if (!(entry->serverFlags[idx] & ITSROVOL)) {
3329          continue;   /* not a RO */
3330       }
3331  
3332       if(VolumeExists(cellHandle, entry->serverNumber[idx],
3333                       entry->serverPartition[idx], entry->volumeId[ROVOL],
3334                       &tst)) {
3335          foundro++;
3336       } else if (tst == ENODEV) {                /* RW volume does not exist */
3337          Lp_SetROValue(cellHandle, entry, entry->serverNumber[idx],
3338                        entry->serverPartition[idx], 0L, 0L);
3339          entry->nServers--;
3340          idx--;
3341          modentry++;
3342       }
3343       else {
3344           goto fail_CheckVldbRO;
3345       }
3346    }
3347  
3348    if (foundro) {                            /* A RO volume exists */
3349       if (!(entry->flags & RO_EXISTS)) {     /* ... yet entry says RW does not e
3350 xist */
3351          entry->flags |= RO_EXISTS;          /* ... so say RW does exist */
3352          modentry++;
3353       }
3354    } else {                                  /* A RO volume does not exist */
3355       if (entry->flags & RO_EXISTS) {        /* ... yet entry says RO exists */
3356          entry->flags &= ~RO_EXISTS;         /* ... so say RO does not exist */
3357          modentry++;
3358       }
3359    }
3360    rc = 1;
3361  
3362 fail_CheckVldbRO:
3363
3364    if (modified) *modified = modentry;
3365
3366    if (st != NULL) {
3367        *st = tst;
3368    }
3369    return rc;
3370 }
3371
3372 /*ensure that <entry> matches with the info on file servers */
3373 int CheckVldb(
3374   afs_cell_handle_p cellHandle,
3375   struct nvldbentry *entry,
3376   afs_int32 *modified,
3377   afs_status_p st)
3378 {
3379     int rc = 0;
3380     afs_status_t tst = 0;
3381     afs_int32 vcode;
3382     int islocked = 0;
3383     int pass = 0;
3384     afs_int32 modentry = 0;
3385     afs_int32 delentry = 0;
3386
3387     if (modified) {
3388         *modified = 0;
3389     }
3390
3391     if(strlen(entry->name) > (VOLSER_OLDMAXVOLNAME - 10)) {
3392         tst = VOLSERBADOP;
3393         goto fail_CheckVldb;
3394     }
3395
3396 retry:
3397
3398    /* Check to see if the VLDB is ok without locking it (pass 1).
3399     * If it will change, then lock the VLDB entry, read it again,
3400     * then make the changes to it (pass 2).
3401     */
3402    if (++pass == 2) {
3403       tst = ubik_Call(VL_SetLock, cellHandle->vos, 0,
3404                       entry->volumeId[RWVOL], RWVOL, VLOP_DELETE);
3405       if (tst) {
3406           goto fail_CheckVldb;
3407       }
3408       islocked = 1;
3409  
3410       if (!VLDB_GetEntryByID(cellHandle, entry->volumeId[RWVOL], RWVOL,
3411                              entry, &tst)) {
3412           goto fail_CheckVldb;
3413       }
3414    }
3415  
3416    modentry = 0;
3417  
3418    /* Check if the RW and BK entries are ok */
3419    if (!CheckVldbRWBK(cellHandle, entry, &modentry, &tst)) {
3420       goto fail_CheckVldb;
3421    }
3422    if (modentry && (pass == 1)) goto retry;
3423  
3424    /* Check if the RO volumes entries are ok */
3425    if (!CheckVldbRO(cellHandle, entry, &modentry, &tst)) {
3426       goto fail_CheckVldb;
3427    }
3428    if (modentry && (pass == 1)) goto retry;
3429  
3430    /* The VLDB entry has been updated. If it as been modified, then
3431     * write the entry back out the the VLDB.
3432     */
3433    if (modentry) {
3434       if (pass == 1) goto retry;
3435  
3436       if (!(entry->flags & RW_EXISTS)   &&
3437           !(entry->flags & BACK_EXISTS) &&
3438           !(entry->flags & RO_EXISTS)) {
3439          /* The RW, BK, nor RO volumes do not exist. Delete the VLDB entry */
3440          tst = ubik_Call(VL_DeleteEntry, cellHandle->vos, 0,
3441                          entry->volumeId[RWVOL], RWVOL);
3442          if (tst) {
3443             goto fail_CheckVldb;
3444          }
3445          delentry = 1;
3446       }
3447       else {
3448          /* Replace old entry with our new one */
3449          if (!VLDB_ReplaceEntry(cellHandle, entry->volumeId[RWVOL], RWVOL,
3450                                 entry,
3451                                 (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP), &tst)) {
3452             goto fail_CheckVldb;
3453          }
3454       }
3455       if (modified) *modified = 1;
3456       islocked = 0;
3457    }
3458    rc = 1;
3459  
3460  fail_CheckVldb:
3461  
3462    if (islocked) {
3463       vcode = ubik_Call(VL_ReleaseLock, cellHandle->vos, 0,
3464                         entry->volumeId[RWVOL], RWVOL,
3465                        (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP),
3466                        &tst);
3467       if (vcode) {
3468          if (!tst) tst = vcode;
3469       }
3470    }
3471
3472    if (st != NULL) {
3473        *st = tst;
3474    }
3475    return rc;
3476 }
3477
3478 /*synchronise <aserver> <apart>(if flags = 1) with the vldb .
3479 *if flags = 0, synchronise all the valid partitions on <aserver>*/
3480 int UV_SyncServer(
3481   afs_cell_handle_p cellHandle,
3482   struct rx_connection *server,
3483   afs_int32 apart,
3484   int flags,
3485   afs_status_p st)
3486 {
3487     int rc = 0;
3488     afs_status_t tst = 0;
3489     afs_int32 code, vcode;
3490     int noError;
3491     afs_int32 nentries, tentries = 0;
3492     struct VldbListByAttributes attributes;
3493     nbulkentries arrayEntries;
3494     int totalF;
3495     register struct nvldbentry *vllist;
3496     register int j;
3497     afs_int32 si, nsi;
3498     afs_int32 modified = 0;
3499
3500     code = 0;
3501     vcode = 0;
3502     noError = 1;
3503     arrayEntries.nbulkentries_val = 0;
3504
3505     /* Set up attributes to search VLDB  */
3506     attributes.server = ntohl(rx_HostOf(rx_PeerOf(server)));
3507     attributes.Mask = VLLIST_SERVER;
3508     if(flags) {
3509         attributes.partition = apart;
3510         attributes.Mask |= VLLIST_PARTITION;
3511     }
3512
3513     for(si = 0;si != -1; si=nsi) {
3514             /*initialize to hint the stub  to alloc space */
3515             memset(&arrayEntries, 0, sizeof(arrayEntries)); 
3516             if (!VLDB_ListAttributes(cellHandle, &attributes, &nentries,
3517                                      &arrayEntries, &tst)) {
3518                 goto fail_UV_SyncServer;
3519             }
3520             nsi = -1;
3521             tentries += nentries;
3522             totalF = 0;
3523             for(j=0;j<nentries;j++) {   /* process each entry */
3524                 vllist = &arrayEntries.nbulkentries_val[j];
3525                 if (!CheckVldb(cellHandle, vllist, &modified, &tst)) {
3526                     noError = 0;
3527                     totalF++;
3528                 }
3529             }
3530             if(arrayEntries.nbulkentries_val) {
3531                 free(arrayEntries.nbulkentries_val);
3532                 arrayEntries.nbulkentries_val = 0;
3533             }
3534     }
3535     rc = 1;
3536
3537 fail_UV_SyncServer:    
3538
3539     if(arrayEntries.nbulkentries_val) {
3540         free(arrayEntries.nbulkentries_val);
3541     }
3542     if(!noError) tst = VOLSERFAILEDOP;
3543     if (st != NULL) {
3544         *st = tst;
3545     }
3546     return rc;
3547 }
3548
3549 /*rename volume <oldname> to <newname>, changing the names of the related 
3550  *readonly and backup volumes. This operation is also idempotent.
3551  *salvager is capable of recovering from rename operation stopping halfway.
3552  *to recover run syncserver on the affected machines,it will force renaming to completion. name clashes should have been detected before calling this proc */
3553 int UV_RenameVolume(
3554   afs_cell_handle_p cellHandle,
3555   struct nvldbentry *entry,
3556   const char *newname,
3557   afs_status_p st)
3558 {
3559     int rc = 0;
3560     afs_status_t tst = 0;
3561     afs_status_t etst = 0;
3562     afs_int32 rcode,error;
3563     int i,index;
3564     char nameBuffer[256];
3565     afs_int32 tid;
3566     struct rx_connection *aconn;
3567     int islocked;
3568
3569     error = 0;
3570     aconn = (struct rx_connection *)0;
3571     tid = 0;
3572     islocked = 0;
3573
3574     tst = ubik_Call(VL_SetLock, cellHandle->vos, 0, entry->volumeId[RWVOL],
3575                     RWVOL, VLOP_ADDSITE);/*last param is dummy*/
3576     if(tst){
3577         goto fail_UV_RenameVolume;
3578     }
3579     islocked = 1;
3580
3581     strncpy(entry->name,newname,VOLSER_OLDMAXVOLNAME);
3582
3583     if (!VLDB_ReplaceEntry(cellHandle, entry->volumeId[RWVOL], RWVOL,
3584                            entry, 0, &tst)) {
3585         goto fail_UV_RenameVolume;
3586     }
3587     /*at this stage the intent to rename is recorded in the vldb, as far
3588       as the vldb 
3589       is concerned, oldname is lost */
3590     if(entry->flags & RW_EXISTS) {
3591         index = Lp_GetRwIndex(cellHandle, entry, 0);
3592         if(index == -1){ /* there is a serious discrepancy */
3593             tst = VOLSERVLDB_ERROR;
3594             goto fail_UV_RenameVolume;
3595         }
3596         aconn = UV_Bind(cellHandle, entry->serverNumber[index], 
3597                         AFSCONF_VOLUMEPORT);
3598         tst = AFSVolTransCreate(aconn,entry->volumeId[RWVOL],entry->serverPartition[index],  ITOffline, &tid);
3599         if(tst) { /*volume doesnot exist */
3600             goto fail_UV_RenameVolume;
3601         }
3602         else {/*volume exists, process it */
3603
3604             tst = AFSVolSetIdsTypes(aconn, tid, newname,RWVOL, entry->volumeId[RWVOL],entry->volumeId[ROVOL],entry->volumeId[BACKVOL]);
3605             if(!tst) {
3606                 tst = AFSVolEndTrans(aconn, tid, &rcode);
3607                 tid = 0;
3608                 if(tst) {
3609                     goto fail_UV_RenameVolume;
3610                 }
3611             }
3612             else {
3613                 goto fail_UV_RenameVolume;
3614             }
3615         }
3616         if(aconn) rx_ReleaseCachedConnection(aconn);
3617         aconn = (struct rx_connection *)0;
3618     } /*end rw volume processing */
3619
3620     if(entry->flags & BACK_EXISTS) {/*process the backup volume */
3621         index = Lp_GetRwIndex(cellHandle, entry, 0);
3622         if(index == -1){ /* there is a serious discrepancy */
3623             tst = VOLSERVLDB_ERROR;
3624             goto fail_UV_RenameVolume;
3625         }
3626         aconn = UV_Bind(cellHandle, entry->serverNumber[index],
3627                         AFSCONF_VOLUMEPORT);
3628         tst = AFSVolTransCreate(aconn,entry->volumeId[BACKVOL],entry->serverPartition[index],  ITOffline, &tid);
3629         if(tst) { /*volume doesnot exist */
3630             goto fail_UV_RenameVolume;
3631         }
3632         else {/*volume exists, process it */
3633             if(strlen(newname) > (VOLSER_OLDMAXVOLNAME - 8)){
3634                 goto fail_UV_RenameVolume;
3635             }
3636             strcpy(nameBuffer,newname);
3637             strcat(nameBuffer,".backup");
3638
3639             tst = AFSVolSetIdsTypes(aconn, tid,nameBuffer ,BACKVOL, entry->volumeId[RWVOL],0,0);
3640             if(!tst) {
3641                 tst = AFSVolEndTrans(aconn, tid, &rcode);
3642                 tid = 0;
3643                 if(tst) {
3644                     goto fail_UV_RenameVolume;
3645                 }
3646             }
3647             else {
3648                 goto fail_UV_RenameVolume;
3649             }
3650         }
3651     } /* end backup processing */
3652     if(aconn) rx_ReleaseCachedConnection(aconn);
3653     aconn = (struct rx_connection *)0;
3654     if(entry->flags & RO_EXISTS){  /*process the ro volumes */
3655         for(i = 0; i < entry->nServers; i++){
3656             if(entry->serverFlags[i] & ITSROVOL) {
3657                 aconn = UV_Bind(cellHandle, entry->serverNumber[i],
3658                                 AFSCONF_VOLUMEPORT);
3659                 tst = AFSVolTransCreate(aconn,entry->volumeId[ROVOL],entry->serverPartition[i],  ITOffline, &tid);
3660                 if(tst) { /*volume doesnot exist */
3661                     goto fail_UV_RenameVolume;
3662                 }
3663                 else {/*volume exists, process it */
3664                     strcpy(nameBuffer,newname);
3665                     strcat(nameBuffer,".readonly");
3666                     if(strlen(nameBuffer) > (VOLSER_OLDMAXVOLNAME - 1)){
3667                         goto fail_UV_RenameVolume;
3668                     }
3669                     tst = AFSVolSetIdsTypes(aconn, tid, nameBuffer,ROVOL, entry->volumeId[RWVOL],0,0);
3670                     if(!tst) {
3671                         tst = AFSVolEndTrans(aconn, tid, &rcode);
3672                         tid = 0;
3673                         if (tst) {
3674                             goto fail_UV_RenameVolume;
3675                         }
3676                     }
3677                     else {
3678                         goto fail_UV_RenameVolume;
3679                     }
3680                 }
3681                 if(aconn) rx_ReleaseCachedConnection(aconn);
3682                 aconn = (struct rx_connection *)0;
3683             }
3684         }
3685     }
3686     rc = 1;
3687
3688 fail_UV_RenameVolume:
3689
3690     if(islocked) {
3691         etst = ubik_Call(VL_ReleaseLock, cellHandle->vos, 0,entry->volumeId[RWVOL] , RWVOL, LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP);
3692         if(etst){
3693             if(!tst) tst = etst;
3694         }
3695     }
3696     if(tid) {
3697         etst = AFSVolEndTrans(aconn, tid, &rcode);
3698         if(etst){
3699             if(!tst) tst = etst;
3700         }
3701     }
3702     if(aconn) rx_ReleaseCachedConnection(aconn);
3703
3704     if (st != NULL) {
3705         *st = tst;
3706     }
3707     return rc;
3708 }
3709
3710 /*group all the volume entries on< apart >by their parentid or by their ids'
3711 *if the volume is rw. <count> is the number of entries to be processesd.
3712 *<pntr> points to the first entry.< myQueue> is the queue with entries 
3713 *grouped */
3714 static afs_int32 GroupEntries(
3715   struct rx_connection *server,
3716   volintInfo *pntr,
3717   afs_int32 count,
3718   struct qHead *myQueue,
3719   afs_int32 apart)
3720 {
3721     struct aqueue *qPtr;
3722     int success;
3723     afs_int32 curId, code;
3724     int i;
3725     afs_int32 error = 0;
3726     
3727     
3728     Lp_QInit(myQueue);
3729     if(count == 0)
3730         return 0;
3731     for(i = 0; i < count; i++){/*process each entry */
3732         if(pntr->status) {/* entry is valid */
3733             if(pntr->type == RWVOL) curId = pntr->volid;
3734             else curId = pntr->parentID;
3735             Lp_QScan(myQueue,curId, &success, &qPtr, 0);
3736             if(success){ /*entry exists in the queue */
3737                 if(pntr->type == RWVOL) {
3738                     /*check if the rw exists already, if so hang on the
3739                         later version if the present version is ok */
3740                     if(qPtr->isValid[RWVOL]) {
3741                     /*this should not happen, there is a serious error here*/
3742                         if(!error) error  = VOLSERMULTIRWVOL;
3743                     }
3744                     else {
3745                         qPtr->isValid[RWVOL] = 1;
3746                         qPtr->copyDate[RWVOL] = pntr->copyDate;
3747                         if(!qPtr->isValid[BACKVOL]) qPtr->ids[BACKVOL] = pntr->backupID;
3748                         if(!qPtr->isValid[ROVOL]) qPtr->ids[ROVOL] = pntr->cloneID;
3749                     }
3750                 }
3751                 else if(pntr->type == BACKVOL) {
3752                     if(qPtr->isValid[BACKVOL]){
3753                         /*do different checks, delete superflous volume */
3754                         if(qPtr->copyDate[BACKVOL] > pntr->copyDate) {
3755                             /*delete the present volume .*/
3756                             code = CheckAndDeleteVolume(server,apart,0,pntr->volid);
3757                             if(code) {
3758                                 if(!error) error = code;
3759                             }
3760                             
3761                         }
3762                         else {
3763                             /*delete the older volume after making sure, current one is ok*/
3764                             code = CheckAndDeleteVolume(server,apart,pntr->volid,qPtr->ids[BACKVOL]);
3765                             if(code) {
3766                                 if(!error) error = code;
3767                             }
3768                             
3769                             qPtr->copyDate[BACKVOL] = pntr->copyDate;
3770                             qPtr->ids[BACKVOL] = pntr->volid;
3771
3772                         }
3773                     }
3774                     else {
3775                         qPtr->isValid[BACKVOL] = 1;
3776                         qPtr->ids[BACKVOL] = pntr->volid;
3777                         qPtr->copyDate[BACKVOL] = pntr->copyDate;
3778                     }
3779                 }
3780                 else if(pntr->type == ROVOL) {
3781                     if(qPtr->isValid[ROVOL]){
3782                         /*do different checks, delete superflous volume */
3783                         if(qPtr->copyDate[ROVOL] > pntr->copyDate) {
3784                             /*delete the present volume .*/
3785                             /*a hack */
3786                             code = CheckAndDeleteVolume(server,apart,0,pntr->volid);
3787                             if(code) {
3788                                 if(!error) error = code;
3789                             }
3790                         }
3791                         else {
3792                             /*delete the older volume after making sure, current one is ok*/
3793                             code = CheckAndDeleteVolume(server,apart,pntr->volid,qPtr->ids[ROVOL]);
3794                             if(code) {
3795                                 if(!error) error = code;
3796                             }
3797
3798                             qPtr->copyDate[ROVOL] = pntr->copyDate;
3799                             qPtr->ids[ROVOL] = pntr->volid;
3800
3801                         }
3802                     }
3803                     else {
3804                         qPtr->isValid[ROVOL] = 1;
3805                         qPtr->ids[ROVOL] = pntr->volid;
3806                         qPtr->copyDate[ROVOL] = pntr->copyDate;
3807                     }
3808                 }
3809                 else {
3810                     if(!error) error = VOLSERBADOP;
3811                 }
3812             }
3813             else { /*create a fresh entry */
3814                 qPtr = (struct aqueue *) malloc(sizeof(struct aqueue));
3815                 if(pntr->type == RWVOL) {
3816                     qPtr->isValid[RWVOL] = 1;
3817                     qPtr->isValid[BACKVOL] = 0;
3818                     qPtr->isValid[ROVOL] = 0;
3819                     qPtr->ids[RWVOL] = pntr->volid;
3820                     qPtr->ids[BACKVOL] = pntr->backupID;
3821                     qPtr->ids[ROVOL] = pntr->cloneID;
3822                     qPtr->copyDate[RWVOL] = pntr->copyDate;
3823                     strncpy(qPtr->name, pntr->name,VOLSER_OLDMAXVOLNAME);
3824                     qPtr->next = (struct aqueue *)0;
3825                 }
3826                 else if(pntr->type == BACKVOL) {
3827                     qPtr->isValid[RWVOL] = 0;
3828                     qPtr->isValid[BACKVOL] = 1;
3829                     qPtr->isValid[ROVOL] = 0;
3830                     qPtr->ids[RWVOL] = pntr->parentID;
3831                     qPtr->ids[BACKVOL] = pntr->volid;
3832                     qPtr->ids[ROVOL] = 0;
3833                     qPtr->copyDate[BACKVOL] = pntr->copyDate;
3834                     vsu_ExtractName(qPtr->name, pntr->name);
3835                     qPtr->next = (struct aqueue *)0;
3836                 }
3837                 else if(pntr->type == ROVOL) {
3838                     qPtr->isValid[RWVOL] = 0;
3839                     qPtr->isValid[BACKVOL] = 0;
3840                     qPtr->isValid[ROVOL] = 1;
3841                     qPtr->ids[RWVOL] = pntr->parentID;
3842                     qPtr->ids[BACKVOL] = 0;
3843                     qPtr->ids[ROVOL] = pntr->volid;
3844                     qPtr->copyDate[ROVOL] = pntr->copyDate;
3845                     vsu_ExtractName(qPtr->name, pntr->name);
3846                     qPtr->next = (struct aqueue *)0;
3847
3848                 }
3849                 Lp_QAdd(myQueue,qPtr);
3850             }
3851             pntr++;/*get next entry */
3852         }
3853         else {
3854             pntr++;
3855             continue;
3856         }
3857     }/* for loop */
3858
3859     return error;
3860 }
3861
3862 /*report on all the active transactions on volser */
3863 int UV_VolserStatus(
3864   struct rx_connection *server,
3865   transDebugInfo **rpntr,
3866   afs_int32 *rcount,
3867   afs_status_p st)
3868 {
3869     int rc = 0;
3870     afs_status_t tst = 0;
3871     transDebugEntries transInfo;
3872     
3873     transInfo.transDebugEntries_val = (transDebugInfo *) 0;
3874     transInfo.transDebugEntries_len = 0;
3875     tst = AFSVolMonitor(server,&transInfo);
3876     if(tst) {
3877         goto fail_UV_VolserStatus;
3878     }
3879
3880     *rcount = transInfo.transDebugEntries_len;
3881     *rpntr = transInfo.transDebugEntries_val;
3882     rc = 1;
3883
3884 fail_UV_VolserStatus:
3885
3886     if (rc == 0) {
3887         if (transInfo.transDebugEntries_val) {
3888             free(transInfo.transDebugEntries_val);
3889         }
3890     }
3891
3892     if (st != NULL) {
3893         *st = tst;
3894     }
3895     return rc;
3896 }
3897
3898 /*delete the volume without interacting with the vldb */
3899 int UV_VolumeZap(
3900   afs_cell_handle_p cellHandle,
3901   struct rx_connection *server,
3902   unsigned int partition,
3903   unsigned int volumeId,
3904   afs_status_p st)
3905 {
3906     afs_int32 rcode,ttid,error,code;
3907     int rc = 0;
3908     afs_status_t tst = 0;
3909
3910     code = 0;
3911     error = 0;
3912     ttid = 0;
3913  
3914     tst = AFSVolTransCreate(server, volumeId, partition, ITOffline, &ttid);
3915     if(!tst){
3916         tst = AFSVolDeleteVolume(server, ttid);
3917         if (!tst) {
3918             tst = AFSVolEndTrans(server, ttid, &rcode);
3919             if (!tst) {
3920                 if (rcode) {
3921                     tst = rcode;
3922                 }
3923             }
3924         } else {
3925             /*
3926              * We failed to delete the volume, but we still need
3927              * to end the transaction.
3928              */
3929             AFSVolEndTrans(server, ttid, &rcode);
3930         }
3931         rc = 1;
3932     }
3933                 
3934     if (st != NULL) {
3935         *st = tst;
3936     }
3937     return rc;
3938 }
3939
3940 int UV_SetVolume(
3941   struct rx_connection *server,
3942   afs_int32 partition,
3943   afs_int32 volid,
3944   afs_int32 transflag,
3945   afs_int32 setflag,
3946   unsigned int sleepTime,
3947   afs_status_p st)
3948 {
3949     int rc = 0;
3950     afs_status_t tst = 0;
3951     afs_status_t etst = 0;
3952     afs_int32 tid=0;
3953     afs_int32 rcode;
3954
3955     tst = AFSVolTransCreate(server, volid, partition, transflag, &tid);
3956     if (tst) {
3957         goto fail_UV_SetVolume;
3958     }
3959
3960     tst = AFSVolSetFlags(server, tid, setflag);
3961     if (tst) {
3962         goto fail_UV_SetVolume;
3963     }
3964
3965     if (sleepTime) {
3966         sleep(sleepTime);
3967     }
3968     rc = 1;
3969
3970 fail_UV_SetVolume:
3971
3972     if (tid) {
3973         etst = AFSVolEndTrans(server, tid, &rcode);
3974         if (etst || etst) {
3975             if (!tst) tst = (etst ? etst : rcode);
3976         }
3977     }
3978
3979     if (st != NULL) {
3980         *st = tst;
3981     }
3982     return rc;
3983 }