]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/amd/amd/nfs_subr.c
This commit was generated by cvs2svn to compensate for changes in r133931,
[FreeBSD/FreeBSD.git] / contrib / amd / amd / nfs_subr.c
1 /*
2  * Copyright (c) 1997-2004 Erez Zadok
3  * Copyright (c) 1990 Jan-Simon Pendry
4  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgment:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *      %W% (Berkeley) %G%
40  *
41  * $Id: nfs_subr.c,v 1.6.2.6 2004/01/06 03:15:16 ezk Exp $
42  *
43  */
44
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif /* HAVE_CONFIG_H */
48 #include <am_defs.h>
49 #include <amd.h>
50
51 /*
52  * Convert from UN*X to NFS error code.
53  * Some systems like linux define their own (see
54  * conf/mount/mount_linux.h).
55  */
56 #ifndef nfs_error
57 # define nfs_error(e) ((nfsstat)(e))
58 #endif /* nfs_error */
59
60 /* forward declarations */
61 static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
62
63
64 static char *
65 do_readlink(am_node *mp, int *error_return, nfsattrstat **attrpp)
66 {
67   char *ln;
68
69   /*
70    * If there is a readlink method, then use
71    * that, otherwise if a link exists use
72    * that, otherwise use the mount point.
73    */
74   if (mp->am_mnt->mf_ops->readlink) {
75     int retry = 0;
76     mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry);
77     if (mp == 0) {
78       *error_return = retry;
79       return 0;
80     }
81     /* reschedule_timeout_mp(); */
82   }
83
84   if (mp->am_link) {
85     ln = mp->am_link;
86   } else {
87     ln = mp->am_mnt->mf_mount;
88   }
89   if (attrpp)
90     *attrpp = &mp->am_attr;
91
92   return ln;
93 }
94
95
96 voidp
97 nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
98 {
99   static char res;
100
101   return (voidp) &res;
102 }
103
104
105 nfsattrstat *
106 nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
107 {
108   static nfsattrstat res;
109   am_node *mp;
110   int retry;
111   time_t now = clocktime();
112
113 #ifdef DEBUG
114   amuDebug(D_TRACE)
115     plog(XLOG_DEBUG, "getattr:");
116 #endif /* DEBUG */
117
118   mp = fh_to_mp2(argp, &retry);
119   if (mp == 0) {
120
121 #ifdef DEBUG
122     amuDebug(D_TRACE)
123       plog(XLOG_DEBUG, "\tretry=%d", retry);
124 #endif /* DEBUG */
125
126     if (retry < 0) {
127       amd_stats.d_drops++;
128       return 0;
129     }
130     res.ns_status = nfs_error(retry);
131   } else {
132     nfsattrstat *attrp = &mp->am_attr;
133
134 #ifdef DEBUG
135     amuDebug(D_TRACE)
136       plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld",
137            mp->am_path,
138            (int) attrp->ns_u.ns_attr_u.na_size,
139            (long) attrp->ns_u.ns_attr_u.na_mtime.nt_seconds);
140 #endif /* DEBUG */
141
142     /* Delay unmount of what was looked up */
143     if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
144       mp->am_timeo_w += gopt.am_timeo_w;
145     mp->am_ttl = now + mp->am_timeo_w;
146
147     mp->am_stats.s_getattr++;
148     return attrp;
149   }
150
151   return &res;
152 }
153
154
155 nfsattrstat *
156 nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
157 {
158   static nfsattrstat res;
159
160   if (!fh_to_mp(&argp->sag_fhandle))
161     res.ns_status = nfs_error(ESTALE);
162   else
163     res.ns_status = nfs_error(EROFS);
164
165   return &res;
166 }
167
168
169 voidp
170 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
171 {
172   static char res;
173
174   return (voidp) &res;
175 }
176
177
178 nfsdiropres *
179 nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
180 {
181   static nfsdiropres res;
182   am_node *mp;
183   int retry;
184   uid_t uid;
185   gid_t gid;
186
187 #ifdef DEBUG
188   amuDebug(D_TRACE)
189     plog(XLOG_DEBUG, "lookup:");
190 #endif /* DEBUG */
191
192   /* finally, find the effective uid/gid from RPC request */
193   if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
194     plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
195   sprintf(opt_uid, "%d", (int) uid);
196   sprintf(opt_gid, "%d", (int) gid);
197
198   mp = fh_to_mp2(&argp->da_fhandle, &retry);
199   if (mp == 0) {
200     if (retry < 0) {
201       amd_stats.d_drops++;
202       return 0;
203     }
204     res.dr_status = nfs_error(retry);
205   } else {
206     int error;
207     am_node *ap;
208 #ifdef DEBUG
209     amuDebug(D_TRACE)
210       plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->da_name);
211 #endif /* DEBUG */
212     ap = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &error, VLOOK_CREATE);
213     if (ap == 0) {
214       if (error < 0) {
215         amd_stats.d_drops++;
216         return 0;
217       }
218       res.dr_status = nfs_error(error);
219     } else {
220       /*
221        * XXX: EXPERIMENTAL! Delay unmount of what was looked up.  This
222        * should reduce the chance for race condition between unmounting an
223        * entry synchronously, and re-mounting it asynchronously.
224        */
225       if (ap->am_ttl < mp->am_ttl)
226         ap->am_ttl = mp->am_ttl;
227       mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
228       res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
229       res.dr_status = NFS_OK;
230     }
231     mp->am_stats.s_lookup++;
232     /* reschedule_timeout_mp(); */
233   }
234
235   return &res;
236 }
237
238
239 void
240 quick_reply(am_node *mp, int error)
241 {
242   SVCXPRT *transp = mp->am_transp;
243   nfsdiropres res;
244   xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
245
246   /*
247    * If there's a transp structure then we can reply to the client's
248    * nfs lookup request.
249    */
250   if (transp) {
251     if (error == 0) {
252       /*
253        * Construct a valid reply to a lookup request.  Same
254        * code as in nfsproc_lookup_2_svc() above.
255        */
256       mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
257       res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
258       res.dr_status = NFS_OK;
259     } else
260       /*
261        * Return the error that was passed to us.
262        */
263       res.dr_status = nfs_error(error);
264
265     /*
266      * Send off our reply
267      */
268     if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
269       svcerr_systemerr(transp);
270
271     /*
272      * Free up transp.  It's only used for one reply.
273      */
274     XFREE(transp);
275     mp->am_transp = NULL;
276 #ifdef DEBUG
277     dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
278 #endif /* DEBUG */
279   }
280 }
281
282
283 nfsreadlinkres *
284 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
285 {
286   static nfsreadlinkres res;
287   am_node *mp;
288   int retry;
289
290 #ifdef DEBUG
291   amuDebug(D_TRACE)
292     plog(XLOG_DEBUG, "readlink:");
293 #endif /* DEBUG */
294
295   mp = fh_to_mp2(argp, &retry);
296   if (mp == 0) {
297   readlink_retry:
298     if (retry < 0) {
299       amd_stats.d_drops++;
300       return 0;
301     }
302     res.rlr_status = nfs_error(retry);
303   } else {
304     char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0);
305     if (ln == 0)
306       goto readlink_retry;
307     res.rlr_status = NFS_OK;
308 #ifdef DEBUG
309     amuDebug(D_TRACE)
310       if (ln)
311         plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
312 #endif /* DEBUG */
313     res.rlr_u.rlr_data_u = ln;
314     mp->am_stats.s_readlink++;
315   }
316
317   return &res;
318 }
319
320
321 nfsreadres *
322 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
323 {
324   static nfsreadres res;
325
326   memset((char *) &res, 0, sizeof(res));
327   res.rr_status = nfs_error(EACCES);
328
329   return &res;
330 }
331
332
333 voidp
334 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
335 {
336   static char res;
337
338   return (voidp) &res;
339 }
340
341
342 nfsattrstat *
343 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
344 {
345   static nfsattrstat res;
346
347   if (!fh_to_mp(&argp->wra_fhandle))
348     res.ns_status = nfs_error(ESTALE);
349   else
350     res.ns_status = nfs_error(EROFS);
351
352   return &res;
353 }
354
355
356 nfsdiropres *
357 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
358 {
359   static nfsdiropres res;
360
361   if (!fh_to_mp(&argp->ca_where.da_fhandle))
362     res.dr_status = nfs_error(ESTALE);
363   else
364     res.dr_status = nfs_error(EROFS);
365
366   return &res;
367 }
368
369
370 static nfsstat *
371 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
372 {
373   static nfsstat res;
374   int retry;
375
376   am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
377   if (mp == 0) {
378     if (retry < 0) {
379       amd_stats.d_drops++;
380       return 0;
381     }
382     res = nfs_error(retry);
383     goto out;
384   }
385
386   if (mp->am_fattr.na_type != NFDIR) {
387     res = nfs_error(ENOTDIR);
388     goto out;
389   }
390
391 #ifdef DEBUG
392   amuDebug(D_TRACE)
393     plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
394 #endif /* DEBUG */
395
396   mp = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &retry, VLOOK_DELETE);
397   if (mp == 0) {
398     /*
399      * Ignore retries...
400      */
401     if (retry < 0)
402       retry = 0;
403     /*
404      * Usual NFS workaround...
405      */
406     else if (retry == ENOENT)
407       retry = 0;
408     res = nfs_error(retry);
409   } else {
410     forcibly_timeout_mp(mp);
411     res = NFS_OK;
412   }
413
414 out:
415   return &res;
416 }
417
418
419 nfsstat *
420 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
421 {
422   return unlink_or_rmdir(argp, rqstp, TRUE);
423 }
424
425
426 nfsstat *
427 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
428 {
429   static nfsstat res;
430
431   if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
432     res = nfs_error(ESTALE);
433   /*
434    * If the kernel is doing clever things with referenced files
435    * then let it pretend...
436    */
437   else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
438     res = NFS_OK;
439   /*
440    * otherwise a failure
441    */
442   else
443     res = nfs_error(EROFS);
444
445   return &res;
446 }
447
448
449 nfsstat *
450 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
451 {
452   static nfsstat res;
453
454   if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
455     res = nfs_error(ESTALE);
456   else
457     res = nfs_error(EROFS);
458
459   return &res;
460 }
461
462
463 nfsstat *
464 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
465 {
466   static nfsstat res;
467
468   if (!fh_to_mp(&argp->sla_from.da_fhandle))
469     res = nfs_error(ESTALE);
470   else
471     res = nfs_error(EROFS);
472
473   return &res;
474 }
475
476
477 nfsdiropres *
478 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
479 {
480   static nfsdiropres res;
481
482   if (!fh_to_mp(&argp->ca_where.da_fhandle))
483     res.dr_status = nfs_error(ESTALE);
484   else
485     res.dr_status = nfs_error(EROFS);
486
487   return &res;
488 }
489
490
491 nfsstat *
492 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
493 {
494   return unlink_or_rmdir(argp, rqstp, FALSE);
495 }
496
497
498 nfsreaddirres *
499 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
500 {
501   static nfsreaddirres res;
502   static nfsentry e_res[MAX_READDIR_ENTRIES];
503   am_node *mp;
504   int retry;
505
506 #ifdef DEBUG
507   amuDebug(D_TRACE)
508     plog(XLOG_DEBUG, "readdir:");
509 #endif /* DEBUG */
510
511   mp = fh_to_mp2(&argp->rda_fhandle, &retry);
512   if (mp == 0) {
513     if (retry < 0) {
514       amd_stats.d_drops++;
515       return 0;
516     }
517     res.rdr_status = nfs_error(retry);
518   } else {
519 #ifdef DEBUG
520     amuDebug(D_TRACE)
521       plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
522 #endif /* DEBUG */
523     res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir)
524                            (mp, argp->rda_cookie,
525                             &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
526     mp->am_stats.s_readdir++;
527   }
528
529   return &res;
530 }
531
532
533 nfsstatfsres *
534 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
535 {
536   static nfsstatfsres res;
537   am_node *mp;
538   int retry;
539   mntent_t mnt;
540
541 #ifdef DEBUG
542   amuDebug(D_TRACE)
543     plog(XLOG_DEBUG, "statfs:");
544 #endif /* DEBUG */
545
546   mp = fh_to_mp2(argp, &retry);
547   if (mp == 0) {
548     if (retry < 0) {
549       amd_stats.d_drops++;
550       return 0;
551     }
552     res.sfr_status = nfs_error(retry);
553   } else {
554     nfsstatfsokres *fp;
555 #ifdef DEBUG
556     amuDebug(D_TRACE)
557       plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
558 #endif /* DEBUG */
559
560     /*
561      * just return faked up file system information
562      */
563     fp = &res.sfr_u.sfr_reply_u;
564
565     fp->sfrok_tsize = 1024;
566     fp->sfrok_bsize = 1024;
567
568     /* check if map is browsable and show_statfs_entries=yes  */
569     if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
570         mp->am_mnt && mp->am_mnt->mf_mopts) {
571       mnt.mnt_opts = mp->am_mnt->mf_mopts;
572       if (hasmntopt(&mnt, "browsable")) {
573         count_map_entries(mp,
574                           &fp->sfrok_blocks,
575                           &fp->sfrok_bfree,
576                           &fp->sfrok_bavail);
577       }
578     } else {
579       fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
580       fp->sfrok_bfree = 0;
581       fp->sfrok_bavail = 0;
582     }
583
584     res.sfr_status = NFS_OK;
585     mp->am_stats.s_statfs++;
586   }
587
588   return &res;
589 }
590
591
592 /*
593  * count how many total entries there are in a map, and how many
594  * of them are in use.
595  */
596 static void
597 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
598 {
599   u_int blocks, bfree, bavail, i;
600   mntfs *mf;
601   mnt_map *mmp;
602   kv *k;
603
604   blocks = bfree = bavail = 0;
605   if (!mp)
606     goto out;
607   mf = mp->am_mnt;
608   if (!mf)
609     goto out;
610   mmp = (mnt_map *) mf->mf_private;
611   if (!mmp)
612     goto out;
613
614   /* iterate over keys */
615   for (i = 0; i < NKVHASH; i++) {
616     for (k = mmp->kvhash[i]; k ; k = k->next) {
617       if (!k->key)
618         continue;
619       blocks++;
620       /*
621        * XXX: Need to count how many are actively in use and recompute
622        * bfree and bavail based on it.
623        */
624     }
625   }
626
627 out:
628   *out_blocks = blocks;
629   *out_bfree = bfree;
630   *out_bavail = bavail;
631 }