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