post-1-3-70-windows-changes-20040816
[openafs.git] / src / WINNT / afsd / cm_volume.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #include <nb30.h>
17 #else
18 #include <sys/socket.h>
19 #endif /* !DJGPP */
20 #include <string.h>
21 #include <malloc.h>
22 #include <osi.h>
23 #include <rx/rx.h>
24
25 #include "afsd.h"
26
27 osi_rwlock_t cm_volumeLock;
28 cm_volume_t *cm_allVolumesp;
29
30 void cm_InitVolume(void)
31 {
32         static osi_once_t once;
33         if (osi_Once(&once)) {
34                 lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock");
35                 cm_allVolumesp = NULL;
36                 osi_EndOnce(&once);
37         }
38 }
39
40 /*
41  * Update a volume.  Caller holds volume's lock (volp->mx).
42  */
43 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
44         cm_volume_t *volp)
45 {
46     cm_conn_t *connp;
47     int i;
48         cm_serverRef_t *tsrp;
49     cm_server_t *tsp;
50     struct sockaddr_in tsockAddr;
51     long tflags;
52     u_long tempAddr;
53     struct vldbentry vldbEntry; /* don't use NVLDB yet; they're not common */
54         int ROcount = 0;
55         long code;
56
57         /* clear out old bindings */
58     cm_FreeServerList(&volp->rwServersp);
59     cm_FreeServerList(&volp->roServersp);
60     cm_FreeServerList(&volp->bkServersp);
61
62     /* now we have volume structure locked and held; make RPC to fill it */
63     do {
64                 code = cm_ConnByMServers(cellp->vlServersp, userp, reqp,
65                                   &connp);
66         if (code) continue;
67                 osi_Log1(afsd_logp, "CALL VL_GetEntryByNameO name %s",
68                   volp->namep);
69         code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
70         } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
71     code = cm_MapVLRPCError(code, reqp);
72
73     if (code == 0) {
74                 /* decode the response */
75                 lock_ObtainWrite(&cm_volumeLock);
76         if (vldbEntry.flags & VLF_RWEXISTS)
77             volp->rwID = vldbEntry.volumeId[0];
78                 else
79             volp->rwID = 0;
80         if (vldbEntry.flags & VLF_ROEXISTS)
81             volp->roID = vldbEntry.volumeId[1];
82         else
83             volp->roID = 0;
84         if (vldbEntry.flags & VLF_BACKEXISTS)
85             volp->bkID = vldbEntry.volumeId[2];
86                 else
87             volp->bkID = 0;
88                 lock_ReleaseWrite(&cm_volumeLock);
89         for(i=0; i<vldbEntry.nServers; i++) {
90                         /* create a server entry */
91                         tflags = vldbEntry.serverFlags[i];
92                         if (tflags & VLSF_DONTUSE) continue;
93                         tsockAddr.sin_family = AF_INET;
94                         tempAddr = htonl(vldbEntry.serverNumber[i]);
95                         tsockAddr.sin_addr.s_addr = tempAddr;
96                         tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
97                         if (!tsp)
98                 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
99                                     cellp);
100
101                         /* if this server was created by fs setserverprefs */
102                         if ( !tsp->cellp ) 
103                                 tsp->cellp = cellp;
104
105             osi_assert(tsp != NULL);
106                         
107             /* and add it to the list(s). */
108                         /*
109              * Each call to cm_NewServerRef() increments the
110              * ref count of tsp.  These reference will be dropped,
111                          * if and when the volume is reset; see reset code
112                          * earlier in this function.
113                          */
114                         if ((tflags & VLSF_RWVOL)
115                  && (vldbEntry.flags & VLF_RWEXISTS)) {
116                                 tsrp = cm_NewServerRef(tsp);
117                 cm_InsertServerList(&volp->rwServersp, tsrp);
118                 lock_ObtainWrite(&cm_serverLock);
119                 tsrp->refCount--;       /* drop allocation reference */
120                 lock_ReleaseWrite(&cm_serverLock);
121                         }
122             if ((tflags & VLSF_ROVOL)
123                  && (vldbEntry.flags & VLF_ROEXISTS)) {
124                                 tsrp = cm_NewServerRef(tsp);
125                                 cm_InsertServerList(&volp->roServersp, tsrp);
126                 lock_ObtainWrite(&cm_serverLock);
127                 tsrp->refCount--;       /* drop allocation reference */
128                 lock_ReleaseWrite(&cm_serverLock);
129                                 ROcount++;
130             }
131                         /* We don't use VLSF_BACKVOL !?! */
132             if ((tflags & VLSF_RWVOL)
133                  && (vldbEntry.flags & VLF_BACKEXISTS)) {
134                                 tsrp = cm_NewServerRef(tsp);
135                 cm_InsertServerList(&volp->bkServersp, tsrp);
136                 lock_ObtainWrite(&cm_serverLock);
137                 tsrp->refCount--;       /* drop allocation reference */
138                 lock_ReleaseWrite(&cm_serverLock);
139                         }
140                         /* Drop the reference obtained by cm_FindServer() */
141                         cm_PutServer(tsp);
142         }
143
144                 /*
145                  * Randomize RO list
146                  *
147                  * If the first n servers have the same ipRank, then we 
148                  * randomly pick one among them and move it to the beginning.
149                  * We don't bother to re-order the whole list because
150                  * the rest of the list is used only if the first server is
151                  * down.  We only do this for the RO list; we assume the other
152                  * lists are length 1.
153                  */
154                 if (ROcount > 1) {
155                         cm_RandomizeServer(&volp->roServersp);
156                 }
157         }
158         return code;
159 }
160
161 long cm_GetVolumeByID(cm_cell_t *cellp, long volumeID, cm_user_t *userp,
162         cm_req_t *reqp, cm_volume_t **outVolpp)
163 {
164         cm_volume_t *volp;
165         char volNameString[100];
166         long code;
167
168         lock_ObtainWrite(&cm_volumeLock);
169         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
170                 if (cellp == volp->cellp &&
171                         ((unsigned) volumeID == volp->rwID ||
172                          (unsigned) volumeID == volp->roID ||
173                          (unsigned) volumeID == volp->bkID))
174                                 break;
175         }
176
177         /* hold the volume if we found it */
178     if (volp) 
179         volp->refCount++;
180         lock_ReleaseWrite(&cm_volumeLock);
181         
182         /* return it held */
183         if (volp) {
184                 lock_ObtainMutex(&volp->mx);
185         
186                 if (volp->flags & CM_VOLUMEFLAG_RESET) {
187                         code = cm_UpdateVolume(cellp, userp, reqp, volp);
188                         if (code == 0) {
189                                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
190                         }
191                 }
192                 else
193                         code = 0;
194                 lock_ReleaseMutex(&volp->mx);
195                 if (code == 0)
196                         *outVolpp = volp;
197                 return code;
198         }
199         
200         /* otherwise, we didn't find it so consult the VLDB */
201         sprintf(volNameString, "%u", volumeID);
202         code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
203                                   0, outVolpp);
204         return code;
205 }
206
207 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
208         struct cm_user *userp, struct cm_req *reqp,
209         long flags, cm_volume_t **outVolpp)
210 {
211         cm_volume_t *volp;
212         long code;
213         
214         /* initialize this */
215         code = 0;
216
217         lock_ObtainWrite(&cm_volumeLock);
218         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
219                 if (cellp == volp->cellp && strcmp(volumeNamep, volp->namep) == 0) {
220                         break;
221                 }
222         }
223         
224         /* otherwise, get from VLDB */
225         if (!volp) {
226                 volp = malloc(sizeof(*volp));
227                 memset(volp, 0, sizeof(*volp));
228                 volp->cellp = cellp;
229                 volp->nextp = cm_allVolumesp;
230                 cm_allVolumesp = volp;
231                 volp->namep = malloc(strlen(volumeNamep)+1);
232                 strcpy(volp->namep, volumeNamep);
233                 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
234                 volp->refCount = 1;     /* starts off held */
235                 volp->flags |= CM_VOLUMEFLAG_RESET;
236         }
237         else {
238                 volp->refCount++;
239         }
240         
241         /* next should work since no one could have gotten ptr to this structure yet */
242         lock_ReleaseWrite(&cm_volumeLock);
243         lock_ObtainMutex(&volp->mx);
244         
245         if (volp->flags & CM_VOLUMEFLAG_RESET) {
246                 code = cm_UpdateVolume(cellp, userp, reqp, volp);
247                 if (code == 0)
248                         volp->flags &= ~CM_VOLUMEFLAG_RESET;
249         }
250
251         if (code == 0)
252                 *outVolpp = volp;
253         lock_ReleaseMutex(&volp->mx);
254         return code;
255 }
256
257 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
258 {
259         cm_cell_t *cellp;
260         cm_volume_t *volp;
261         long code;
262
263         if (!fidp) return;
264
265         cellp = cm_FindCellByID(fidp->cell);
266         if (!cellp) return;
267
268         /* search for the volume */
269         lock_ObtainWrite(&cm_volumeLock);
270         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
271                 if (cellp == volp->cellp &&
272                         (fidp->volume == volp->rwID ||
273                          fidp->volume == volp->roID ||
274                          fidp->volume == volp->bkID))
275                                 break;
276         }
277
278         /* hold the volume if we found it */
279         if (volp) volp->refCount++;
280         lock_ReleaseWrite(&cm_volumeLock);
281
282         /* update it */
283         cm_mountRootGen++;
284         lock_ObtainMutex(&volp->mx);
285         volp->flags |= CM_VOLUMEFLAG_RESET;
286 #ifdef COMMENT
287     /* Mark the volume to be updated but don't update it now.
288      * This function is called only from within cm_Analyze
289      * when cm_ConnByMServers has failed with all servers down
290      * The problem is that cm_UpdateVolume is going to call
291      * cm_ConnByMServers which may cause a recursive chain
292      * of calls each returning a retry on failure.
293      * Instead, set the flag so the next time the volume is
294      * accessed by Name or ID the UpdateVolume call will
295      * occur.
296      */
297         code = cm_UpdateVolume(cellp, userp, reqp, volp);
298         if (code == 0)
299                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
300 #endif
301         lock_ReleaseMutex(&volp->mx);
302
303         cm_PutVolume(volp);
304 }
305
306 /* find the appropriate servers from a volume */
307 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, unsigned long volume)
308 {
309         cm_serverRef_t **serverspp;
310     cm_serverRef_t *current;;
311
312     lock_ObtainWrite(&cm_serverLock);
313
314         if (volume == volp->rwID)
315         serverspp = &volp->rwServersp;
316         else if (volume == volp->roID)
317         serverspp = &volp->roServersp;
318         else if (volume == volp->bkID)
319         serverspp = &volp->bkServersp;
320     else 
321         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
322         
323     for (current = *serverspp; current; current = current->next)
324         current->refCount++;
325
326     lock_ReleaseWrite(&cm_serverLock);
327
328     return serverspp;
329 }
330
331 void cm_PutVolume(cm_volume_t *volp)
332 {
333         lock_ObtainWrite(&cm_volumeLock);
334         osi_assert(volp->refCount-- > 0);
335         lock_ReleaseWrite(&cm_volumeLock);
336 }
337
338 /* return the read-only volume, if there is one, or the read-write volume if
339  * not.
340  */
341 long cm_GetROVolumeID(cm_volume_t *volp)
342 {
343         long id;
344
345         lock_ObtainMutex(&volp->mx);
346         if (volp->roID && volp->roServersp)
347                 id = volp->roID;
348         else
349                 id = volp->rwID;
350         lock_ReleaseMutex(&volp->mx);
351
352         return id;
353 }
354
355 void cm_CheckVolumes(void)
356 {
357         cm_volume_t *volp;
358
359         cm_mountRootGen++;
360         lock_ObtainWrite(&cm_volumeLock);
361         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
362                 volp->refCount++;
363                 lock_ReleaseWrite(&cm_volumeLock);
364                 lock_ObtainMutex(&volp->mx);
365
366                 volp->flags |= CM_VOLUMEFLAG_RESET;
367
368                 lock_ReleaseMutex(&volp->mx);
369                 lock_ObtainWrite(&cm_volumeLock);
370                 osi_assert(volp->refCount-- > 0);
371         }
372         lock_ReleaseWrite(&cm_volumeLock);
373
374         /* We should also refresh cached mount points */
375 }
376
377 /*
378 ** Finds all volumes that reside on this server and reorders their
379 ** RO list according to the changed rank of server.
380 */
381 void cm_ChangeRankVolume(cm_server_t       *tsp)
382 {
383         int             code;
384         cm_volume_t*    volp;
385
386         /* find volumes which might have RO copy on server*/
387         lock_ObtainWrite(&cm_volumeLock);
388         for(volp = cm_allVolumesp; volp; volp=volp->nextp)
389         {
390                 code = 1 ;      /* assume that list is unchanged */
391                 volp->refCount++;
392                 lock_ReleaseWrite(&cm_volumeLock);
393                 lock_ObtainMutex(&volp->mx);
394
395                 if ((tsp->cellp==volp->cellp) && (volp->roServersp))
396                     code =cm_ChangeRankServer(&volp->roServersp, tsp);
397
398                 /* this volume list was changed */
399                 if ( !code )
400                         cm_RandomizeServer(&volp->roServersp);
401
402                 lock_ReleaseMutex(&volp->mx);
403                 lock_ObtainWrite(&cm_volumeLock);
404                 osi_assert(volp->refCount-- > 0);
405         }
406         lock_ReleaseWrite(&cm_volumeLock);
407 }