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