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