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