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