windows-misc-20050102
[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  *
44  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:38    (JHutz)
45  *    Yes, we support multihomed fileservers.
46  *    Since before we got the code from IBM.
47  *    But to find out about multiple addresses on a multihomed server, you need
48  *    to use VL_GetEntryByNameU and VL_GetAddrsU.  If you use
49  *    VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
50  *    address per server.
51  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:39    (JHutz)
52  *    see src/afs/afs_volume.c, paying particular attention to
53  *    afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
54  *  shadow / openafs / jaltman {ANDREW.CMU.EDU}  01:40    (Jeffrey Altman)
55  *    thanks.  The windows client calls the 0 versions.
56  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:51    (JHutz)
57  *    Oh.  Ew.
58  *    By not using the N versions, you only get up to 8 sites instead of 13.
59  *    By not using the U versions, you don't get to know about multihomed serve
60  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:52    (JHutz)
61  *    Of course, you probably want to support the older versions for backward
62  *    compatibility.  If you do that, you need to call the newest interface
63  *    first, and fall back to successively older versions if you get
64  *    RXGEN_OPCODE.
65  */
66 #define MULTIHOMED 1
67 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
68         cm_volume_t *volp)
69 {
70     cm_conn_t *connp;
71     int i, j, k;
72     cm_serverRef_t *tsrp;
73     cm_server_t *tsp;
74     struct sockaddr_in tsockAddr;
75     long tflags;
76     u_long tempAddr;
77     struct vldbentry vldbEntry;
78     struct nvldbentry nvldbEntry;
79 #ifdef MULTIHOMED
80     struct uvldbentry uvldbEntry;
81 #endif
82     int type = -1;
83     int ROcount = 0;
84     long code;
85
86     /* clear out old bindings */
87     cm_FreeServerList(&volp->rwServersp);
88     cm_FreeServerList(&volp->roServersp);
89     cm_FreeServerList(&volp->bkServersp);
90
91     /* now we have volume structure locked and held; make RPC to fill it */
92     do {
93         code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
94         if (code) 
95             continue;
96         osi_Log1(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s", volp->namep);
97 #ifdef MULTIHOMED
98         code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry);
99                 type = 2;
100         if ( code == RXGEN_OPCODE ) 
101 #endif
102         {
103             code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry);
104             type = 1;
105         }
106         if ( code == RXGEN_OPCODE ) {
107             code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
108             type = 0;
109         }
110     } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
111     code = cm_MapVLRPCError(code, reqp);
112
113     if (code == 0) {
114         afs_int32 flags;
115         afs_int32 nServers;
116         afs_int32 rwID;
117         afs_int32 roID;
118         afs_int32 bkID;
119         afs_int32 serverNumber[NMAXNSERVERS];
120         afs_int32 serverFlags[NMAXNSERVERS];
121
122         switch ( type ) {
123         case 0:
124             flags = vldbEntry.flags;
125             nServers = vldbEntry.nServers;
126             rwID = vldbEntry.volumeId[0];
127             roID = vldbEntry.volumeId[1];
128             bkID = vldbEntry.volumeId[2];
129             for ( i=0; i<nServers; i++ ) {
130                 serverFlags[i] = vldbEntry.serverFlags[i];
131                 serverNumber[i] = vldbEntry.serverNumber[i];
132             }
133             break;
134         case 1:
135             flags = nvldbEntry.flags;
136             nServers = nvldbEntry.nServers;
137             rwID = nvldbEntry.volumeId[0];
138             roID = nvldbEntry.volumeId[1];
139             bkID = nvldbEntry.volumeId[2];
140             for ( i=0; i<nServers; i++ ) {
141                 serverFlags[i] = nvldbEntry.serverFlags[i];
142                 serverNumber[i] = nvldbEntry.serverNumber[i];
143             }
144             break;
145 #ifdef MULTIHOMED
146         case 2:
147             flags = uvldbEntry.flags;
148             nServers = uvldbEntry.nServers;
149             rwID = uvldbEntry.volumeId[0];
150             roID = uvldbEntry.volumeId[1];
151             bkID = uvldbEntry.volumeId[2];
152             for ( i=0, j=0; i<nServers && j<NMAXNSERVERS; i++ ) {
153                 if ( !(uvldbEntry.serverFlags[i] & VLSERVER_FLAG_UUID) ) {
154                     serverFlags[j] = uvldbEntry.serverFlags[i];
155                     serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
156                                         j++;
157                 } else {
158                     afs_uint32 * addrp, nentries, code, unique;
159                     bulkaddrs  addrs;
160                     ListAddrByAttributes attrs;
161                     afsUUID uuid;
162
163                     memset((char *)&attrs, 0, sizeof(attrs));
164                     attrs.Mask = VLADDR_UUID;
165                     attrs.uuid = uvldbEntry.serverNumber[i];
166                     memset((char *)&uuid, 0, sizeof(uuid));
167                     memset((char *)&addrs, 0, sizeof(addrs));
168
169                     do {
170                         code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
171                         if (code) 
172                             continue;
173                    
174                         code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs);
175
176                         if (code == 0 && nentries == 0)
177                             code = VL_NOENT;
178                     } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
179                     code = cm_MapVLRPCError(code, reqp);
180                     if (code)
181                         return code;
182
183                     addrp = addrs.bulkaddrs_val;
184                     for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) {
185                         serverFlags[j] = uvldbEntry.serverFlags[i];
186                         serverNumber[j] = addrp[k];
187                     }
188
189                     free(addrs.bulkaddrs_val);  /* This is wrong */
190                 }
191             }
192                         nServers = j;                                   /* update the server count */
193             break;
194 #endif
195         }
196
197         /* decode the response */
198         lock_ObtainWrite(&cm_volumeLock);
199         if (flags & VLF_RWEXISTS)
200             volp->rwID = rwID;
201         else
202             volp->rwID = 0;
203         if (flags & VLF_ROEXISTS)
204             volp->roID = roID;
205         else
206             volp->roID = 0;
207         if (flags & VLF_BACKEXISTS)
208             volp->bkID = bkID;
209         else
210             volp->bkID = 0;
211         lock_ReleaseWrite(&cm_volumeLock);
212         for (i=0; i<nServers; i++) {
213             /* create a server entry */
214             tflags = serverFlags[i];
215             if (tflags & VLSF_DONTUSE) 
216                 continue;
217             tsockAddr.sin_family = AF_INET;
218             tempAddr = htonl(serverNumber[i]);
219             tsockAddr.sin_addr.s_addr = tempAddr;
220             tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
221             if (!tsp)
222                 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
223                                     cellp);
224
225             /* if this server was created by fs setserverprefs */
226             if ( !tsp->cellp ) 
227                 tsp->cellp = cellp;
228
229             osi_assert(tsp != NULL);
230                         
231             /* and add it to the list(s). */
232             /*
233              * Each call to cm_NewServerRef() increments the
234              * ref count of tsp.  These reference will be dropped,
235              * if and when the volume is reset; see reset code
236              * earlier in this function.
237              */
238             if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
239                 tsrp = cm_NewServerRef(tsp);
240                 cm_InsertServerList(&volp->rwServersp, tsrp);
241                 lock_ObtainWrite(&cm_serverLock);
242                 tsrp->refCount--;       /* drop allocation reference */
243                 lock_ReleaseWrite(&cm_serverLock);
244             }
245             if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) {
246                 tsrp = cm_NewServerRef(tsp);
247                 cm_InsertServerList(&volp->roServersp, tsrp);
248                 lock_ObtainWrite(&cm_serverLock);
249                 tsrp->refCount--;       /* drop allocation reference */
250                 lock_ReleaseWrite(&cm_serverLock);
251                 ROcount++;
252             }
253             /* We don't use VLSF_BACKVOL !?! */
254             if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
255                 tsrp = cm_NewServerRef(tsp);
256                 cm_InsertServerList(&volp->bkServersp, tsrp);
257                 lock_ObtainWrite(&cm_serverLock);
258                 tsrp->refCount--;       /* drop allocation reference */
259                 lock_ReleaseWrite(&cm_serverLock);
260             }
261             /* Drop the reference obtained by cm_FindServer() */
262             cm_PutServer(tsp);
263         }       
264
265         /*
266          * Randomize RO list
267          *
268          * If the first n servers have the same ipRank, then we 
269          * randomly pick one among them and move it to the beginning.
270          * We don't bother to re-order the whole list because
271          * the rest of the list is used only if the first server is
272          * down.  We only do this for the RO list; we assume the other
273          * lists are length 1.
274          */
275         if (ROcount > 1) {
276             cm_RandomizeServer(&volp->roServersp);
277         }
278     }
279     return code;
280 }
281
282 long cm_GetVolumeByID(cm_cell_t *cellp, long volumeID, cm_user_t *userp,
283         cm_req_t *reqp, cm_volume_t **outVolpp)
284 {
285         cm_volume_t *volp;
286         char volNameString[100];
287         long code;
288
289         lock_ObtainWrite(&cm_volumeLock);
290         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
291                 if (cellp == volp->cellp &&
292                         ((unsigned) volumeID == volp->rwID ||
293                          (unsigned) volumeID == volp->roID ||
294                          (unsigned) volumeID == volp->bkID))
295                                 break;
296         }
297
298         /* hold the volume if we found it */
299     if (volp) 
300         volp->refCount++;
301         lock_ReleaseWrite(&cm_volumeLock);
302         
303         /* return it held */
304         if (volp) {
305                 lock_ObtainMutex(&volp->mx);
306         
307                 if (volp->flags & CM_VOLUMEFLAG_RESET) {
308                         code = cm_UpdateVolume(cellp, userp, reqp, volp);
309                         if (code == 0) {
310                                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
311                         }
312                 }
313                 else
314                         code = 0;
315                 lock_ReleaseMutex(&volp->mx);
316                 if (code == 0)
317                         *outVolpp = volp;
318                 return code;
319         }
320         
321         /* otherwise, we didn't find it so consult the VLDB */
322         sprintf(volNameString, "%u", volumeID);
323         code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
324                                   0, outVolpp);
325         return code;
326 }
327
328 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
329         struct cm_user *userp, struct cm_req *reqp,
330         long flags, cm_volume_t **outVolpp)
331 {
332         cm_volume_t *volp;
333         long code;
334         
335         /* initialize this */
336         code = 0;
337
338         lock_ObtainWrite(&cm_volumeLock);
339         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
340                 if (cellp == volp->cellp && strcmp(volumeNamep, volp->namep) == 0) {
341                         break;
342                 }
343         }
344         
345         /* otherwise, get from VLDB */
346         if (!volp) {
347                 volp = malloc(sizeof(*volp));
348                 memset(volp, 0, sizeof(*volp));
349                 volp->cellp = cellp;
350                 volp->nextp = cm_allVolumesp;
351                 cm_allVolumesp = volp;
352                 volp->namep = malloc(strlen(volumeNamep)+1);
353                 strcpy(volp->namep, volumeNamep);
354                 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
355                 volp->refCount = 1;     /* starts off held */
356                 volp->flags |= CM_VOLUMEFLAG_RESET;
357         }
358         else {
359                 volp->refCount++;
360         }
361         
362         /* next should work since no one could have gotten ptr to this structure yet */
363         lock_ReleaseWrite(&cm_volumeLock);
364         lock_ObtainMutex(&volp->mx);
365         
366         if (volp->flags & CM_VOLUMEFLAG_RESET) {
367                 code = cm_UpdateVolume(cellp, userp, reqp, volp);
368                 if (code == 0)
369                         volp->flags &= ~CM_VOLUMEFLAG_RESET;
370         }
371
372         if (code == 0)
373                 *outVolpp = volp;
374         lock_ReleaseMutex(&volp->mx);
375         return code;
376 }
377
378 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
379 {
380         cm_cell_t *cellp;
381         cm_volume_t *volp;
382         long code;
383
384         if (!fidp) return;
385
386         cellp = cm_FindCellByID(fidp->cell);
387         if (!cellp) return;
388
389         /* search for the volume */
390         lock_ObtainWrite(&cm_volumeLock);
391         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
392                 if (cellp == volp->cellp &&
393                         (fidp->volume == volp->rwID ||
394                          fidp->volume == volp->roID ||
395                          fidp->volume == volp->bkID))
396                                 break;
397         }
398
399         /* hold the volume if we found it */
400         if (volp) volp->refCount++;
401         lock_ReleaseWrite(&cm_volumeLock);
402
403         /* update it */
404         cm_mountRootGen++;
405         lock_ObtainMutex(&volp->mx);
406         volp->flags |= CM_VOLUMEFLAG_RESET;
407 #ifdef COMMENT
408     /* Mark the volume to be updated but don't update it now.
409      * This function is called only from within cm_Analyze
410      * when cm_ConnByMServers has failed with all servers down
411      * The problem is that cm_UpdateVolume is going to call
412      * cm_ConnByMServers which may cause a recursive chain
413      * of calls each returning a retry on failure.
414      * Instead, set the flag so the next time the volume is
415      * accessed by Name or ID the UpdateVolume call will
416      * occur.
417      */
418         code = cm_UpdateVolume(cellp, userp, reqp, volp);
419         if (code == 0)
420                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
421 #endif
422         lock_ReleaseMutex(&volp->mx);
423
424         cm_PutVolume(volp);
425 }
426
427 /* find the appropriate servers from a volume */
428 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, unsigned long volume)
429 {
430         cm_serverRef_t **serverspp;
431     cm_serverRef_t *current;;
432
433     lock_ObtainWrite(&cm_serverLock);
434
435         if (volume == volp->rwID)
436         serverspp = &volp->rwServersp;
437         else if (volume == volp->roID)
438         serverspp = &volp->roServersp;
439         else if (volume == volp->bkID)
440         serverspp = &volp->bkServersp;
441     else 
442         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
443         
444     for (current = *serverspp; current; current = current->next)
445         current->refCount++;
446
447     lock_ReleaseWrite(&cm_serverLock);
448
449     return serverspp;
450 }
451
452 void cm_PutVolume(cm_volume_t *volp)
453 {
454         lock_ObtainWrite(&cm_volumeLock);
455         osi_assert(volp->refCount-- > 0);
456         lock_ReleaseWrite(&cm_volumeLock);
457 }
458
459 /* return the read-only volume, if there is one, or the read-write volume if
460  * not.
461  */
462 long cm_GetROVolumeID(cm_volume_t *volp)
463 {
464         long id;
465
466         lock_ObtainMutex(&volp->mx);
467         if (volp->roID && volp->roServersp)
468                 id = volp->roID;
469         else
470                 id = volp->rwID;
471         lock_ReleaseMutex(&volp->mx);
472
473         return id;
474 }
475
476 void cm_CheckVolumes(void)
477 {
478         cm_volume_t *volp;
479
480         cm_mountRootGen++;
481         lock_ObtainWrite(&cm_volumeLock);
482         for(volp = cm_allVolumesp; volp; volp=volp->nextp) {
483                 volp->refCount++;
484                 lock_ReleaseWrite(&cm_volumeLock);
485                 lock_ObtainMutex(&volp->mx);
486
487                 volp->flags |= CM_VOLUMEFLAG_RESET;
488
489                 lock_ReleaseMutex(&volp->mx);
490                 lock_ObtainWrite(&cm_volumeLock);
491                 osi_assert(volp->refCount-- > 0);
492         }
493         lock_ReleaseWrite(&cm_volumeLock);
494
495         /* We should also refresh cached mount points */
496 }
497
498 /*
499 ** Finds all volumes that reside on this server and reorders their
500 ** RO list according to the changed rank of server.
501 */
502 void cm_ChangeRankVolume(cm_server_t       *tsp)
503 {
504         int             code;
505         cm_volume_t*    volp;
506
507         /* find volumes which might have RO copy on server*/
508         lock_ObtainWrite(&cm_volumeLock);
509         for(volp = cm_allVolumesp; volp; volp=volp->nextp)
510         {
511                 code = 1 ;      /* assume that list is unchanged */
512                 volp->refCount++;
513                 lock_ReleaseWrite(&cm_volumeLock);
514                 lock_ObtainMutex(&volp->mx);
515
516                 if ((tsp->cellp==volp->cellp) && (volp->roServersp))
517                     code =cm_ChangeRankServer(&volp->roServersp, tsp);
518
519                 /* this volume list was changed */
520                 if ( !code )
521                         cm_RandomizeServer(&volp->roServersp);
522
523                 lock_ReleaseMutex(&volp->mx);
524                 lock_ObtainWrite(&cm_volumeLock);
525                 osi_assert(volp->refCount-- > 0);
526         }
527         lock_ReleaseWrite(&cm_volumeLock);
528 }