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