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