e9025c26384820a1e3bac3da4a6c39f6a5b285bd
[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)) {
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         do {
1022             code = cm_ConnFromVolume(volp, volp->rw.ID, cm_rootUserp, &req, &connp);
1023             if (code) 
1024                 continue;
1025
1026             callp = cm_GetRxConn(connp);
1027             code = RXAFS_GetVolumeStatus(callp, volp->rw.ID,
1028                                           &volStat, &Name, &OfflineMsg, &MOTD);
1029             rx_PutConnection(callp);        
1030
1031         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1032         code = cm_MapRPCError(code, &req);
1033
1034         if (code == 0 && volStat.Online) {
1035             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_online);
1036             volp->rw.state = vl_online;
1037             online = 1;
1038         } else if (code == CM_ERROR_NOACCESS) {
1039             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
1040             volp->rw.state = vl_unknown;
1041             online = 1;
1042         }
1043     }
1044
1045     if (volp->ro.ID != 0 && (!volID || volID == volp->ro.ID) &&
1046          (volp->ro.state == vl_busy || volp->ro.state == vl_offline)) {
1047         cm_InitReq(&req);
1048
1049         for (serversp = volp->ro.serversp; serversp; serversp = serversp->next) {
1050             if (serversp->status == srv_busy || serversp->status == srv_offline)
1051                 serversp->status = srv_not_busy;
1052         }
1053
1054         do {
1055             code = cm_ConnFromVolume(volp, volp->ro.ID, cm_rootUserp, &req, &connp);
1056             if (code) 
1057                 continue;
1058
1059             callp = cm_GetRxConn(connp);
1060             code = RXAFS_GetVolumeStatus(callp, volp->ro.ID,
1061                                           &volStat, &Name, &OfflineMsg, &MOTD);
1062             rx_PutConnection(callp);        
1063
1064         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1065         code = cm_MapRPCError(code, &req);
1066
1067         if (code == 0 && volStat.Online) {
1068             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_online);
1069             volp->ro.state = vl_online;
1070             online = 1;
1071         } else if (code == CM_ERROR_NOACCESS) {
1072             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
1073             volp->ro.state = vl_unknown;
1074             online = 1;
1075         }
1076     }
1077
1078     if (volp->bk.ID != 0 && (!volID || volID == volp->bk.ID) &&
1079          (volp->bk.state == vl_busy || volp->bk.state == vl_offline)) {
1080         cm_InitReq(&req);
1081
1082         for (serversp = volp->bk.serversp; serversp; serversp = serversp->next) {
1083             if (serversp->status == srv_busy || serversp->status == srv_offline)
1084                 serversp->status = srv_not_busy;
1085         }
1086
1087         do {
1088             code = cm_ConnFromVolume(volp, volp->bk.ID, cm_rootUserp, &req, &connp);
1089             if (code) 
1090                 continue;
1091
1092             callp = cm_GetRxConn(connp);
1093             code = RXAFS_GetVolumeStatus(callp, volp->bk.ID,
1094                                           &volStat, &Name, &OfflineMsg, &MOTD);
1095             rx_PutConnection(callp);        
1096
1097         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1098         code = cm_MapRPCError(code, &req);
1099
1100         if (code == 0 && volStat.Online) {
1101             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_online);
1102             volp->bk.state = vl_online;
1103             online = 1;
1104         } else if (code == CM_ERROR_NOACCESS) {
1105             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
1106             volp->bk.state = vl_unknown;
1107             online = 1;
1108         }
1109     }
1110
1111     lock_ReleaseMutex(&volp->mx);
1112     return online;
1113 }
1114
1115
1116 /* called from the Daemon thread */
1117 void cm_CheckOfflineVolumes(void)
1118 {
1119     cm_volume_t *volp;
1120
1121     lock_ObtainWrite(&cm_volumeLock);
1122     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1123         volp->refCount++;
1124         lock_ReleaseWrite(&cm_volumeLock);
1125
1126         cm_CheckOfflineVolume(volp, 0);
1127
1128         lock_ObtainWrite(&cm_volumeLock);
1129         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1130     }
1131     lock_ReleaseWrite(&cm_volumeLock);
1132 }
1133
1134
1135 static void
1136 cm_UpdateVolumeStatusInt(cm_volume_t *volp, struct cm_vol_state *statep)
1137 {
1138     enum volstatus newStatus;
1139     cm_serverRef_t *tsrp;
1140     cm_server_t *tsp;
1141     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
1142
1143     if (!volp || !statep) {
1144 #ifdef DEBUG
1145         DebugBreak();
1146 #endif
1147         return;
1148     }
1149
1150     lock_ObtainWrite(&cm_serverLock);
1151     for (tsrp = statep->serversp; tsrp; tsrp=tsrp->next) {
1152         tsp = tsrp->server;
1153         cm_GetServerNoLock(tsp);
1154         if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
1155             allDown = 0;
1156             if (tsrp->status == srv_busy) {
1157                 allOffline = 0;
1158                 someBusy = 1;
1159             } else if (tsrp->status == srv_offline) {
1160                 allBusy = 0;
1161                 someOffline = 1;
1162             } else {
1163                 allOffline = 0;
1164                 allBusy = 0;
1165             }
1166         }
1167         cm_PutServerNoLock(tsp);
1168     }   
1169     lock_ReleaseWrite(&cm_serverLock);
1170
1171     if (allDown)
1172         newStatus = vl_alldown;
1173     else if (allBusy || (someBusy && someOffline)) 
1174         newStatus = vl_busy;
1175     else if (allOffline)
1176         newStatus = vl_offline;
1177     else
1178         newStatus = vl_online;
1179
1180     if (statep->ID && statep->state != newStatus)
1181         cm_VolumeStatusNotification(volp, statep->ID, statep->state, newStatus);
1182
1183     statep->state = newStatus;
1184 }
1185
1186 void
1187 cm_UpdateVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1188 {
1189
1190     if (volp->rw.ID == volID) {
1191         cm_UpdateVolumeStatusInt(volp, &volp->rw);
1192     } else if (volp->ro.ID == volID) {
1193         cm_UpdateVolumeStatusInt(volp, &volp->ro);
1194     } else if (volp->bk.ID == volID) {
1195         cm_UpdateVolumeStatusInt(volp, &volp->bk);
1196     } else {
1197         /*
1198          * If we are called with volID == 0 then something has gone wrong.
1199          * Most likely a race occurred in the server volume list maintenance.
1200          * Since we don't know which volume's status should be updated, 
1201          * just update all of them that are known to exist.  Better to be 
1202          * correct than fast.
1203          */
1204         if (volp->rw.ID != 0)
1205             cm_UpdateVolumeStatusInt(volp, &volp->rw);
1206         if (volp->ro.ID != 0)
1207             cm_UpdateVolumeStatusInt(volp, &volp->ro);
1208         if (volp->bk.ID != 0)
1209             cm_UpdateVolumeStatusInt(volp, &volp->bk);
1210     }
1211 }
1212
1213 /*
1214 ** Finds all volumes that reside on this server and reorders their
1215 ** RO list according to the changed rank of server.
1216 */
1217 void cm_ChangeRankVolume(cm_server_t *tsp)
1218 {       
1219     int                 code;
1220     cm_volume_t*        volp;
1221
1222     /* find volumes which might have RO copy on server*/
1223     lock_ObtainWrite(&cm_volumeLock);
1224     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1225     {
1226         code = 1 ;      /* assume that list is unchanged */
1227         volp->refCount++;
1228         lock_ReleaseWrite(&cm_volumeLock);
1229         lock_ObtainMutex(&volp->mx);
1230
1231         if ((tsp->cellp==volp->cellp) && (volp->ro.serversp))
1232             code =cm_ChangeRankServer(&volp->ro.serversp, tsp);
1233
1234         /* this volume list was changed */
1235         if ( !code )
1236             cm_RandomizeServer(&volp->ro.serversp);
1237
1238         lock_ReleaseMutex(&volp->mx);
1239         lock_ObtainWrite(&cm_volumeLock);
1240         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1241     }
1242     lock_ReleaseWrite(&cm_volumeLock);
1243 }       
1244
1245 /* dump all volumes that have reference count > 0 to a file. 
1246  * cookie is used to identify this batch for easy parsing, 
1247  * and it a string provided by a caller 
1248  */
1249 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
1250 {
1251     int zilch;
1252     cm_volume_t *volp;
1253     char output[1024];
1254   
1255     if (lock) {
1256         lock_ObtainRead(&cm_scacheLock);
1257         lock_ObtainRead(&cm_volumeLock);
1258     }
1259   
1260     sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\r\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
1261     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1262   
1263     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1264     {
1265         cm_scache_t *scp;
1266         int scprefs = 0;
1267
1268         for (scp = cm_data.allSCachesp; scp; scp = scp->allNextp) 
1269         {
1270             if (scp->volp == volp)
1271                 scprefs++;
1272         }   
1273
1274         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", 
1275                  cookie, volp, volp->cellp->name, volp->namep, volp->rw.ID, volp->ro.ID, volp->bk.ID, volp->flags, 
1276                  volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
1277                  volp->refCount, scprefs);
1278         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1279     }
1280     sprintf(output, "%s - Done dumping volumes.\r\n", cookie);
1281     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1282   
1283     if (lock) {
1284         lock_ReleaseRead(&cm_volumeLock);
1285         lock_ReleaseRead(&cm_scacheLock);
1286     }
1287     return (0);     
1288 }
1289
1290
1291 /* 
1292  * String hash function used by SDBM project.
1293  * It was chosen because it is fast and provides
1294  * decent coverage.
1295  */
1296 afs_uint32 SDBMHash(const char * str)
1297 {
1298     afs_uint32 hash = 0;
1299     size_t i, len;
1300
1301     if (str == NULL)
1302         return 0;
1303
1304     for(i = 0, len = strlen(str); i < len; i++)
1305     {
1306         hash = str[i] + (hash << 6) + (hash << 16) - hash;
1307     }
1308
1309     return (hash & 0x7FFFFFFF);
1310 }
1311
1312 /* call with volume write-locked and mutex held */
1313 void cm_AddVolumeToNameHashTable(cm_volume_t *volp)
1314 {
1315     int i;
1316     
1317     if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
1318         return;
1319
1320     i = CM_VOLUME_NAME_HASH(volp->namep);
1321
1322     volp->nameNextp = cm_data.volumeNameHashTablep[i];
1323     cm_data.volumeNameHashTablep[i] = volp;
1324     volp->flags |= CM_VOLUMEFLAG_IN_HASH;
1325 }
1326
1327 /* call with volume write-locked and mutex held */
1328 void cm_RemoveVolumeFromNameHashTable(cm_volume_t *volp)
1329 {
1330     cm_volume_t **lvolpp;
1331     cm_volume_t *tvolp;
1332     int i;
1333         
1334     if (volp->flags & CM_VOLUMEFLAG_IN_HASH) {
1335         /* hash it out first */
1336         i = CM_VOLUME_NAME_HASH(volp->namep);
1337         for (lvolpp = &cm_data.volumeNameHashTablep[i], tvolp = cm_data.volumeNameHashTablep[i];
1338              tvolp;
1339              lvolpp = &tvolp->nameNextp, tvolp = tvolp->nameNextp) {
1340             if (tvolp == volp) {
1341                 *lvolpp = volp->nameNextp;
1342                 volp->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1343                 volp->nameNextp = NULL;
1344                 break;
1345             }
1346         }
1347     }
1348 }
1349
1350 /* call with volume write-locked and mutex held */
1351 void cm_AddVolumeToIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1352 {
1353     int i;
1354     struct cm_vol_state * statep;
1355
1356     switch (volType) {
1357     case RWVOL:
1358         statep = &volp->rw;
1359         break;
1360     case ROVOL:                                
1361         statep = &volp->ro;
1362         break;
1363     case BACKVOL:
1364         statep = &volp->bk;
1365         break;
1366     default:
1367         return;
1368     }
1369
1370     if (statep->flags & CM_VOLUMEFLAG_IN_HASH)
1371         return;
1372
1373     i = CM_VOLUME_ID_HASH(statep->ID);
1374
1375     switch (volType) {
1376     case RWVOL:
1377         statep->nextp = cm_data.volumeRWIDHashTablep[i];
1378         cm_data.volumeRWIDHashTablep[i] = volp;
1379         break;
1380     case ROVOL:                                
1381         statep->nextp = cm_data.volumeROIDHashTablep[i];
1382         cm_data.volumeROIDHashTablep[i] = volp;
1383         break;
1384     case BACKVOL:
1385         statep->nextp = cm_data.volumeBKIDHashTablep[i];
1386         cm_data.volumeBKIDHashTablep[i] = volp;
1387         break;
1388     }
1389     statep->flags |= CM_VOLUMEFLAG_IN_HASH;
1390 }
1391
1392
1393 /* call with volume write-locked and mutex held */
1394 void cm_RemoveVolumeFromIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1395 {
1396     cm_volume_t **lvolpp;
1397     cm_volume_t *tvolp;
1398     struct cm_vol_state * statep;
1399     int i;
1400         
1401     switch (volType) {
1402     case RWVOL:
1403         statep = &volp->rw;
1404         break;
1405     case ROVOL:                                
1406         statep = &volp->ro;
1407         break;
1408     case BACKVOL:
1409         statep = &volp->bk;
1410         break;
1411     default:
1412         return;
1413     }
1414
1415     if (statep->flags & CM_VOLUMEFLAG_IN_HASH) {
1416         /* hash it out first */
1417         i = CM_VOLUME_ID_HASH(statep->ID);
1418
1419         switch (volType) {
1420         case RWVOL:
1421             lvolpp = &cm_data.volumeRWIDHashTablep[i];
1422             tvolp = cm_data.volumeRWIDHashTablep[i];
1423             break;
1424         case ROVOL:
1425             lvolpp = &cm_data.volumeROIDHashTablep[i];
1426             tvolp = cm_data.volumeROIDHashTablep[i];
1427             break;
1428         case BACKVOL:
1429             lvolpp = &cm_data.volumeBKIDHashTablep[i];
1430             tvolp = cm_data.volumeBKIDHashTablep[i];
1431             break;
1432         }
1433         do {
1434             if (tvolp == volp) {
1435                 *lvolpp = statep->nextp;
1436                 statep->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1437                 statep->nextp = NULL;
1438                 break;
1439             }
1440
1441             switch (volType) {
1442             case RWVOL:
1443                 lvolpp = &tvolp->rw.nextp;
1444                 tvolp = tvolp->rw.nextp;
1445                 break;
1446             case ROVOL:                                
1447                 lvolpp = &tvolp->ro.nextp;
1448                 tvolp = tvolp->ro.nextp;
1449                 break;
1450             case BACKVOL:
1451                 lvolpp = &tvolp->bk.nextp;
1452                 tvolp = tvolp->bk.nextp;
1453                 break;
1454             }
1455         } while(tvolp);
1456     }
1457 }
1458
1459 /* must be called with cm_volumeLock write-locked! */
1460 void cm_AdjustVolumeLRU(cm_volume_t *volp)
1461 {
1462     if (volp == cm_data.volumeLRULastp)
1463         cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1464     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1465         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1466     osi_QAdd((osi_queue_t **) &cm_data.volumeLRUFirstp, &volp->q);
1467     volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1468     if (!cm_data.volumeLRULastp) 
1469         cm_data.volumeLRULastp = volp;
1470 }
1471
1472 /* must be called with cm_volumeLock write-locked! */
1473 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1474 {
1475     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) {
1476         if (volp == cm_data.volumeLRULastp)
1477             cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1478         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1479         volp->flags &= ~CM_VOLUMEFLAG_IN_LRU_QUEUE;
1480     }
1481 }
1482
1483 static char * volstatus_str(enum volstatus vs)
1484 {
1485     switch (vs) {
1486     case vl_online:
1487         return "online";
1488     case vl_busy:
1489         return "busy";
1490     case vl_offline:
1491         return "offline";
1492     case vl_alldown:
1493         return "alldown";
1494     default:
1495         return "unknown";
1496     }
1497 }
1498
1499 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1500 {
1501     char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1502     char *ext = "";
1503
1504     if (volID == volp->rw.ID)
1505         ext = "";
1506     else if (volID == volp->ro.ID)
1507         ext = ".readonly";
1508     else if (volID == volp->bk.ID)
1509         ext = ".backup";
1510     else
1511         ext = ".nomatch";
1512     snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1513
1514     osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1515              osi_LogSaveString(afsd_logp, volstr), volID, volstatus_str(old), volstatus_str(new));
1516
1517     cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);
1518 }       
1519
1520 enum volstatus cm_GetVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1521 {
1522     if (volp->rw.ID == volID) {
1523         return volp->rw.state;
1524     } else if (volp->ro.ID == volID) {
1525         return volp->ro.state;
1526     } else if (volp->bk.ID == volID) {
1527         return volp->bk.state;
1528     } else {
1529         return vl_unknown;
1530     }
1531 }
1532
1533 /* Renew .readonly volume callbacks that are more than
1534  * 30 minutes old.  (A volume callback is issued for 2 hours.)
1535  */
1536 void 
1537 cm_VolumeRenewROCallbacks(void)
1538 {
1539     cm_volume_t * volp;
1540     time_t minexp = time(NULL) + 90 * 60;
1541
1542     lock_ObtainRead(&cm_volumeLock);
1543     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1544         if ( volp->cbExpiresRO > 0 && volp->cbExpiresRO < minexp) {
1545             cm_req_t      req;
1546             cm_fid_t      fid;
1547             cm_scache_t * scp;
1548
1549             fid.cell = volp->cellp->cellID;
1550             fid.volume = volp->ro.ID;
1551             fid.vnode = 1;
1552             fid.unique = 1;
1553
1554             cm_InitReq(&req);
1555
1556             lock_ReleaseRead(&cm_volumeLock);
1557             if (cm_GetSCache(&fid, &scp, cm_rootUserp, &req) == 0) {
1558                 lock_ObtainMutex(&scp->mx);
1559                 cm_GetCallback(scp, cm_rootUserp, &req, 1);
1560                 lock_ReleaseMutex(&scp->mx);
1561                 cm_ReleaseSCache(scp);
1562             }
1563             lock_ObtainRead(&cm_volumeLock);
1564         }
1565     }
1566     lock_ReleaseRead(&cm_volumeLock);
1567 }