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