split-dcache-fixes-20050604
[openafs.git] / src / afs / afs_nfsclnt.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
13 RCSID
14     ("$Header$");
15
16 #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV)
17 #include "afs/sysincludes.h"    /* Standard vendor system headers */
18 #include "afsincludes.h"        /* Afs-based standard headers */
19 #include "afs/afs_stats.h"      /* statistics */
20 #include "afs/nfsclient.h"
21
22 int afs_nfsclient_reqhandler(), afs_nfsclient_hold(), afs_PutNfsClientPag();
23 int afs_nfsclient_sysname(), afs_nfsclient_GC(), afs_nfsclient_stats();
24 #ifdef AFS_AIX_IAUTH_ENV
25 int afs_allnfsreqs, afs_nfscalls;
26 #endif
27
28 /* routines exported to the "AFS exporter" layer */
29 struct exporterops nfs_exportops = {
30     afs_nfsclient_reqhandler,
31     afs_nfsclient_hold,
32     afs_PutNfsClientPag,        /* Used to be afs_nfsclient_rele */
33     afs_nfsclient_sysname,
34     afs_nfsclient_GC,
35     afs_nfsclient_stats,
36 };
37
38
39 struct nfsclientpag *afs_nfspags[NNFSCLIENTS];
40 afs_lock_t afs_xnfspag /*, afs_xnfsreq */ ;
41 extern struct afs_exporter *afs_nfsexporter;
42
43 /* Creates an nfsclientpag structure for the (uid, host) pair if one doesn't exist. RefCount is incremented and it's time stamped. */
44 static struct nfsclientpag *
45 afs_GetNfsClientPag(uid, host)
46      register afs_int32 uid, host;
47 {
48     register struct nfsclientpag *np;
49     register afs_int32 i, now;
50
51 #if defined(AFS_SGIMP_ENV)
52     osi_Assert(ISAFS_GLOCK());
53 #endif
54     AFS_STATCNT(afs_GetNfsClientPag);
55     i = NHash(host);
56     now = osi_Time();
57     MObtainWriteLock(&afs_xnfspag, 314);
58     for (np = afs_nfspags[i]; np; np = np->next) {
59         if (np->uid == uid && np->host == host) {
60             np->refCount++;
61             np->lastcall = now;
62             MReleaseWriteLock(&afs_xnfspag);
63             return np;
64         }
65     }
66     /* next try looking for NOPAG dude, if we didn't find an exact match */
67     for (np = afs_nfspags[i]; np; np = np->next) {
68         if (np->uid == NOPAG && np->host == host) {
69             np->refCount++;
70             np->lastcall = now;
71             MReleaseWriteLock(&afs_xnfspag);
72             return np;
73         }
74     }
75     np = (struct nfsclientpag *)afs_osi_Alloc(sizeof(struct nfsclientpag));
76     memset((char *)np, 0, sizeof(struct nfsclientpag));
77     /* Copy the necessary afs_exporter fields */
78     memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
79     np->next = afs_nfspags[i];
80     afs_nfspags[i] = np;
81     np->uid = uid;
82     np->host = host;
83     np->refCount = 1;
84     np->lastcall = now;
85     MReleaseWriteLock(&afs_xnfspag);
86     return np;
87 }
88
89
90 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
91 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
92 int
93 afs_PutNfsClientPag(np)
94      register struct nfsclientpag *np;
95 {
96 #if defined(AFS_SGIMP_ENV)
97     osi_Assert(ISAFS_GLOCK());
98 #endif
99     AFS_STATCNT(afs_PutNfsClientPag);
100     --np->refCount;
101 }
102
103
104 /* Return the nfsclientpag structure associated with the (uid, host) or {pag, host} pair, if pag is nonzero. RefCount is incremented and it's time stamped. */
105 static struct nfsclientpag *
106 afs_FindNfsClientPag(uid, host, pag)
107      register afs_int32 uid, host, pag;
108 {
109     register struct nfsclientpag *np;
110     register afs_int32 i;
111
112 #if defined(AFS_SGIMP_ENV)
113     osi_Assert(ISAFS_GLOCK());
114 #endif
115     AFS_STATCNT(afs_FindNfsClientPag);
116     i = NHash(host);
117     MObtainWriteLock(&afs_xnfspag, 315);
118     for (np = afs_nfspags[i]; np; np = np->next) {
119         if (np->host == host) {
120             if ((pag && pag == np->pag) || (!pag && (uid == np->uid))) {
121                 np->refCount++;
122                 np->lastcall = osi_Time();
123                 MReleaseWriteLock(&afs_xnfspag);
124                 return np;
125             }
126         }
127     }
128     /* still not there, try looking for a wildcard dude */
129     for (np = afs_nfspags[i]; np; np = np->next) {
130         if (np->host == host) {
131             if (np->uid == NOPAG) {
132                 np->refCount++;
133                 np->lastcall = osi_Time();
134                 MReleaseWriteLock(&afs_xnfspag);
135                 return np;
136             }
137         }
138     }
139     MReleaseWriteLock(&afs_xnfspag);
140     return NULL;
141 }
142
143
144 /* routine to initialize the exporter, made global so we can call it
145  * from pioctl calls.
146  */
147 struct afs_exporter *afs_nfsexported = 0;
148 static afs_int32 init_nfsexporter = 0;
149 afs_nfsclient_init()
150 {
151 #if defined(AFS_SGIMP_ENV)
152     osi_Assert(ISAFS_GLOCK());
153 #endif
154     if (!init_nfsexporter) {
155         extern struct afs_exporter *exporter_add();
156
157         init_nfsexporter = 1;
158         LOCK_INIT(&afs_xnfspag, "afs_xnfspag");
159         afs_nfsexported =
160             exporter_add(0, &nfs_exportops, EXP_EXPORTED, EXP_NFS, NULL);
161     }
162 }
163
164
165 /* Main handler routine for the NFS exporter. It's called in the early
166  * phases of any remote call (via the NFS server or pioctl).
167  */
168 int
169 afs_nfsclient_reqhandler(exporter, cred, host, pagparam, outexporter)
170      register struct afs_exporter *exporter, **outexporter;
171      struct AFS_UCRED **cred;
172      register afs_int32 host;
173      afs_int32 *pagparam;
174 {
175     register struct nfsclientpag *np, *tnp;
176     extern struct unixuser *afs_FindUser(), *afs_GetUser();
177     register struct unixuser *au = 0;
178     afs_int32 pag, code = 0;
179
180     AFS_ASSERT_GLOCK();
181     AFS_STATCNT(afs_nfsclient_reqhandler);
182     if (!afs_nfsexporter)
183         afs_nfsexporter = afs_nfsexported;
184
185     afs_nfsexporter->exp_stats.calls++;
186     if (!(afs_nfsexporter->exp_states & EXP_EXPORTED)) {
187         /* No afs requests accepted as long as EXPORTED flag is turned 'off'. Set/Reset via a pioctl call (fs exportafs). Note that this is on top of the /etc/exports nfs requirement (i.e. /afs must be exported to all or whomever there too!)
188          */
189         afs_nfsexporter->exp_stats.rejectedcalls++;
190         return EINVAL;
191     }
192 /*    ObtainWriteLock(&afs_xnfsreq); */
193     pag = PagInCred(*cred);
194     if (pag != NOPAG) {
195         /* Do some minimal pag verification */
196         if (pag > getpag()) {
197             pag = NOPAG;        /* treat it as not paged since couldn't be good  */
198         } else {
199             if (au = afs_FindUser(pag, -1, READ_LOCK)) {
200                 if (!au->exporter) {
201                     pag = NOPAG;
202                     afs_PutUser(au, READ_LOCK);
203                     au = NULL;
204                 }
205             } else
206                 pag = NOPAG;    /*  No unixuser struct so pag not trusted  */
207         }
208     }
209     np = afs_FindNfsClientPag((*cred)->cr_uid, host, 0);
210     afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
211                ICL_TYPE_LONG, (*cred)->cr_uid, ICL_TYPE_INT32, host,
212                ICL_TYPE_POINTER, np);
213     if (!np) {
214         /* Even if there is a "good" pag coming in we don't accept it if no nfsclientpag struct exists for the user since that would mean that the translator rebooted and therefore we ignore all older pag values */
215 #ifdef  AFS_OSF_ENV
216         if (code = setpag(u.u_procp, cred, -1, &pag, 1)) {      /* XXX u.u_procp is a no-op XXX */
217 #else
218         if (code = setpag(cred, -1, &pag, 1)) {
219 #endif
220             if (au)
221                 afs_PutUser(au, READ_LOCK);
222 /*          ReleaseWriteLock(&afs_xnfsreq);             */
223 #if defined(KERNEL_HAVE_UERROR)
224             setuerror(code);
225 #endif
226             return (code);
227         }
228         np = afs_GetNfsClientPag((*cred)->cr_uid, host);
229         np->pag = pag;
230     } else {
231         if (pag == NOPAG) {
232 #ifdef  AFS_OSF_ENV
233             if (code = setpag(u.u_procp, cred, np->pag, &pag, 1)) {     /* XXX u.u_procp is a no-op XXX */
234 #else
235             if (code = setpag(cred, np->pag, &pag, 1)) {
236 #endif
237                 afs_PutNfsClientPag(np);
238 /*              ReleaseWriteLock(&afs_xnfsreq); */
239 #if defined(KERNEL_HAVE_UERROR)
240                 setuerror(code);
241 #endif
242                 return (code);
243             }
244         } else if (au->exporter
245                    && ((struct afs_exporter *)np != au->exporter)) {
246             tnp = (struct nfsclientpag *)au->exporter;
247             if (tnp->uid && (tnp->uid != (afs_int32) - 2)) {    /* allow "root" initiators */
248                 /* Pag doesn't belong to caller; treat it as an unpaged call too */
249 #ifdef  AFS_OSF_ENV
250                 if (code = setpag(u.u_procp, cred, np->pag, &pag, 1)) { /* XXX u.u_procp is a no-op XXX */
251 #else
252                 if (code = setpag(cred, np->pag, &pag, 1)) {
253 #endif
254                     afs_PutNfsClientPag(np);
255                     afs_PutUser(au, READ_LOCK);
256                     /*      ReleaseWriteLock(&afs_xnfsreq);     */
257 #if defined(KERNEL_HAVE_UERROR)
258                     setuerror(code);
259 #endif
260                     return (code);
261                 }
262                 afs_nfsexporter->exp_stats.invalidpag++;
263             }
264         }
265     }
266     if (au)
267         afs_PutUser(au, READ_LOCK);
268     au = afs_GetUser(pag, -1, WRITE_LOCK);
269     if (!(au->exporter)) {      /* Created new unixuser struct */
270         np->refCount++;         /* so it won't disappear */
271         au->exporter = (struct afs_exporter *)np;
272     }
273     *pagparam = pag;
274     *outexporter = (struct afs_exporter *)np;
275     afs_PutUser(au, WRITE_LOCK);
276 #ifdef  AFS_OSF_ENV
277     (*cred)->cr_ruid = NFSXLATOR_CRED;  /* Identify it as nfs xlator call */
278 #else
279     (*cred)->cr_rgid = NFSXLATOR_CRED;  /* Identify it as nfs xlator call */
280 #endif
281 /*    ReleaseWriteLock(&afs_xnfsreq);   */
282     return 0;
283 }
284
285
286 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
287 int
288 afs_nfsclient_hold(np)
289      register struct nfsclientpag *np;
290 {
291 #if defined(AFS_SGIMP_ENV)
292     osi_Assert(ISAFS_GLOCK());
293 #endif
294     AFS_STATCNT(afs_nfsclient_hold);
295     np->refCount++;
296 }
297
298
299 /* if inname is non-null, a new system name value is set for the remote user (inname contains the new sysname). In all cases, outname returns the current sysname value for this remote user */
300 int 
301 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname, 
302                       char **outname[], int *num)
303 {
304     char *cp;
305     int count, t;
306 #if defined(AFS_SGIMP_ENV)
307     osi_Assert(ISAFS_GLOCK());
308 #endif
309     AFS_STATCNT(afs_nfsclient_sysname);
310     if (inname) {
311         if (np->sysname) {
312             for(count=0; count < np->sysnamecount;++count) {
313                 afs_osi_Free(np->sysname[count], MAXSYSNAME);
314             }
315         }
316         for(count=0; count < *num;++count) {
317             np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
318         }
319         cp = inname;
320         for(count=0; count < *num;++count) {
321             t = strlen(cp);
322             memcpy(np->sysname[count], cp, t+1); /* include null */
323             cp += t+1;
324         }
325         np->sysnamecount = *num;
326     } else if (!np->sysname) {
327         return ENODEV;      /* XXX */
328     }
329     *outname = np->sysname;
330     *num = np->sysnamecount;
331     return 0;
332 }
333
334
335 /* Garbage collect routine for the nfs exporter. When pag is -1 then all entries are removed (used by the nfsclient_shutdown routine); else if it's non zero then only the entry with that pag is removed, else all "timedout" entries are removed. TimedOut entries are those who have no "unixuser" structures associated with them (i.e. unixusercnt == 0) and they haven't had any activity the last NFSCLIENTGC seconds */
336 int
337 afs_nfsclient_GC(exporter, pag)
338      register struct afs_exporter *exporter;
339      register afs_int32 pag;
340 {
341     register struct nfsclientpag *np, **tnp, *nnp;
342     register afs_int32 i, delflag;
343
344 #if defined(AFS_SGIMP_ENV)
345     osi_Assert(ISAFS_GLOCK());
346 #endif
347     AFS_STATCNT(afs_nfsclient_GC);
348     MObtainWriteLock(&afs_xnfspag, 316);
349     for (i = 0; i < NNFSCLIENTS; i++) {
350         for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
351             nnp = np->next;
352             delflag = 0;
353             if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
354                 delflag = 1;
355             if ((pag == -1) || (!pag && delflag)
356                 || (pag && (np->refCount == 0) && (np->pag == pag))) {
357                 *tnp = np->next;
358                 if (np->sysname)
359                     afs_osi_Free(np->sysname, MAXSYSNAME);
360                 afs_osi_Free(np, sizeof(struct nfsclientpag));
361             } else {
362                 tnp = &np->next;
363             }
364         }
365     }
366     MReleaseWriteLock(&afs_xnfspag);
367 }
368
369
370 int
371 afs_nfsclient_stats(export)
372      register struct afs_exporter *export;
373 {
374     /* Nothing much to do here yet since most important stats are collected directly in the afs_exporter structure itself */
375     AFS_STATCNT(afs_nfsclient_stats);
376     return 0;
377 }
378
379 #ifdef AFS_AIX41_ENV
380 /* This is exposed so that vop_fid can test it, even if iauth is not
381  *  installed.
382  */
383 extern int afs_iauth_initd;
384 #endif
385
386 #ifdef AFS_AIX_IAUTH_ENV
387 char *afs_nfs_id = "AFSNFSTRANS";
388 /* afs_iauth_verify is the AFS authenticator for NFS.
389  *
390  * always returns 0.
391  */
392 int
393 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
394                  struct AFS_UCRED *credp, struct exportinfo *exp)
395 {
396     int code;
397     struct nfsclientpag *nfs_pag;
398     afs_int32 dummypag;
399     struct afs_exporter *outexporter = 0;
400
401
402     /* Still needs basic test to see if exporter is on. And need to check the
403      * whole no submounts bit.
404      */
405
406     if (id != (long)id)
407         return 0;               /* not us. */
408
409     /* Only care if it's AFS */
410     if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
411         return 0;
412     }
413
414     AFS_GLOCK();
415     code =
416         afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
417                                  &dummypag, &outexporter);
418     if (!code && outexporter)
419         EXP_RELE(outexporter);
420
421     if (code) {
422         /* ensure anonymous cred. */
423         credp->cr_uid = credp->cr_ruid = (uid_t) - 2;   /* anonymous */
424     }
425
426     /* Mark this thread as an NFS translator thread. */
427     credp->cr_rgid = NFSXLATOR_CRED;
428
429     AFS_GUNLOCK();
430     return 0;
431 }
432
433 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
434  * and -1 on failure. Can fail because DFS has already registered.
435  */
436 int
437 afs_iauth_register()
438 {
439     if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
440         return -1;
441     else {
442         afs_iauth_initd = 1;
443         return 0;
444     }
445 }
446
447 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown. 
448  */
449 void
450 afs_iauth_unregister()
451 {
452     if (afs_iauth_initd)
453         nfs_iauth_unregister((unsigned long)afs_nfs_id);
454     afs_iauth_initd = 0;
455 }
456 #endif /* AFS_AIX_IAUTH_ENV */
457
458
459
460 shutdown_nfsclnt()
461 {
462 #if defined(AFS_SGIMP_ENV)
463     osi_Assert(ISAFS_GLOCK());
464 #endif
465     AFS_STATCNT(afs_nfsclient_shutdown);
466 #ifdef AFS_AIX_IAUTH_ENV
467     afs_iauth_register();
468 #endif
469     afs_nfsclient_GC(afs_nfsexporter, -1);
470     init_nfsexporter = 0;
471 }
472 #endif /* AFS_NONFSTRANS */