Windows: do not leak cm_volume_t objects from LRU queue
[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 ((volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) &&
972             !(flags & CM_GETVOL_FLAG_NO_LRU_UPDATE)) {
973             lock_ObtainWrite(&cm_volumeLock);
974             cm_AdjustVolumeLRU(volp);
975             lock_ReleaseWrite(&cm_volumeLock);
976         }
977     } else {
978         /*
979          * do not return it to the caller but do insert it in the LRU
980          * otherwise it will be lost
981          */
982         lock_ObtainRead(&cm_volumeLock);
983         if (!(volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) ||
984              (flags & CM_GETVOL_FLAG_NO_LRU_UPDATE))
985             cm_AdjustVolumeLRU(volp);
986         cm_PutVolume(volp);
987         lock_ReleaseRead(&cm_volumeLock);
988     }
989
990     if (code == CM_ERROR_NOSUCHVOLUME && cellp->linkedName[0] && 
991         !(flags & CM_GETVOL_FLAG_IGNORE_LINKED_CELL)) {
992         cm_cell_t *linkedCellp = cm_GetCell(cellp->linkedName, flags);
993
994         if (linkedCellp)
995             code = cm_FindVolumeByName(linkedCellp, volumeNamep, userp, reqp, 
996                                        flags | CM_GETVOL_FLAG_IGNORE_LINKED_CELL, 
997                                        outVolpp);
998     }
999     return code;
1000 }       
1001
1002 /* 
1003  * Only call this function in response to a VNOVOL or VMOVED error
1004  * from a file server.  Do not call it in response to CM_ERROR_NOSUCHVOLUME
1005  * as that can lead to recursive calls.
1006  */
1007 long cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
1008 {
1009     cm_cell_t *cellp;
1010     cm_volume_t *volp;
1011 #ifdef SEARCH_ALL_VOLUMES
1012     cm_volume_t *volp2;
1013 #endif
1014     afs_uint32  hash;
1015     long code;
1016
1017     if (!fidp) 
1018         return CM_ERROR_INVAL;
1019
1020     cellp = cm_FindCellByID(fidp->cell, 0);
1021     if (!cellp) 
1022         return CM_ERROR_NOSUCHCELL;
1023
1024     /* search for the volume */
1025     lock_ObtainRead(&cm_volumeLock);
1026 #ifdef SEARCH_ALL_VOLUMES
1027     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1028         if (cellp == volp->cellp &&
1029              (fidp->volume == volp->vol[RWVOL].ID ||
1030                fidp->volume == volp->vol[ROVOL].ID ||
1031                fidp->volume == volp->vol[BACKVOL].ID))
1032             break;
1033     }   
1034 #endif /* SEARCH_ALL_VOLUMES */
1035
1036     hash = CM_VOLUME_ID_HASH(fidp->volume);
1037     /* The volumeID can be any one of the three types.  So we must
1038      * search the hash table for all three types until we find it.
1039      * We will search in the order of RO, RW, BK.
1040      */
1041     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->vol[ROVOL].nextp) {
1042         if ( cellp == volp->cellp && fidp->volume == volp->vol[ROVOL].ID )
1043             break;
1044     }
1045     if (!volp) {
1046         /* try RW volumes */
1047         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->vol[RWVOL].nextp) {
1048             if ( cellp == volp->cellp && fidp->volume == volp->vol[RWVOL].ID )
1049                 break;
1050         }
1051     }
1052     if (!volp) {
1053         /* try BK volumes */
1054         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->vol[BACKVOL].nextp) {
1055             if ( cellp == volp->cellp && fidp->volume == volp->vol[BACKVOL].ID )
1056                 break;
1057         }
1058     }
1059
1060 #ifdef SEARCH_ALL_VOLUMES
1061     osi_assertx(volp == volp2, "unexpected cm_vol_t");
1062 #endif
1063     /* hold the volume if we found it */
1064     if (volp) 
1065         cm_GetVolume(volp);
1066
1067     lock_ReleaseRead(&cm_volumeLock);
1068
1069     if (!volp)
1070         return CM_ERROR_NOSUCHVOLUME;
1071
1072     /* update it */
1073     cm_data.mountRootGen = time(NULL);
1074     lock_ObtainWrite(&volp->rw);
1075     volp->flags |= CM_VOLUMEFLAG_RESET;
1076
1077     code = cm_UpdateVolumeLocation(cellp, userp, reqp, volp);
1078     lock_ReleaseWrite(&volp->rw);
1079
1080     lock_ObtainRead(&cm_volumeLock);
1081     cm_PutVolume(volp);
1082     lock_ReleaseRead(&cm_volumeLock);
1083
1084     return code;
1085 }
1086
1087 /* find the appropriate servers from a volume */
1088 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volume, cm_user_t *userp, cm_req_t *reqp)
1089 {
1090     cm_serverRef_t **serverspp;
1091     cm_serverRef_t *current;
1092     int firstTry = 1;
1093
1094   start:
1095     lock_ObtainWrite(&cm_serverLock);
1096
1097     if (volume == volp->vol[RWVOL].ID)
1098         serverspp = &volp->vol[RWVOL].serversp;
1099     else if (volume == volp->vol[ROVOL].ID)
1100         serverspp = &volp->vol[ROVOL].serversp;
1101     else if (volume == volp->vol[BACKVOL].ID)
1102         serverspp = &volp->vol[BACKVOL].serversp;
1103     else {
1104         lock_ReleaseWrite(&cm_serverLock);
1105         if (firstTry) {
1106             afs_int32 code;
1107             firstTry = 0;
1108             lock_ObtainWrite(&volp->rw);
1109             volp->flags |= CM_VOLUMEFLAG_RESET;
1110             code = cm_UpdateVolumeLocation(volp->cellp, userp, reqp, volp);
1111             lock_ReleaseWrite(&volp->rw);
1112             if (code == 0)
1113                 goto start;
1114         }
1115         return NULL;
1116     }
1117
1118     /* 
1119      * Increment the refCount on deleted items as well.
1120      * They will be freed by cm_FreeServerList when they get to zero 
1121      */
1122     for (current = *serverspp; current; current = current->next) 
1123         current->refCount++;
1124
1125     lock_ReleaseWrite(&cm_serverLock);
1126
1127     return serverspp;
1128 }
1129
1130 void cm_PutVolume(cm_volume_t *volp)
1131 {
1132     afs_int32 refCount = InterlockedDecrement(&volp->refCount);
1133     osi_assertx(refCount >= 0, "cm_volume_t refCount underflow has occurred");
1134 }
1135
1136 /* return the read-only volume, if there is one, or the read-write volume if
1137  * not.
1138  */
1139 long cm_GetROVolumeID(cm_volume_t *volp)
1140 {
1141     long id;
1142
1143     lock_ObtainRead(&volp->rw);
1144     if (volp->vol[ROVOL].ID && volp->vol[ROVOL].serversp)
1145         id = volp->vol[ROVOL].ID;
1146     else
1147         id = volp->vol[RWVOL].ID;
1148     lock_ReleaseRead(&volp->rw);
1149
1150     return id;
1151 }
1152
1153 void cm_RefreshVolumes(int lifetime)
1154 {
1155     cm_volume_t *volp;
1156     afs_int32 refCount;
1157     time_t now;
1158
1159     now = time(NULL);
1160
1161     /* force mount point target updates */
1162     if (cm_data.mountRootGen + lifetime <= now)
1163         cm_data.mountRootGen = now;
1164
1165     /*
1166      * force a re-loading of volume data from the vldb
1167      * if the lifetime for the cached data has expired
1168      */
1169     lock_ObtainRead(&cm_volumeLock);
1170     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1171         InterlockedIncrement(&volp->refCount);
1172         lock_ReleaseRead(&cm_volumeLock);
1173
1174         if (!(volp->flags & CM_VOLUMEFLAG_RESET)) {
1175             lock_ObtainWrite(&volp->rw);
1176             if (volp->lastUpdateTime + lifetime <= now)
1177                 volp->flags |= CM_VOLUMEFLAG_RESET;
1178             lock_ReleaseWrite(&volp->rw);
1179         }
1180
1181         lock_ObtainRead(&cm_volumeLock);
1182         refCount = InterlockedDecrement(&volp->refCount);
1183         osi_assertx(refCount >= 0, "cm_volume_t refCount underflow");
1184     }
1185     lock_ReleaseRead(&cm_volumeLock);
1186 }
1187
1188 void
1189 cm_CheckOfflineVolumeState(cm_volume_t *volp, cm_vol_state_t *statep, afs_uint32 volID,
1190                            afs_uint32 *onlinep, afs_uint32 *volumeUpdatedp)
1191 {
1192     cm_conn_t *connp;
1193     long code;
1194     AFSFetchVolumeStatus volStat;
1195     char *Name;
1196     char *OfflineMsg;
1197     char *MOTD;
1198     cm_req_t req;
1199     struct rx_connection * rxconnp;
1200     char volName[32];
1201     char offLineMsg[256];
1202     char motd[256];
1203     long alldown, alldeleted;
1204     cm_serverRef_t *serversp;
1205     cm_fid_t fid;
1206
1207     Name = volName;
1208     OfflineMsg = offLineMsg;
1209     MOTD = motd;
1210
1211     if (statep->ID != 0 && (!volID || volID == statep->ID)) {
1212         /* create fid for volume root so that VNOVOL and VMOVED errors can be processed */
1213         cm_SetFid(&fid, volp->cellp->cellID, statep->ID, 1, 1);
1214
1215         if (!statep->serversp && !(*volumeUpdatedp)) {
1216             cm_InitReq(&req);
1217             code = cm_UpdateVolumeLocation(volp->cellp, cm_rootUserp, &req, volp);
1218             *volumeUpdatedp = 1;
1219         }
1220
1221         if (statep->serversp) {
1222             alldown = 1;
1223             alldeleted = 1;
1224             for (serversp = statep->serversp; serversp; serversp = serversp->next) {
1225                 if (serversp->status == srv_deleted)
1226                     continue;
1227
1228                 alldeleted = 0;
1229                 *onlinep = 1;
1230                 alldown = 0;
1231                 
1232                 if (serversp->status == srv_busy || serversp->status == srv_offline)
1233                     serversp->status = srv_not_busy;
1234             }
1235
1236             if (alldeleted && !(*volumeUpdatedp)) {
1237                 cm_InitReq(&req);
1238                 code = cm_UpdateVolumeLocation(volp->cellp, cm_rootUserp, &req, volp);
1239                 *volumeUpdatedp = 1;
1240             }
1241
1242             if (statep->state == vl_busy || statep->state == vl_offline || statep->state == vl_unknown ||
1243                 (!alldown && statep->state == vl_alldown)) {
1244                 cm_InitReq(&req);
1245
1246                 lock_ReleaseWrite(&volp->rw);
1247                 do {
1248                     code = cm_ConnFromVolume(volp, statep->ID, cm_rootUserp, &req, &connp);
1249                     if (code) 
1250                         continue;
1251
1252                     rxconnp = cm_GetRxConn(connp);
1253                     code = RXAFS_GetVolumeStatus(rxconnp, statep->ID,
1254                                                  &volStat, &Name, &OfflineMsg, &MOTD);
1255                     rx_PutConnection(rxconnp);            
1256                 } while (cm_Analyze(connp, cm_rootUserp, &req, &fid, 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.volumeLRUFirstp)
1693         return;
1694
1695     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1696         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1697     osi_QAddH((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1698     volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1699 }
1700
1701 /* must be called with cm_volumeLock write-locked! */
1702 void cm_MoveVolumeToLRULast(cm_volume_t *volp)
1703 {
1704     if (volp == cm_data.volumeLRULastp)
1705         return;
1706
1707     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1708         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1709     osi_QAddT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1710     volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1711 }
1712
1713 /* must be called with cm_volumeLock write-locked! */
1714 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1715 {
1716     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) {
1717         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1718         volp->flags &= ~CM_VOLUMEFLAG_IN_LRU_QUEUE;
1719     }
1720 }
1721
1722 static char * volstatus_str(enum volstatus vs)
1723 {
1724     switch (vs) {
1725     case vl_online:
1726         return "online";
1727     case vl_busy:
1728         return "busy";
1729     case vl_offline:
1730         return "offline";
1731     case vl_alldown:
1732         return "alldown";
1733     default:
1734         return "unknown";
1735     }
1736 }
1737
1738 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1739 {
1740     char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1741     char *ext = "";
1742
1743     if (volID == volp->vol[RWVOL].ID)
1744         ext = "";
1745     else if (volID == volp->vol[ROVOL].ID)
1746         ext = ".readonly";
1747     else if (volID == volp->vol[BACKVOL].ID)
1748         ext = ".backup";
1749     else
1750         ext = ".nomatch";
1751     snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1752
1753     osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1754              osi_LogSaveString(afsd_logp, volstr), volID, volstatus_str(old), volstatus_str(new));
1755
1756     cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);
1757 }       
1758
1759 enum volstatus cm_GetVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1760 {
1761     cm_vol_state_t * statep = cm_VolumeStateByID(volp, volID);
1762     if (statep)
1763         return statep->state;
1764     else
1765         return vl_unknown;
1766 }
1767
1768 /* Renew .readonly volume callbacks that are more than
1769  * 30 minutes old.  (A volume callback is issued for 2 hours.)
1770  */
1771 void 
1772 cm_VolumeRenewROCallbacks(void)
1773 {
1774     cm_volume_t * volp;
1775     time_t minexp = time(NULL) + 90 * 60;
1776     extern int daemon_ShutdownFlag;
1777     extern int powerStateSuspended;
1778
1779     lock_ObtainRead(&cm_volumeLock);
1780     for (volp = cm_data.allVolumesp;
1781          volp && !daemon_ShutdownFlag && !powerStateSuspended;
1782          volp=volp->allNextp) {
1783         if ( volp->cbExpiresRO > 0 && volp->cbExpiresRO < minexp) {
1784             cm_req_t      req;
1785             cm_fid_t      fid;
1786             cm_scache_t * scp;
1787
1788             cm_SetFid(&fid, volp->cellp->cellID, volp->vol[ROVOL].ID, 1, 1);
1789
1790             cm_InitReq(&req);
1791
1792             lock_ReleaseRead(&cm_volumeLock);
1793             if (cm_GetSCache(&fid, &scp, cm_rootUserp, &req) == 0) {
1794                 lock_ObtainWrite(&scp->rw);
1795                 cm_GetCallback(scp, cm_rootUserp, &req, 1);
1796                 lock_ReleaseWrite(&scp->rw);
1797                 cm_ReleaseSCache(scp);
1798             }
1799             lock_ObtainRead(&cm_volumeLock);
1800         }
1801     }
1802     lock_ReleaseRead(&cm_volumeLock);
1803 }
1804
1805 cm_vol_state_t * 
1806 cm_VolumeStateByType(cm_volume_t *volp, afs_uint32 volType)
1807 {
1808     return &volp->vol[volType];
1809 }
1810
1811 cm_vol_state_t * 
1812 cm_VolumeStateByID(cm_volume_t *volp, afs_uint32 id)
1813 {
1814     cm_vol_state_t * statep = NULL;
1815
1816     if (id == volp->vol[RWVOL].ID)
1817         statep = &volp->vol[RWVOL];
1818     else if (id == volp->vol[ROVOL].ID)
1819         statep = &volp->vol[ROVOL];
1820     else if (id == volp->vol[BACKVOL].ID)
1821         statep = &volp->vol[BACKVOL];
1822
1823     return(statep);
1824 }
1825
1826 cm_vol_state_t * 
1827 cm_VolumeStateByName(cm_volume_t *volp, char *volname)
1828 {
1829     size_t len = strlen(volname);
1830     cm_vol_state_t *statep;
1831
1832     if (cm_stricmp_utf8N(".readonly", &volname[len-9]) == 0)
1833         statep = &volp->vol[ROVOL];
1834     else if (cm_stricmp_utf8N(".backup", &volname[len-7]) == 0)
1835         statep = &volp->vol[BACKVOL];
1836     else 
1837         statep = &volp->vol[RWVOL];
1838
1839     return statep;
1840 }
1841
1842 afs_int32 
1843 cm_VolumeType(cm_volume_t *volp, afs_uint32 id)
1844 {
1845     if (id == volp->vol[RWVOL].ID)
1846         return(RWVOL);
1847     else if (id == volp->vol[ROVOL].ID)
1848         return(ROVOL);
1849     else if (id == volp->vol[BACKVOL].ID)
1850         return (BACKVOL);
1851
1852     return -1;
1853 }