windows-afsd-volume-ref-leak-20070202
[openafs.git] / src / WINNT / afsd / cm_volume.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <winsock2.h>
15 #include <nb30.h>
16 #include <string.h>
17 #include <malloc.h>
18 #include "afsd.h"
19 #include <osi.h>
20 #include <rx/rx.h>
21
22 osi_rwlock_t cm_volumeLock;
23
24 long 
25 cm_ValidateVolume(void)
26 {
27     cm_volume_t * volp;
28     afs_uint32 count;
29
30     for (volp = cm_data.allVolumesp, count = 0; volp; volp=volp->nextp, count++) {
31         if ( volp->magic != CM_VOLUME_MAGIC ) {
32             afsi_log("cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC");
33             fprintf(stderr, "cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC\n");
34             return -1;
35         }
36         if ( volp->cellp && volp->cellp->magic != CM_CELL_MAGIC ) {
37             afsi_log("cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC");
38             fprintf(stderr, "cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC\n");
39             return -2;
40         }
41         if ( volp->nextp && volp->nextp->magic != CM_VOLUME_MAGIC ) {
42             afsi_log("cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC");
43             fprintf(stderr, "cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC\n");
44             return -3;
45         }
46         if ( count != 0 && volp == cm_data.allVolumesp || 
47              count > cm_data.maxVolumes ) {
48             afsi_log("cm_ValidateVolume failure: cm_data.allVolumep loop detected");
49             fprintf(stderr, "cm_ValidateVolume failure: cm_data.allVolumep loop detected\n");
50             return -4;
51         }
52     }
53
54     if ( count != cm_data.currentVolumes ) {
55         afsi_log("cm_ValidateVolume failure: count != cm_data.currentVolumes");
56         fprintf(stderr, "cm_ValidateVolume failure: count != cm_data.currentVolumes\n");
57         return -5;
58     }
59     
60     return 0;
61 }
62
63 long
64 cm_ShutdownVolume(void)
65 {
66     cm_volume_t * volp;
67
68     for (volp = cm_data.allVolumesp; volp; volp=volp->nextp)
69         lock_FinalizeMutex(&volp->mx);
70
71     return 0;
72 }
73
74 void cm_InitVolume(int newFile, long maxVols)
75 {
76     static osi_once_t once;
77
78     if (osi_Once(&once)) {
79         lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock");
80
81         if ( newFile ) {
82             cm_data.allVolumesp = NULL;
83             cm_data.currentVolumes = 0;
84             cm_data.maxVolumes = maxVols;
85         } else {
86             cm_volume_t * volp;
87
88             for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
89                 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
90                 volp->flags |= CM_VOLUMEFLAG_RESET;
91                 volp->rwServersp = NULL;
92                 volp->roServersp = NULL;
93                 volp->bkServersp = NULL;
94             }
95         }
96         osi_EndOnce(&once);
97     }
98 }
99
100 /*
101  * Update a volume.  Caller holds volume's lock (volp->mx).
102  *
103  *
104  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:38    (JHutz)
105  *    Yes, we support multihomed fileservers.
106  *    Since before we got the code from IBM.
107  *    But to find out about multiple addresses on a multihomed server, you need
108  *    to use VL_GetEntryByNameU and VL_GetAddrsU.  If you use
109  *    VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
110  *    address per server.
111  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:39    (JHutz)
112  *    see src/afs/afs_volume.c, paying particular attention to
113  *    afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
114  *  shadow / openafs / jaltman {ANDREW.CMU.EDU}  01:40    (Jeffrey Altman)
115  *    thanks.  The windows client calls the 0 versions.
116  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:51    (JHutz)
117  *    Oh.  Ew.
118  *    By not using the N versions, you only get up to 8 sites instead of 13.
119  *    By not using the U versions, you don't get to know about multihomed serve
120  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:52    (JHutz)
121  *    Of course, you probably want to support the older versions for backward
122  *    compatibility.  If you do that, you need to call the newest interface
123  *    first, and fall back to successively older versions if you get
124  *    RXGEN_OPCODE.
125  */
126 #define MULTIHOMED 1
127 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
128                      cm_volume_t *volp)
129 {
130     cm_conn_t *connp;
131     int i, j, k;
132     cm_serverRef_t *tsrp;
133     cm_server_t *tsp;
134     struct sockaddr_in tsockAddr;
135     long tflags;
136     u_long tempAddr;
137     struct vldbentry vldbEntry;
138     struct nvldbentry nvldbEntry;
139 #ifdef MULTIHOMED
140     struct uvldbentry uvldbEntry;
141 #endif
142     int type = -1;
143     int ROcount = 0;
144     long code;
145
146     /* clear out old bindings */
147     if (volp->rwServersp)
148         cm_FreeServerList(&volp->rwServersp);
149     if (volp->roServersp)
150         cm_FreeServerList(&volp->roServersp);
151     if (volp->bkServersp)
152         cm_FreeServerList(&volp->bkServersp);
153
154 #ifdef AFS_FREELANCE_CLIENT
155     if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && atoi(volp->namep)==AFS_FAKE_ROOT_VOL_ID ) 
156     {
157         memset(&vldbEntry, 0, sizeof(vldbEntry));
158         vldbEntry.flags |= VLF_RWEXISTS;
159         vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID;
160         code = 0;
161         type = 0;
162     } else
163 #endif
164     {
165         /* now we have volume structure locked and held; make RPC to fill it */
166         osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, volp->namep);
167         do {
168             code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
169             if (code) 
170                 continue;
171 #ifdef MULTIHOMED
172             code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry);
173             type = 2;
174             if ( code == RXGEN_OPCODE ) 
175 #endif
176             {
177                 code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry);
178                 type = 1;
179             }
180             if ( code == RXGEN_OPCODE ) {
181                 code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
182                 type = 0;
183             }
184         } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
185         code = cm_MapVLRPCError(code, reqp);
186         if ( code )
187             osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x", 
188                       volp->cellp->name, volp->namep, code);
189         else
190             osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS", 
191                       volp->cellp->name, volp->namep);
192     }
193     if (code == 0) {
194         afs_int32 flags;
195         afs_int32 nServers;
196         afs_int32 rwID;
197         afs_int32 roID;
198         afs_int32 bkID;
199         afs_int32 serverNumber[NMAXNSERVERS];
200         afs_int32 serverFlags[NMAXNSERVERS];
201
202         switch ( type ) {
203         case 0:
204             flags = vldbEntry.flags;
205             nServers = vldbEntry.nServers;
206             rwID = vldbEntry.volumeId[0];
207             roID = vldbEntry.volumeId[1];
208             bkID = vldbEntry.volumeId[2];
209             for ( i=0; i<nServers; i++ ) {
210                 serverFlags[i] = vldbEntry.serverFlags[i];
211                 serverNumber[i] = vldbEntry.serverNumber[i];
212             }
213             break;
214         case 1:
215             flags = nvldbEntry.flags;
216             nServers = nvldbEntry.nServers;
217             rwID = nvldbEntry.volumeId[0];
218             roID = nvldbEntry.volumeId[1];
219             bkID = nvldbEntry.volumeId[2];
220             for ( i=0; i<nServers; i++ ) {
221                 serverFlags[i] = nvldbEntry.serverFlags[i];
222                 serverNumber[i] = nvldbEntry.serverNumber[i];
223             }
224             break;
225 #ifdef MULTIHOMED
226         case 2:
227             flags = uvldbEntry.flags;
228             nServers = uvldbEntry.nServers;
229             rwID = uvldbEntry.volumeId[0];
230             roID = uvldbEntry.volumeId[1];
231             bkID = uvldbEntry.volumeId[2];
232             for ( i=0, j=0; i<nServers && j<NMAXNSERVERS; i++ ) {
233                 if ( !(uvldbEntry.serverFlags[i] & VLSERVER_FLAG_UUID) ) {
234                     serverFlags[j] = uvldbEntry.serverFlags[i];
235                     serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
236                     j++;
237                 } else {
238                     afs_uint32 * addrp, nentries, code, unique;
239                     bulkaddrs  addrs;
240                     ListAddrByAttributes attrs;
241                     afsUUID uuid;
242
243                     memset((char *)&attrs, 0, sizeof(attrs));
244                     attrs.Mask = VLADDR_UUID;
245                     attrs.uuid = uvldbEntry.serverNumber[i];
246                     memset((char *)&uuid, 0, sizeof(uuid));
247                     memset((char *)&addrs, 0, sizeof(addrs));
248
249                     do {
250                         code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
251                         if (code) 
252                             continue;
253                    
254                         code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs);
255
256                         if (code == 0 && nentries == 0)
257                             code = VL_NOENT;
258                     } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
259                     code = cm_MapVLRPCError(code, reqp);
260                     if (code)
261                         return code;
262
263                     addrp = addrs.bulkaddrs_val;
264                     for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) {
265                         serverFlags[j] = uvldbEntry.serverFlags[i];
266                         serverNumber[j] = addrp[k];
267                     }
268
269                     free(addrs.bulkaddrs_val);  /* This is wrong */
270                 }
271             }
272             nServers = j;                                       /* update the server count */
273             break;
274 #endif
275         }
276
277         /* decode the response */
278         lock_ObtainWrite(&cm_volumeLock);
279         if (flags & VLF_RWEXISTS)
280             volp->rwID = rwID;
281         else
282             volp->rwID = 0;
283         if (flags & VLF_ROEXISTS)
284             volp->roID = roID;
285         else
286             volp->roID = 0;
287         if (flags & VLF_BACKEXISTS)
288             volp->bkID = bkID;
289         else
290             volp->bkID = 0;
291         lock_ReleaseWrite(&cm_volumeLock);
292         for (i=0; i<nServers; i++) {
293             /* create a server entry */
294             tflags = serverFlags[i];
295             if (tflags & VLSF_DONTUSE) 
296                 continue;
297             tsockAddr.sin_family = AF_INET;
298             tempAddr = htonl(serverNumber[i]);
299             tsockAddr.sin_addr.s_addr = tempAddr;
300             tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
301             if (!tsp)
302                 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
303                                     cellp);
304
305             /* if this server was created by fs setserverprefs */
306             if ( !tsp->cellp ) 
307                 tsp->cellp = cellp;
308
309             osi_assert(tsp != NULL);
310                         
311             /* and add it to the list(s). */
312             /*
313              * Each call to cm_NewServerRef() increments the
314              * ref count of tsp.  These reference will be dropped,
315              * if and when the volume is reset; see reset code
316              * earlier in this function.
317              */
318             if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
319                 tsrp = cm_NewServerRef(tsp);
320                 cm_InsertServerList(&volp->rwServersp, tsrp);
321                 lock_ObtainWrite(&cm_serverLock);
322                 tsrp->refCount--;       /* drop allocation reference */
323                 lock_ReleaseWrite(&cm_serverLock);
324             }
325             if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) {
326                 tsrp = cm_NewServerRef(tsp);
327                 cm_InsertServerList(&volp->roServersp, tsrp);
328                 lock_ObtainWrite(&cm_serverLock);
329                 tsrp->refCount--;       /* drop allocation reference */
330                 lock_ReleaseWrite(&cm_serverLock);
331                 ROcount++;
332             }
333             /* We don't use VLSF_BACKVOL !?! */
334             if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
335                 tsrp = cm_NewServerRef(tsp);
336                 cm_InsertServerList(&volp->bkServersp, tsrp);
337                 lock_ObtainWrite(&cm_serverLock);
338                 tsrp->refCount--;       /* drop allocation reference */
339                 lock_ReleaseWrite(&cm_serverLock);
340             }
341             /* Drop the reference obtained by cm_FindServer() */
342             cm_PutServer(tsp);
343         }       
344
345         /*
346          * Randomize RO list
347          *
348          * If the first n servers have the same ipRank, then we 
349          * randomly pick one among them and move it to the beginning.
350          * We don't bother to re-order the whole list because
351          * the rest of the list is used only if the first server is
352          * down.  We only do this for the RO list; we assume the other
353          * lists are length 1.
354          */
355         if (ROcount > 1) {
356             cm_RandomizeServer(&volp->roServersp);
357         }
358     }
359     return code;
360 }
361
362 void cm_GetVolume(cm_volume_t *volp)
363 {
364     if (volp) {
365         lock_ObtainWrite(&cm_volumeLock);
366         volp->refCount++;
367         lock_ReleaseWrite(&cm_volumeLock);
368     }
369 }
370
371 long cm_GetVolumeByID(cm_cell_t *cellp, long volumeID, cm_user_t *userp,
372         cm_req_t *reqp, cm_volume_t **outVolpp)
373 {
374     cm_volume_t *volp;
375     char volNameString[100];
376     long code = 0;
377
378     lock_ObtainWrite(&cm_volumeLock);
379     for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
380         if (cellp == volp->cellp &&
381              ((unsigned) volumeID == volp->rwID ||
382                (unsigned) volumeID == volp->roID ||
383                (unsigned) volumeID == volp->bkID))
384             break;
385     }   
386
387     /* hold the volume if we found it */
388     if (volp) 
389         volp->refCount++;
390     lock_ReleaseWrite(&cm_volumeLock);
391         
392     /* return it held */
393     if (volp) {
394         lock_ObtainMutex(&volp->mx);
395         
396         code = 0;
397         if (volp->flags & CM_VOLUMEFLAG_RESET) {
398             code = cm_UpdateVolume(cellp, userp, reqp, volp);
399             if (code == 0)
400                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
401         }
402         lock_ReleaseMutex(&volp->mx);
403         if (code == 0)
404             *outVolpp = volp;
405         else
406             cm_PutVolume(volp);
407
408         return code;
409     }
410         
411     /* otherwise, we didn't find it so consult the VLDB */
412     sprintf(volNameString, "%u", volumeID);
413     code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
414                                0, outVolpp);
415     return code;
416 }
417
418 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
419                          struct cm_user *userp, struct cm_req *reqp,
420                          long flags, cm_volume_t **outVolpp)
421 {
422     cm_volume_t *volp;
423     long code = 0;
424         
425     lock_ObtainWrite(&cm_volumeLock);
426     for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
427         if (cellp == volp->cellp && strcmp(volumeNamep, volp->namep) == 0) {
428             break;
429         }
430     }   
431         
432     /* otherwise, get from VLDB */
433     if (!volp) {
434         if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
435             for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
436                 if ( volp->refCount == 0 ) {
437                     /* There is one we can re-use */
438                     break;
439                 }
440             }
441             if (!volp)
442                 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
443         }       
444
445         if (volp) {
446             volp->rwID = volp->roID = volp->bkID = 0;
447             volp->dotdotFid.cell = 0;
448             volp->dotdotFid.volume = 0;
449             volp->dotdotFid.unique = 0;
450             volp->dotdotFid.vnode = 0;
451         } else {
452             volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
453             memset(volp, 0, sizeof(cm_volume_t));
454             volp->magic = CM_VOLUME_MAGIC;
455             volp->nextp = cm_data.allVolumesp;
456             cm_data.allVolumesp = volp;
457             lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
458         }
459         volp->cellp = cellp;
460         strncpy(volp->namep, volumeNamep, VL_MAXNAMELEN);
461         volp->namep[VL_MAXNAMELEN-1] = '\0';
462         volp->refCount = 1;     /* starts off held */
463         volp->flags = CM_VOLUMEFLAG_RESET;
464     }
465     else {
466         volp->refCount++;
467     }
468
469     /* next should work since no one could have gotten ptr to this structure yet */
470     lock_ReleaseWrite(&cm_volumeLock);
471     lock_ObtainMutex(&volp->mx);
472
473     if (volp->flags & CM_VOLUMEFLAG_RESET) {
474         code = cm_UpdateVolume(cellp, userp, reqp, volp);
475         if (code == 0)
476             volp->flags &= ~CM_VOLUMEFLAG_RESET;
477     }   
478
479     if (code == 0)
480         *outVolpp = volp;
481     else
482         cm_PutVolume(volp);
483
484     lock_ReleaseMutex(&volp->mx);
485     return code;
486 }       
487
488 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
489 {
490     cm_cell_t *cellp;
491     cm_volume_t *volp;
492
493     if (!fidp) return;
494
495     cellp = cm_FindCellByID(fidp->cell);
496     if (!cellp) return;
497
498     /* search for the volume */
499     lock_ObtainWrite(&cm_volumeLock);
500     for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
501         if (cellp == volp->cellp &&
502              (fidp->volume == volp->rwID ||
503                fidp->volume == volp->roID ||
504                fidp->volume == volp->bkID))
505             break;
506     }   
507
508     /* hold the volume if we found it */
509     if (volp) 
510         volp->refCount++;
511     lock_ReleaseWrite(&cm_volumeLock);
512
513     /* update it */
514     cm_data.mountRootGen = time(NULL);
515     lock_ObtainMutex(&volp->mx);
516     volp->flags |= CM_VOLUMEFLAG_RESET;
517 #ifdef COMMENT
518     /* Mark the volume to be updated but don't update it now.
519      * This function is called only from within cm_Analyze
520      * when cm_ConnByMServers has failed with all servers down
521      * The problem is that cm_UpdateVolume is going to call
522      * cm_ConnByMServers which may cause a recursive chain
523      * of calls each returning a retry on failure.
524      * Instead, set the flag so the next time the volume is
525      * accessed by Name or ID the UpdateVolume call will
526      * occur.
527      */
528     code = cm_UpdateVolume(cellp, userp, reqp, volp);
529     if (code == 0)
530         volp->flags &= ~CM_VOLUMEFLAG_RESET;
531 #endif
532     lock_ReleaseMutex(&volp->mx);
533
534     cm_PutVolume(volp);
535 }
536
537 /* find the appropriate servers from a volume */
538 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, unsigned long volume)
539 {
540     cm_serverRef_t **serverspp;
541     cm_serverRef_t *current;;
542
543     lock_ObtainWrite(&cm_serverLock);
544
545     if (volume == volp->rwID)
546         serverspp = &volp->rwServersp;
547     else if (volume == volp->roID)
548         serverspp = &volp->roServersp;
549     else if (volume == volp->bkID)
550         serverspp = &volp->bkServersp;
551     else 
552         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
553         
554     for (current = *serverspp; current; current = current->next)
555         current->refCount++;
556
557     lock_ReleaseWrite(&cm_serverLock);
558
559     return serverspp;
560 }
561
562 void cm_PutVolume(cm_volume_t *volp)
563 {
564     lock_ObtainWrite(&cm_volumeLock);
565     osi_assert(volp->refCount-- > 0);
566     lock_ReleaseWrite(&cm_volumeLock);
567 }
568
569 /* return the read-only volume, if there is one, or the read-write volume if
570  * not.
571  */
572 long cm_GetROVolumeID(cm_volume_t *volp)
573 {
574     long id;
575
576     lock_ObtainMutex(&volp->mx);
577     if (volp->roID && volp->roServersp)
578         id = volp->roID;
579     else
580         id = volp->rwID;
581     lock_ReleaseMutex(&volp->mx);
582
583     return id;
584 }
585
586 void cm_CheckVolumes(void)
587 {
588     cm_volume_t *volp;
589
590     cm_data.mountRootGen = time(NULL);
591     lock_ObtainWrite(&cm_volumeLock);
592     for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
593         volp->refCount++;
594         lock_ReleaseWrite(&cm_volumeLock);
595         lock_ObtainMutex(&volp->mx);
596
597         volp->flags |= CM_VOLUMEFLAG_RESET;
598
599         lock_ReleaseMutex(&volp->mx);
600         lock_ObtainWrite(&cm_volumeLock);
601         osi_assert(volp->refCount-- > 0);
602     }
603     lock_ReleaseWrite(&cm_volumeLock);
604
605     /* We should also refresh cached mount points */
606 }
607
608 /*
609 ** Finds all volumes that reside on this server and reorders their
610 ** RO list according to the changed rank of server.
611 */
612 void cm_ChangeRankVolume(cm_server_t       *tsp)
613 {       
614     int                 code;
615     cm_volume_t*        volp;
616
617     /* find volumes which might have RO copy on server*/
618     lock_ObtainWrite(&cm_volumeLock);
619     for(volp = cm_data.allVolumesp; volp; volp=volp->nextp)
620     {
621         code = 1 ;      /* assume that list is unchanged */
622         volp->refCount++;
623         lock_ReleaseWrite(&cm_volumeLock);
624         lock_ObtainMutex(&volp->mx);
625
626         if ((tsp->cellp==volp->cellp) && (volp->roServersp))
627             code =cm_ChangeRankServer(&volp->roServersp, tsp);
628
629         /* this volume list was changed */
630         if ( !code )
631             cm_RandomizeServer(&volp->roServersp);
632
633         lock_ReleaseMutex(&volp->mx);
634         lock_ObtainWrite(&cm_volumeLock);
635         osi_assert(volp->refCount-- > 0);
636     }
637     lock_ReleaseWrite(&cm_volumeLock);
638 }       
639
640 /* dump all scp's that have reference count > 0 to a file. 
641  * cookie is used to identify this batch for easy parsing, 
642  * and it a string provided by a caller 
643  */
644 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
645 {
646     int zilch;
647     cm_volume_t *volp;
648     char output[1024];
649   
650     if (lock) {
651         lock_ObtainRead(&cm_scacheLock);
652         lock_ObtainRead(&cm_volumeLock);
653     }
654   
655     sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
656     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
657   
658     for (volp = cm_data.allVolumesp; volp; volp=volp->nextp)
659     {
660         if (volp->refCount != 0)
661         {
662             cm_scache_t *scp;
663             int scprefs = 0;
664
665             for (scp = cm_data.scacheLRULastp; scp; scp = (cm_scache_t *) osi_QPrev(&scp->q)) 
666             {
667                 if (scp->volp == volp)
668                     scprefs++;
669             }
670
671             sprintf(output, "%s cell=%s name=%s rwID=%u roID=%u bkID=%u flags=0x%x fid (cell=%d, volume=%d, vnode=%d, unique=%d) refCount=%u scpRefs=%u\n", 
672                     cookie, volp->cellp->name, volp->namep, volp->rwID, volp->roID, volp->bkID, volp->flags, 
673                     volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
674                     volp->refCount, scprefs);
675             WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
676         }
677     }
678     sprintf(output, "%s - Done dumping volumes.\n", cookie);
679     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
680   
681     if (lock) {
682         lock_ReleaseRead(&cm_volumeLock);
683         lock_ReleaseRead(&cm_scacheLock);
684     }
685     return (0);     
686 }
687
688