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