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