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