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