615b5a40f2a6e445f4aefddfcad0e097bdb2327f
[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, 0);
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) && !(flags & CM_GETVOL_FLAG_NO_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             if (!(flags & CM_GETVOL_FLAG_NO_LRU_UPDATE)) {
629                 lock_ObtainWrite(&cm_volumeLock);
630                 cm_AdjustVolumeLRU(volp);
631                 lock_ReleaseWrite(&cm_volumeLock);
632             }
633         } else
634             cm_PutVolume(volp);
635
636         return code;
637     }
638         
639     /* otherwise, we didn't find it so consult the VLDB */
640     sprintf(volNameString, "%u", volumeID);
641     code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
642                               flags, outVolpp);
643     return code;
644 }
645
646
647 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
648                         struct cm_user *userp, struct cm_req *reqp,
649                         afs_uint32 flags, cm_volume_t **outVolpp)
650 {
651     cm_volume_t *volp;
652 #ifdef SEARCH_ALL_VOLUMES
653     cm_volume_t *volp2;
654 #endif
655     long        code = 0;
656     char        name[VL_MAXNAMELEN];
657     size_t      len;
658     int         type;
659     afs_uint32  hash;
660
661     strncpy(name, volumeNamep, VL_MAXNAMELEN);
662     name[VL_MAXNAMELEN-1] = '\0';
663     len = strlen(name);
664
665     if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
666         type = BACKVOL;
667         name[len - 7] = '\0';
668     } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
669         type = ROVOL;
670         name[len - 9] = '\0';
671     } else {
672         type = RWVOL;
673     }
674
675     lock_ObtainRead(&cm_volumeLock);
676 #ifdef SEARCH_ALL_VOLUMES
677     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
678         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0) {
679             break;
680         }
681     }   
682     volp2 = volp;
683 #endif /* SEARCH_ALL_VOLUMES */
684
685     hash = CM_VOLUME_NAME_HASH(name);
686     for (volp = cm_data.volumeNameHashTablep[hash]; volp; volp = volp->nameNextp) {
687         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0)
688             break;
689     }
690
691 #ifdef SEARCH_ALL_VOLUMES
692     osi_assertx(volp2 == volp, "unexpected cm_vol_t");
693 #endif
694
695     if (!volp && (flags & CM_GETVOL_FLAG_CREATE)) {
696         /* otherwise, get from VLDB */
697
698         if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
699
700 #ifdef RECYCLE_FROM_ALL_VOLUMES_LIST
701             for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
702                 if ( volp->refCount == 0 ) {
703                     /* There is one we can re-use */
704                     break;
705                 }
706             }
707 #else
708             for ( volp = cm_data.volumeLRULastp;
709                   volp;
710                   volp = (cm_volume_t *) osi_QPrev(&volp->q)) 
711             {
712                 if ( volp->refCount == 0 ) {
713                     /* There is one we can re-use */
714                     break;
715                 }
716             }
717 #endif
718             if (!volp)
719                 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
720
721             lock_ReleaseRead(&cm_volumeLock);
722             lock_ObtainMutex(&volp->mx);
723             lock_ObtainWrite(&cm_volumeLock);
724
725             osi_Log2(afsd_logp, "Recycling Volume %s:%s",
726                      volp->cellp->name, volp->namep);
727
728             if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
729                 cm_RemoveVolumeFromLRU(volp);
730             if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
731                 cm_RemoveVolumeFromNameHashTable(volp);
732             if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
733                 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
734             if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
735                 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
736             if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
737                 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
738
739             if (volp->rw.ID)
740                 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
741             if (volp->ro.ID)
742                 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
743             if (volp->bk.ID)
744                 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
745
746             volp->rw.ID = volp->ro.ID = volp->bk.ID = 0;
747             volp->dotdotFid.cell = 0;
748             volp->dotdotFid.volume = 0;
749             volp->dotdotFid.unique = 0;
750             volp->dotdotFid.vnode = 0;
751         } else {
752             volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
753             memset(volp, 0, sizeof(cm_volume_t));
754             volp->magic = CM_VOLUME_MAGIC;
755             volp->allNextp = cm_data.allVolumesp;
756             cm_data.allVolumesp = volp;
757             lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
758             lock_ReleaseRead(&cm_volumeLock);
759             lock_ObtainMutex(&volp->mx);
760             lock_ObtainWrite(&cm_volumeLock);
761         }
762         volp->cellp = cellp;
763         strncpy(volp->namep, name, VL_MAXNAMELEN);
764         volp->namep[VL_MAXNAMELEN-1] = '\0';
765         volp->refCount = 1;     /* starts off held */
766         volp->flags = CM_VOLUMEFLAG_RESET;
767         volp->rw.state = volp->ro.state = volp->bk.state = vl_unknown;
768         volp->rw.nextp = volp->ro.nextp = volp->bk.nextp = NULL;
769         volp->rw.flags = volp->ro.flags = volp->bk.flags = 0;
770         volp->cbExpiresRO = 0;
771         cm_AddVolumeToNameHashTable(volp);
772         lock_ReleaseWrite(&cm_volumeLock);
773     }
774     else {
775         lock_ReleaseRead(&cm_volumeLock);
776         if (volp) {
777             cm_GetVolume(volp);
778             lock_ObtainMutex(&volp->mx);
779         } else {
780             return CM_ERROR_NOSUCHVOLUME;
781         }
782     }
783
784     /* if we get here we are holding the mutex */
785     if ((volp->flags & CM_VOLUMEFLAG_RESET) && !(flags & CM_GETVOL_FLAG_NO_RESET)) {
786         code = cm_UpdateVolume(cellp, userp, reqp, volp);
787         if (code == 0)
788             volp->flags &= ~CM_VOLUMEFLAG_RESET;
789     }   
790     lock_ReleaseMutex(&volp->mx);
791
792     if (code == 0 && (type == BACKVOL && volp->bk.ID == 0 ||
793                       type == ROVOL && volp->ro.ID == 0))
794         code = CM_ERROR_NOSUCHVOLUME;
795
796     if (code == 0) {
797                 *outVolpp = volp;
798                 
799                 if (!(flags & CM_GETVOL_FLAG_NO_LRU_UPDATE)) {
800                 lock_ObtainWrite(&cm_volumeLock);
801                         cm_AdjustVolumeLRU(volp);
802                         lock_ReleaseWrite(&cm_volumeLock);
803                 }
804     } else
805                 cm_PutVolume(volp);
806
807     return code;
808 }       
809
810 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
811 {
812     cm_cell_t *cellp;
813     cm_volume_t *volp;
814 #ifdef SEARCH_ALL_VOLUMES
815     cm_volume_t *volp2;
816 #endif
817     afs_uint32  hash;
818
819     if (!fidp) return;
820
821     cellp = cm_FindCellByID(fidp->cell, 0);
822     if (!cellp) return;
823
824     /* search for the volume */
825     lock_ObtainRead(&cm_volumeLock);
826 #ifdef SEARCH_ALL_VOLUMES
827     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
828         if (cellp == volp->cellp &&
829              (fidp->volume == volp->rw.ID ||
830                fidp->volume == volp->ro.ID ||
831                fidp->volume == volp->bk.ID))
832             break;
833     }   
834 #endif /* SEARCH_ALL_VOLUMES */
835
836     hash = CM_VOLUME_ID_HASH(fidp->volume);
837     /* The volumeID can be any one of the three types.  So we must
838      * search the hash table for all three types until we find it.
839      * We will search in the order of RO, RW, BK.
840      */
841     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
842         if ( cellp == volp->cellp && fidp->volume == volp->ro.ID )
843             break;
844     }
845     if (!volp) {
846         /* try RW volumes */
847         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
848             if ( cellp == volp->cellp && fidp->volume == volp->rw.ID )
849                 break;
850         }
851     }
852     if (!volp) {
853         /* try BK volumes */
854         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
855             if ( cellp == volp->cellp && fidp->volume == volp->bk.ID )
856                 break;
857         }
858     }
859
860 #ifdef SEARCH_ALL_VOLUMES
861     osi_assertx(volp == volp2, "unexpected cm_vol_t");
862 #endif
863
864     lock_ReleaseRead(&cm_volumeLock);
865
866     /* hold the volume if we found it */
867     if (volp) 
868         cm_GetVolume(volp);
869
870     /* update it */
871     cm_data.mountRootGen = time(NULL);
872     lock_ObtainMutex(&volp->mx);
873     volp->flags |= CM_VOLUMEFLAG_RESET;
874 #ifdef COMMENT
875     /* Mark the volume to be updated but don't update it now.
876      * This function is called only from within cm_Analyze
877      * when cm_ConnByMServers has failed with all servers down
878      * The problem is that cm_UpdateVolume is going to call
879      * cm_ConnByMServers which may cause a recursive chain
880      * of calls each returning a retry on failure.
881      * Instead, set the flag so the next time the volume is
882      * accessed by Name or ID the UpdateVolume call will
883      * occur.
884      */
885     code = cm_UpdateVolume(cellp, userp, reqp, volp);
886     if (code == 0)
887         volp->flags &= ~CM_VOLUMEFLAG_RESET;
888 #endif
889     lock_ReleaseMutex(&volp->mx);
890
891     cm_PutVolume(volp);
892 }
893
894 /* find the appropriate servers from a volume */
895 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volume)
896 {
897     cm_serverRef_t **serverspp;
898     cm_serverRef_t *current;;
899
900     lock_ObtainWrite(&cm_serverLock);
901
902     if (volume == volp->rw.ID)
903         serverspp = &volp->rw.serversp;
904     else if (volume == volp->ro.ID)
905         serverspp = &volp->ro.serversp;
906     else if (volume == volp->bk.ID)
907         serverspp = &volp->bk.serversp;
908     else 
909         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
910         
911     for (current = *serverspp; current; current = current->next)
912         current->refCount++;
913
914     lock_ReleaseWrite(&cm_serverLock);
915
916     return serverspp;
917 }
918
919 void cm_PutVolume(cm_volume_t *volp)
920 {
921     lock_ObtainWrite(&cm_volumeLock);
922     osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
923     lock_ReleaseWrite(&cm_volumeLock);
924 }
925
926 /* return the read-only volume, if there is one, or the read-write volume if
927  * not.
928  */
929 long cm_GetROVolumeID(cm_volume_t *volp)
930 {
931     long id;
932
933     lock_ObtainMutex(&volp->mx);
934     if (volp->ro.ID && volp->ro.serversp)
935         id = volp->ro.ID;
936     else
937         id = volp->rw.ID;
938     lock_ReleaseMutex(&volp->mx);
939
940     return id;
941 }
942
943 void cm_RefreshVolumes(void)
944 {
945     cm_volume_t *volp;
946     cm_scache_t *scp;
947
948     cm_data.mountRootGen = time(NULL);
949
950     /* force a re-loading of volume data from the vldb */
951     lock_ObtainWrite(&cm_volumeLock);
952     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
953         volp->refCount++;
954         lock_ReleaseWrite(&cm_volumeLock);
955
956         lock_ObtainMutex(&volp->mx);
957         volp->flags |= CM_VOLUMEFLAG_RESET;
958         lock_ReleaseMutex(&volp->mx);
959         
960         lock_ObtainWrite(&cm_volumeLock);
961         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
962     }
963     lock_ReleaseWrite(&cm_volumeLock);
964
965     /* force mount points to be re-evaluated so that 
966      * if the volume location has changed we will pick 
967      * that up
968      */
969     for ( scp = cm_data.scacheLRUFirstp; 
970           scp;
971           scp = (cm_scache_t *) osi_QNext(&scp->q)) {
972         if ( scp->fileType == CM_SCACHETYPE_MOUNTPOINT 
973 #ifdef AFS_FREELANCE_CLIENT
974              && !(scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID)
975 #endif
976              ) {
977             lock_ObtainMutex(&scp->mx);
978             scp->mountPointStringp[0] = '\0';
979             lock_ReleaseMutex(&scp->mx);
980         }
981     }
982
983 }
984
985
986 /* The return code is 0 if the volume is not online and 
987  * 1 if the volume is online
988  */
989 long
990 cm_CheckOfflineVolume(cm_volume_t *volp, afs_uint32 volID)
991 {
992     cm_conn_t *connp;
993     long code;
994     AFSFetchVolumeStatus volStat;
995     char *Name;
996     char *OfflineMsg;
997     char *MOTD;
998     cm_req_t req;
999     struct rx_connection * callp;
1000     char volName[32];
1001     char offLineMsg[256];
1002     char motd[256];
1003     long online = 0;
1004     cm_serverRef_t *serversp;
1005
1006     Name = volName;
1007     OfflineMsg = offLineMsg;
1008     MOTD = motd;
1009
1010     lock_ObtainMutex(&volp->mx);
1011
1012     if (volp->rw.ID != 0 && (!volID || volID == volp->rw.ID) &&
1013          (volp->rw.state == vl_busy || volp->rw.state == vl_offline || volp->rw.state == vl_unknown)) {
1014         cm_InitReq(&req);
1015
1016         for (serversp = volp->rw.serversp; serversp; serversp = serversp->next) {
1017             if (serversp->status == srv_busy || serversp->status == srv_offline)
1018                 serversp->status = srv_not_busy;
1019         }
1020
1021         lock_ReleaseMutex(&volp->mx);
1022         do {
1023             code = cm_ConnFromVolume(volp, volp->rw.ID, cm_rootUserp, &req, &connp);
1024             if (code) 
1025                 continue;
1026
1027             callp = cm_GetRxConn(connp);
1028             code = RXAFS_GetVolumeStatus(callp, volp->rw.ID,
1029                                           &volStat, &Name, &OfflineMsg, &MOTD);
1030             rx_PutConnection(callp);        
1031
1032         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1033         code = cm_MapRPCError(code, &req);
1034
1035         lock_ObtainMutex(&volp->mx);
1036         if (code == 0 && volStat.Online) {
1037             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_online);
1038             volp->rw.state = vl_online;
1039             online = 1;
1040         } else if (code == CM_ERROR_NOACCESS) {
1041             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
1042             volp->rw.state = vl_unknown;
1043             online = 1;
1044         }
1045     }
1046
1047     if (volp->ro.ID != 0 && (!volID || volID == volp->ro.ID) &&
1048          (volp->ro.state == vl_busy || volp->ro.state == vl_offline || volp->ro.state == vl_unknown)) {
1049         cm_InitReq(&req);
1050
1051         for (serversp = volp->ro.serversp; serversp; serversp = serversp->next) {
1052             if (serversp->status == srv_busy || serversp->status == srv_offline)
1053                 serversp->status = srv_not_busy;
1054         }
1055
1056         lock_ReleaseMutex(&volp->mx);
1057         do {
1058             code = cm_ConnFromVolume(volp, volp->ro.ID, cm_rootUserp, &req, &connp);
1059             if (code) 
1060                 continue;
1061
1062             callp = cm_GetRxConn(connp);
1063             code = RXAFS_GetVolumeStatus(callp, volp->ro.ID,
1064                                           &volStat, &Name, &OfflineMsg, &MOTD);
1065             rx_PutConnection(callp);        
1066
1067         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1068         code = cm_MapRPCError(code, &req);
1069
1070         lock_ObtainMutex(&volp->mx);
1071         if (code == 0 && volStat.Online) {
1072             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_online);
1073             volp->ro.state = vl_online;
1074             online = 1;
1075         } else if (code == CM_ERROR_NOACCESS) {
1076             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
1077             volp->ro.state = vl_unknown;
1078             online = 1;
1079         }
1080     }
1081
1082     if (volp->bk.ID != 0 && (!volID || volID == volp->bk.ID) &&
1083          (volp->bk.state == vl_busy || volp->bk.state == vl_offline || volp->bk.state == vl_unknown)) {
1084         cm_InitReq(&req);
1085
1086         for (serversp = volp->bk.serversp; serversp; serversp = serversp->next) {
1087             if (serversp->status == srv_busy || serversp->status == srv_offline)
1088                 serversp->status = srv_not_busy;
1089         }
1090
1091         lock_ReleaseMutex(&volp->mx);
1092         do {
1093             code = cm_ConnFromVolume(volp, volp->bk.ID, cm_rootUserp, &req, &connp);
1094             if (code) 
1095                 continue;
1096
1097             callp = cm_GetRxConn(connp);
1098             code = RXAFS_GetVolumeStatus(callp, volp->bk.ID,
1099                                           &volStat, &Name, &OfflineMsg, &MOTD);
1100             rx_PutConnection(callp);        
1101
1102         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1103         code = cm_MapRPCError(code, &req);
1104
1105         lock_ObtainMutex(&volp->mx);
1106         if (code == 0 && volStat.Online) {
1107             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_online);
1108             volp->bk.state = vl_online;
1109             online = 1;
1110         } else if (code == CM_ERROR_NOACCESS) {
1111             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
1112             volp->bk.state = vl_unknown;
1113             online = 1;
1114         }
1115     }
1116
1117     lock_ReleaseMutex(&volp->mx);
1118     return online;
1119 }
1120
1121
1122 /* called from the Daemon thread */
1123 void cm_CheckOfflineVolumes(void)
1124 {
1125     cm_volume_t *volp;
1126
1127     lock_ObtainWrite(&cm_volumeLock);
1128     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1129         volp->refCount++;
1130         lock_ReleaseWrite(&cm_volumeLock);
1131
1132         cm_CheckOfflineVolume(volp, 0);
1133
1134         lock_ObtainWrite(&cm_volumeLock);
1135         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1136     }
1137     lock_ReleaseWrite(&cm_volumeLock);
1138 }
1139
1140
1141 static void
1142 cm_UpdateVolumeStatusInt(cm_volume_t *volp, struct cm_vol_state *statep)
1143 {
1144     enum volstatus newStatus;
1145     cm_serverRef_t *tsrp;
1146     cm_server_t *tsp;
1147     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
1148
1149     if (!volp || !statep) {
1150 #ifdef DEBUG
1151         DebugBreak();
1152 #endif
1153         return;
1154     }
1155
1156     lock_ObtainWrite(&cm_serverLock);
1157     for (tsrp = statep->serversp; tsrp; tsrp=tsrp->next) {
1158         tsp = tsrp->server;
1159         cm_GetServerNoLock(tsp);
1160         if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
1161             allDown = 0;
1162             if (tsrp->status == srv_busy) {
1163                 allOffline = 0;
1164                 someBusy = 1;
1165             } else if (tsrp->status == srv_offline) {
1166                 allBusy = 0;
1167                 someOffline = 1;
1168             } else {
1169                 allOffline = 0;
1170                 allBusy = 0;
1171             }
1172         }
1173         cm_PutServerNoLock(tsp);
1174     }   
1175     lock_ReleaseWrite(&cm_serverLock);
1176
1177     if (allDown)
1178         newStatus = vl_alldown;
1179     else if (allBusy || (someBusy && someOffline)) 
1180         newStatus = vl_busy;
1181     else if (allOffline)
1182         newStatus = vl_offline;
1183     else
1184         newStatus = vl_online;
1185
1186     if (statep->ID && statep->state != newStatus)
1187         cm_VolumeStatusNotification(volp, statep->ID, statep->state, newStatus);
1188
1189     statep->state = newStatus;
1190 }
1191
1192 void
1193 cm_UpdateVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1194 {
1195
1196     if (volp->rw.ID == volID) {
1197         cm_UpdateVolumeStatusInt(volp, &volp->rw);
1198     } else if (volp->ro.ID == volID) {
1199         cm_UpdateVolumeStatusInt(volp, &volp->ro);
1200     } else if (volp->bk.ID == volID) {
1201         cm_UpdateVolumeStatusInt(volp, &volp->bk);
1202     } else {
1203         /*
1204          * If we are called with volID == 0 then something has gone wrong.
1205          * Most likely a race occurred in the server volume list maintenance.
1206          * Since we don't know which volume's status should be updated, 
1207          * just update all of them that are known to exist.  Better to be 
1208          * correct than fast.
1209          */
1210         if (volp->rw.ID != 0)
1211             cm_UpdateVolumeStatusInt(volp, &volp->rw);
1212         if (volp->ro.ID != 0)
1213             cm_UpdateVolumeStatusInt(volp, &volp->ro);
1214         if (volp->bk.ID != 0)
1215             cm_UpdateVolumeStatusInt(volp, &volp->bk);
1216     }
1217 }
1218
1219 /*
1220 ** Finds all volumes that reside on this server and reorders their
1221 ** RO list according to the changed rank of server.
1222 */
1223 void cm_ChangeRankVolume(cm_server_t *tsp)
1224 {       
1225     int                 code;
1226     cm_volume_t*        volp;
1227
1228     /* find volumes which might have RO copy on server*/
1229     lock_ObtainWrite(&cm_volumeLock);
1230     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1231     {
1232         code = 1 ;      /* assume that list is unchanged */
1233         volp->refCount++;
1234         lock_ReleaseWrite(&cm_volumeLock);
1235         lock_ObtainMutex(&volp->mx);
1236
1237         if ((tsp->cellp==volp->cellp) && (volp->ro.serversp))
1238             code =cm_ChangeRankServer(&volp->ro.serversp, tsp);
1239
1240         /* this volume list was changed */
1241         if ( !code )
1242             cm_RandomizeServer(&volp->ro.serversp);
1243
1244         lock_ReleaseMutex(&volp->mx);
1245         lock_ObtainWrite(&cm_volumeLock);
1246         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1247     }
1248     lock_ReleaseWrite(&cm_volumeLock);
1249 }       
1250
1251 /* dump all volumes that have reference count > 0 to a file. 
1252  * cookie is used to identify this batch for easy parsing, 
1253  * and it a string provided by a caller 
1254  */
1255 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
1256 {
1257     int zilch;
1258     cm_volume_t *volp;
1259     char output[1024];
1260   
1261     if (lock) {
1262         lock_ObtainRead(&cm_scacheLock);
1263         lock_ObtainRead(&cm_volumeLock);
1264     }
1265   
1266     sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\r\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
1267     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1268   
1269     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1270     {
1271         cm_scache_t *scp;
1272         int scprefs = 0;
1273
1274         for (scp = cm_data.allSCachesp; scp; scp = scp->allNextp) 
1275         {
1276             if (scp->volp == volp)
1277                 scprefs++;
1278         }   
1279
1280         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", 
1281                  cookie, volp, volp->cellp->name, volp->namep, volp->rw.ID, volp->ro.ID, volp->bk.ID, volp->flags, 
1282                  volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
1283                  volp->refCount, scprefs);
1284         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1285     }
1286     sprintf(output, "%s - Done dumping volumes.\r\n", cookie);
1287     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1288   
1289     if (lock) {
1290         lock_ReleaseRead(&cm_volumeLock);
1291         lock_ReleaseRead(&cm_scacheLock);
1292     }
1293     return (0);     
1294 }
1295
1296
1297 /* 
1298  * String hash function used by SDBM project.
1299  * It was chosen because it is fast and provides
1300  * decent coverage.
1301  */
1302 afs_uint32 SDBMHash(const char * str)
1303 {
1304     afs_uint32 hash = 0;
1305     size_t i, len;
1306
1307     if (str == NULL)
1308         return 0;
1309
1310     for(i = 0, len = strlen(str); i < len; i++)
1311     {
1312         hash = str[i] + (hash << 6) + (hash << 16) - hash;
1313     }
1314
1315     return (hash & 0x7FFFFFFF);
1316 }
1317
1318 /* call with volume write-locked and mutex held */
1319 void cm_AddVolumeToNameHashTable(cm_volume_t *volp)
1320 {
1321     int i;
1322     
1323     if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
1324         return;
1325
1326     i = CM_VOLUME_NAME_HASH(volp->namep);
1327
1328     volp->nameNextp = cm_data.volumeNameHashTablep[i];
1329     cm_data.volumeNameHashTablep[i] = volp;
1330     volp->flags |= CM_VOLUMEFLAG_IN_HASH;
1331 }
1332
1333 /* call with volume write-locked and mutex held */
1334 void cm_RemoveVolumeFromNameHashTable(cm_volume_t *volp)
1335 {
1336     cm_volume_t **lvolpp;
1337     cm_volume_t *tvolp;
1338     int i;
1339         
1340     if (volp->flags & CM_VOLUMEFLAG_IN_HASH) {
1341         /* hash it out first */
1342         i = CM_VOLUME_NAME_HASH(volp->namep);
1343         for (lvolpp = &cm_data.volumeNameHashTablep[i], tvolp = cm_data.volumeNameHashTablep[i];
1344              tvolp;
1345              lvolpp = &tvolp->nameNextp, tvolp = tvolp->nameNextp) {
1346             if (tvolp == volp) {
1347                 *lvolpp = volp->nameNextp;
1348                 volp->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1349                 volp->nameNextp = NULL;
1350                 break;
1351             }
1352         }
1353     }
1354 }
1355
1356 /* call with volume write-locked and mutex held */
1357 void cm_AddVolumeToIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1358 {
1359     int i;
1360     struct cm_vol_state * statep;
1361
1362     switch (volType) {
1363     case RWVOL:
1364         statep = &volp->rw;
1365         break;
1366     case ROVOL:                                
1367         statep = &volp->ro;
1368         break;
1369     case BACKVOL:
1370         statep = &volp->bk;
1371         break;
1372     default:
1373         return;
1374     }
1375
1376     if (statep->flags & CM_VOLUMEFLAG_IN_HASH)
1377         return;
1378
1379     i = CM_VOLUME_ID_HASH(statep->ID);
1380
1381     switch (volType) {
1382     case RWVOL:
1383         statep->nextp = cm_data.volumeRWIDHashTablep[i];
1384         cm_data.volumeRWIDHashTablep[i] = volp;
1385         break;
1386     case ROVOL:                                
1387         statep->nextp = cm_data.volumeROIDHashTablep[i];
1388         cm_data.volumeROIDHashTablep[i] = volp;
1389         break;
1390     case BACKVOL:
1391         statep->nextp = cm_data.volumeBKIDHashTablep[i];
1392         cm_data.volumeBKIDHashTablep[i] = volp;
1393         break;
1394     }
1395     statep->flags |= CM_VOLUMEFLAG_IN_HASH;
1396 }
1397
1398
1399 /* call with volume write-locked and mutex held */
1400 void cm_RemoveVolumeFromIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1401 {
1402     cm_volume_t **lvolpp;
1403     cm_volume_t *tvolp;
1404     struct cm_vol_state * statep;
1405     int i;
1406         
1407     switch (volType) {
1408     case RWVOL:
1409         statep = &volp->rw;
1410         break;
1411     case ROVOL:                                
1412         statep = &volp->ro;
1413         break;
1414     case BACKVOL:
1415         statep = &volp->bk;
1416         break;
1417     default:
1418         return;
1419     }
1420
1421     if (statep->flags & CM_VOLUMEFLAG_IN_HASH) {
1422         /* hash it out first */
1423         i = CM_VOLUME_ID_HASH(statep->ID);
1424
1425         switch (volType) {
1426         case RWVOL:
1427             lvolpp = &cm_data.volumeRWIDHashTablep[i];
1428             tvolp = cm_data.volumeRWIDHashTablep[i];
1429             break;
1430         case ROVOL:
1431             lvolpp = &cm_data.volumeROIDHashTablep[i];
1432             tvolp = cm_data.volumeROIDHashTablep[i];
1433             break;
1434         case BACKVOL:
1435             lvolpp = &cm_data.volumeBKIDHashTablep[i];
1436             tvolp = cm_data.volumeBKIDHashTablep[i];
1437             break;
1438         }
1439         do {
1440             if (tvolp == volp) {
1441                 *lvolpp = statep->nextp;
1442                 statep->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1443                 statep->nextp = NULL;
1444                 break;
1445             }
1446
1447             switch (volType) {
1448             case RWVOL:
1449                 lvolpp = &tvolp->rw.nextp;
1450                 tvolp = tvolp->rw.nextp;
1451                 break;
1452             case ROVOL:                                
1453                 lvolpp = &tvolp->ro.nextp;
1454                 tvolp = tvolp->ro.nextp;
1455                 break;
1456             case BACKVOL:
1457                 lvolpp = &tvolp->bk.nextp;
1458                 tvolp = tvolp->bk.nextp;
1459                 break;
1460             }
1461         } while(tvolp);
1462     }
1463 }
1464
1465 /* must be called with cm_volumeLock write-locked! */
1466 void cm_AdjustVolumeLRU(cm_volume_t *volp)
1467 {
1468     if (volp == cm_data.volumeLRULastp)
1469         cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1470     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1471         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1472     osi_QAdd((osi_queue_t **) &cm_data.volumeLRUFirstp, &volp->q);
1473     volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1474     if (!cm_data.volumeLRULastp) 
1475         cm_data.volumeLRULastp = volp;
1476 }
1477
1478 /* must be called with cm_volumeLock write-locked! */
1479 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1480 {
1481     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) {
1482         if (volp == cm_data.volumeLRULastp)
1483             cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1484         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1485         volp->flags &= ~CM_VOLUMEFLAG_IN_LRU_QUEUE;
1486     }
1487 }
1488
1489 static char * volstatus_str(enum volstatus vs)
1490 {
1491     switch (vs) {
1492     case vl_online:
1493         return "online";
1494     case vl_busy:
1495         return "busy";
1496     case vl_offline:
1497         return "offline";
1498     case vl_alldown:
1499         return "alldown";
1500     default:
1501         return "unknown";
1502     }
1503 }
1504
1505 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1506 {
1507     char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1508     char *ext = "";
1509
1510     if (volID == volp->rw.ID)
1511         ext = "";
1512     else if (volID == volp->ro.ID)
1513         ext = ".readonly";
1514     else if (volID == volp->bk.ID)
1515         ext = ".backup";
1516     else
1517         ext = ".nomatch";
1518     snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1519
1520     osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1521              osi_LogSaveString(afsd_logp, volstr), volID, volstatus_str(old), volstatus_str(new));
1522
1523     cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);
1524 }       
1525
1526 enum volstatus cm_GetVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1527 {
1528     if (volp->rw.ID == volID) {
1529         return volp->rw.state;
1530     } else if (volp->ro.ID == volID) {
1531         return volp->ro.state;
1532     } else if (volp->bk.ID == volID) {
1533         return volp->bk.state;
1534     } else {
1535         return vl_unknown;
1536     }
1537 }
1538
1539 /* Renew .readonly volume callbacks that are more than
1540  * 30 minutes old.  (A volume callback is issued for 2 hours.)
1541  */
1542 void 
1543 cm_VolumeRenewROCallbacks(void)
1544 {
1545     cm_volume_t * volp;
1546     time_t minexp = time(NULL) + 90 * 60;
1547
1548     lock_ObtainRead(&cm_volumeLock);
1549     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1550         if ( volp->cbExpiresRO > 0 && volp->cbExpiresRO < minexp) {
1551             cm_req_t      req;
1552             cm_fid_t      fid;
1553             cm_scache_t * scp;
1554
1555             fid.cell = volp->cellp->cellID;
1556             fid.volume = volp->ro.ID;
1557             fid.vnode = 1;
1558             fid.unique = 1;
1559
1560             cm_InitReq(&req);
1561
1562             lock_ReleaseRead(&cm_volumeLock);
1563             if (cm_GetSCache(&fid, &scp, cm_rootUserp, &req) == 0) {
1564                 lock_ObtainMutex(&scp->mx);
1565                 cm_GetCallback(scp, cm_rootUserp, &req, 1);
1566                 lock_ReleaseMutex(&scp->mx);
1567                 cm_ReleaseSCache(scp);
1568             }
1569             lock_ObtainRead(&cm_volumeLock);
1570         }
1571     }
1572     lock_ReleaseRead(&cm_volumeLock);
1573 }