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