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