cf95d00d90642b9754744f42ad1a3fcddd7e9467
[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 <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <windows.h>
17 #include <winsock2.h>
18 #include <nb30.h>
19 #include <string.h>
20 #include <strsafe.h>
21 #include <malloc.h>
22 #include "afsd.h"
23 #include "cm_getaddrs.h"
24 #include <osi.h>
25 #include <rx/rx.h>
26
27 osi_rwlock_t cm_volumeLock;
28
29 long
30 cm_ValidateVolume(void)
31 {
32     cm_volume_t * volp;
33     afs_uint32 count;
34
35     for (volp = cm_data.allVolumesp, count = 0; volp; volp=volp->allNextp, count++) {
36
37         if ( volp < (cm_volume_t *)cm_data.volumeBaseAddress ||
38              volp >= (cm_volume_t *)cm_data.cellBaseAddress) {
39             afsi_log("cm_ValidateVolume failure: out of range cm_volume_t pointers");
40             fprintf(stderr, "cm_ValidateVolume failure: out of range cm_volume_t pointers\n");
41             return -10;
42         }
43
44         if ( volp->magic != CM_VOLUME_MAGIC ) {
45             afsi_log("cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC");
46             fprintf(stderr, "cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC\n");
47             return -1;
48         }
49
50         if ( volp->cellp < (cm_cell_t *)cm_data.cellBaseAddress ||
51              volp->cellp >= (cm_cell_t *)cm_data.aclBaseAddress) {
52             afsi_log("cm_ValidateVolume failure: out of range cm_cell_t pointers");
53             fprintf(stderr, "cm_ValidateVolume failure: out of range cm_cell_t pointers\n");
54             return -11;
55         }
56
57         if ( volp->cellp && volp->cellp->magic != CM_CELL_MAGIC ) {
58             afsi_log("cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC");
59             fprintf(stderr, "cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC\n");
60             return -2;
61         }
62
63         if ( volp->allNextp) {
64             if ( volp->allNextp < (cm_volume_t *)cm_data.volumeBaseAddress ||
65                  volp->allNextp >= (cm_volume_t *)cm_data.cellBaseAddress) {
66                 afsi_log("cm_ValidateVolume failure: out of range cm_volume_t pointers");
67                 fprintf(stderr, "cm_ValidateVolume failure: out of range cm_volume_t pointers\n");
68                 return -12;
69             }
70
71             if ( volp->allNextp->magic != CM_VOLUME_MAGIC ) {
72                 afsi_log("cm_ValidateVolume failure: volp->allNextp->magic != CM_VOLUME_MAGIC");
73                 fprintf(stderr, "cm_ValidateVolume failure: volp->allNextp->magic != CM_VOLUME_MAGIC\n");
74                 return -3;
75             }
76         }
77
78         if ( count != 0 && volp == cm_data.allVolumesp ||
79              count > cm_data.maxVolumes ) {
80             afsi_log("cm_ValidateVolume failure: cm_data.allVolumep loop detected");
81             fprintf(stderr, "cm_ValidateVolume failure: cm_data.allVolumep loop detected\n");
82             return -4;
83         }
84     }
85
86     if ( count != cm_data.currentVolumes ) {
87         afsi_log("cm_ValidateVolume failure: count != cm_data.currentVolumes");
88         fprintf(stderr, "cm_ValidateVolume failure: count != cm_data.currentVolumes\n");
89         return -5;
90     }
91
92     return 0;
93 }
94
95 long
96 cm_ShutdownVolume(void)
97 {
98     cm_volume_t * volp;
99
100     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
101         afs_uint32 volType;
102         for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
103             if (volp->vol[volType].ID)
104                 cm_VolumeStatusNotification(volp, volp->vol[volType].ID, volp->vol[volType].state, vl_alldown);
105         }
106         volp->cbExpiresRO = 0;
107         volp->cbIssuedRO = 0;
108         volp->cbServerpRO = NULL;
109         volp->volumeSizeRO = 0;
110         _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_RO_SIZE_VALID);
111
112         lock_FinalizeRWLock(&volp->rw);
113     }
114
115     return 0;
116 }
117
118 void cm_InitVolume(int newFile, long maxVols)
119 {
120     static osi_once_t once;
121
122     if (osi_Once(&once)) {
123         lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock", LOCK_HIERARCHY_VOLUME_GLOBAL);
124
125         if ( newFile ) {
126             cm_data.allVolumesp = NULL;
127             cm_data.currentVolumes = 0;
128             cm_data.maxVolumes = maxVols;
129             memset(cm_data.volumeNameHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
130             memset(cm_data.volumeRWIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
131             memset(cm_data.volumeROIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
132             memset(cm_data.volumeBKIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
133             cm_data.volumeLRUFirstp = cm_data.volumeLRULastp = NULL;
134         } else {
135             cm_volume_t * volp;
136
137             for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
138                 afs_uint32 volType;
139
140                 lock_InitializeRWLock(&volp->rw, "cm_volume_t rwlock", LOCK_HIERARCHY_VOLUME);
141                 _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RESET);
142                 _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_UPDATING_VL);
143                 volp->lastUpdateTime = 0;
144                 for (volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
145                     volp->vol[volType].state = vl_unknown;
146                     volp->vol[volType].serversp = NULL;
147                     if (volp->vol[volType].ID)
148                         cm_VolumeStatusNotification(volp, volp->vol[volType].ID, vl_unknown, volp->vol[volType].state);
149                 }
150                 volp->cbExpiresRO = 0;
151                 volp->cbIssuedRO = 0;
152                 volp->cbServerpRO = NULL;
153                 volp->volumeSizeRO = 0;
154                 _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_RO_SIZE_VALID);
155             }
156         }
157         osi_EndOnce(&once);
158     }
159 }
160
161
162 /* returns true if the id is a decimal integer, in which case we interpret it
163  * as an id.  make the cache manager much simpler.
164  * Stolen from src/volser/vlprocs.c */
165 int
166 cm_VolNameIsID(char *aname)
167 {
168     int tc;
169     while (tc = *aname++) {
170         if (tc > '9' || tc < '0')
171             return 0;
172     }
173     return 1;
174 }
175
176
177 /*
178  * Update a volume.  Caller holds a write lock on the volume (volp->rw).
179  *
180  *
181  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:38    (JHutz)
182  *    Yes, we support multihomed fileservers.
183  *    Since before we got the code from IBM.
184  *    But to find out about multiple addresses on a multihomed server, you need
185  *    to use VL_GetEntryByNameU and VL_GetAddrsU.  If you use
186  *    VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
187  *    address per server.
188  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:39    (JHutz)
189  *    see src/afs/afs_volume.c, paying particular attention to
190  *    afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
191  *  shadow / openafs / jaltman {ANDREW.CMU.EDU}  01:40    (Jeffrey Altman)
192  *    thanks.  The windows client calls the 0 versions.
193  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:51    (JHutz)
194  *    Oh.  Ew.
195  *    By not using the N versions, you only get up to 8 sites instead of 13.
196  *    By not using the U versions, you don't get to know about multihomed serve
197  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:52    (JHutz)
198  *    Of course, you probably want to support the older versions for backward
199  *    compatibility.  If you do that, you need to call the newest interface
200  *    first, and fall back to successively older versions if you get
201  *    RXGEN_OPCODE.
202  */
203 static long
204 cm_GetEntryByName( struct cm_cell *cellp, const char *name,
205                    struct vldbentry *vldbEntryp,
206                    struct nvldbentry *nvldbEntryp,
207                    struct uvldbentry *uvldbEntryp,
208                    int *methodp,
209                    cm_user_t *userp,
210                    cm_req_t *reqp
211                    )
212 {
213     long code;
214     cm_conn_t *connp;
215     struct rx_connection * rxconnp;
216
217     osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s",
218               osi_LogSaveString(afsd_logp,cellp->name),
219               osi_LogSaveString(afsd_logp,name));
220     do {
221
222         code = cm_ConnByMServers(cellp->vlServersp, FALSE, userp, reqp, &connp);
223         if (code)
224             continue;
225
226         rxconnp = cm_GetRxConn(connp);
227         code = VL_GetEntryByNameU(rxconnp, name, uvldbEntryp);
228         *methodp = 2;
229         if ( code == RXGEN_OPCODE )
230         {
231             code = VL_GetEntryByNameN(rxconnp, name, nvldbEntryp);
232             *methodp = 1;
233         }
234         if ( code == RXGEN_OPCODE ) {
235             code = VL_GetEntryByNameO(rxconnp, name, vldbEntryp);
236             *methodp = 0;
237         }
238         rx_PutConnection(rxconnp);
239     } while (cm_Analyze(connp, userp, reqp, NULL, cellp, 0, NULL, NULL, &cellp->vlServersp, NULL, code));
240     code = cm_MapVLRPCError(code, reqp);
241     if ( code )
242         osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x",
243                   osi_LogSaveString(afsd_logp,cellp->name),
244                   osi_LogSaveString(afsd_logp,name), code);
245     else
246         osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS",
247                   osi_LogSaveString(afsd_logp,cellp->name),
248                   osi_LogSaveString(afsd_logp,name));
249     return code;
250 }
251
252 static long
253 cm_GetEntryByID( struct cm_cell *cellp, afs_uint32 id,
254                  struct vldbentry *vldbEntryp,
255                  struct nvldbentry *nvldbEntryp,
256                  struct uvldbentry *uvldbEntryp,
257                  int *methodp,
258                  cm_user_t *userp,
259                  cm_req_t *reqp
260                  )
261 {
262     char name[64];
263
264     StringCbPrintf(name, sizeof(name), "%u", id);
265
266     return cm_GetEntryByName(cellp, name, vldbEntryp, nvldbEntryp, uvldbEntryp, methodp, userp, reqp);
267 }
268
269 long cm_UpdateVolumeLocation(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
270                      cm_volume_t *volp)
271 {
272     int i;
273     afs_uint32 j;
274     cm_serverRef_t *tsrp;
275     cm_server_t *tsp;
276     struct sockaddr_in tsockAddr;
277     long tflags;
278     u_long tempAddr;
279     struct vldbentry vldbEntry;
280     struct nvldbentry nvldbEntry;
281     struct uvldbentry uvldbEntry;
282     int method = -1;
283     int ROcount = 0;
284     int isMixed = 0;
285     long code;
286     enum volstatus rwNewstate = vl_online;
287     enum volstatus roNewstate = vl_online;
288     enum volstatus bkNewstate = vl_online;
289 #ifdef AFS_FREELANCE_CLIENT
290     int freelance = 0;
291 #endif
292     afs_uint32 volType;
293     time_t now;
294
295     lock_AssertWrite(&volp->rw);
296
297     /*
298      * If the last volume update was in the last five
299      * minutes and it did not exist, then avoid the RPC
300      * and return No Such Volume immediately.
301      */
302     now = time(NULL);
303     if ((volp->flags & CM_VOLUMEFLAG_NOEXIST) &&
304         (now < volp->lastUpdateTime + 600))
305     {
306         return CM_ERROR_NOSUCHVOLUME;
307     }
308
309 #ifdef AFS_FREELANCE_CLIENT
310     if (cellp->cellID == AFS_FAKE_ROOT_CELL_ID)
311     {
312         freelance = 1;
313         memset(&vldbEntry, 0, sizeof(vldbEntry));
314         vldbEntry.flags |= VLF_RWEXISTS;
315         vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID;
316         code = 0;
317         method = 0;
318     } else
319 #endif
320     {
321         while (volp->flags & CM_VOLUMEFLAG_UPDATING_VL) {
322             osi_Log3(afsd_logp, "cm_UpdateVolumeLocation sleeping name %s:%s flags 0x%x",
323                      volp->cellp->name, volp->namep, volp->flags);
324             osi_SleepW((LONG_PTR) &volp->flags, &volp->rw);
325             lock_ObtainWrite(&volp->rw);
326             osi_Log3(afsd_logp, "cm_UpdateVolumeLocation awake name %s:%s flags 0x%x",
327                      volp->cellp->name, volp->namep, volp->flags);
328             if (!(volp->flags & CM_VOLUMEFLAG_RESET)) {
329                 osi_Log3(afsd_logp, "cm_UpdateVolumeLocation nothing to do, waking others name %s:%s flags 0x%x",
330                          volp->cellp->name, volp->namep, volp->flags);
331                 osi_Wakeup((LONG_PTR) &volp->flags);
332                 return 0;
333             }
334             now = time(NULL);
335         }
336
337         /* Do not query again if the last update attempt failed in the last 60 seconds */
338         if ((volp->flags & CM_VOLUMEFLAG_RESET) && (volp->lastUpdateTime > now - 60))
339         {
340             osi_Log3(afsd_logp, "cm_UpdateVolumeLocation unsuccessful update in last 60 seconds -- name %s:%s flags 0x%x",
341                       volp->cellp->name, volp->namep, volp->flags);
342             return(CM_ERROR_ALLDOWN);
343         }
344
345         _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_UPDATING_VL);
346
347         /* Do not hold the volume lock across the RPC calls */
348         lock_ReleaseWrite(&volp->rw);
349
350         if (cellp->flags & CM_CELLFLAG_VLSERVER_INVALID)
351              cm_UpdateCell(cellp, 0);
352
353         /* now we have volume structure locked and held; make RPC to fill it */
354         code = cm_GetEntryByName(cellp, volp->namep, &vldbEntry, &nvldbEntry,
355                                  &uvldbEntry,
356                                  &method, userp, reqp);
357
358         /* We can end up here with code == CM_ERROR_NOSUCHVOLUME if the base volume name
359          * does not exist and is not a numeric string but there might exist a .readonly volume.
360          * If the base name doesn't exist we will not care about the .backup that might be left
361          * behind since there should be no method to access it.
362          */
363         if (code == CM_ERROR_NOSUCHVOLUME &&
364              _atoi64(volp->namep) == 0 &&
365              volp->vol[RWVOL].ID == 0 &&
366              strlen(volp->namep) < (VL_MAXNAMELEN - 9)) {
367             char name[VL_MAXNAMELEN];
368
369             snprintf(name, VL_MAXNAMELEN, "%s.readonly", volp->namep);
370
371             /* now we have volume structure locked and held; make RPC to fill it */
372             code = cm_GetEntryByName(cellp, name, &vldbEntry, &nvldbEntry,
373                                      &uvldbEntry,
374                                      &method, userp, reqp);
375         }
376
377         /*
378          * What if there was a volume rename?  The volume name no longer exists but the
379          * volume id might.  Try to refresh the volume location information based one
380          * of the readwrite or readonly volume id.
381          */
382         if (code == CM_ERROR_NOSUCHVOLUME) {
383             if (volp->vol[RWVOL].ID != 0) {
384                 code = cm_GetEntryByID(cellp, volp->vol[RWVOL].ID, &vldbEntry, &nvldbEntry,
385                                        &uvldbEntry,
386                                        &method, userp, reqp);
387             } else if (volp->vol[ROVOL].ID != 0) {
388                 code = cm_GetEntryByID(cellp, volp->vol[ROVOL].ID, &vldbEntry, &nvldbEntry,
389                                        &uvldbEntry,
390                                        &method, userp, reqp);
391             }
392         }
393         lock_ObtainWrite(&volp->rw);
394     }
395
396     if (code == 0) {
397         afs_int32 flags;
398         afs_int32 nServers;
399         afs_int32 rwID;
400         afs_int32 roID;
401         afs_int32 bkID;
402         afs_int32 serverNumber[NMAXNSERVERS];
403         afs_int32 serverUnique[NMAXNSERVERS];
404         afs_int32 serverFlags[NMAXNSERVERS];
405         afsUUID   serverUUID[NMAXNSERVERS];
406         afs_int32 rwServers_alldown = 1;
407         afs_int32 roServers_alldown = 1;
408         afs_int32 bkServers_alldown = 1;
409         char      name[VL_MAXNAMELEN];
410
411 #ifdef AFS_FREELANCE_CLIENT
412         if (freelance)
413             rwServers_alldown = 0;
414 #endif
415
416         /* clear out old bindings */
417         for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
418             if (volp->vol[volType].serversp)
419                 cm_FreeServerList(&volp->vol[volType].serversp, CM_FREESERVERLIST_DELETE);
420         }
421
422         memset(serverUUID, 0, sizeof(serverUUID));
423         memset(serverUnique, 0, sizeof(serverUnique));
424
425         switch ( method ) {
426         case 0:
427             flags = vldbEntry.flags;
428             nServers = vldbEntry.nServers;
429             rwID = vldbEntry.volumeId[0];
430             roID = vldbEntry.volumeId[1];
431             bkID = vldbEntry.volumeId[2];
432             for ( i=0; i<nServers; i++ ) {
433                 serverFlags[i] = vldbEntry.serverFlags[i];
434                 serverNumber[i] = vldbEntry.serverNumber[i];
435             }
436             strncpy(name, vldbEntry.name, VL_MAXNAMELEN);
437             name[VL_MAXNAMELEN - 1] = '\0';
438             break;
439         case 1:
440             flags = nvldbEntry.flags;
441             nServers = nvldbEntry.nServers;
442             rwID = nvldbEntry.volumeId[0];
443             roID = nvldbEntry.volumeId[1];
444             bkID = nvldbEntry.volumeId[2];
445             for ( i=0; i<nServers; i++ ) {
446                 serverFlags[i] = nvldbEntry.serverFlags[i];
447                 serverNumber[i] = nvldbEntry.serverNumber[i];
448             }
449             strncpy(name, nvldbEntry.name, VL_MAXNAMELEN);
450             name[VL_MAXNAMELEN - 1] = '\0';
451             break;
452         case 2:
453             flags = uvldbEntry.flags;
454             nServers = uvldbEntry.nServers;
455             rwID = uvldbEntry.volumeId[0];
456             roID = uvldbEntry.volumeId[1];
457             bkID = uvldbEntry.volumeId[2];
458             for ( i=0, j=0; code == 0 && i<nServers && j<NMAXNSERVERS; i++ ) {
459                 if ( !(uvldbEntry.serverFlags[i] & VLSF_UUID) ) {
460                     serverFlags[j] = uvldbEntry.serverFlags[i];
461                     serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
462                     j++;
463                 } else {
464                     code = cm_GetAddrsU(cellp, userp, reqp,
465                                         &uvldbEntry.serverNumber[i],
466                                         uvldbEntry.serverUnique[i],
467                                         uvldbEntry.serverFlags[i], &j,
468                                         serverFlags, serverNumber,
469                                         serverUUID, serverUnique);
470                     if (code == CM_ERROR_RETRY)
471                         continue;
472                 }
473             }
474             nServers = j;                                       /* update the server count */
475             strncpy(name, uvldbEntry.name, VL_MAXNAMELEN);
476             name[VL_MAXNAMELEN - 1] = '\0';
477             break;
478         }
479
480         /* decode the response */
481         lock_ObtainWrite(&cm_volumeLock);
482         if (!cm_VolNameIsID(volp->namep)) {
483             size_t    len;
484
485             len = strlen(name);
486
487             if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
488                 name[len - 7] = '\0';
489             } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
490                 name[len - 9] = '\0';
491             }
492
493             osi_Log2(afsd_logp, "cm_UpdateVolume name %s -> %s",
494                      osi_LogSaveString(afsd_logp,volp->namep), osi_LogSaveString(afsd_logp,name));
495
496             if (volp->qflags & CM_VOLUME_QFLAG_IN_HASH)
497                 cm_RemoveVolumeFromNameHashTable(volp);
498
499             strcpy(volp->namep, name);
500
501             cm_AddVolumeToNameHashTable(volp);
502         }
503
504         if (flags & VLF_DFSFILESET) {
505             _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_DFS_VOLUME);
506             osi_Log1(afsd_logp, "cm_UpdateVolume Volume Group '%s' is a DFS File Set.  Correct behavior is not implemented.",
507                      osi_LogSaveString(afsd_logp, volp->namep));
508         }
509
510         if (flags & VLF_RWEXISTS) {
511             if (volp->vol[RWVOL].ID != rwID) {
512                 if (volp->vol[RWVOL].qflags & CM_VOLUME_QFLAG_IN_HASH)
513                     cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
514                 volp->vol[RWVOL].ID = rwID;
515                 cm_AddVolumeToIDHashTable(volp, RWVOL);
516             }
517         } else {
518             if (volp->vol[RWVOL].qflags & CM_VOLUME_QFLAG_IN_HASH)
519                 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
520             volp->vol[RWVOL].ID = 0;
521         }
522         if (flags & VLF_ROEXISTS) {
523             if (volp->vol[ROVOL].ID != roID) {
524                 if (volp->vol[ROVOL].qflags & CM_VOLUME_QFLAG_IN_HASH)
525                     cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
526                 volp->vol[ROVOL].ID = roID;
527                 cm_AddVolumeToIDHashTable(volp, ROVOL);
528             }
529         } else {
530             if (volp->vol[ROVOL].qflags & CM_VOLUME_QFLAG_IN_HASH)
531                 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
532             volp->vol[ROVOL].ID = 0;
533         }
534         if (flags & VLF_BACKEXISTS) {
535             if (volp->vol[BACKVOL].ID != bkID) {
536                 if (volp->vol[BACKVOL].qflags & CM_VOLUME_QFLAG_IN_HASH)
537                     cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
538                 volp->vol[BACKVOL].ID = bkID;
539                 cm_AddVolumeToIDHashTable(volp, BACKVOL);
540             }
541         } else {
542             if (volp->vol[BACKVOL].qflags & CM_VOLUME_QFLAG_IN_HASH)
543                 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
544             volp->vol[BACKVOL].ID = 0;
545         }
546         lock_ReleaseWrite(&cm_volumeLock);
547
548         /* See if the replica sites are mixed versions */
549         for (i=0; i<nServers; i++) {
550             if (serverFlags[i] & VLSF_NEWREPSITE) {
551                 isMixed = 1;
552                 break;
553             }
554         }
555
556         for (i=0; i<nServers; i++) {
557             /* create a server entry */
558             tflags = serverFlags[i];
559             if (tflags & VLSF_DONTUSE)
560                 continue;
561             tsockAddr.sin_port = htons(7000);
562             tsockAddr.sin_family = AF_INET;
563             tempAddr = htonl(serverNumber[i]);
564             tsockAddr.sin_addr.s_addr = tempAddr;
565             tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE, FALSE);
566             if (tsp && (method == 2) && (tsp->flags & CM_SERVERFLAG_UUID)) {
567                 /*
568                  * Check to see if the uuid of the server we know at this address
569                  * matches the uuid of the server we are being told about by the
570                  * vlserver.  If not, ...?
571                  */
572                 if (!afs_uuid_equal(&serverUUID[i], &tsp->uuid)) {
573                     char uuid1[128], uuid2[128];
574                     char hoststr[16];
575
576                     afsUUID_to_string(&serverUUID[i], uuid1, sizeof(uuid1));
577                     afsUUID_to_string(&tsp->uuid, uuid2, sizeof(uuid2));
578                     afs_inet_ntoa_r(serverNumber[i], hoststr);
579
580                     osi_Log3(afsd_logp, "cm_UpdateVolumeLocation UUIDs do not match! %s != %s (%s)",
581                               osi_LogSaveString(afsd_logp, uuid1),
582                               osi_LogSaveString(afsd_logp, uuid2),
583                               osi_LogSaveString(afsd_logp, hoststr));
584                 }
585             }
586             if (!tsp) {
587                 /*
588                  * cm_NewServer will probe the file server which in turn will
589                  * update the state on the volume group object.  Do not probe
590                  * in this thread.  It will block the thread and can result in
591                  * a recursive call to cm_UpdateVolumeLocation().
592                  */
593                 lock_ReleaseWrite(&volp->rw);
594                 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE, cellp, &serverUUID[i], CM_FLAG_NOPROBE);
595                 lock_ObtainWrite(&volp->rw);
596             }
597             osi_assertx(tsp != NULL, "null cm_server_t");
598
599             /*
600              * if this server was created by fs setserverprefs
601              * then it won't have either a cell assignment or
602              * a server uuid.
603              */
604             if ( !tsp->cellp )
605                 tsp->cellp = cellp;
606             if ( (method == 2) && !(tsp->flags & CM_SERVERFLAG_UUID) &&
607                  !afs_uuid_is_nil(&serverUUID[i])) {
608                 tsp->uuid = serverUUID[i];
609                 _InterlockedOr(&tsp->flags, CM_SERVERFLAG_UUID);
610             }
611
612             /* and add it to the list(s). */
613             /*
614              * Each call to cm_NewServerRef() increments the
615              * ref count of tsp.  These reference will be dropped,
616              * if and when the volume is reset; see reset code
617              * earlier in this function.
618              */
619             if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
620                 tsrp = cm_NewServerRef(tsp, rwID);
621                 cm_InsertServerList(&volp->vol[RWVOL].serversp, tsrp);
622                 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
623                     rwServers_alldown = 0;
624             }
625             /*
626              * If there are mixed versions of RO releases on the replica
627              * sites, skip the servers with the out of date versions.
628              */
629             if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS) &&
630                 (!isMixed || (tflags & VLSF_NEWREPSITE))) {
631                 tsrp = cm_NewServerRef(tsp, roID);
632                 cm_InsertServerList(&volp->vol[ROVOL].serversp, tsrp);
633                 ROcount++;
634
635                 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
636                     roServers_alldown = 0;
637             }
638             /* We don't use VLSF_BACKVOL !?! */
639             /* Because only the backup on the server holding the RW
640              * volume can be valid.  This check prevents errors if a
641              * RW is moved but the old backup is not removed.
642              */
643             if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
644                 tsrp = cm_NewServerRef(tsp, bkID);
645                 cm_InsertServerList(&volp->vol[BACKVOL].serversp, tsrp);
646
647                 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
648                     bkServers_alldown = 0;
649             }
650             /* Drop the reference obtained by cm_FindServer() */
651             cm_PutServer(tsp);
652         }
653
654         /*
655          * Randomize RO list
656          *
657          * If the first n servers have the same rank, then we
658          * randomly pick one among them and move it to the beginning.
659          * We don't bother to re-order the whole list because
660          * the rest of the list is used only if the first server is
661          * down.  We only do this for the RO list; we assume the other
662          * lists are length 1.
663          */
664         if (ROcount > 1) {
665             cm_RandomizeServer(&volp->vol[ROVOL].serversp);
666             _InterlockedOr(&volp->vol[ROVOL].flags, CM_VOL_STATE_FLAG_REPLICATED);
667         }
668         else {
669             _InterlockedAnd(&volp->vol[ROVOL].flags, ~CM_VOL_STATE_FLAG_REPLICATED);
670         }
671
672         rwNewstate = rwServers_alldown ? vl_alldown : vl_online;
673         roNewstate = roServers_alldown ? vl_alldown : vl_online;
674         bkNewstate = bkServers_alldown ? vl_alldown : vl_online;
675
676         _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_NOEXIST);
677     } else if (code == CM_ERROR_NOSUCHVOLUME || code == VL_NOENT || code == VL_BADNAME) {
678         _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_NOEXIST);
679     } else {
680         rwNewstate = roNewstate = bkNewstate = vl_alldown;
681
682         /*
683          * we are updating lastUpdateTime but didn't get an answer
684          * so clear the no exist flag.
685          */
686         _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_NOEXIST);
687     }
688
689     if (volp->vol[RWVOL].state != rwNewstate) {
690         if (volp->vol[RWVOL].ID)
691             cm_VolumeStatusNotification(volp, volp->vol[RWVOL].ID, volp->vol[RWVOL].state, rwNewstate);
692         volp->vol[RWVOL].state = rwNewstate;
693     }
694     if (volp->vol[ROVOL].state != roNewstate) {
695         if (volp->vol[ROVOL].ID)
696             cm_VolumeStatusNotification(volp, volp->vol[ROVOL].ID, volp->vol[ROVOL].state, roNewstate);
697         volp->vol[ROVOL].state = roNewstate;
698     }
699     if (volp->vol[BACKVOL].state != bkNewstate) {
700         if (volp->vol[BACKVOL].ID)
701             cm_VolumeStatusNotification(volp, volp->vol[BACKVOL].ID, volp->vol[BACKVOL].state, bkNewstate);
702         volp->vol[BACKVOL].state = bkNewstate;
703     }
704
705     volp->lastUpdateTime = time(NULL);
706     if (isMixed)
707         _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RO_MIXED);
708     else
709         _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_RO_MIXED);
710
711     if (code == 0)
712         _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_RESET);
713
714     _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_UPDATING_VL);
715     osi_Log4(afsd_logp, "cm_UpdateVolumeLocation done, waking others name %s:%s flags 0x%x code 0x%x",
716              osi_LogSaveString(afsd_logp,volp->cellp->name),
717              osi_LogSaveString(afsd_logp,volp->namep), volp->flags, code);
718     osi_Wakeup((LONG_PTR) &volp->flags);
719
720     return code;
721 }
722
723 /* Requires read or write lock on cm_volumeLock */
724 void cm_GetVolume(cm_volume_t *volp)
725 {
726     InterlockedIncrement(&volp->refCount);
727 }
728
729 cm_volume_t *cm_GetVolumeByFID(cm_fid_t *fidp)
730 {
731     cm_volume_t *volp;
732     afs_uint32 hash;
733
734     lock_ObtainRead(&cm_volumeLock);
735     hash = CM_VOLUME_ID_HASH(fidp->volume);
736     /* The volumeID can be any one of the three types.  So we must
737      * search the hash table for all three types until we find it.
738      * We will search in the order of RO, RW, BK.
739      */
740     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->vol[ROVOL].nextp) {
741         if ( fidp->cell == volp->cellp->cellID && fidp->volume == volp->vol[ROVOL].ID )
742             break;
743     }
744     if (!volp) {
745         /* try RW volumes */
746         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->vol[RWVOL].nextp) {
747             if ( fidp->cell == volp->cellp->cellID && fidp->volume == volp->vol[RWVOL].ID )
748                 break;
749         }
750     }
751     if (!volp) {
752         /* try BK volumes */
753         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->vol[BACKVOL].nextp) {
754             if ( fidp->cell == volp->cellp->cellID && fidp->volume == volp->vol[BACKVOL].ID )
755                 break;
756         }
757     }
758
759     /* hold the volume if we found it */
760     if (volp)
761         cm_GetVolume(volp);
762
763     lock_ReleaseRead(&cm_volumeLock);
764     return volp;
765 }
766
767 cm_volume_t *cm_FindVolumeByFID(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
768 {
769     cm_volume_t *volp = NULL;
770     cm_cell_t   *cellp;
771     long         code;
772
773     cellp = cm_FindCellByID(fidp->cell, CM_FLAG_NOPROBE);
774     if (!cellp) {
775         return NULL;
776     }
777
778     code = cm_FindVolumeByID(cellp, fidp->volume, userp, reqp, CM_GETVOL_FLAG_CREATE, &volp);
779     if (code)
780         return NULL;
781
782     return volp;
783 }
784
785 long cm_FindVolumeByID(cm_cell_t *cellp, afs_uint32 volumeID, cm_user_t *userp,
786                       cm_req_t *reqp, afs_uint32 flags, cm_volume_t **outVolpp)
787 {
788     cm_volume_t *volp;
789 #ifdef SEARCH_ALL_VOLUMES
790     cm_volume_t *volp2;
791 #endif
792     char volNameString[VL_MAXNAMELEN];
793     afs_uint32 hash;
794     long code = 0;
795
796     lock_ObtainRead(&cm_volumeLock);
797 #ifdef SEARCH_ALL_VOLUMES
798     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
799         if (cellp == volp->cellp &&
800              ((unsigned) volumeID == volp->vol[RWVOL].ID ||
801                (unsigned) volumeID == volp->vol[ROVOL].ID ||
802                (unsigned) volumeID == volp->vol[BACKVOL].ID))
803             break;
804     }
805
806     volp2 = volp;
807 #endif /* SEARCH_ALL_VOLUMES */
808
809     hash = CM_VOLUME_ID_HASH(volumeID);
810     /* The volumeID can be any one of the three types.  So we must
811      * search the hash table for all three types until we find it.
812      * We will search in the order of RO, RW, BK.
813      */
814     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->vol[ROVOL].nextp) {
815         if ( cellp == volp->cellp && volumeID == volp->vol[ROVOL].ID )
816             break;
817     }
818     if (!volp) {
819         /* try RW volumes */
820         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->vol[RWVOL].nextp) {
821             if ( cellp == volp->cellp && volumeID == volp->vol[RWVOL].ID )
822                 break;
823         }
824     }
825     if (!volp) {
826         /* try BK volumes */
827         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->vol[BACKVOL].nextp) {
828             if ( cellp == volp->cellp && volumeID == volp->vol[BACKVOL].ID )
829                 break;
830         }
831     }
832
833 #ifdef SEARCH_ALL_VOLUMES
834     osi_assertx(volp == volp2, "unexpected cm_vol_t");
835 #endif
836
837     /* hold the volume if we found it */
838     if (volp)
839         cm_GetVolume(volp);
840
841     lock_ReleaseRead(&cm_volumeLock);
842
843     /* return it held */
844     if (volp) {
845         lock_ObtainWrite(&volp->rw);
846
847         code = 0;
848         if ((volp->flags & CM_VOLUMEFLAG_RESET) && !(flags & CM_GETVOL_FLAG_NO_RESET)) {
849             code = cm_UpdateVolumeLocation(cellp, userp, reqp, volp);
850         }
851         lock_ReleaseWrite(&volp->rw);
852         if (code == 0) {
853             *outVolpp = volp;
854
855             if (!(flags & CM_GETVOL_FLAG_NO_LRU_UPDATE)) {
856                 lock_ObtainWrite(&cm_volumeLock);
857                 cm_AdjustVolumeLRU(volp);
858                 lock_ReleaseWrite(&cm_volumeLock);
859             }
860         } else {
861             lock_ObtainRead(&cm_volumeLock);
862             cm_PutVolume(volp);
863             lock_ReleaseRead(&cm_volumeLock);
864         }
865         return code;
866     }
867
868     /* otherwise, we didn't find it so consult the VLDB */
869     sprintf(volNameString, "%u", volumeID);
870     code = cm_FindVolumeByName(cellp, volNameString, userp, reqp,
871                               flags | CM_GETVOL_FLAG_IGNORE_LINKED_CELL, outVolpp);
872
873     if (code == CM_ERROR_NOSUCHVOLUME && cellp->linkedName[0] &&
874         !(flags & CM_GETVOL_FLAG_IGNORE_LINKED_CELL)) {
875         cm_cell_t *linkedCellp = cm_GetCell(cellp->linkedName, flags);
876
877         if (linkedCellp)
878             code = cm_FindVolumeByID(linkedCellp, volumeID, userp, reqp,
879                                      flags | CM_GETVOL_FLAG_IGNORE_LINKED_CELL,
880                                      outVolpp);
881     }
882     return code;
883 }
884
885
886 long cm_FindVolumeByName(struct cm_cell *cellp, char *volumeNamep,
887                         struct cm_user *userp, struct cm_req *reqp,
888                         afs_uint32 flags, cm_volume_t **outVolpp)
889 {
890     cm_volume_t *volp;
891 #ifdef SEARCH_ALL_VOLUMES
892     cm_volume_t *volp2;
893 #endif
894     long        code = 0;
895     char        name[VL_MAXNAMELEN];
896     size_t      len;
897     int         type;
898     afs_uint32  hash;
899
900     strncpy(name, volumeNamep, VL_MAXNAMELEN);
901     name[VL_MAXNAMELEN-1] = '\0';
902     len = strlen(name);
903
904     if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
905         type = BACKVOL;
906         name[len - 7] = '\0';
907     } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
908         type = ROVOL;
909         name[len - 9] = '\0';
910     } else {
911         type = RWVOL;
912     }
913
914     lock_ObtainRead(&cm_volumeLock);
915 #ifdef SEARCH_ALL_VOLUMES
916     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
917         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0) {
918             break;
919         }
920     }
921     volp2 = volp;
922 #endif /* SEARCH_ALL_VOLUMES */
923
924     hash = CM_VOLUME_NAME_HASH(name);
925     for (volp = cm_data.volumeNameHashTablep[hash]; volp; volp = volp->nameNextp) {
926         if (cellp == volp->cellp && strcmp(name, volp->namep) == 0)
927             break;
928     }
929
930 #ifdef SEARCH_ALL_VOLUMES
931     osi_assertx(volp2 == volp, "unexpected cm_vol_t");
932 #endif
933
934     if (!volp && (flags & CM_GETVOL_FLAG_CREATE)) {
935         afs_uint32 volType;
936         /* otherwise, get from VLDB */
937
938         /*
939          * Change to a write lock so that we have exclusive use of
940          * the first cm_volume_t with a refCount of 0 so that we
941          * have time to increment it.
942          */
943         lock_ConvertRToW(&cm_volumeLock);
944
945         /*
946          * While the lock was converted it may have been dropped
947          * Search again now that we are exclusive.
948          */
949         for (volp = cm_data.volumeNameHashTablep[hash]; volp; volp = volp->nameNextp) {
950             if (cellp == volp->cellp && strcmp(name, volp->namep) == 0)
951                 break;
952         }
953
954         if (volp) {
955             cm_GetVolume(volp);
956             lock_ReleaseWrite(&cm_volumeLock);
957             lock_ObtainWrite(&volp->rw);
958         } else {
959             if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
960 #ifdef RECYCLE_FROM_ALL_VOLUMES_LIST
961                 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
962                     if ( volp->refCount == 0 ) {
963                         /* There is one we can re-use */
964                         break;
965                     }
966                 }
967 #else
968                 for ( volp = cm_data.volumeLRULastp;
969                       volp;
970                       volp = (cm_volume_t *) osi_QPrev(&volp->q))
971                 {
972                     if ( volp->refCount == 0 ) {
973                         /* There is one we can re-use */
974                         break;
975                     }
976                 }
977 #endif
978                 if (!volp)
979                     osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
980
981                 osi_Log2(afsd_logp, "Recycling Volume %s:%s",
982                          volp->cellp->name, volp->namep);
983
984                 /* The volp is removed from the LRU queue in order to
985                  * prevent two threads from attempting to recycle the
986                  * same object.  This volp must be re-inserted back into
987                  * the LRU queue before this function exits.
988                  */
989                 if (volp->qflags & CM_VOLUME_QFLAG_IN_LRU_QUEUE)
990                     cm_RemoveVolumeFromLRU(volp);
991                 if (volp->qflags & CM_VOLUME_QFLAG_IN_HASH)
992                     cm_RemoveVolumeFromNameHashTable(volp);
993
994                 for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
995                     if (volp->vol[volType].qflags & CM_VOLUME_QFLAG_IN_HASH)
996                         cm_RemoveVolumeFromIDHashTable(volp, volType);
997                     if (volp->vol[volType].ID)
998                         cm_VolumeStatusNotification(volp, volp->vol[volType].ID, volp->vol[volType].state, vl_unknown);
999                     volp->vol[volType].ID = 0;
1000                     cm_SetFid(&volp->vol[volType].dotdotFid, 0, 0, 0, 0);
1001                 }
1002             } else {
1003                 volp = &cm_data.volumeBaseAddress[InterlockedIncrement(&cm_data.currentVolumes) - 1];
1004                 memset(volp, 0, sizeof(cm_volume_t));
1005                 volp->magic = CM_VOLUME_MAGIC;
1006                 volp->allNextp = cm_data.allVolumesp;
1007                 cm_data.allVolumesp = volp;
1008                 lock_InitializeRWLock(&volp->rw, "cm_volume_t rwlock", LOCK_HIERARCHY_VOLUME);
1009             }
1010             /*
1011              * no one else can find this object and we have not dropped
1012              * cm_volumeLock in any case.  The object is either new or
1013              * recycled.  Initialize its new values and put it into the
1014              * name hash table before dropping cm_volumeLock which makes
1015              * it visible to competing threads.
1016              */
1017             volp->cellp = cellp;
1018             strncpy(volp->namep, name, VL_MAXNAMELEN);
1019             volp->namep[VL_MAXNAMELEN-1] = '\0';
1020             volp->flags = CM_VOLUMEFLAG_RESET;
1021             volp->lastUpdateTime = 0;
1022
1023             for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
1024                 volp->vol[volType].state = vl_unknown;
1025                 volp->vol[volType].nextp = NULL;
1026                 volp->vol[volType].flags = 0;
1027             }
1028             volp->cbExpiresRO = 0;
1029             volp->cbIssuedRO = 0;
1030             volp->cbServerpRO = NULL;
1031             volp->creationDateRO = 0;
1032             cm_AddVolumeToNameHashTable(volp);
1033             cm_GetVolume(volp);
1034             lock_ReleaseWrite(&cm_volumeLock);
1035             lock_ObtainWrite(&volp->rw);
1036             for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
1037                 cm_FreeServerList(&volp->vol[volType].serversp, CM_FREESERVERLIST_DELETE);
1038             }
1039         }
1040     }
1041     else if (volp) {
1042         cm_GetVolume(volp);
1043         lock_ReleaseRead(&cm_volumeLock);
1044         lock_ObtainWrite(&volp->rw);
1045     } else {
1046         lock_ReleaseRead(&cm_volumeLock);
1047         return CM_ERROR_NOSUCHVOLUME;
1048     }
1049
1050     /* if we get here we are holding the mutex */
1051     if ((volp->flags & CM_VOLUMEFLAG_RESET) && !(flags & CM_GETVOL_FLAG_NO_RESET)) {
1052         code = cm_UpdateVolumeLocation(cellp, userp, reqp, volp);
1053     }
1054     lock_ReleaseWrite(&volp->rw);
1055
1056     if (code == 0 && (type == BACKVOL && volp->vol[BACKVOL].ID == 0 ||
1057                       type == ROVOL && volp->vol[ROVOL].ID == 0))
1058         code = CM_ERROR_NOSUCHVOLUME;
1059
1060     if (code == 0) {
1061         *outVolpp = volp;
1062
1063         lock_ObtainWrite(&cm_volumeLock);
1064         if (!(volp->qflags & CM_VOLUME_QFLAG_IN_LRU_QUEUE) ||
1065              (flags & CM_GETVOL_FLAG_NO_LRU_UPDATE))
1066             cm_AdjustVolumeLRU(volp);
1067         lock_ReleaseWrite(&cm_volumeLock);
1068     } else {
1069         /*
1070          * do not return it to the caller but do insert it in the LRU
1071          * otherwise it will be lost
1072          */
1073         lock_ObtainWrite(&cm_volumeLock);
1074         if (!(volp->qflags & CM_VOLUME_QFLAG_IN_LRU_QUEUE) ||
1075              (flags & CM_GETVOL_FLAG_NO_LRU_UPDATE))
1076             cm_AdjustVolumeLRU(volp);
1077         cm_PutVolume(volp);
1078         lock_ReleaseWrite(&cm_volumeLock);
1079     }
1080
1081     if (code == CM_ERROR_NOSUCHVOLUME && cellp->linkedName[0] &&
1082         !(flags & CM_GETVOL_FLAG_IGNORE_LINKED_CELL)) {
1083         cm_cell_t *linkedCellp = cm_GetCell(cellp->linkedName, flags);
1084
1085         if (linkedCellp)
1086             code = cm_FindVolumeByName(linkedCellp, volumeNamep, userp, reqp,
1087                                        flags | CM_GETVOL_FLAG_IGNORE_LINKED_CELL,
1088                                        outVolpp);
1089     }
1090     return code;
1091 }
1092
1093 /*
1094  * Only call this function in response to a VNOVOL or VMOVED error
1095  * from a file server.  Do not call it in response to CM_ERROR_NOSUCHVOLUME
1096  * as that can lead to recursive calls.
1097  */
1098 long cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
1099 {
1100     cm_cell_t *cellp;
1101     cm_volume_t *volp;
1102 #ifdef SEARCH_ALL_VOLUMES
1103     cm_volume_t *volp2;
1104 #endif
1105     afs_uint32  hash;
1106     long code;
1107
1108     if (!fidp)
1109         return CM_ERROR_INVAL;
1110
1111     cellp = cm_FindCellByID(fidp->cell, 0);
1112     if (!cellp)
1113         return CM_ERROR_NOSUCHCELL;
1114
1115     /* search for the volume */
1116     lock_ObtainRead(&cm_volumeLock);
1117 #ifdef SEARCH_ALL_VOLUMES
1118     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1119         if (cellp == volp->cellp &&
1120              (fidp->volume == volp->vol[RWVOL].ID ||
1121                fidp->volume == volp->vol[ROVOL].ID ||
1122                fidp->volume == volp->vol[BACKVOL].ID))
1123             break;
1124     }
1125 #endif /* SEARCH_ALL_VOLUMES */
1126
1127     hash = CM_VOLUME_ID_HASH(fidp->volume);
1128     /* The volumeID can be any one of the three types.  So we must
1129      * search the hash table for all three types until we find it.
1130      * We will search in the order of RO, RW, BK.
1131      */
1132     for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->vol[ROVOL].nextp) {
1133         if ( cellp == volp->cellp && fidp->volume == volp->vol[ROVOL].ID )
1134             break;
1135     }
1136     if (!volp) {
1137         /* try RW volumes */
1138         for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->vol[RWVOL].nextp) {
1139             if ( cellp == volp->cellp && fidp->volume == volp->vol[RWVOL].ID )
1140                 break;
1141         }
1142     }
1143     if (!volp) {
1144         /* try BK volumes */
1145         for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->vol[BACKVOL].nextp) {
1146             if ( cellp == volp->cellp && fidp->volume == volp->vol[BACKVOL].ID )
1147                 break;
1148         }
1149     }
1150
1151 #ifdef SEARCH_ALL_VOLUMES
1152     osi_assertx(volp == volp2, "unexpected cm_vol_t");
1153 #endif
1154     /* hold the volume if we found it */
1155     if (volp)
1156         cm_GetVolume(volp);
1157
1158     lock_ReleaseRead(&cm_volumeLock);
1159
1160     if (!volp)
1161         return CM_ERROR_NOSUCHVOLUME;
1162
1163     /* update it */
1164     cm_data.mountRootGen = time(NULL);
1165     lock_ObtainWrite(&volp->rw);
1166     _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RESET);
1167     volp->lastUpdateTime = 0;
1168
1169     code = cm_UpdateVolumeLocation(cellp, userp, reqp, volp);
1170     lock_ReleaseWrite(&volp->rw);
1171
1172     lock_ObtainRead(&cm_volumeLock);
1173     cm_PutVolume(volp);
1174     lock_ReleaseRead(&cm_volumeLock);
1175
1176     return code;
1177 }
1178
1179 /* find the appropriate servers from a volume */
1180 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volid, cm_user_t *userp, cm_req_t *reqp, afs_uint32 *replicated)
1181 {
1182     cm_serverRef_t **serverspp;
1183     cm_serverRef_t *current;
1184     int firstTry = 1;
1185     cm_vol_state_t *volstatep = NULL;
1186
1187   start:
1188     volstatep = cm_VolumeStateByID(volp, volid);
1189
1190     lock_ObtainWrite(&cm_serverLock);
1191     if (volstatep) {
1192         if (replicated)
1193             *replicated = (volstatep->flags & CM_VOL_STATE_FLAG_REPLICATED);
1194         serverspp = &volstatep->serversp;
1195     } else {
1196         lock_ReleaseWrite(&cm_serverLock);
1197         if (firstTry) {
1198             afs_int32 code;
1199             firstTry = 0;
1200             lock_ObtainWrite(&volp->rw);
1201             _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RESET);
1202             volp->lastUpdateTime = 0;
1203             code = cm_UpdateVolumeLocation(volp->cellp, userp, reqp, volp);
1204             lock_ReleaseWrite(&volp->rw);
1205             if (code == 0)
1206                 goto start;
1207         }
1208         return NULL;
1209     }
1210
1211     /*
1212      * Increment the refCount on deleted items as well.
1213      * They will be freed by cm_FreeServerList when they get to zero
1214      */
1215     for (current = *serverspp; current; current = current->next)
1216         cm_GetServerRef(current, TRUE);
1217
1218     lock_ReleaseWrite(&cm_serverLock);
1219
1220     return serverspp;
1221 }
1222
1223 void cm_PutVolume(cm_volume_t *volp)
1224 {
1225     afs_int32 refCount = InterlockedDecrement(&volp->refCount);
1226     osi_assertx(refCount >= 0, "cm_volume_t refCount underflow has occurred");
1227 }
1228
1229 /* return the read-only volume, if there is one, or the read-write volume if
1230  * not.
1231  */
1232 long cm_GetROVolumeID(cm_volume_t *volp)
1233 {
1234     long id;
1235
1236     lock_ObtainRead(&volp->rw);
1237     if (volp->vol[ROVOL].ID && !cm_IsServerListEmpty(volp->vol[ROVOL].serversp))
1238         id = volp->vol[ROVOL].ID;
1239     else
1240         id = volp->vol[RWVOL].ID;
1241     lock_ReleaseRead(&volp->rw);
1242
1243     return id;
1244 }
1245
1246 void cm_RefreshVolumes(int lifetime)
1247 {
1248     cm_volume_t *volp;
1249     afs_int32 refCount;
1250     time_t now;
1251
1252     now = time(NULL);
1253
1254     /* force mount point target updates */
1255     if (cm_data.mountRootGen + lifetime <= now)
1256         cm_data.mountRootGen = now;
1257
1258     /*
1259      * force a re-loading of volume data from the vldb
1260      * if the lifetime for the cached data has expired
1261      */
1262     lock_ObtainRead(&cm_volumeLock);
1263     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1264         cm_GetVolume(volp);
1265         lock_ReleaseRead(&cm_volumeLock);
1266
1267         if (!(volp->flags & CM_VOLUMEFLAG_RESET) ||
1268             (volp->flags & CM_VOLUMEFLAG_NOEXIST)) {
1269             lock_ObtainWrite(&volp->rw);
1270             if (volp->flags & CM_VOLUMEFLAG_NOEXIST) {
1271                 _InterlockedAnd(&volp->flags, ~CM_VOLUMEFLAG_NOEXIST);
1272             }
1273
1274             if (volp->flags & CM_VOLUMEFLAG_RO_MIXED) {
1275                 if (volp->lastUpdateTime + 300 <= now) {
1276                     _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RESET);
1277                     volp->lastUpdateTime = 0;
1278                 }
1279             } else {
1280                 if (volp->lastUpdateTime + lifetime <= now) {
1281                     _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RESET);
1282                     volp->lastUpdateTime = 0;
1283                 }
1284             }
1285             lock_ReleaseWrite(&volp->rw);
1286         }
1287
1288         lock_ObtainRead(&cm_volumeLock);
1289         cm_PutVolume(volp);
1290     }
1291     lock_ReleaseRead(&cm_volumeLock);
1292 }
1293
1294 void
1295 cm_CheckOfflineVolumeState(cm_volume_t *volp, cm_vol_state_t *statep, afs_uint32 volID,
1296                            afs_uint32 *onlinep, afs_uint32 *volumeUpdatedp)
1297 {
1298     cm_conn_t *connp;
1299     long code;
1300     AFSFetchVolumeStatus volStat;
1301     char *Name;
1302     char *OfflineMsg;
1303     char *MOTD;
1304     cm_req_t req;
1305     struct rx_connection * rxconnp;
1306     char volName[32];
1307     afs_uint32 volType;
1308     char offLineMsg[256];
1309     char motd[256];
1310     long alldown, alldeleted;
1311     cm_serverRef_t *serversp;
1312     cm_fid_t vfid;
1313     cm_scache_t *vscp = NULL;
1314
1315     Name = volName;
1316     OfflineMsg = offLineMsg;
1317     MOTD = motd;
1318
1319     volType = cm_VolumeType(volp, volID);
1320
1321     if (statep->ID != 0 && (!volID || volID == statep->ID)) {
1322         /* create fid for volume root so that VNOVOL and VMOVED errors can be processed */
1323         cm_SetFid(&vfid, volp->cellp->cellID, statep->ID, 1, 1);
1324
1325         if (!statep->serversp && !(*volumeUpdatedp)) {
1326             cm_InitReq(&req);
1327             code = cm_UpdateVolumeLocation(volp->cellp, cm_rootUserp, &req, volp);
1328             *volumeUpdatedp = 1;
1329         }
1330
1331         lock_ObtainRead(&cm_serverLock);
1332         if (statep->serversp) {
1333             alldown = 1;
1334             alldeleted = 1;
1335             for (serversp = statep->serversp; serversp; serversp = serversp->next) {
1336                 if (serversp->status == srv_deleted)
1337                     continue;
1338
1339                 alldeleted = 0;
1340
1341                 if (!(serversp->server->flags & CM_SERVERFLAG_DOWN))
1342                     alldown = 0;
1343
1344                 if (serversp->status == srv_busy || serversp->status == srv_offline)
1345                     serversp->status = srv_not_busy;
1346             }
1347             lock_ReleaseRead(&cm_serverLock);
1348
1349             if (alldeleted && !(*volumeUpdatedp)) {
1350                 cm_InitReq(&req);
1351                 code = cm_UpdateVolumeLocation(volp->cellp, cm_rootUserp, &req, volp);
1352                 *volumeUpdatedp = 1;
1353             }
1354
1355             if (statep->state == vl_busy || statep->state == vl_offline || statep->state == vl_unknown ||
1356                 (!alldown && statep->state == vl_alldown)) {
1357                 cm_InitReq(&req);
1358                 req.flags |= CM_REQ_OFFLINE_VOL_CHK;
1359                 lock_ReleaseWrite(&volp->rw);
1360
1361                 code = cm_GetSCache(&vfid, NULL, &vscp, cm_rootUserp, &req);
1362                 if (code = 0) {
1363                     do {
1364                         code = cm_ConnFromVolume(volp, statep->ID, cm_rootUserp, &req, &connp);
1365                         if (code)
1366                            continue;
1367
1368                         rxconnp = cm_GetRxConn(connp);
1369                         code = RXAFS_GetVolumeStatus(rxconnp, statep->ID,
1370                                                      &volStat, &Name, &OfflineMsg, &MOTD);
1371                         rx_PutConnection(rxconnp);
1372                     } while (cm_Analyze(connp, cm_rootUserp, &req, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
1373                     code = cm_MapRPCError(code, &req);
1374
1375                     if (code == 0 && volType == ROVOL)
1376                     {
1377                         lock_ObtainWrite(&volp->rw);
1378                         volp->volumeSizeRO = volStat.BlocksInUse * 1024;
1379                         _InterlockedOr(&volp->flags, CM_VOLUMEFLAG_RO_SIZE_VALID);
1380                         lock_ReleaseWrite(&volp->rw);
1381                     }
1382
1383                     cm_ReleaseSCache(vscp);
1384                 }
1385                 lock_ObtainWrite(&volp->rw);
1386                 if (code == 0 && volStat.Online) {
1387                     cm_VolumeStatusNotification(volp, statep->ID, statep->state, vl_online);
1388                     statep->state = vl_online;
1389                     *onlinep = 1;
1390                 } else if (code == CM_ERROR_NOACCESS) {
1391                     cm_VolumeStatusNotification(volp, statep->ID, statep->state, vl_unknown);
1392                     statep->state = vl_unknown;
1393                     *onlinep = 1;
1394                 }
1395             } else if (alldown && statep->state != vl_alldown) {
1396                 cm_VolumeStatusNotification(volp, statep->ID, statep->state, vl_alldown);
1397                 statep->state = vl_alldown;
1398             }
1399         } else {
1400             lock_ReleaseRead(&cm_serverLock);
1401             if (statep->state != vl_alldown) {
1402                 cm_VolumeStatusNotification(volp, statep->ID, statep->state, vl_alldown);
1403                 statep->state = vl_alldown;
1404             }
1405         }
1406     }
1407 }
1408
1409 /* The return code is 0 if the volume is not online and
1410  * 1 if the volume is online
1411  */
1412 long
1413 cm_CheckOfflineVolume(cm_volume_t *volp, afs_uint32 volID)
1414 {
1415     long code;
1416     cm_req_t req;
1417     afs_uint32 online = 0;
1418     afs_uint32 volumeUpdated = 0;
1419
1420     lock_ObtainWrite(&volp->rw);
1421
1422     if (volp->flags & CM_VOLUMEFLAG_RESET) {
1423         cm_InitReq(&req);
1424         code = cm_UpdateVolumeLocation(volp->cellp, cm_rootUserp, &req, volp);
1425         volumeUpdated = 1;
1426     }
1427
1428     cm_CheckOfflineVolumeState(volp, &volp->vol[RWVOL], volID, &online, &volumeUpdated);
1429     cm_CheckOfflineVolumeState(volp, &volp->vol[ROVOL], volID, &online, &volumeUpdated);
1430     cm_CheckOfflineVolumeState(volp, &volp->vol[BACKVOL], volID, &online, &volumeUpdated);
1431
1432     lock_ReleaseWrite(&volp->rw);
1433     return online;
1434 }
1435
1436
1437 /*
1438  * called from the Daemon thread.
1439  * when checking the offline status, check those of the most recently used volumes first.
1440  */
1441 void cm_CheckOfflineVolumes(void)
1442 {
1443     cm_volume_t *volp;
1444     afs_int32 refCount;
1445     extern int daemon_ShutdownFlag;
1446     extern int powerStateSuspended;
1447
1448     lock_ObtainRead(&cm_volumeLock);
1449     for (volp = cm_data.volumeLRULastp;
1450          volp && !daemon_ShutdownFlag && !powerStateSuspended;
1451          volp=(cm_volume_t *) osi_QPrev(&volp->q)) {
1452         /*
1453          * Skip volume entries that did not exist last time
1454          * the vldb was queried.  For those entries wait until
1455          * the next actual request is received for the volume
1456          * before checking its state.
1457          */
1458         if ((volp->qflags & CM_VOLUME_QFLAG_IN_HASH) &&
1459             !(volp->flags & CM_VOLUMEFLAG_NOEXIST)) {
1460             cm_GetVolume(volp);
1461             lock_ReleaseRead(&cm_volumeLock);
1462             cm_CheckOfflineVolume(volp, 0);
1463             lock_ObtainRead(&cm_volumeLock);
1464             cm_PutVolume(volp);
1465         }
1466     }
1467     lock_ReleaseRead(&cm_volumeLock);
1468 }
1469
1470
1471 static void
1472 cm_UpdateVolumeStatusInt(cm_volume_t *volp, struct cm_vol_state *statep)
1473 {
1474     enum volstatus newStatus;
1475     cm_serverRef_t *tsrp;
1476     cm_server_t *tsp;
1477     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
1478     char addr[16];
1479
1480     if (!volp || !statep) {
1481 #ifdef DEBUG
1482         DebugBreak();
1483 #endif
1484         return;
1485     }
1486
1487     lock_ObtainWrite(&cm_serverLock);
1488     for (tsrp = statep->serversp; tsrp; tsrp=tsrp->next) {
1489         tsp = tsrp->server;
1490         sprintf(addr, "%d.%d.%d.%d",
1491                  ((tsp->addr.sin_addr.s_addr & 0xff)),
1492                  ((tsp->addr.sin_addr.s_addr & 0xff00)>> 8),
1493                  ((tsp->addr.sin_addr.s_addr & 0xff0000)>> 16),
1494                  ((tsp->addr.sin_addr.s_addr & 0xff000000)>> 24));
1495
1496         if (tsrp->status == srv_deleted) {
1497             osi_Log2(afsd_logp, "cm_UpdateVolumeStatusInt volume %d server reference %s deleted",
1498                      statep->ID, osi_LogSaveString(afsd_logp,addr));
1499             continue;
1500         }
1501         if (tsp) {
1502             cm_GetServerNoLock(tsp);
1503             if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
1504                 allDown = 0;
1505                 if (tsrp->status == srv_busy) {
1506                     osi_Log2(afsd_logp, "cm_UpdateVolumeStatusInt volume %d server reference %s busy",
1507                               statep->ID, osi_LogSaveString(afsd_logp,addr));
1508                     allOffline = 0;
1509                     someBusy = 1;
1510                 } else if (tsrp->status == srv_offline) {
1511                     osi_Log2(afsd_logp, "cm_UpdateVolumeStatusInt volume %d server reference %s offline",
1512                               statep->ID, osi_LogSaveString(afsd_logp,addr));
1513                     allBusy = 0;
1514                     someOffline = 1;
1515                 } else {
1516                     osi_Log2(afsd_logp, "cm_UpdateVolumeStatusInt volume %d server reference %s online",
1517                               statep->ID, osi_LogSaveString(afsd_logp,addr));
1518                     allOffline = 0;
1519                     allBusy = 0;
1520                 }
1521             } else {
1522                 osi_Log2(afsd_logp, "cm_UpdateVolumeStatusInt volume %d server reference %s down",
1523                           statep->ID, osi_LogSaveString(afsd_logp,addr));
1524             }
1525             cm_PutServerNoLock(tsp);
1526         }
1527     }
1528     lock_ReleaseWrite(&cm_serverLock);
1529
1530     osi_Log5(afsd_logp, "cm_UpdateVolumeStatusInt allDown %d allBusy %d someBusy %d someOffline %d allOffline %d",
1531              allDown, allBusy, someBusy, someOffline, allOffline);
1532
1533     if (allDown)
1534         newStatus = vl_alldown;
1535     else if (allBusy || (someBusy && someOffline))
1536         newStatus = vl_busy;
1537     else if (allOffline)
1538         newStatus = vl_offline;
1539     else
1540         newStatus = vl_online;
1541
1542     if (statep->ID && statep->state != newStatus)
1543         cm_VolumeStatusNotification(volp, statep->ID, statep->state, newStatus);
1544
1545     statep->state = newStatus;
1546 }
1547
1548 void
1549 cm_UpdateVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1550 {
1551
1552     if (volp->vol[RWVOL].ID == volID) {
1553         cm_UpdateVolumeStatusInt(volp, &volp->vol[RWVOL]);
1554     } else if (volp->vol[ROVOL].ID == volID) {
1555         cm_UpdateVolumeStatusInt(volp, &volp->vol[ROVOL]);
1556     } else if (volp->vol[BACKVOL].ID == volID) {
1557         cm_UpdateVolumeStatusInt(volp, &volp->vol[BACKVOL]);
1558     } else {
1559         /*
1560          * If we are called with volID == 0 then something has gone wrong.
1561          * Most likely a race occurred in the server volume list maintenance.
1562          * Since we don't know which volume's status should be updated,
1563          * just update all of them that are known to exist.  Better to be
1564          * correct than fast.
1565          */
1566         afs_uint32 volType;
1567         for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) {
1568             if (volp->vol[volType].ID != 0)
1569                 cm_UpdateVolumeStatusInt(volp, &volp->vol[volType]);
1570         }
1571     }
1572 }
1573
1574 /*
1575 ** Finds all volumes that reside on this server and reorders their
1576 ** RO list according to the changed rank of server.
1577 */
1578 void cm_ChangeRankVolume(cm_server_t *tsp)
1579 {
1580     int                 code;
1581     cm_volume_t*        volp;
1582     afs_int32 refCount;
1583
1584     /* find volumes which might have RO copy on server*/
1585     lock_ObtainRead(&cm_volumeLock);
1586     for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1587     {
1588         code = 1 ;      /* assume that list is unchanged */
1589         cm_GetVolume(volp);
1590         lock_ReleaseRead(&cm_volumeLock);
1591         lock_ObtainWrite(&volp->rw);
1592
1593         if ((tsp->cellp==volp->cellp) && (volp->vol[ROVOL].serversp))
1594             code =cm_ChangeRankServer(&volp->vol[ROVOL].serversp, tsp);
1595
1596         /* this volume list was changed */
1597         if ( !code )
1598             cm_RandomizeServer(&volp->vol[ROVOL].serversp);
1599
1600         lock_ReleaseWrite(&volp->rw);
1601         lock_ObtainRead(&cm_volumeLock);
1602         cm_PutVolume(volp);
1603     }
1604     lock_ReleaseRead(&cm_volumeLock);
1605 }
1606
1607 /* dump all volumes that have reference count > 0 to a file.
1608  * cookie is used to identify this batch for easy parsing,
1609  * and it a string provided by a caller
1610  */
1611 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
1612 {
1613     int zilch;
1614     cm_volume_t *volp;
1615     char output[1024];
1616
1617     if (lock) {
1618         lock_ObtainRead(&cm_scacheLock);
1619         lock_ObtainRead(&cm_volumeLock);
1620     }
1621
1622     sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\r\n",
1623             cookie, cm_data.currentVolumes, cm_data.maxVolumes);
1624     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1625
1626     for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1627     {
1628         time_t t;
1629         char *srvStr = NULL;
1630         afs_uint32 srvStrRpc = TRUE;
1631         char *cbt = NULL;
1632         char *cdrot = NULL;
1633
1634         if (volp->cbServerpRO) {
1635             if (!((volp->cbServerpRO->flags & CM_SERVERFLAG_UUID) &&
1636                 UuidToString((UUID *)&volp->cbServerpRO->uuid, &srvStr) == RPC_S_OK)) {
1637                 srvStr = malloc(16);
1638                 if (srvStr != NULL)
1639                     afs_inet_ntoa_r(volp->cbServerpRO->addr.sin_addr.s_addr, srvStr);
1640                 srvStrRpc = FALSE;
1641             }
1642         }
1643         if (volp->cbExpiresRO) {
1644             t = volp->cbExpiresRO;
1645             cbt = ctime(&t);
1646             if (cbt) {
1647                 cbt = strdup(cbt);
1648                 cbt[strlen(cbt)-1] = '\0';
1649             }
1650         }
1651         if (volp->creationDateRO) {
1652             t = volp->creationDateRO;
1653             cdrot = ctime(&t);
1654             if (cdrot) {
1655                 cdrot = strdup(cdrot);
1656                 cdrot[strlen(cdrot)-1] = '\0';
1657             }
1658         }
1659
1660         sprintf(output,
1661                 "%s - volp=0x%p cell=%s name=%s rwID=%u roID=%u bkID=%u flags=0x%x:%x "
1662                 "cbServerpRO='%s' cbExpiresRO='%s' creationDateRO='%s' refCount=%u\r\n",
1663                  cookie, volp, volp->cellp->name, volp->namep, volp->vol[RWVOL].ID,
1664                  volp->vol[ROVOL].ID, volp->vol[BACKVOL].ID, volp->flags, volp->qflags,
1665                  srvStr ? srvStr : "<none>", cbt ? cbt : "<none>", cdrot ? cdrot : "<none>",
1666                  volp->refCount);
1667         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1668
1669         if (srvStr) {
1670             if (srvStrRpc)
1671                 RpcStringFree(&srvStr);
1672             else
1673                 free(srvStr);
1674         }
1675         if (cbt)
1676             free(cbt);
1677         if (cdrot)
1678             free(cdrot);
1679     }
1680     sprintf(output, "%s - Done dumping volumes.\r\n", cookie);
1681     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1682
1683     if (lock) {
1684         lock_ReleaseRead(&cm_volumeLock);
1685         lock_ReleaseRead(&cm_scacheLock);
1686     }
1687     return (0);
1688 }
1689
1690
1691 /*
1692  * String hash function used by SDBM project.
1693  * It was chosen because it is fast and provides
1694  * decent coverage.
1695  */
1696 afs_uint32 SDBMHash(const char * str)
1697 {
1698     afs_uint32 hash = 0;
1699     size_t i, len;
1700
1701     if (str == NULL)
1702         return 0;
1703
1704     for(i = 0, len = strlen(str); i < len; i++)
1705     {
1706         hash = str[i] + (hash << 6) + (hash << 16) - hash;
1707     }
1708
1709     return (hash & 0x7FFFFFFF);
1710 }
1711
1712 /* call with volume write-locked and mutex held */
1713 void cm_AddVolumeToNameHashTable(cm_volume_t *volp)
1714 {
1715     int i;
1716
1717     if (volp->qflags & CM_VOLUME_QFLAG_IN_HASH)
1718         return;
1719
1720     i = CM_VOLUME_NAME_HASH(volp->namep);
1721
1722     volp->nameNextp = cm_data.volumeNameHashTablep[i];
1723     cm_data.volumeNameHashTablep[i] = volp;
1724     _InterlockedOr(&volp->qflags, CM_VOLUME_QFLAG_IN_HASH);
1725 }
1726
1727 /* call with volume write-locked and mutex held */
1728 void cm_RemoveVolumeFromNameHashTable(cm_volume_t *volp)
1729 {
1730     cm_volume_t **lvolpp;
1731     cm_volume_t *tvolp;
1732     int i;
1733
1734     if (volp->qflags & CM_VOLUME_QFLAG_IN_HASH) {
1735         /* hash it out first */
1736         i = CM_VOLUME_NAME_HASH(volp->namep);
1737         for (lvolpp = &cm_data.volumeNameHashTablep[i], tvolp = cm_data.volumeNameHashTablep[i];
1738              tvolp;
1739              lvolpp = &tvolp->nameNextp, tvolp = tvolp->nameNextp) {
1740             if (tvolp == volp) {
1741                 *lvolpp = volp->nameNextp;
1742                 _InterlockedAnd(&volp->qflags, ~CM_VOLUME_QFLAG_IN_HASH);
1743                 volp->nameNextp = NULL;
1744                 break;
1745             }
1746         }
1747     }
1748 }
1749
1750 /* call with volume write-locked and mutex held */
1751 void cm_AddVolumeToIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1752 {
1753     int i;
1754     struct cm_vol_state * statep;
1755
1756     statep = cm_VolumeStateByType(volp, volType);
1757
1758     if (statep->qflags & CM_VOLUME_QFLAG_IN_HASH)
1759         return;
1760
1761     i = CM_VOLUME_ID_HASH(statep->ID);
1762
1763     switch (volType) {
1764     case RWVOL:
1765         statep->nextp = cm_data.volumeRWIDHashTablep[i];
1766         cm_data.volumeRWIDHashTablep[i] = volp;
1767         break;
1768     case ROVOL:
1769         statep->nextp = cm_data.volumeROIDHashTablep[i];
1770         cm_data.volumeROIDHashTablep[i] = volp;
1771         break;
1772     case BACKVOL:
1773         statep->nextp = cm_data.volumeBKIDHashTablep[i];
1774         cm_data.volumeBKIDHashTablep[i] = volp;
1775         break;
1776     }
1777     _InterlockedOr(&statep->qflags, CM_VOLUME_QFLAG_IN_HASH);
1778 }
1779
1780
1781 /* call with volume write-locked and mutex held */
1782 void cm_RemoveVolumeFromIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1783 {
1784     cm_volume_t **lvolpp;
1785     cm_volume_t *tvolp;
1786     struct cm_vol_state * statep;
1787     int i;
1788
1789     statep = cm_VolumeStateByType(volp, volType);
1790
1791     if (statep->qflags & CM_VOLUME_QFLAG_IN_HASH) {
1792         /* hash it out first */
1793         i = CM_VOLUME_ID_HASH(statep->ID);
1794
1795         switch (volType) {
1796         case RWVOL:
1797             lvolpp = &cm_data.volumeRWIDHashTablep[i];
1798             tvolp = cm_data.volumeRWIDHashTablep[i];
1799             break;
1800         case ROVOL:
1801             lvolpp = &cm_data.volumeROIDHashTablep[i];
1802             tvolp = cm_data.volumeROIDHashTablep[i];
1803             break;
1804         case BACKVOL:
1805             lvolpp = &cm_data.volumeBKIDHashTablep[i];
1806             tvolp = cm_data.volumeBKIDHashTablep[i];
1807             break;
1808         default:
1809             osi_assertx(0, "invalid volume type");
1810         }
1811         do {
1812             if (tvolp == volp) {
1813                 *lvolpp = statep->nextp;
1814                 _InterlockedAnd(&statep->qflags, ~CM_VOLUME_QFLAG_IN_HASH);
1815                 statep->nextp = NULL;
1816                 break;
1817             }
1818
1819             lvolpp = &tvolp->vol[volType].nextp;
1820             tvolp = tvolp->vol[volType].nextp;
1821         } while(tvolp);
1822     }
1823 }
1824
1825 /* must be called with cm_volumeLock write-locked! */
1826 void cm_AdjustVolumeLRU(cm_volume_t *volp)
1827 {
1828     lock_AssertWrite(&cm_volumeLock);
1829
1830     if (volp == cm_data.volumeLRUFirstp)
1831         return;
1832
1833     if (volp->qflags & CM_VOLUME_QFLAG_IN_LRU_QUEUE)
1834         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1835     osi_QAddH((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1836     _InterlockedOr(&volp->qflags, CM_VOLUME_QFLAG_IN_LRU_QUEUE);
1837
1838     osi_assertx(cm_data.volumeLRULastp != NULL, "null cm_data.volumeLRULastp");
1839 }
1840
1841 /* must be called with cm_volumeLock write-locked! */
1842 void cm_MoveVolumeToLRULast(cm_volume_t *volp)
1843 {
1844     lock_AssertWrite(&cm_volumeLock);
1845
1846     if (volp == cm_data.volumeLRULastp)
1847         return;
1848
1849     if (volp->qflags & CM_VOLUME_QFLAG_IN_LRU_QUEUE)
1850         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1851     osi_QAddT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1852     _InterlockedOr(&volp->qflags, CM_VOLUME_QFLAG_IN_LRU_QUEUE);
1853
1854     osi_assertx(cm_data.volumeLRULastp != NULL, "null cm_data.volumeLRULastp");
1855 }
1856
1857 /* must be called with cm_volumeLock write-locked! */
1858 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1859 {
1860     lock_AssertWrite(&cm_volumeLock);
1861
1862     if (volp->qflags & CM_VOLUME_QFLAG_IN_LRU_QUEUE) {
1863         osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1864         _InterlockedAnd(&volp->qflags, ~CM_VOLUME_QFLAG_IN_LRU_QUEUE);
1865     }
1866
1867     osi_assertx(cm_data.volumeLRULastp != NULL, "null cm_data.volumeLRULastp");
1868 }
1869
1870 static char * volstatus_str(enum volstatus vs)
1871 {
1872     switch (vs) {
1873     case vl_online:
1874         return "online";
1875     case vl_busy:
1876         return "busy";
1877     case vl_offline:
1878         return "offline";
1879     case vl_alldown:
1880         return "alldown";
1881     default:
1882         return "unknown";
1883     }
1884 }
1885
1886 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1887 {
1888     char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1889     char *ext = "";
1890
1891     if (volID == volp->vol[RWVOL].ID)
1892         ext = "";
1893     else if (volID == volp->vol[ROVOL].ID)
1894         ext = ".readonly";
1895     else if (volID == volp->vol[BACKVOL].ID)
1896         ext = ".backup";
1897     else
1898         ext = ".nomatch";
1899     snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1900
1901     osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1902              osi_LogSaveString(afsd_logp, volstr), volID, volstatus_str(old), volstatus_str(new));
1903
1904     cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);
1905 }
1906
1907 enum volstatus cm_GetVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1908 {
1909     cm_vol_state_t * statep = cm_VolumeStateByID(volp, volID);
1910     if (statep)
1911         return statep->state;
1912     else
1913         return vl_unknown;
1914 }
1915
1916 /* Renew .readonly volume callbacks that are more than
1917  * 30 minutes old.  (A volume callback is issued for 2 hours.)
1918  */
1919 void
1920 cm_VolumeRenewROCallbacks(void)
1921 {
1922     cm_volume_t * volp;
1923     time_t minexp = time(NULL) + 90 * 60;
1924     extern int daemon_ShutdownFlag;
1925     extern int powerStateSuspended;
1926
1927     lock_ObtainRead(&cm_volumeLock);
1928     for (volp = cm_data.allVolumesp;
1929          volp && !daemon_ShutdownFlag && !powerStateSuspended;
1930          volp=volp->allNextp) {
1931         if ( volp->cbExpiresRO > 0 && volp->cbExpiresRO < minexp) {
1932             cm_req_t      req;
1933             cm_fid_t      fid;
1934             cm_scache_t * scp;
1935
1936             cm_SetFid(&fid, volp->cellp->cellID, volp->vol[ROVOL].ID, 1, 1);
1937
1938             cm_InitReq(&req);
1939
1940             lock_ReleaseRead(&cm_volumeLock);
1941             if (cm_GetSCache(&fid, NULL, &scp, cm_rootUserp, &req) == 0) {
1942                 lock_ObtainWrite(&scp->rw);
1943                 cm_GetCallback(scp, cm_rootUserp, &req, 1);
1944                 lock_ReleaseWrite(&scp->rw);
1945                 cm_ReleaseSCache(scp);
1946             }
1947             lock_ObtainRead(&cm_volumeLock);
1948         }
1949     }
1950     lock_ReleaseRead(&cm_volumeLock);
1951 }
1952
1953 cm_vol_state_t *
1954 cm_VolumeStateByType(cm_volume_t *volp, afs_uint32 volType)
1955 {
1956     return &volp->vol[volType];
1957 }
1958
1959 cm_vol_state_t *
1960 cm_VolumeStateByID(cm_volume_t *volp, afs_uint32 id)
1961 {
1962     cm_vol_state_t * statep = NULL;
1963
1964     if (id == volp->vol[RWVOL].ID)
1965         statep = &volp->vol[RWVOL];
1966     else if (id == volp->vol[ROVOL].ID)
1967         statep = &volp->vol[ROVOL];
1968     else if (id == volp->vol[BACKVOL].ID)
1969         statep = &volp->vol[BACKVOL];
1970
1971     return(statep);
1972 }
1973
1974 cm_vol_state_t *
1975 cm_VolumeStateByName(cm_volume_t *volp, char *volname)
1976 {
1977     size_t len = strlen(volname);
1978     cm_vol_state_t *statep;
1979
1980     if (cm_stricmp_utf8N(".readonly", &volname[len-9]) == 0)
1981         statep = &volp->vol[ROVOL];
1982     else if (cm_stricmp_utf8N(".backup", &volname[len-7]) == 0)
1983         statep = &volp->vol[BACKVOL];
1984     else
1985         statep = &volp->vol[RWVOL];
1986
1987     return statep;
1988 }
1989
1990 afs_int32
1991 cm_VolumeType(cm_volume_t *volp, afs_uint32 id)
1992 {
1993     if (id == volp->vol[RWVOL].ID)
1994         return(RWVOL);
1995     else if (id == volp->vol[ROVOL].ID)
1996         return(ROVOL);
1997     else if (id == volp->vol[BACKVOL].ID)
1998         return (BACKVOL);
1999
2000     return -1;
2001 }
2002
2003 LONG_PTR
2004 cm_ChecksumVolumeServerList(struct cm_fid *fidp, cm_user_t *userp, cm_req_t *reqp)
2005 {
2006     LONG_PTR cksum = 0;
2007     long code;
2008     afs_uint32 replicated;
2009     cm_serverRef_t **serverspp;
2010
2011     code = cm_GetServerList(fidp, userp, reqp, &replicated, &serverspp);
2012     if (code == 0) {
2013         cksum = cm_ChecksumServerList(*serverspp);
2014         cm_FreeServerList(serverspp, 0);
2015     }
2016     return cksum;
2017 }
2018
2019 afs_int32
2020 cm_IsVolumeReplicated(cm_fid_t *fidp)
2021 {
2022     afs_int32 replicated = 0;
2023     cm_volume_t *volp;
2024     cm_vol_state_t * volstatep;
2025
2026     volp = cm_GetVolumeByFID(fidp);
2027     if (volp) {
2028         volstatep = cm_VolumeStateByID(volp, fidp->volume);
2029         replicated = (volstatep->flags & CM_VOL_STATE_FLAG_REPLICATED);
2030         cm_PutVolume(volp);
2031     }
2032
2033     return replicated;
2034 }