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