nfs_subr.c revision 82794
1/*
2 * Copyright (c) 1997-2001 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.3 2001/04/14 21:08:22 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 */
61static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
62
63
64static char *
65do_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
96voidp
97nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
98{
99  static char res;
100
101  return (voidp) &res;
102}
103
104
105nfsattrstat *
106nfsproc_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
155nfsattrstat *
156nfsproc_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
169voidp
170nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
171{
172  static char res;
173
174  return (voidp) &res;
175}
176
177
178nfsdiropres *
179nfsproc_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
239void
240quick_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
283nfsreadlinkres *
284nfsproc_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
321nfsreadres *
322nfsproc_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
333voidp
334nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
335{
336  static char res;
337
338  return (voidp) &res;
339}
340
341
342nfsattrstat *
343nfsproc_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
356nfsdiropres *
357nfsproc_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
370static nfsstat *
371unlink_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
414out:
415  return &res;
416}
417
418
419nfsstat *
420nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
421{
422  return unlink_or_rmdir(argp, rqstp, TRUE);
423}
424
425
426nfsstat *
427nfsproc_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
449nfsstat *
450nfsproc_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
463nfsstat *
464nfsproc_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
477nfsdiropres *
478nfsproc_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
491nfsstat *
492nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
493{
494  return unlink_or_rmdir(argp, rqstp, FALSE);
495}
496
497
498nfsreaddirres *
499nfsproc_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
533nfsstatfsres *
534nfsproc_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 */
596static void
597count_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
627out:
628  *out_blocks = blocks;
629  *out_bfree = bfree;
630  *out_bavail = bavail;
631}
632