windows-volume-logsavestring-20080225
[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 {
528         rwNewstate = roNewstate = bkNewstate = vl_alldown;
529     }
530
531     if (volp->rw.state != rwNewstate) {
532         if (volp->rw.ID)
533             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, rwNewstate);
534         volp->rw.state = rwNewstate;
535     }
536     if (volp->ro.state != roNewstate) {
537         if (volp->ro.ID)
538             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, roNewstate);
539         volp->ro.state = roNewstate;
540     }
541     if (volp->bk.state != bkNewstate) {
542         if (volp->bk.ID)
543             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, bkNewstate);
544         volp->bk.state = bkNewstate;
545     }
546
547     return code;
548 }
549
550 void cm_GetVolume(cm_volume_t *volp)
551 {
552     if (volp) {
553         lock_ObtainWrite(&cm_volumeLock);
554         volp->refCount++;
555         lock_ReleaseWrite(&cm_volumeLock);
556     }
557 }
558
559
560 long cm_GetVolumeByID(cm_cell_t *cellp, afs_uint32 volumeID, cm_user_t *userp,
561                       cm_req_t *reqp, afs_uint32 flags, cm_volume_t **outVolpp)
562 {
563     cm_volume_t *volp;
564 #ifdef SEARCH_ALL_VOLUMES
565     cm_volume_t *volp2;
566 #endif
567     char volNameString[VL_MAXNAMELEN];
568     afs_uint32 hash;
569     long code = 0;
570
571     lock_ObtainRead(&cm_volumeLock);
572 #ifdef SEARCH_ALL_VOLUMES
573     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
574         if (cellp == volp->cellp &&
575              ((unsigned) volumeID == volp->rw.ID ||
576                (unsigned) volumeID == volp->ro.ID ||
577                (unsigned) volumeID == volp->bk.ID))
578             break;
579     }   
580
581     volp2 = volp;
582 #endif /* SEARCH_ALL_VOLUMES */
583
584     hash = CM_VOLUME_ID_HASH(volumeID);
585     /* The volumeID can be any one of the three types.  So we must
586      * search the hash table for all three types until we find it.
587      * We will search in the order of RO, RW, BK.
588      */
589     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
590         if ( cellp == volp->cellp && volumeID == volp->ro.ID )
591             break;
592     }
593     if (!volp) {
594         /* try RW volumes */
595         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
596             if ( cellp == volp->cellp && volumeID == volp->rw.ID )
597                 break;
598         }
599     }
600     if (!volp) {
601         /* try BK volumes */
602         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
603             if ( cellp == volp->cellp && volumeID == volp->bk.ID )
604                 break;
605         }
606     }
607
608 #ifdef SEARCH_ALL_VOLUMES
609     osi_assertx(volp == volp2, "unexpected cm_vol_t");
610 #endif
611
612     lock_ReleaseRead(&cm_volumeLock);
613
614     /* hold the volume if we found it */
615     if (volp) 
616         cm_GetVolume(volp);
617         
618     /* return it held */
619     if (volp) {
620         lock_ObtainMutex(&volp->mx);
621         
622         code = 0;
623         if ((volp->flags & CM_VOLUMEFLAG_RESET) && !(flags & CM_GETVOL_FLAG_NO_RESET)) {
624             code = cm_UpdateVolume(cellp, userp, reqp, volp);
625             if (code == 0)
626                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
627         }
628         lock_ReleaseMutex(&volp->mx);
629         if (code == 0) {
630             *outVolpp = volp;
631
632             if (!(flags & CM_GETVOL_FLAG_NO_LRU_UPDATE)) {
633                 lock_ObtainWrite(&cm_volumeLock);
634                 cm_AdjustVolumeLRU(volp);
635                 lock_ReleaseWrite(&cm_volumeLock);
636             }
637         } else
638             cm_PutVolume(volp);
639
640         return code;
641     }
642         
643     /* otherwise, we didn't find it so consult the VLDB */
644     sprintf(volNameString, "%u", volumeID);
645     code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
646                               flags, outVolpp);
647     return code;
648 }
649
650
651 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
652                         struct cm_user *userp, struct cm_req *reqp,
653                         afs_uint32 flags, cm_volume_t **outVolpp)
654 {
655     cm_volume_t *volp;
656 #ifdef SEARCH_ALL_VOLUMES
657     cm_volume_t *volp2;
658 #endif
659     long        code = 0;
660     char        name[VL_MAXNAMELEN];
661     size_t      len;
662     int         type;
663     afs_uint32  hash;
664
665     strncpy(name, volumeNamep, VL_MAXNAMELEN);
666     name[VL_MAXNAMELEN-1] = '\0';
667     len = strlen(name);
668
669     if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
670         type = BACKVOL;
671         name[len - 7] = '\0';
672     } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
673         type = ROVOL;
674         name[len - 9] = '\0';
675     } else {
676         type = RWVOL;
677     }
678
679     lock_ObtainRead(&cm_volumeLock);
680 #ifdef SEARCH_ALL_VOLUMES
681     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
682         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0) {
683             break;
684         }
685     }   
686     volp2 = volp;
687 #endif /* SEARCH_ALL_VOLUMES */
688
689     hash = CM_VOLUME_NAME_HASH(name);
690     for (volp = cm_data.volumeNameHashTablep[hash]; volp; volp = volp->nameNextp) {
691         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0)
692             break;
693     }
694
695 #ifdef SEARCH_ALL_VOLUMES
696     osi_assertx(volp2 == volp, "unexpected cm_vol_t");
697 #endif
698
699     if (!volp && (flags & CM_GETVOL_FLAG_CREATE)) {
700         /* otherwise, get from VLDB */
701
702         if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
703
704 #ifdef RECYCLE_FROM_ALL_VOLUMES_LIST
705             for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
706                 if ( volp->refCount == 0 ) {
707                     /* There is one we can re-use */
708                     break;
709                 }
710             }
711 #else
712             for ( volp = cm_data.volumeLRULastp;
713                   volp;
714                   volp = (cm_volume_t *) osi_QPrev(&volp->q)) 
715             {
716                 if ( volp->refCount == 0 ) {
717                     /* There is one we can re-use */
718                     break;
719                 }
720             }
721 #endif
722             if (!volp)
723                 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
724
725             lock_ReleaseRead(&cm_volumeLock);
726             lock_ObtainMutex(&volp->mx);
727             lock_ObtainWrite(&cm_volumeLock);
728
729             osi_Log2(afsd_logp, "Recycling Volume %s:%s",
730                      volp->cellp->name, volp->namep);
731
732             if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
733                 cm_RemoveVolumeFromLRU(volp);
734             if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
735                 cm_RemoveVolumeFromNameHashTable(volp);
736             if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
737                 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
738             if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
739                 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
740             if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
741                 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
742
743             if (volp->rw.ID)
744                 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
745             if (volp->ro.ID)
746                 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
747             if (volp->bk.ID)
748                 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
749
750             volp->rw.ID = volp->ro.ID = volp->bk.ID = 0;
751             volp->dotdotFid.cell = 0;
752             volp->dotdotFid.volume = 0;
753             volp->dotdotFid.unique = 0;
754             volp->dotdotFid.vnode = 0;
755             volp->dotdotFid.hash = 0;
756         } else {
757             volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
758             memset(volp, 0, sizeof(cm_volume_t));
759             volp->magic = CM_VOLUME_MAGIC;
760             volp->allNextp = cm_data.allVolumesp;
761             cm_data.allVolumesp = volp;
762             lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
763             lock_ReleaseRead(&cm_volumeLock);
764             lock_ObtainMutex(&volp->mx);
765             lock_ObtainWrite(&cm_volumeLock);
766         }
767         volp->cellp = cellp;
768         strncpy(volp->namep, name, VL_MAXNAMELEN);
769         volp->namep[VL_MAXNAMELEN-1] = '\0';
770         volp->refCount = 1;     /* starts off held */
771         volp->flags = CM_VOLUMEFLAG_RESET;
772         volp->rw.state = volp->ro.state = volp->bk.state = vl_unknown;
773         volp->rw.nextp = volp->ro.nextp = volp->bk.nextp = NULL;
774         volp->rw.flags = volp->ro.flags = volp->bk.flags = 0;
775         volp->cbExpiresRO = 0;
776         cm_AddVolumeToNameHashTable(volp);
777         lock_ReleaseWrite(&cm_volumeLock);
778     }
779     else {
780         lock_ReleaseRead(&cm_volumeLock);
781         if (volp) {
782             cm_GetVolume(volp);
783             lock_ObtainMutex(&volp->mx);
784         } else {
785             return CM_ERROR_NOSUCHVOLUME;
786         }
787     }
788
789     /* if we get here we are holding the mutex */
790     if ((volp->flags & CM_VOLUMEFLAG_RESET) && !(flags & CM_GETVOL_FLAG_NO_RESET)) {
791         code = cm_UpdateVolume(cellp, userp, reqp, volp);
792         if (code == 0)
793             volp->flags &= ~CM_VOLUMEFLAG_RESET;
794     }   
795     lock_ReleaseMutex(&volp->mx);
796
797     if (code == 0 && (type == BACKVOL && volp->bk.ID == 0 ||
798                       type == ROVOL && volp->ro.ID == 0))
799         code = CM_ERROR_NOSUCHVOLUME;
800
801     if (code == 0) {
802                 *outVolpp = volp;
803                 
804                 if (!(flags & CM_GETVOL_FLAG_NO_LRU_UPDATE)) {
805                 lock_ObtainWrite(&cm_volumeLock);
806                         cm_AdjustVolumeLRU(volp);
807                         lock_ReleaseWrite(&cm_volumeLock);
808                 }
809     } else
810                 cm_PutVolume(volp);
811
812     return code;
813 }       
814
815 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
816 {
817     cm_cell_t *cellp;
818     cm_volume_t *volp;
819 #ifdef SEARCH_ALL_VOLUMES
820     cm_volume_t *volp2;
821 #endif
822     afs_uint32  hash;
823
824     if (!fidp) return;
825
826     cellp = cm_FindCellByID(fidp->cell, 0);
827     if (!cellp) return;
828
829     /* search for the volume */
830     lock_ObtainRead(&cm_volumeLock);
831 #ifdef SEARCH_ALL_VOLUMES
832     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
833         if (cellp == volp->cellp &&
834              (fidp->volume == volp->rw.ID ||
835                fidp->volume == volp->ro.ID ||
836                fidp->volume == volp->bk.ID))
837             break;
838     }   
839 #endif /* SEARCH_ALL_VOLUMES */
840
841     hash = CM_VOLUME_ID_HASH(fidp->volume);
842     /* The volumeID can be any one of the three types.  So we must
843      * search the hash table for all three types until we find it.
844      * We will search in the order of RO, RW, BK.
845      */
846     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
847         if ( cellp == volp->cellp && fidp->volume == volp->ro.ID )
848             break;
849     }
850     if (!volp) {
851         /* try RW volumes */
852         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
853             if ( cellp == volp->cellp && fidp->volume == volp->rw.ID )
854                 break;
855         }
856     }
857     if (!volp) {
858         /* try BK volumes */
859         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
860             if ( cellp == volp->cellp && fidp->volume == volp->bk.ID )
861                 break;
862         }
863     }
864
865 #ifdef SEARCH_ALL_VOLUMES
866     osi_assertx(volp == volp2, "unexpected cm_vol_t");
867 #endif
868
869     lock_ReleaseRead(&cm_volumeLock);
870
871     /* hold the volume if we found it */
872     if (volp) 
873         cm_GetVolume(volp);
874
875     /* update it */
876     cm_data.mountRootGen = time(NULL);
877     lock_ObtainMutex(&volp->mx);
878     volp->flags |= CM_VOLUMEFLAG_RESET;
879 #ifdef COMMENT
880     /* Mark the volume to be updated but don't update it now.
881      * This function is called only from within cm_Analyze
882      * when cm_ConnByMServers has failed with all servers down
883      * The problem is that cm_UpdateVolume is going to call
884      * cm_ConnByMServers which may cause a recursive chain
885      * of calls each returning a retry on failure.
886      * Instead, set the flag so the next time the volume is
887      * accessed by Name or ID the UpdateVolume call will
888      * occur.
889      */
890     code = cm_UpdateVolume(cellp, userp, reqp, volp);
891     if (code == 0)
892         volp->flags &= ~CM_VOLUMEFLAG_RESET;
893 #endif
894     lock_ReleaseMutex(&volp->mx);
895
896     cm_PutVolume(volp);
897 }
898
899 /* find the appropriate servers from a volume */
900 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volume)
901 {
902     cm_serverRef_t **serverspp;
903     cm_serverRef_t *current;;
904
905     lock_ObtainWrite(&cm_serverLock);
906
907     if (volume == volp->rw.ID)
908         serverspp = &volp->rw.serversp;
909     else if (volume == volp->ro.ID)
910         serverspp = &volp->ro.serversp;
911     else if (volume == volp->bk.ID)
912         serverspp = &volp->bk.serversp;
913     else 
914         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
915         
916     for (current = *serverspp; current; current = current->next)
917         current->refCount++;
918
919     lock_ReleaseWrite(&cm_serverLock);
920
921     return serverspp;
922 }
923
924 void cm_PutVolume(cm_volume_t *volp)
925 {
926     lock_ObtainWrite(&cm_volumeLock);
927     osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
928     lock_ReleaseWrite(&cm_volumeLock);
929 }
930
931 /* return the read-only volume, if there is one, or the read-write volume if
932  * not.
933  */
934 long cm_GetROVolumeID(cm_volume_t *volp)
935 {
936     long id;
937
938     lock_ObtainMutex(&volp->mx);
939     if (volp->ro.ID && volp->ro.serversp)
940         id = volp->ro.ID;
941     else
942         id = volp->rw.ID;
943     lock_ReleaseMutex(&volp->mx);
944
945     return id;
946 }
947
948 void cm_RefreshVolumes(void)
949 {
950     cm_volume_t *volp;
951     cm_scache_t *scp;
952
953     cm_data.mountRootGen = time(NULL);
954
955     /* force a re-loading of volume data from the vldb */
956     lock_ObtainWrite(&cm_volumeLock);
957     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
958         volp->refCount++;
959         lock_ReleaseWrite(&cm_volumeLock);
960
961         lock_ObtainMutex(&volp->mx);
962         volp->flags |= CM_VOLUMEFLAG_RESET;
963         lock_ReleaseMutex(&volp->mx);
964         
965         lock_ObtainWrite(&cm_volumeLock);
966         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
967     }
968     lock_ReleaseWrite(&cm_volumeLock);
969
970     /* force mount points to be re-evaluated so that 
971      * if the volume location has changed we will pick 
972      * that up
973      */
974     for ( scp = cm_data.scacheLRUFirstp; 
975           scp;
976           scp = (cm_scache_t *) osi_QNext(&scp->q)) {
977         if ( scp->fileType == CM_SCACHETYPE_MOUNTPOINT 
978 #ifdef AFS_FREELANCE_CLIENT
979              && !(scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID)
980 #endif
981              ) {
982             lock_ObtainMutex(&scp->mx);
983             scp->mountPointStringp[0] = '\0';
984             lock_ReleaseMutex(&scp->mx);
985         }
986     }
987
988 }
989
990
991 /* The return code is 0 if the volume is not online and 
992  * 1 if the volume is online
993  */
994 long
995 cm_CheckOfflineVolume(cm_volume_t *volp, afs_uint32 volID)
996 {
997     cm_conn_t *connp;
998     long code;
999     AFSFetchVolumeStatus volStat;
1000     char *Name;
1001     char *OfflineMsg;
1002     char *MOTD;
1003     cm_req_t req;
1004     struct rx_connection * callp;
1005     char volName[32];
1006     char offLineMsg[256];
1007     char motd[256];
1008     long online = 0;
1009     cm_serverRef_t *serversp;
1010
1011     Name = volName;
1012     OfflineMsg = offLineMsg;
1013     MOTD = motd;
1014
1015     lock_ObtainMutex(&volp->mx);
1016
1017     if (volp->flags & CM_VOLUMEFLAG_RESET) {
1018         cm_InitReq(&req);
1019         code = cm_UpdateVolume(volp->cellp, cm_rootUserp, &req, volp);
1020         if (code == 0)
1021             volp->flags &= ~CM_VOLUMEFLAG_RESET;
1022     }
1023
1024     if (volp->rw.ID != 0 && (!volID || volID == volp->rw.ID) &&
1025                 volp->rw.serversp &&
1026          (volp->rw.state == vl_busy || volp->rw.state == vl_offline || volp->rw.state == vl_unknown)) {
1027         cm_InitReq(&req);
1028
1029         for (serversp = volp->rw.serversp; serversp; serversp = serversp->next) {
1030             if (serversp->status == srv_busy || serversp->status == srv_offline)
1031                 serversp->status = srv_not_busy;
1032         }
1033
1034         lock_ReleaseMutex(&volp->mx);
1035         do {
1036             code = cm_ConnFromVolume(volp, volp->rw.ID, cm_rootUserp, &req, &connp);
1037             if (code) 
1038                 continue;
1039
1040             callp = cm_GetRxConn(connp);
1041             code = RXAFS_GetVolumeStatus(callp, volp->rw.ID,
1042                                           &volStat, &Name, &OfflineMsg, &MOTD);
1043             rx_PutConnection(callp);        
1044
1045         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1046         code = cm_MapRPCError(code, &req);
1047
1048         lock_ObtainMutex(&volp->mx);
1049         if (code == 0 && volStat.Online) {
1050             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_online);
1051             volp->rw.state = vl_online;
1052             online = 1;
1053         } else if (code == CM_ERROR_NOACCESS) {
1054             cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
1055             volp->rw.state = vl_unknown;
1056             online = 1;
1057         }
1058     }
1059
1060     if (volp->ro.ID != 0 && (!volID || volID == volp->ro.ID) &&
1061                 volp->ro.serversp &&
1062          (volp->ro.state == vl_busy || volp->ro.state == vl_offline || volp->ro.state == vl_unknown)) {
1063         cm_InitReq(&req);
1064
1065         for (serversp = volp->ro.serversp; serversp; serversp = serversp->next) {
1066             if (serversp->status == srv_busy || serversp->status == srv_offline)
1067                 serversp->status = srv_not_busy;
1068         }
1069
1070         lock_ReleaseMutex(&volp->mx);
1071         do {
1072             code = cm_ConnFromVolume(volp, volp->ro.ID, cm_rootUserp, &req, &connp);
1073             if (code) 
1074                 continue;
1075
1076             callp = cm_GetRxConn(connp);
1077             code = RXAFS_GetVolumeStatus(callp, volp->ro.ID,
1078                                           &volStat, &Name, &OfflineMsg, &MOTD);
1079             rx_PutConnection(callp);        
1080
1081         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1082         code = cm_MapRPCError(code, &req);
1083
1084         lock_ObtainMutex(&volp->mx);
1085         if (code == 0 && volStat.Online) {
1086             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_online);
1087             volp->ro.state = vl_online;
1088             online = 1;
1089         } else if (code == CM_ERROR_NOACCESS) {
1090             cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
1091             volp->ro.state = vl_unknown;
1092             online = 1;
1093         }
1094     }
1095
1096     if (volp->bk.ID != 0 && (!volID || volID == volp->bk.ID) &&
1097                 volp->bk.serversp &&
1098          (volp->bk.state == vl_busy || volp->bk.state == vl_offline || volp->bk.state == vl_unknown)) {
1099         cm_InitReq(&req);
1100
1101         for (serversp = volp->bk.serversp; serversp; serversp = serversp->next) {
1102             if (serversp->status == srv_busy || serversp->status == srv_offline)
1103                 serversp->status = srv_not_busy;
1104         }
1105
1106         lock_ReleaseMutex(&volp->mx);
1107         do {
1108             code = cm_ConnFromVolume(volp, volp->bk.ID, cm_rootUserp, &req, &connp);
1109             if (code) 
1110                 continue;
1111
1112             callp = cm_GetRxConn(connp);
1113             code = RXAFS_GetVolumeStatus(callp, volp->bk.ID,
1114                                           &volStat, &Name, &OfflineMsg, &MOTD);
1115             rx_PutConnection(callp);        
1116
1117         } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1118         code = cm_MapRPCError(code, &req);
1119
1120         lock_ObtainMutex(&volp->mx);
1121         if (code == 0 && volStat.Online) {
1122             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_online);
1123             volp->bk.state = vl_online;
1124             online = 1;
1125         } else if (code == CM_ERROR_NOACCESS) {
1126             cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
1127             volp->bk.state = vl_unknown;
1128             online = 1;
1129         }
1130     }
1131
1132     lock_ReleaseMutex(&volp->mx);
1133     return online;
1134 }
1135
1136
1137 /* called from the Daemon thread */
1138 void cm_CheckOfflineVolumes(void)
1139 {
1140     cm_volume_t *volp;
1141
1142     lock_ObtainWrite(&cm_volumeLock);
1143     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1144         volp->refCount++;
1145         lock_ReleaseWrite(&cm_volumeLock);
1146
1147         cm_CheckOfflineVolume(volp, 0);
1148
1149         lock_ObtainWrite(&cm_volumeLock);
1150         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1151     }
1152     lock_ReleaseWrite(&cm_volumeLock);
1153 }
1154
1155
1156 static void
1157 cm_UpdateVolumeStatusInt(cm_volume_t *volp, struct cm_vol_state *statep)
1158 {
1159     enum volstatus newStatus;
1160     cm_serverRef_t *tsrp;
1161     cm_server_t *tsp;
1162     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
1163
1164     if (!volp || !statep) {
1165 #ifdef DEBUG
1166         DebugBreak();
1167 #endif
1168         return;
1169     }
1170
1171     lock_ObtainWrite(&cm_serverLock);
1172     for (tsrp = statep->serversp; tsrp; tsrp=tsrp->next) {
1173         tsp = tsrp->server;
1174         cm_GetServerNoLock(tsp);
1175         if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
1176             allDown = 0;
1177             if (tsrp->status == srv_busy) {
1178                 allOffline = 0;
1179                 someBusy = 1;
1180             } else if (tsrp->status == srv_offline) {
1181                 allBusy = 0;
1182                 someOffline = 1;
1183             } else {
1184                 allOffline = 0;
1185                 allBusy = 0;
1186             }
1187         }
1188         cm_PutServerNoLock(tsp);
1189     }   
1190     lock_ReleaseWrite(&cm_serverLock);
1191
1192     if (allDown)
1193         newStatus = vl_alldown;
1194     else if (allBusy || (someBusy && someOffline)) 
1195         newStatus = vl_busy;
1196     else if (allOffline)
1197         newStatus = vl_offline;
1198     else
1199         newStatus = vl_online;
1200
1201     if (statep->ID && statep->state != newStatus)
1202         cm_VolumeStatusNotification(volp, statep->ID, statep->state, newStatus);
1203
1204     statep->state = newStatus;
1205 }
1206
1207 void
1208 cm_UpdateVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1209 {
1210
1211     if (volp->rw.ID == volID) {
1212         cm_UpdateVolumeStatusInt(volp, &volp->rw);
1213     } else if (volp->ro.ID == volID) {
1214         cm_UpdateVolumeStatusInt(volp, &volp->ro);
1215     } else if (volp->bk.ID == volID) {
1216         cm_UpdateVolumeStatusInt(volp, &volp->bk);
1217     } else {
1218         /*
1219          * If we are called with volID == 0 then something has gone wrong.
1220          * Most likely a race occurred in the server volume list maintenance.
1221          * Since we don't know which volume's status should be updated, 
1222          * just update all of them that are known to exist.  Better to be 
1223          * correct than fast.
1224          */
1225         if (volp->rw.ID != 0)
1226             cm_UpdateVolumeStatusInt(volp, &volp->rw);
1227         if (volp->ro.ID != 0)
1228             cm_UpdateVolumeStatusInt(volp, &volp->ro);
1229         if (volp->bk.ID != 0)
1230             cm_UpdateVolumeStatusInt(volp, &volp->bk);
1231     }
1232 }
1233
1234 /*
1235 ** Finds all volumes that reside on this server and reorders their
1236 ** RO list according to the changed rank of server.
1237 */
1238 void cm_ChangeRankVolume(cm_server_t *tsp)
1239 {       
1240     int                 code;
1241     cm_volume_t*        volp;
1242
1243     /* find volumes which might have RO copy on server*/
1244     lock_ObtainWrite(&cm_volumeLock);
1245     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1246     {
1247         code = 1 ;      /* assume that list is unchanged */
1248         volp->refCount++;
1249         lock_ReleaseWrite(&cm_volumeLock);
1250         lock_ObtainMutex(&volp->mx);
1251
1252         if ((tsp->cellp==volp->cellp) && (volp->ro.serversp))
1253             code =cm_ChangeRankServer(&volp->ro.serversp, tsp);
1254
1255         /* this volume list was changed */
1256         if ( !code )
1257             cm_RandomizeServer(&volp->ro.serversp);
1258
1259         lock_ReleaseMutex(&volp->mx);
1260         lock_ObtainWrite(&cm_volumeLock);
1261         osi_assertx(volp->refCount-- > 0, "cm_volume_t refCount 0");
1262     }
1263     lock_ReleaseWrite(&cm_volumeLock);
1264 }       
1265
1266 /* dump all volumes that have reference count > 0 to a file. 
1267  * cookie is used to identify this batch for easy parsing, 
1268  * and it a string provided by a caller 
1269  */
1270 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
1271 {
1272     int zilch;
1273     cm_volume_t *volp;
1274     char output[1024];
1275   
1276     if (lock) {
1277         lock_ObtainRead(&cm_scacheLock);
1278         lock_ObtainRead(&cm_volumeLock);
1279     }
1280   
1281     sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\r\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
1282     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1283   
1284     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1285     {
1286         cm_scache_t *scp;
1287         int scprefs = 0;
1288
1289         for (scp = cm_data.allSCachesp; scp; scp = scp->allNextp) 
1290         {
1291             if (scp->volp == volp)
1292                 scprefs++;
1293         }   
1294
1295         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", 
1296                  cookie, volp, volp->cellp->name, volp->namep, volp->rw.ID, volp->ro.ID, volp->bk.ID, volp->flags, 
1297                  volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
1298                  volp->refCount, scprefs);
1299         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1300     }
1301     sprintf(output, "%s - Done dumping volumes.\r\n", cookie);
1302     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1303   
1304     if (lock) {
1305         lock_ReleaseRead(&cm_volumeLock);
1306         lock_ReleaseRead(&cm_scacheLock);
1307     }
1308     return (0);     
1309 }
1310
1311
1312 /* 
1313  * String hash function used by SDBM project.
1314  * It was chosen because it is fast and provides
1315  * decent coverage.
1316  */
1317 afs_uint32 SDBMHash(const char * str)
1318 {
1319     afs_uint32 hash = 0;
1320     size_t i, len;
1321
1322     if (str == NULL)
1323         return 0;
1324
1325     for(i = 0, len = strlen(str); i < len; i++)
1326     {
1327         hash = str[i] + (hash << 6) + (hash << 16) - hash;
1328     }
1329
1330     return (hash & 0x7FFFFFFF);
1331 }
1332
1333 /* call with volume write-locked and mutex held */
1334 void cm_AddVolumeToNameHashTable(cm_volume_t *volp)
1335 {
1336     int i;
1337     
1338     if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
1339         return;
1340
1341     i = CM_VOLUME_NAME_HASH(volp->namep);
1342
1343     volp->nameNextp = cm_data.volumeNameHashTablep[i];
1344     cm_data.volumeNameHashTablep[i] = volp;
1345     volp->flags |= CM_VOLUMEFLAG_IN_HASH;
1346 }
1347
1348 /* call with volume write-locked and mutex held */
1349 void cm_RemoveVolumeFromNameHashTable(cm_volume_t *volp)
1350 {
1351     cm_volume_t **lvolpp;
1352     cm_volume_t *tvolp;
1353     int i;
1354         
1355     if (volp->flags & CM_VOLUMEFLAG_IN_HASH) {
1356         /* hash it out first */
1357         i = CM_VOLUME_NAME_HASH(volp->namep);
1358         for (lvolpp = &cm_data.volumeNameHashTablep[i], tvolp = cm_data.volumeNameHashTablep[i];
1359              tvolp;
1360              lvolpp = &tvolp->nameNextp, tvolp = tvolp->nameNextp) {
1361             if (tvolp == volp) {
1362                 *lvolpp = volp->nameNextp;
1363                 volp->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1364                 volp->nameNextp = NULL;
1365                 break;
1366             }
1367         }
1368     }
1369 }
1370
1371 /* call with volume write-locked and mutex held */
1372 void cm_AddVolumeToIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1373 {
1374     int i;
1375     struct cm_vol_state * statep;
1376
1377     switch (volType) {
1378     case RWVOL:
1379         statep = &volp->rw;
1380         break;
1381     case ROVOL:                                
1382         statep = &volp->ro;
1383         break;
1384     case BACKVOL:
1385         statep = &volp->bk;
1386         break;
1387     default:
1388         return;
1389     }
1390
1391     if (statep->flags & CM_VOLUMEFLAG_IN_HASH)
1392         return;
1393
1394     i = CM_VOLUME_ID_HASH(statep->ID);
1395
1396     switch (volType) {
1397     case RWVOL:
1398         statep->nextp = cm_data.volumeRWIDHashTablep[i];
1399         cm_data.volumeRWIDHashTablep[i] = volp;
1400         break;
1401     case ROVOL:                                
1402         statep->nextp = cm_data.volumeROIDHashTablep[i];
1403         cm_data.volumeROIDHashTablep[i] = volp;
1404         break;
1405     case BACKVOL:
1406         statep->nextp = cm_data.volumeBKIDHashTablep[i];
1407         cm_data.volumeBKIDHashTablep[i] = volp;
1408         break;
1409     }
1410     statep->flags |= CM_VOLUMEFLAG_IN_HASH;
1411 }
1412
1413
1414 /* call with volume write-locked and mutex held */
1415 void cm_RemoveVolumeFromIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1416 {
1417     cm_volume_t **lvolpp;
1418     cm_volume_t *tvolp;
1419     struct cm_vol_state * statep;
1420     int i;
1421         
1422     switch (volType) {
1423     case RWVOL:
1424         statep = &volp->rw;
1425         break;
1426     case ROVOL:                                
1427         statep = &volp->ro;
1428         break;
1429     case BACKVOL:
1430         statep = &volp->bk;
1431         break;
1432     default:
1433         return;
1434     }
1435
1436     if (statep->flags & CM_VOLUMEFLAG_IN_HASH) {
1437         /* hash it out first */
1438         i = CM_VOLUME_ID_HASH(statep->ID);
1439
1440         switch (volType) {
1441         case RWVOL:
1442             lvolpp = &cm_data.volumeRWIDHashTablep[i];
1443             tvolp = cm_data.volumeRWIDHashTablep[i];
1444             break;
1445         case ROVOL:
1446             lvolpp = &cm_data.volumeROIDHashTablep[i];
1447             tvolp = cm_data.volumeROIDHashTablep[i];
1448             break;
1449         case BACKVOL:
1450             lvolpp = &cm_data.volumeBKIDHashTablep[i];
1451             tvolp = cm_data.volumeBKIDHashTablep[i];
1452             break;
1453         }
1454         do {
1455             if (tvolp == volp) {
1456                 *lvolpp = statep->nextp;
1457                 statep->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1458                 statep->nextp = NULL;
1459                 break;
1460             }
1461
1462             switch (volType) {
1463             case RWVOL:
1464                 lvolpp = &tvolp->rw.nextp;
1465                 tvolp = tvolp->rw.nextp;
1466                 break;
1467             case ROVOL:                                
1468                 lvolpp = &tvolp->ro.nextp;
1469                 tvolp = tvolp->ro.nextp;
1470                 break;
1471             case BACKVOL:
1472                 lvolpp = &tvolp->bk.nextp;
1473                 tvolp = tvolp->bk.nextp;
1474                 break;
1475             }
1476         } while(tvolp);
1477     }
1478 }
1479
1480 /* must be called with cm_volumeLock write-locked! */
1481 void cm_AdjustVolumeLRU(cm_volume_t *volp)
1482 {
1483     if (volp == cm_data.volumeLRULastp)
1484         cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1485     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1486         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1487     osi_QAdd((osi_queue_t **) &cm_data.volumeLRUFirstp, &volp->q);
1488     volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1489     if (!cm_data.volumeLRULastp) 
1490         cm_data.volumeLRULastp = volp;
1491 }
1492
1493 /* must be called with cm_volumeLock write-locked! */
1494 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1495 {
1496     if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) {
1497         if (volp == cm_data.volumeLRULastp)
1498             cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1499         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1500         volp->flags &= ~CM_VOLUMEFLAG_IN_LRU_QUEUE;
1501     }
1502 }
1503
1504 static char * volstatus_str(enum volstatus vs)
1505 {
1506     switch (vs) {
1507     case vl_online:
1508         return "online";
1509     case vl_busy:
1510         return "busy";
1511     case vl_offline:
1512         return "offline";
1513     case vl_alldown:
1514         return "alldown";
1515     default:
1516         return "unknown";
1517     }
1518 }
1519
1520 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1521 {
1522     char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1523     char *ext = "";
1524
1525     if (volID == volp->rw.ID)
1526         ext = "";
1527     else if (volID == volp->ro.ID)
1528         ext = ".readonly";
1529     else if (volID == volp->bk.ID)
1530         ext = ".backup";
1531     else
1532         ext = ".nomatch";
1533     snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1534
1535     osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1536              osi_LogSaveString(afsd_logp, volstr), volID, volstatus_str(old), volstatus_str(new));
1537
1538     cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);
1539 }       
1540
1541 enum volstatus cm_GetVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1542 {
1543     if (volp->rw.ID == volID) {
1544         return volp->rw.state;
1545     } else if (volp->ro.ID == volID) {
1546         return volp->ro.state;
1547     } else if (volp->bk.ID == volID) {
1548         return volp->bk.state;
1549     } else {
1550         return vl_unknown;
1551     }
1552 }
1553
1554 /* Renew .readonly volume callbacks that are more than
1555  * 30 minutes old.  (A volume callback is issued for 2 hours.)
1556  */
1557 void 
1558 cm_VolumeRenewROCallbacks(void)
1559 {
1560     cm_volume_t * volp;
1561     time_t minexp = time(NULL) + 90 * 60;
1562
1563     lock_ObtainRead(&cm_volumeLock);
1564     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1565         if ( volp->cbExpiresRO > 0 && volp->cbExpiresRO < minexp) {
1566             cm_req_t      req;
1567             cm_fid_t      fid;
1568             cm_scache_t * scp;
1569
1570             cm_SetFid(&fid, volp->cellp->cellID, volp->ro.ID, 1, 1);
1571
1572             cm_InitReq(&req);
1573
1574             lock_ReleaseRead(&cm_volumeLock);
1575             if (cm_GetSCache(&fid, &scp, cm_rootUserp, &req) == 0) {
1576                 lock_ObtainMutex(&scp->mx);
1577                 cm_GetCallback(scp, cm_rootUserp, &req, 1);
1578                 lock_ReleaseMutex(&scp->mx);
1579                 cm_ReleaseSCache(scp);
1580             }
1581             lock_ObtainRead(&cm_volumeLock);
1582         }
1583     }
1584     lock_ReleaseRead(&cm_volumeLock);
1585 }