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