windows-afsd-notification-20071104
[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->allNextp, 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->allNextp && volp->allNextp->magic != CM_VOLUME_MAGIC ) {
42             afsi_log("cm_ValidateVolume failure: volp->allNextp->magic != CM_VOLUME_MAGIC");
43             fprintf(stderr, "cm_ValidateVolume failure: volp->allNextp->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->allNextp) {
69
70         if (volp->rw.ID)
71             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_alldown);
72         if (volp->ro.ID)
73             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_alldown);
74         if (volp->bk.ID)
75             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_alldown);
76
77         lock_FinalizeMutex(&volp->mx);
78     }
79
80     return 0;
81 }
82
83 void cm_InitVolume(int newFile, long maxVols)
84 {
85     static osi_once_t once;
86
87     if (osi_Once(&once)) {
88         lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock");
89
90         if ( newFile ) {
91             cm_data.allVolumesp = NULL;
92             cm_data.currentVolumes = 0;
93             cm_data.maxVolumes = maxVols;
94             memset(cm_data.volumeNameHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
95             memset(cm_data.volumeRWIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
96             memset(cm_data.volumeROIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
97             memset(cm_data.volumeBKIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
98             cm_data.volumeLRUFirstp = cm_data.volumeLRULastp = NULL;
99         } else {
100             cm_volume_t * volp;
101
102             for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
103                 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
104                 volp->flags |= CM_VOLUMEFLAG_RESET;
105                 volp->rw.state = vl_unknown;
106                 volp->rw.serversp = NULL;
107                 volp->ro.state = vl_unknown;
108                 volp->ro.serversp = NULL;
109                 volp->bk.state = vl_unknown;
110                 volp->bk.serversp = NULL;
111                 if (volp->rw.ID)
112                     cm_VolumeStatusNotification(volp, volp->rw.ID, vl_alldown, volp->rw.state);
113                 if (volp->ro.ID)
114                     cm_VolumeStatusNotification(volp, volp->ro.ID, vl_alldown, volp->ro.state);
115                 if (volp->bk.ID)
116                     cm_VolumeStatusNotification(volp, volp->bk.ID, vl_alldown, volp->bk.state);
117             }
118         }
119         osi_EndOnce(&once);
120     }
121 }
122
123
124 /* returns true if the id is a decimal integer, in which case we interpret it
125  * as an id.  make the cache manager much simpler.  
126  * Stolen from src/volser/vlprocs.c */
127 int
128 cm_VolNameIsID(char *aname)
129 {
130     int tc;
131     while (tc = *aname++) {
132         if (tc > '9' || tc < '0')
133             return 0;
134     }
135     return 1;
136 }
137
138
139 /*
140  * Update a volume.  Caller holds volume's lock (volp->mx).
141  *
142  *
143  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:38    (JHutz)
144  *    Yes, we support multihomed fileservers.
145  *    Since before we got the code from IBM.
146  *    But to find out about multiple addresses on a multihomed server, you need
147  *    to use VL_GetEntryByNameU and VL_GetAddrsU.  If you use
148  *    VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
149  *    address per server.
150  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:39    (JHutz)
151  *    see src/afs/afs_volume.c, paying particular attention to
152  *    afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
153  *  shadow / openafs / jaltman {ANDREW.CMU.EDU}  01:40    (Jeffrey Altman)
154  *    thanks.  The windows client calls the 0 versions.
155  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:51    (JHutz)
156  *    Oh.  Ew.
157  *    By not using the N versions, you only get up to 8 sites instead of 13.
158  *    By not using the U versions, you don't get to know about multihomed serve
159  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:52    (JHutz)
160  *    Of course, you probably want to support the older versions for backward
161  *    compatibility.  If you do that, you need to call the newest interface
162  *    first, and fall back to successively older versions if you get
163  *    RXGEN_OPCODE.
164  */
165 #define MULTIHOMED 1
166 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
167                      cm_volume_t *volp)
168 {
169     cm_conn_t *connp;
170     int i, j, k;
171     cm_serverRef_t *tsrp;
172     cm_server_t *tsp;
173     struct sockaddr_in tsockAddr;
174     long tflags;
175     u_long tempAddr;
176     struct vldbentry vldbEntry;
177     struct nvldbentry nvldbEntry;
178 #ifdef MULTIHOMED
179     struct uvldbentry uvldbEntry;
180 #endif
181     int method = -1;
182     int ROcount = 0;
183     long code;
184     enum volstatus rwNewstate = vl_online;
185     enum volstatus roNewstate = vl_online;
186     enum volstatus bkNewstate = vl_online;
187 #ifdef AFS_FREELANCE_CLIENT
188     int freelance = 0;
189 #endif
190
191     /* clear out old bindings */
192     if (volp->rw.serversp)
193         cm_FreeServerList(&volp->rw.serversp, CM_FREESERVERLIST_DELETE);
194     if (volp->ro.serversp)
195         cm_FreeServerList(&volp->ro.serversp, CM_FREESERVERLIST_DELETE);
196     if (volp->bk.serversp)
197         cm_FreeServerList(&volp->bk.serversp, CM_FREESERVERLIST_DELETE);
198
199 #ifdef AFS_FREELANCE_CLIENT
200     if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && volp->rw.ID == AFS_FAKE_ROOT_VOL_ID ) 
201     {
202         freelance = 1;
203         memset(&vldbEntry, 0, sizeof(vldbEntry));
204         vldbEntry.flags |= VLF_RWEXISTS;
205         vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID;
206         code = 0;
207         method = 0;
208     } else
209 #endif
210     {
211         /* now we have volume structure locked and held; make RPC to fill it */
212         osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, volp->namep);
213         do {
214             code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
215             if (code) 
216                 continue;
217 #ifdef MULTIHOMED
218             code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry);
219             method = 2;
220             if ( code == RXGEN_OPCODE ) 
221 #endif
222             {
223                 code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry);
224                 method = 1;
225             }
226             if ( code == RXGEN_OPCODE ) {
227                 code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
228                 method = 0;
229             }
230         } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
231         code = cm_MapVLRPCError(code, reqp);
232         if ( code )
233             osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x", 
234                       volp->cellp->name, volp->namep, code);
235         else
236             osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS", 
237                       volp->cellp->name, volp->namep);
238     }
239
240     /* We can end up here with code == CM_ERROR_NOSUCHVOLUME if the base volume name
241      * does not exist but there might exist a .readonly volume.  If the base name 
242      * doesn't exist we will not care about the .backup that might be left behind
243      * since there should be no method to access it.  
244      */
245     if (code == CM_ERROR_NOSUCHVOLUME && volp->rw.ID == 0 && strlen(volp->namep) < (VL_MAXNAMELEN - 9)) {
246         char name[VL_MAXNAMELEN];
247
248         snprintf(name, VL_MAXNAMELEN, "%s.readonly", volp->namep);
249                 
250         /* now we have volume structure locked and held; make RPC to fill it */
251         osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, name);
252         do {
253             code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
254             if (code) 
255                 continue;
256 #ifdef MULTIHOMED
257             code = VL_GetEntryByNameU(connp->callp, name, &uvldbEntry);
258             method = 2;
259             if ( code == RXGEN_OPCODE ) 
260 #endif
261             {
262                 code = VL_GetEntryByNameN(connp->callp, name, &nvldbEntry);
263                 method = 1;
264             }
265             if ( code == RXGEN_OPCODE ) {
266                 code = VL_GetEntryByNameO(connp->callp, name, &vldbEntry);
267                 method = 0;
268             }
269         } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
270         code = cm_MapVLRPCError(code, reqp);
271         if ( code )
272             osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x", 
273                       volp->cellp->name, name, code);
274         else
275             osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS", 
276                       volp->cellp->name, name);
277     }
278
279     if (code == 0) {
280         afs_int32 flags;
281         afs_int32 nServers;
282         afs_int32 rwID;
283         afs_int32 roID;
284         afs_int32 bkID;
285         afs_int32 serverNumber[NMAXNSERVERS];
286         afs_int32 serverFlags[NMAXNSERVERS];
287         afs_int32 rwServers_alldown = 1;
288         afs_int32 roServers_alldown = 1;
289         afs_int32 bkServers_alldown = 1;
290         char      name[VL_MAXNAMELEN];
291
292 #ifdef AFS_FREELANCE_CLIENT
293         if (freelance)
294             rwServers_alldown = 0;
295 #endif
296
297         switch ( method ) {
298         case 0:
299             flags = vldbEntry.flags;
300             nServers = vldbEntry.nServers;
301             rwID = vldbEntry.volumeId[0];
302             roID = vldbEntry.volumeId[1];
303             bkID = vldbEntry.volumeId[2];
304             for ( i=0; i<nServers; i++ ) {
305                 serverFlags[i] = vldbEntry.serverFlags[i];
306                 serverNumber[i] = vldbEntry.serverNumber[i];
307             }
308             strncpy(name, vldbEntry.name, VL_MAXNAMELEN);
309             name[VL_MAXNAMELEN - 1] = '\0';
310             break;
311         case 1:
312             flags = nvldbEntry.flags;
313             nServers = nvldbEntry.nServers;
314             rwID = nvldbEntry.volumeId[0];
315             roID = nvldbEntry.volumeId[1];
316             bkID = nvldbEntry.volumeId[2];
317             for ( i=0; i<nServers; i++ ) {
318                 serverFlags[i] = nvldbEntry.serverFlags[i];
319                 serverNumber[i] = nvldbEntry.serverNumber[i];
320             }
321             strncpy(name, nvldbEntry.name, VL_MAXNAMELEN);
322             name[VL_MAXNAMELEN - 1] = '\0';
323             break;
324 #ifdef MULTIHOMED
325         case 2:
326             flags = uvldbEntry.flags;
327             nServers = uvldbEntry.nServers;
328             rwID = uvldbEntry.volumeId[0];
329             roID = uvldbEntry.volumeId[1];
330             bkID = uvldbEntry.volumeId[2];
331             for ( i=0, j=0; i<nServers && j<NMAXNSERVERS; i++ ) {
332                 if ( !(uvldbEntry.serverFlags[i] & VLSERVER_FLAG_UUID) ) {
333                     serverFlags[j] = uvldbEntry.serverFlags[i];
334                     serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
335                     j++;
336                 } else {
337                     afs_uint32 * addrp, nentries, code, unique;
338                     bulkaddrs  addrs;
339                     ListAddrByAttributes attrs;
340                     afsUUID uuid;
341
342                     memset((char *)&attrs, 0, sizeof(attrs));
343                     attrs.Mask = VLADDR_UUID;
344                     attrs.uuid = uvldbEntry.serverNumber[i];
345                     memset((char *)&uuid, 0, sizeof(uuid));
346                     memset((char *)&addrs, 0, sizeof(addrs));
347
348                     do {
349                         code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
350                         if (code) 
351                             continue;
352                    
353                         code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs);
354
355                         if (code == 0 && nentries == 0)
356                             code = VL_NOENT;
357                     } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
358                     code = cm_MapVLRPCError(code, reqp);
359                     if (code)
360                         return code;
361
362                     addrp = addrs.bulkaddrs_val;
363                     for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) {
364                         serverFlags[j] = uvldbEntry.serverFlags[i];
365                         serverNumber[j] = addrp[k];
366                     }
367
368                     free(addrs.bulkaddrs_val);  /* This is wrong */
369                 }
370             }
371             nServers = j;                                       /* update the server count */
372             strncpy(name, uvldbEntry.name, VL_MAXNAMELEN);
373             name[VL_MAXNAMELEN - 1] = '\0';
374             break;
375 #endif
376         }
377
378         /* decode the response */
379         lock_ObtainWrite(&cm_volumeLock);
380         if (cm_VolNameIsID(volp->namep)) {
381             size_t    len;
382
383             len = strlen(name);
384
385             if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
386                 name[len - 7] = '\0';
387             } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
388                 name[len - 9] = '\0';
389             }
390             
391             osi_Log2(afsd_logp, "cm_UpdateVolume name %s -> %s", volp->namep, name);
392
393             if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
394                 cm_RemoveVolumeFromNameHashTable(volp);
395
396             strcpy(volp->namep, name);
397
398             cm_AddVolumeToNameHashTable(volp);
399         }
400
401         if (flags & VLF_RWEXISTS) {
402             if (volp->rw.ID != rwID) {
403                 if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
404                     cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
405                 volp->rw.ID = rwID;
406                 cm_AddVolumeToIDHashTable(volp, RWVOL);
407             }
408         } else {
409             if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
410                 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
411             volp->rw.ID = 0;
412         }
413         if (flags & VLF_ROEXISTS) {
414             if (volp->ro.ID != roID) {
415                 if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
416                     cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
417                 volp->ro.ID = roID;
418                 cm_AddVolumeToIDHashTable(volp, ROVOL);
419             }
420         } else {
421             if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
422                 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
423             volp->ro.ID = 0;
424         }
425         if (flags & VLF_BACKEXISTS) {
426             if (volp->bk.ID != bkID) {
427                 if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
428                     cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
429                 volp->bk.ID = bkID;
430                 cm_AddVolumeToIDHashTable(volp, BACKVOL);
431             }
432         } else {
433             if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
434                 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
435             volp->bk.ID = 0;
436         }
437         lock_ReleaseWrite(&cm_volumeLock);
438         for (i=0; i<nServers; i++) {
439             /* create a server entry */
440             tflags = serverFlags[i];
441             if (tflags & VLSF_DONTUSE) 
442                 continue;
443             tsockAddr.sin_family = AF_INET;
444             tempAddr = htonl(serverNumber[i]);
445             tsockAddr.sin_addr.s_addr = tempAddr;
446             tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
447             if (!tsp)
448                 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
449                                     cellp);
450
451             /* if this server was created by fs setserverprefs */
452             if ( !tsp->cellp ) 
453                 tsp->cellp = cellp;
454
455             osi_assertx(tsp != NULL, "null cm_server_t");
456                         
457             /* and add it to the list(s). */
458             /*
459              * Each call to cm_NewServerRef() increments the
460              * ref count of tsp.  These reference will be dropped,
461              * if and when the volume is reset; see reset code
462              * earlier in this function.
463              */
464             if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
465                 tsrp = cm_NewServerRef(tsp, rwID);
466                 cm_InsertServerList(&volp->rw.serversp, tsrp);
467
468                 lock_ObtainWrite(&cm_serverLock);
469                 tsrp->refCount--;       /* drop allocation reference */
470                 lock_ReleaseWrite(&cm_serverLock);
471
472                 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
473                     rwServers_alldown = 0;
474             }
475             if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) {
476                 tsrp = cm_NewServerRef(tsp, roID);
477                 cm_InsertServerList(&volp->ro.serversp, tsrp);
478                 lock_ObtainWrite(&cm_serverLock);
479                 tsrp->refCount--;       /* drop allocation reference */
480                 lock_ReleaseWrite(&cm_serverLock);
481                 ROcount++;
482
483                 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
484                     roServers_alldown = 0;
485             }
486             /* We don't use VLSF_BACKVOL !?! */
487             /* Because only the backup on the server holding the RW 
488              * volume can be valid.  This check prevents errors if a
489              * RW is moved but the old backup is not removed.
490              */
491             if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
492                 tsrp = cm_NewServerRef(tsp, bkID);
493                 cm_InsertServerList(&volp->bk.serversp, tsrp);
494                 lock_ObtainWrite(&cm_serverLock);
495                 tsrp->refCount--;       /* drop allocation reference */
496                 lock_ReleaseWrite(&cm_serverLock);
497
498                 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
499                     bkServers_alldown = 0;
500             }
501             /* Drop the reference obtained by cm_FindServer() */
502             cm_PutServer(tsp);
503         }       
504
505         /*
506          * Randomize RO list
507          *
508          * If the first n servers have the same ipRank, then we 
509          * randomly pick one among them and move it to the beginning.
510          * We don't bother to re-order the whole list because
511          * the rest of the list is used only if the first server is
512          * down.  We only do this for the RO list; we assume the other
513          * lists are length 1.
514          */
515         if (ROcount > 1) {
516             cm_RandomizeServer(&volp->ro.serversp);
517         }
518
519         rwNewstate = rwServers_alldown ? vl_alldown : vl_online;
520         roNewstate = roServers_alldown ? vl_alldown : vl_online;
521         bkNewstate = bkServers_alldown ? vl_alldown : vl_online;
522     } else {
523         rwNewstate = roNewstate = bkNewstate = vl_alldown;
524     }
525
526     if (volp->rw.state != rwNewstate) {
527         if (volp->rw.ID)
528             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, rwNewstate);
529         volp->rw.state = rwNewstate;
530     }
531     if (volp->ro.state != roNewstate) {
532         if (volp->ro.ID)
533             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, roNewstate);
534         volp->ro.state = roNewstate;
535     }
536     if (volp->bk.state != bkNewstate) {
537         if (volp->bk.ID)
538             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, bkNewstate);
539         volp->bk.state = bkNewstate;
540     }
541
542     return code;
543 }
544
545 void cm_GetVolume(cm_volume_t *volp)
546 {
547     if (volp) {
548         lock_ObtainWrite(&cm_volumeLock);
549         volp->refCount++;
550         lock_ReleaseWrite(&cm_volumeLock);
551     }
552 }
553
554
555 long cm_GetVolumeByID(cm_cell_t *cellp, afs_uint32 volumeID, cm_user_t *userp,
556                       cm_req_t *reqp, afs_uint32 flags, cm_volume_t **outVolpp)
557 {
558     cm_volume_t *volp;
559 #ifdef SEARCH_ALL_VOLUMES
560     cm_volume_t *volp2;
561 #endif
562     char volNameString[VL_MAXNAMELEN];
563     afs_uint32 hash;
564     long code = 0;
565
566     lock_ObtainRead(&cm_volumeLock);
567 #ifdef SEARCH_ALL_VOLUMES
568     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
569         if (cellp == volp->cellp &&
570              ((unsigned) volumeID == volp->rw.ID ||
571                (unsigned) volumeID == volp->ro.ID ||
572                (unsigned) volumeID == volp->bk.ID))
573             break;
574     }   
575
576     volp2 = volp;
577 #endif /* SEARCH_ALL_VOLUMES */
578
579     hash = CM_VOLUME_ID_HASH(volumeID);
580     /* The volumeID can be any one of the three types.  So we must
581      * search the hash table for all three types until we find it.
582      * We will search in the order of RO, RW, BK.
583      */
584     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
585         if ( cellp == volp->cellp && volumeID == volp->ro.ID )
586             break;
587     }
588     if (!volp) {
589         /* try RW volumes */
590         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
591             if ( cellp == volp->cellp && volumeID == volp->rw.ID )
592                 break;
593         }
594     }
595     if (!volp) {
596         /* try BK volumes */
597         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
598             if ( cellp == volp->cellp && volumeID == volp->bk.ID )
599                 break;
600         }
601     }
602
603 #ifdef SEARCH_ALL_VOLUMES
604     osi_assertx(volp == volp2, "unexpected cm_vol_t");
605 #endif
606
607     lock_ReleaseRead(&cm_volumeLock);
608
609     /* hold the volume if we found it */
610     if (volp) 
611         cm_GetVolume(volp);
612         
613     /* return it held */
614     if (volp) {
615         lock_ObtainMutex(&volp->mx);
616         
617         code = 0;
618         if (volp->flags & CM_VOLUMEFLAG_RESET) {
619             code = cm_UpdateVolume(cellp, userp, reqp, volp);
620             if (code == 0)
621                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
622         }
623         lock_ReleaseMutex(&volp->mx);
624         if (code == 0) {
625             *outVolpp = volp;
626
627             lock_ObtainWrite(&cm_volumeLock);
628             cm_AdjustVolumeLRU(volp);
629             lock_ReleaseWrite(&cm_volumeLock);
630         } else
631             cm_PutVolume(volp);
632
633         return code;
634     }
635         
636     /* otherwise, we didn't find it so consult the VLDB */
637     sprintf(volNameString, "%u", volumeID);
638     code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
639                               flags, outVolpp);
640     return code;
641 }
642
643
644 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
645                         struct cm_user *userp, struct cm_req *reqp,
646                         afs_uint32 flags, cm_volume_t **outVolpp)
647 {
648     cm_volume_t *volp;
649 #ifdef SEARCH_ALL_VOLUMES
650     cm_volume_t *volp2;
651 #endif
652     long        code = 0;
653     char        name[VL_MAXNAMELEN];
654     size_t      len;
655     int         type;
656     afs_uint32  hash;
657
658     strncpy(name, volumeNamep, VL_MAXNAMELEN);
659     name[VL_MAXNAMELEN-1] = '\0';
660     len = strlen(name);
661
662     if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
663         type = BACKVOL;
664         name[len - 7] = '\0';
665     } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
666         type = ROVOL;
667         name[len - 9] = '\0';
668     } else {
669         type = RWVOL;
670     }
671
672     lock_ObtainRead(&cm_volumeLock);
673 #ifdef SEARCH_ALL_VOLUMES
674     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
675         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0) {
676             break;
677         }
678     }   
679     volp2 = volp;
680 #endif /* SEARCH_ALL_VOLUMES */
681
682     hash = CM_VOLUME_NAME_HASH(name);
683     for (volp = cm_data.volumeNameHashTablep[hash]; volp; volp = volp->nameNextp) {
684         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0)
685             break;
686     }
687
688 #ifdef SEARCH_ALL_VOLUMES
689     osi_assertx(volp2 == volp, "unexpected cm_vol_t");
690 #endif
691
692     if (!volp && (flags & CM_GETVOL_FLAG_CREATE)) {
693         /* otherwise, get from VLDB */
694
695         if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
696
697 #ifdef RECYCLE_FROM_ALL_VOLUMES_LIST
698             for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
699                 if ( volp->refCount == 0 ) {
700                     /* There is one we can re-use */
701                     break;
702                 }
703             }
704 #else
705             for ( volp = cm_data.volumeLRULastp;
706                   volp;
707                   volp = (cm_volume_t *) osi_QPrev(&volp->q)) 
708             {
709                 if ( volp->refCount == 0 ) {
710                     /* There is one we can re-use */
711                     break;
712                 }
713             }
714 #endif
715             if (!volp)
716                 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
717
718             lock_ReleaseRead(&cm_volumeLock);
719             lock_ObtainMutex(&volp->mx);
720             lock_ObtainWrite(&cm_volumeLock);
721
722             osi_Log2(afsd_logp, "Recycling Volume %s:%s",
723                      volp->cellp->name, volp->namep);
724
725             if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
726                 cm_RemoveVolumeFromLRU(volp);
727             if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
728                 cm_RemoveVolumeFromNameHashTable(volp);
729             if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
730                 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
731             if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
732                 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
733             if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
734                 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
735
736             if (volp->rw.ID)
737                 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
738             if (volp->ro.ID)
739                 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
740             if (volp->bk.ID)
741                 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
742
743             volp->rw.ID = volp->ro.ID = volp->bk.ID = 0;
744             volp->dotdotFid.cell = 0;
745             volp->dotdotFid.volume = 0;
746             volp->dotdotFid.unique = 0;
747             volp->dotdotFid.vnode = 0;
748         } else {
749             volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
750             memset(volp, 0, sizeof(cm_volume_t));
751             volp->magic = CM_VOLUME_MAGIC;
752             volp->allNextp = cm_data.allVolumesp;
753             cm_data.allVolumesp = volp;
754             lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
755             lock_ReleaseRead(&cm_volumeLock);
756             lock_ObtainMutex(&volp->mx);
757             lock_ObtainWrite(&cm_volumeLock);
758         }
759         volp->cellp = cellp;
760         strncpy(volp->namep, name, VL_MAXNAMELEN);
761         volp->namep[VL_MAXNAMELEN-1] = '\0';
762         volp->refCount = 1;     /* starts off held */
763         volp->flags = CM_VOLUMEFLAG_RESET;
764         volp->rw.state = volp->ro.state = volp->bk.state = vl_unknown;
765         volp->rw.nextp = volp->ro.nextp = volp->bk.nextp = NULL;
766         volp->rw.flags = volp->ro.flags = volp->bk.flags = 0;
767         cm_AddVolumeToNameHashTable(volp);
768         lock_ReleaseWrite(&cm_volumeLock);
769     }
770     else {
771         lock_ReleaseRead(&cm_volumeLock);
772         if (volp) {
773             cm_GetVolume(volp);
774             lock_ObtainMutex(&volp->mx);
775         } else {
776             return CM_ERROR_NOSUCHVOLUME;
777         }
778     }
779
780     /* if we get here we are holding the mutex */
781     if (volp->flags & CM_VOLUMEFLAG_RESET) {
782         code = cm_UpdateVolume(cellp, userp, reqp, volp);
783         if (code == 0)
784             volp->flags &= ~CM_VOLUMEFLAG_RESET;
785     }   
786     lock_ReleaseMutex(&volp->mx);
787
788     if (code == 0 && (type == BACKVOL && volp->bk.ID == 0 ||
789                       type == ROVOL && volp->ro.ID == 0))
790         code = CM_ERROR_NOSUCHVOLUME;
791
792     if (code == 0) {
793         *outVolpp = volp;
794
795         lock_ObtainWrite(&cm_volumeLock);
796         cm_AdjustVolumeLRU(volp);
797         lock_ReleaseWrite(&cm_volumeLock);
798     } else
799         cm_PutVolume(volp);
800
801     return code;
802 }       
803
804 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
805 {
806     cm_cell_t *cellp;
807     cm_volume_t *volp;
808 #ifdef SEARCH_ALL_VOLUMES
809     cm_volume_t *volp2;
810 #endif
811     afs_uint32  hash;
812
813     if (!fidp) return;
814
815     cellp = cm_FindCellByID(fidp->cell);
816     if (!cellp) return;
817
818     /* search for the volume */
819     lock_ObtainRead(&cm_volumeLock);
820 #ifdef SEARCH_ALL_VOLUMES
821     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
822         if (cellp == volp->cellp &&
823              (fidp->volume == volp->rw.ID ||
824                fidp->volume == volp->ro.ID ||
825                fidp->volume == volp->bk.ID))
826             break;
827     }   
828 #endif /* SEARCH_ALL_VOLUMES */
829
830     hash = CM_VOLUME_ID_HASH(fidp->volume);
831     /* The volumeID can be any one of the three types.  So we must
832      * search the hash table for all three types until we find it.
833      * We will search in the order of RO, RW, BK.
834      */
835     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
836         if ( cellp == volp->cellp && fidp->volume == volp->ro.ID )
837             break;
838     }
839     if (!volp) {
840         /* try RW volumes */
841         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
842             if ( cellp == volp->cellp && fidp->volume == volp->rw.ID )
843                 break;
844         }
845     }
846     if (!volp) {
847         /* try BK volumes */
848         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
849             if ( cellp == volp->cellp && fidp->volume == volp->bk.ID )
850                 break;
851         }
852     }
853
854 #ifdef SEARCH_ALL_VOLUMES
855     osi_assertx(volp == volp2, "unexpected cm_vol_t");
856 #endif
857
858     lock_ReleaseRead(&cm_volumeLock);
859
860     /* hold the volume if we found it */
861     if (volp) 
862         cm_GetVolume(volp);
863
864     /* update it */
865     cm_data.mountRootGen = time(NULL);
866     lock_ObtainMutex(&volp->mx);
867     volp->flags |= CM_VOLUMEFLAG_RESET;
868 #ifdef COMMENT
869     /* Mark the volume to be updated but don't update it now.
870      * This function is called only from within cm_Analyze
871      * when cm_ConnByMServers has failed with all servers down
872      * The problem is that cm_UpdateVolume is going to call
873      * cm_ConnByMServers which may cause a recursive chain
874      * of calls each returning a retry on failure.
875      * Instead, set the flag so the next time the volume is
876      * accessed by Name or ID the UpdateVolume call will
877      * occur.
878      */
879     code = cm_UpdateVolume(cellp, userp, reqp, volp);
880     if (code == 0)
881         volp->flags &= ~CM_VOLUMEFLAG_RESET;
882 #endif
883     lock_ReleaseMutex(&volp->mx);
884
885     cm_PutVolume(volp);
886 }
887
888 /* find the appropriate servers from a volume */
889 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volume)
890 {
891     cm_serverRef_t **serverspp;
892     cm_serverRef_t *current;;
893
894     lock_ObtainWrite(&cm_serverLock);
895
896     if (volume == volp->rw.ID)
897         serverspp = &volp->rw.serversp;
898     else if (volume == volp->ro.ID)
899         serverspp = &volp->ro.serversp;
900     else if (volume == volp->bk.ID)
901         serverspp = &volp->bk.serversp;
902     else 
903         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
904         
905     for (current = *serverspp; current; current = current->next)
906         current->refCount++;
907
908     lock_ReleaseWrite(&cm_serverLock);
909
910     return serverspp;
911 }
912
913 void cm_PutVolume(cm_volume_t *volp)
914 {
915     lock_ObtainWrite(&cm_volumeLock);
916     osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
917     lock_ReleaseWrite(&cm_volumeLock);
918 }
919
920 /* return the read-only volume, if there is one, or the read-write volume if
921  * not.
922  */
923 long cm_GetROVolumeID(cm_volume_t *volp)
924 {
925     long id;
926
927     lock_ObtainMutex(&volp->mx);
928     if (volp->ro.ID && volp->ro.serversp)
929         id = volp->ro.ID;
930     else
931         id = volp->rw.ID;
932     lock_ReleaseMutex(&volp->mx);
933
934     return id;
935 }
936
937 void cm_RefreshVolumes(void)
938 {
939     cm_volume_t *volp;
940     cm_scache_t *scp;
941
942     cm_data.mountRootGen = time(NULL);
943
944     /* force a re-loading of volume data from the vldb */
945     lock_ObtainWrite(&cm_volumeLock);
946     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
947         volp->refCount++;
948         lock_ReleaseWrite(&cm_volumeLock);
949
950         lock_ObtainMutex(&volp->mx);
951         volp->flags |= CM_VOLUMEFLAG_RESET;
952         lock_ReleaseMutex(&volp->mx);
953         
954         lock_ObtainWrite(&cm_volumeLock);
955         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
956     }
957     lock_ReleaseWrite(&cm_volumeLock);
958
959     /* force mount points to be re-evaluated so that 
960      * if the volume location has changed we will pick 
961      * that up
962      */
963     for ( scp = cm_data.scacheLRUFirstp; 
964           scp;
965           scp = (cm_scache_t *) osi_QNext(&scp->q)) {
966         if ( scp->fileType == CM_SCACHETYPE_MOUNTPOINT 
967 #ifdef AFS_FREELANCE_CLIENT
968              && !(scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID)
969 #endif
970              ) {
971             lock_ObtainMutex(&scp->mx);
972             scp->mountPointStringp[0] = '\0';
973             lock_ReleaseMutex(&scp->mx);
974         }
975     }
976
977 }
978
979
980 /* The return code is 0 if the volume is not online and 
981  * 1 if the volume is online
982  */
983 long
984 cm_CheckOfflineVolume(cm_volume_t *volp, afs_uint32 volID)
985 {
986     cm_conn_t *connp;
987     long code;
988     AFSFetchVolumeStatus volStat;
989     char *Name;
990     char *OfflineMsg;
991     char *MOTD;
992     cm_req_t req;
993     struct rx_connection * callp;
994     char volName[32];
995     char offLineMsg[256];
996     char motd[256];
997     long online = 0;
998     cm_serverRef_t *serversp;
999
1000     Name = volName;
1001     OfflineMsg = offLineMsg;
1002     MOTD = motd;
1003
1004     lock_ObtainMutex(&volp->mx);
1005
1006     if (volp->rw.ID != 0 && (!volID || volID == volp->rw.ID) &&
1007          (volp->rw.state == vl_busy || volp->rw.state == vl_offline)) {
1008         cm_InitReq(&req);
1009
1010         for (serversp = volp->rw.serversp; serversp; serversp = serversp->next) {
1011             if (serversp->status == srv_busy || serversp->status == srv_offline)
1012                 serversp->status = srv_not_busy;
1013         }
1014
1015         do {
1016             code = cm_ConnFromVolume(volp, volp->rw.ID, cm_rootUserp, &req, &connp);
1017             if (code) 
1018                 continue;
1019
1020             callp = cm_GetRxConn(connp);
1021             code = RXAFS_GetVolumeStatus(callp, volp->rw.ID,
1022                                           &volStat, &Name, &OfflineMsg, &MOTD);
1023             rx_PutConnection(callp);        
1024
1025         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1026         code = cm_MapRPCError(code, &req);
1027
1028         if (code == 0 && volStat.Online) {
1029             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_online);
1030             volp->rw.state = vl_online;
1031             online = 1;
1032         }
1033     }
1034
1035     if (volp->ro.ID != 0 && (!volID || volID == volp->ro.ID) &&
1036          (volp->ro.state == vl_busy || volp->ro.state == vl_offline)) {
1037         cm_InitReq(&req);
1038
1039         for (serversp = volp->ro.serversp; serversp; serversp = serversp->next) {
1040             if (serversp->status == srv_busy || serversp->status == srv_offline)
1041                 serversp->status = srv_not_busy;
1042         }
1043
1044         do {
1045             code = cm_ConnFromVolume(volp, volp->ro.ID, cm_rootUserp, &req, &connp);
1046             if (code) 
1047                 continue;
1048
1049             callp = cm_GetRxConn(connp);
1050             code = RXAFS_GetVolumeStatus(callp, volp->ro.ID,
1051                                           &volStat, &Name, &OfflineMsg, &MOTD);
1052             rx_PutConnection(callp);        
1053
1054         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1055         code = cm_MapRPCError(code, &req);
1056
1057         if (code == 0 && volStat.Online) {
1058             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_online);
1059             volp->ro.state = vl_online;
1060             online = 1;
1061         }
1062     }
1063
1064     if (volp->bk.ID != 0 && (!volID || volID == volp->bk.ID) &&
1065          (volp->bk.state == vl_busy || volp->bk.state == vl_offline)) {
1066         cm_InitReq(&req);
1067
1068         for (serversp = volp->bk.serversp; serversp; serversp = serversp->next) {
1069             if (serversp->status == srv_busy || serversp->status == srv_offline)
1070                 serversp->status = srv_not_busy;
1071         }
1072
1073         do {
1074             code = cm_ConnFromVolume(volp, volp->bk.ID, cm_rootUserp, &req, &connp);
1075             if (code) 
1076                 continue;
1077
1078             callp = cm_GetRxConn(connp);
1079             code = RXAFS_GetVolumeStatus(callp, volp->bk.ID,
1080                                           &volStat, &Name, &OfflineMsg, &MOTD);
1081             rx_PutConnection(callp);        
1082
1083         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1084         code = cm_MapRPCError(code, &req);
1085
1086         if (code == 0 && volStat.Online) {
1087             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_online);
1088             volp->bk.state = vl_online;
1089             online = 1;
1090         }
1091     }
1092
1093     lock_ReleaseMutex(&volp->mx);
1094     return online;
1095 }
1096
1097
1098 /* called from the Daemon thread */
1099 void cm_CheckOfflineVolumes(void)
1100 {
1101     cm_volume_t *volp;
1102
1103     lock_ObtainWrite(&cm_volumeLock);
1104     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1105         volp->refCount++;
1106         lock_ReleaseWrite(&cm_volumeLock);
1107
1108         cm_CheckOfflineVolume(volp, 0);
1109
1110         lock_ObtainWrite(&cm_volumeLock);
1111         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1112     }
1113     lock_ReleaseWrite(&cm_volumeLock);
1114 }
1115
1116 void
1117 cm_UpdateVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1118 {
1119     struct cm_vol_state * statep = NULL;
1120     enum volstatus newStatus;
1121     cm_serverRef_t *tsrp;
1122     cm_server_t *tsp;
1123     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
1124
1125     if (volp->rw.ID == volID) {
1126         statep = &volp->rw;
1127     } else if (volp->ro.ID == volID) {
1128         statep = &volp->ro;
1129     } else if (volp->bk.ID == volID) {
1130         statep = &volp->bk;
1131     }
1132
1133     if (!statep) {
1134 #ifdef DEBUG
1135         DebugBreak();
1136 #endif
1137         return;
1138     }
1139
1140     lock_ObtainWrite(&cm_serverLock);
1141     for (tsrp = statep->serversp; tsrp; tsrp=tsrp->next) {
1142         tsp = tsrp->server;
1143         cm_GetServerNoLock(tsp);
1144         if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
1145             allDown = 0;
1146             if (tsrp->status == srv_busy) {
1147                 allOffline = 0;
1148                 someBusy = 1;
1149             } else if (tsrp->status == srv_offline) {
1150                 allBusy = 0;
1151                 someOffline = 1;
1152             } else {
1153                 allOffline = 0;
1154                 allBusy = 0;
1155             }
1156         }
1157         cm_PutServerNoLock(tsp);
1158     }   
1159     lock_ReleaseWrite(&cm_serverLock);
1160
1161     if (allDown)
1162         newStatus = vl_alldown;
1163     else if (allBusy || (someBusy && someOffline)) 
1164         newStatus = vl_busy;
1165     else if (allOffline)
1166         newStatus = vl_offline;
1167     else
1168         newStatus = vl_online;
1169
1170
1171     if (statep->ID && statep->state != newStatus)
1172         cm_VolumeStatusNotification(volp, statep->ID, statep->state, newStatus);
1173
1174     statep->state = newStatus;
1175 }
1176
1177 /*
1178 ** Finds all volumes that reside on this server and reorders their
1179 ** RO list according to the changed rank of server.
1180 */
1181 void cm_ChangeRankVolume(cm_server_t *tsp)
1182 {       
1183     int                 code;
1184     cm_volume_t*        volp;
1185
1186     /* find volumes which might have RO copy on server*/
1187     lock_ObtainWrite(&cm_volumeLock);
1188     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1189     {
1190         code = 1 ;      /* assume that list is unchanged */
1191         volp->refCount++;
1192         lock_ReleaseWrite(&cm_volumeLock);
1193         lock_ObtainMutex(&volp->mx);
1194
1195         if ((tsp->cellp==volp->cellp) && (volp->ro.serversp))
1196             code =cm_ChangeRankServer(&volp->ro.serversp, tsp);
1197
1198         /* this volume list was changed */
1199         if ( !code )
1200             cm_RandomizeServer(&volp->ro.serversp);
1201
1202         lock_ReleaseMutex(&volp->mx);
1203         lock_ObtainWrite(&cm_volumeLock);
1204         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1205     }
1206     lock_ReleaseWrite(&cm_volumeLock);
1207 }       
1208
1209 /* dump all volumes that have reference count > 0 to a file. 
1210  * cookie is used to identify this batch for easy parsing, 
1211  * and it a string provided by a caller 
1212  */
1213 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
1214 {
1215     int zilch;
1216     cm_volume_t *volp;
1217     char output[1024];
1218   
1219     if (lock) {
1220         lock_ObtainRead(&cm_scacheLock);
1221         lock_ObtainRead(&cm_volumeLock);
1222     }
1223   
1224     sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\r\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
1225     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1226   
1227     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1228     {
1229         cm_scache_t *scp;
1230         int scprefs = 0;
1231
1232         for (scp = cm_data.allSCachesp; scp; scp = scp->allNextp) 
1233         {
1234             if (scp->volp == volp)
1235                 scprefs++;
1236         }   
1237
1238         sprintf(output, "%s - volp=0x%p 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\r\n", 
1239                  cookie, volp, volp->cellp->name, volp->namep, volp->rw.ID, volp->ro.ID, volp->bk.ID, volp->flags, 
1240                  volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
1241                  volp->refCount, scprefs);
1242         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1243     }
1244     sprintf(output, "%s - Done dumping volumes.\r\n", cookie);
1245     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1246   
1247     if (lock) {
1248         lock_ReleaseRead(&cm_volumeLock);
1249         lock_ReleaseRead(&cm_scacheLock);
1250     }
1251     return (0);     
1252 }
1253
1254
1255 /* 
1256  * String hash function used by SDBM project.
1257  * It was chosen because it is fast and provides
1258  * decent coverage.
1259  */
1260 afs_uint32 SDBMHash(const char * str)
1261 {
1262     afs_uint32 hash = 0;
1263     size_t i, len;
1264
1265     if (str == NULL)
1266         return 0;
1267
1268     for(i = 0, len = strlen(str); i < len; i++)
1269     {
1270         hash = str[i] + (hash << 6) + (hash << 16) - hash;
1271     }
1272
1273     return (hash & 0x7FFFFFFF);
1274 }
1275
1276 /* call with volume write-locked and mutex held */
1277 void cm_AddVolumeToNameHashTable(cm_volume_t *volp)
1278 {
1279     int i;
1280     
1281     if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
1282         return;
1283
1284     i = CM_VOLUME_NAME_HASH(volp->namep);
1285
1286     volp->nameNextp = cm_data.volumeNameHashTablep[i];
1287     cm_data.volumeNameHashTablep[i] = volp;
1288     volp->flags |= CM_VOLUMEFLAG_IN_HASH;
1289 }
1290
1291 /* call with volume write-locked and mutex held */
1292 void cm_RemoveVolumeFromNameHashTable(cm_volume_t *volp)
1293 {
1294     cm_volume_t **lvolpp;
1295     cm_volume_t *tvolp;
1296     int i;
1297         
1298     if (volp->flags & CM_VOLUMEFLAG_IN_HASH) {
1299         /* hash it out first */
1300         i = CM_VOLUME_NAME_HASH(volp->namep);
1301         for (lvolpp = &cm_data.volumeNameHashTablep[i], tvolp = cm_data.volumeNameHashTablep[i];
1302              tvolp;
1303              lvolpp = &tvolp->nameNextp, tvolp = tvolp->nameNextp) {
1304             if (tvolp == volp) {
1305                 *lvolpp = volp->nameNextp;
1306                 volp->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1307                 volp->nameNextp = NULL;
1308                 break;
1309             }
1310         }
1311     }
1312 }
1313
1314 /* call with volume write-locked and mutex held */
1315 void cm_AddVolumeToIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1316 {
1317     int i;
1318     struct cm_vol_state * statep;
1319
1320     switch (volType) {
1321     case RWVOL:
1322         statep = &volp->rw;
1323         break;
1324     case ROVOL:                                
1325         statep = &volp->ro;
1326         break;
1327     case BACKVOL:
1328         statep = &volp->bk;
1329         break;
1330     default:
1331         return;
1332     }
1333
1334     if (statep->flags & CM_VOLUMEFLAG_IN_HASH)
1335         return;
1336
1337     i = CM_VOLUME_ID_HASH(statep->ID);
1338
1339     switch (volType) {
1340     case RWVOL:
1341         statep->nextp = cm_data.volumeRWIDHashTablep[i];
1342         cm_data.volumeRWIDHashTablep[i] = volp;
1343         break;
1344     case ROVOL:                                
1345         statep->nextp = cm_data.volumeROIDHashTablep[i];
1346         cm_data.volumeROIDHashTablep[i] = volp;
1347         break;
1348     case BACKVOL:
1349         statep->nextp = cm_data.volumeBKIDHashTablep[i];
1350         cm_data.volumeBKIDHashTablep[i] = volp;
1351         break;
1352     }
1353     statep->flags |= CM_VOLUMEFLAG_IN_HASH;
1354 }
1355
1356
1357 /* call with volume write-locked and mutex held */
1358 void cm_RemoveVolumeFromIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1359 {
1360     cm_volume_t **lvolpp;
1361     cm_volume_t *tvolp;
1362     struct cm_vol_state * statep;
1363     int i;
1364         
1365     switch (volType) {
1366     case RWVOL:
1367         statep = &volp->rw;
1368         break;
1369     case ROVOL:                                
1370         statep = &volp->ro;
1371         break;
1372     case BACKVOL:
1373         statep = &volp->bk;
1374         break;
1375     default:
1376         return;
1377     }
1378
1379     if (statep->flags & CM_VOLUMEFLAG_IN_HASH) {
1380         /* hash it out first */
1381         i = CM_VOLUME_ID_HASH(statep->ID);
1382
1383         switch (volType) {
1384         case RWVOL:
1385             lvolpp = &cm_data.volumeRWIDHashTablep[i];
1386             tvolp = cm_data.volumeRWIDHashTablep[i];
1387             break;
1388         case ROVOL:
1389             lvolpp = &cm_data.volumeROIDHashTablep[i];
1390             tvolp = cm_data.volumeROIDHashTablep[i];
1391             break;
1392         case BACKVOL:
1393             lvolpp = &cm_data.volumeBKIDHashTablep[i];
1394             tvolp = cm_data.volumeBKIDHashTablep[i];
1395             break;
1396         }
1397         do {
1398             if (tvolp == volp) {
1399                 *lvolpp = statep->nextp;
1400                 statep->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1401                 statep->nextp = NULL;
1402                 break;
1403             }
1404
1405             switch (volType) {
1406             case RWVOL:
1407                 lvolpp = &tvolp->rw.nextp;
1408                 tvolp = tvolp->rw.nextp;
1409                 break;
1410             case ROVOL:                                
1411                 lvolpp = &tvolp->ro.nextp;
1412                 tvolp = tvolp->ro.nextp;
1413                 break;
1414             case BACKVOL:
1415                 lvolpp = &tvolp->bk.nextp;
1416                 tvolp = tvolp->bk.nextp;
1417                 break;
1418             }
1419         } while(tvolp);
1420     }
1421 }
1422
1423 /* must be called with cm_volumeLock write-locked! */
1424 void cm_AdjustVolumeLRU(cm_volume_t *volp)
1425 {
1426     if (volp == cm_data.volumeLRULastp)
1427         cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1428     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1429         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1430     osi_QAdd((osi_queue_t **) &cm_data.volumeLRUFirstp, &volp->q);
1431     volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1432     if (!cm_data.volumeLRULastp) 
1433         cm_data.volumeLRULastp = volp;
1434 }
1435
1436 /* must be called with cm_volumeLock write-locked! */
1437 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1438 {
1439     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) {
1440         if (volp == cm_data.volumeLRULastp)
1441             cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1442         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1443         volp->flags &= ~CM_VOLUMEFLAG_IN_LRU_QUEUE;
1444     }
1445 }
1446
1447 static char * volstatus_str(enum volstatus vs)
1448 {
1449     switch (vs) {
1450     case vl_online:
1451         return "online";
1452     case vl_busy:
1453         return "busy";
1454     case vl_offline:
1455         return "offline";
1456     case vl_alldown:
1457         return "alldown";
1458     default:
1459         return "unknown";
1460     }
1461 }
1462
1463 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1464 {
1465     char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1466     char *ext = "";
1467
1468     if (volID == volp->rw.ID)
1469         ext = "";
1470     else if (volID == volp->ro.ID)
1471         ext = ".readonly";
1472     else if (volID == volp->bk.ID)
1473         ext = ".backup";
1474     else
1475         ext = ".nomatch";
1476     snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1477
1478     osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1479              osi_LogSaveString(afsd_logp, volstr), volID, volstatus_str(old), volstatus_str(new));
1480
1481     cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);
1482 }       
1483
1484 enum volstatus cm_GetVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1485 {
1486     if (volp->rw.ID == volID) {
1487         return volp->rw.state;
1488     } else if (volp->ro.ID == volID) {
1489         return volp->ro.state;
1490     } else if (volp->bk.ID == volID) {
1491         return volp->bk.state;
1492     } else {
1493         return vl_unknown;
1494     }
1495 }
1496