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