nfs_subr.c revision 38494
1/*
2 * Copyright (c) 1997-1998 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 acknowledgement:
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 5.2.2.1 1992/02/09 15:08:53 jsp beta $
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
112#ifdef DEBUG
113  amuDebug(D_TRACE)
114    plog(XLOG_DEBUG, "getattr:");
115#endif /* DEBUG */
116
117  mp = fh_to_mp2(argp, &retry);
118  if (mp == 0) {
119
120#ifdef DEBUG
121    amuDebug(D_TRACE)
122      plog(XLOG_DEBUG, "\tretry=%d", retry);
123#endif /* DEBUG */
124
125    if (retry < 0)
126      return 0;
127    res.ns_status = nfs_error(retry);
128  } else {
129    nfsattrstat *attrp = &mp->am_attr;
130
131#ifdef DEBUG
132    amuDebug(D_TRACE)
133      plog(XLOG_DEBUG, "\tstat(%s), size = %d", mp->am_path,
134	   attrp->ns_u.ns_attr_u.na_size);
135#endif /* DEBUG */
136
137    mp->am_stats.s_getattr++;
138    return attrp;
139  }
140
141#ifndef MNT2_NFS_OPT_SYMTTL
142  /*
143   * This code is needed to defeat Solaris 2.4's (and newer) symlink values
144   * cache.  It forces the last-modifed time of the symlink to be current.
145   * It is not needed if the O/S has an nfs flag to turn off the
146   * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez.
147   */
148  if (++res.ns_u.ns_attr_u.na_mtime.nt_useconds == 0)
149    ++res.ns_u.ns_attr_u.na_mtime.nt_seconds;
150#endif /* not MNT2_NFS_OPT_SYMTTL */
151
152  return &res;
153}
154
155
156nfsattrstat *
157nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
158{
159  static nfsattrstat res;
160
161  if (!fh_to_mp(&argp->sag_fhandle))
162    res.ns_status = nfs_error(ESTALE);
163  else
164    res.ns_status = nfs_error(EROFS);
165
166  return &res;
167}
168
169
170voidp
171nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
172{
173  static char res;
174
175  return (voidp) &res;
176}
177
178
179nfsdiropres *
180nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
181{
182  static nfsdiropres res;
183  am_node *mp;
184  int retry;
185
186#ifdef DEBUG
187  amuDebug(D_TRACE)
188    plog(XLOG_DEBUG, "lookup:");
189#endif /* DEBUG */
190
191  mp = fh_to_mp2(&argp->da_fhandle, &retry);
192  if (mp == 0) {
193    if (retry < 0)
194      return 0;
195    res.dr_status = nfs_error(retry);
196  } else {
197    int error;
198    am_node *ap;
199#ifdef DEBUG
200    amuDebug(D_TRACE)
201      plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->da_name);
202#endif /* DEBUG */
203    ap = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &error, VLOOK_CREATE);
204    if (ap == 0) {
205      if (error < 0) {
206#ifdef DEBUG
207	dlog("Not sending RPC reply");
208#endif /* DEBUG */
209	amd_stats.d_drops++;
210	return 0;
211      }
212      res.dr_status = nfs_error(error);
213    } else {
214      mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
215      res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
216      res.dr_status = NFS_OK;
217    }
218    mp->am_stats.s_lookup++;
219    /* reschedule_timeout_mp(); */
220  }
221
222  return &res;
223}
224
225
226void
227quick_reply(am_node *mp, int error)
228{
229  SVCXPRT *transp = mp->am_transp;
230  nfsdiropres res;
231  xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
232
233  /*
234   * If there's a transp structure then we can reply to the client's
235   * nfs lookup request.
236   */
237  if (transp) {
238    if (error == 0) {
239      /*
240       * Construct a valid reply to a lookup request.  Same
241       * code as in nfsproc_lookup_2_svc() above.
242       */
243      mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
244      res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
245      res.dr_status = NFS_OK;
246    } else
247      /*
248       * Return the error that was passed to us.
249       */
250      res.dr_status = nfs_error(error);
251
252    /*
253     * Send off our reply
254     */
255    if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
256      svcerr_systemerr(transp);
257
258    /*
259     * Free up transp.  It's only used for one reply.
260     */
261    XFREE(transp);
262    mp->am_transp = NULL;
263#ifdef DEBUG
264    dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
265#endif /* DEBUG */
266  }
267}
268
269
270nfsreadlinkres *
271nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
272{
273  static nfsreadlinkres res;
274  am_node *mp;
275  int retry;
276
277#ifdef DEBUG
278  amuDebug(D_TRACE)
279    plog(XLOG_DEBUG, "readlink:");
280#endif /* DEBUG */
281
282  mp = fh_to_mp2(argp, &retry);
283  if (mp == 0) {
284  readlink_retry:
285    if (retry < 0)
286      return 0;
287    res.rlr_status = nfs_error(retry);
288  } else {
289    char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0);
290    if (ln == 0)
291      goto readlink_retry;
292    res.rlr_status = NFS_OK;
293#ifdef DEBUG
294    amuDebug(D_TRACE)
295      if (ln)
296	plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
297#endif /* DEBUG */
298    res.rlr_u.rlr_data_u = ln;
299    mp->am_stats.s_readlink++;
300  }
301
302  return &res;
303}
304
305
306nfsreadres *
307nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
308{
309  static nfsreadres res;
310
311  memset((char *) &res, 0, sizeof(res));
312  res.rr_status = nfs_error(EACCES);
313
314  return &res;
315}
316
317
318voidp
319nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
320{
321  static char res;
322
323  return (voidp) &res;
324}
325
326
327nfsattrstat *
328nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
329{
330  static nfsattrstat res;
331
332  if (!fh_to_mp(&argp->wra_fhandle))
333    res.ns_status = nfs_error(ESTALE);
334  else
335    res.ns_status = nfs_error(EROFS);
336
337  return &res;
338}
339
340
341nfsdiropres *
342nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
343{
344  static nfsdiropres res;
345
346  if (!fh_to_mp(&argp->ca_where.da_fhandle))
347    res.dr_status = nfs_error(ESTALE);
348  else
349    res.dr_status = nfs_error(EROFS);
350
351  return &res;
352}
353
354
355static nfsstat *
356unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
357{
358  static nfsstat res;
359  int retry;
360
361  am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
362  if (mp == 0) {
363    if (retry < 0)
364      return 0;
365    res = nfs_error(retry);
366    goto out;
367  }
368
369  if (mp->am_fattr.na_type != NFDIR) {
370    res = nfs_error(ENOTDIR);
371    goto out;
372  }
373
374#ifdef DEBUG
375  amuDebug(D_TRACE)
376    plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
377#endif /* DEBUG */
378
379  mp = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &retry, VLOOK_DELETE);
380  if (mp == 0) {
381    /*
382     * Ignore retries...
383     */
384    if (retry < 0)
385      retry = 0;
386    /*
387     * Usual NFS workaround...
388     */
389    else if (retry == ENOENT)
390      retry = 0;
391    res = nfs_error(retry);
392  } else {
393    forcibly_timeout_mp(mp);
394    res = NFS_OK;
395  }
396
397out:
398  return &res;
399}
400
401
402nfsstat *
403nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
404{
405  return unlink_or_rmdir(argp, rqstp, TRUE);
406}
407
408
409nfsstat *
410nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
411{
412  static nfsstat res;
413
414  if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
415    res = nfs_error(ESTALE);
416  /*
417   * If the kernel is doing clever things with referenced files
418   * then let it pretend...
419   */
420  else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
421    res = NFS_OK;
422  /*
423   * otherwise a failure
424   */
425  else
426    res = nfs_error(EROFS);
427
428  return &res;
429}
430
431
432nfsstat *
433nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
434{
435  static nfsstat res;
436
437  if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
438    res = nfs_error(ESTALE);
439  else
440    res = nfs_error(EROFS);
441
442  return &res;
443}
444
445
446nfsstat *
447nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
448{
449  static nfsstat res;
450
451  if (!fh_to_mp(&argp->sla_from.da_fhandle))
452    res = nfs_error(ESTALE);
453  else
454    res = nfs_error(EROFS);
455
456  return &res;
457}
458
459
460nfsdiropres *
461nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
462{
463  static nfsdiropres res;
464
465  if (!fh_to_mp(&argp->ca_where.da_fhandle))
466    res.dr_status = nfs_error(ESTALE);
467  else
468    res.dr_status = nfs_error(EROFS);
469
470  return &res;
471}
472
473
474nfsstat *
475nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
476{
477  return unlink_or_rmdir(argp, rqstp, FALSE);
478}
479
480
481nfsreaddirres *
482nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
483{
484  static nfsreaddirres res;
485  static nfsentry e_res[MAX_READDIR_ENTRIES];
486  am_node *mp;
487  int retry;
488
489#ifdef DEBUG
490  amuDebug(D_TRACE)
491    plog(XLOG_DEBUG, "readdir:");
492#endif /* DEBUG */
493
494  mp = fh_to_mp2(&argp->rda_fhandle, &retry);
495  if (mp == 0) {
496    if (retry < 0)
497      return 0;
498    res.rdr_status = nfs_error(retry);
499  } else {
500#ifdef DEBUG
501    amuDebug(D_TRACE)
502      plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
503#endif /* DEBUG */
504    res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir)
505			   (mp, argp->rda_cookie,
506			    &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
507    mp->am_stats.s_readdir++;
508  }
509
510  return &res;
511}
512
513
514nfsstatfsres *
515nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
516{
517  static nfsstatfsres res;
518  am_node *mp;
519  int retry;
520  mntent_t mnt;
521
522#ifdef DEBUG
523  amuDebug(D_TRACE)
524    plog(XLOG_DEBUG, "statfs:");
525#endif /* DEBUG */
526
527  mp = fh_to_mp2(argp, &retry);
528  if (mp == 0) {
529    if (retry < 0)
530      return 0;
531    res.sfr_status = nfs_error(retry);
532  } else {
533    nfsstatfsokres *fp;
534#ifdef DEBUG
535    amuDebug(D_TRACE)
536      plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
537#endif /* DEBUG */
538
539    /*
540     * just return faked up file system information
541     */
542    fp = &res.sfr_u.sfr_reply_u;
543
544    fp->sfrok_tsize = 1024;
545    fp->sfrok_bsize = 1024;
546
547    /* check if map is browsable and show_statfs_entries=yes  */
548    if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
549	mp->am_mnt && mp->am_mnt->mf_mopts) {
550      mnt.mnt_opts = mp->am_mnt->mf_mopts;
551      if (hasmntopt(&mnt, "browsable")) {
552	count_map_entries(mp,
553			  &fp->sfrok_blocks,
554			  &fp->sfrok_bfree,
555			  &fp->sfrok_bavail);
556      }
557    } else {
558      fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
559      fp->sfrok_bfree = 0;
560      fp->sfrok_bavail = 0;
561    }
562
563    res.sfr_status = NFS_OK;
564    mp->am_stats.s_statfs++;
565  }
566
567  return &res;
568}
569
570
571/*
572 * count how many total entries there are in a map, and how many
573 * of them are in use.
574 */
575static void
576count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
577{
578  u_int blocks, bfree, bavail, i;
579  mntfs *mf;
580  mnt_map *mmp;
581  kv *k;
582
583  blocks = bfree = bavail = 0;
584  if (!mp)
585    goto out;
586  mf = mp->am_mnt;
587  if (!mf)
588    goto out;
589  mmp = (mnt_map *) mf->mf_private;
590  if (!mmp)
591    goto out;
592
593  /* iterate over keys */
594  for (i = 0; i < NKVHASH; i++) {
595    for (k = mmp->kvhash[i]; k ; k = k->next) {
596      if (!k->key)
597	continue;
598      blocks++;
599      /*
600       * XXX: Need to count how many are actively in use and recompute
601       * bfree and bavail based on it.
602       */
603    }
604  }
605
606out:
607  *out_blocks = blocks;
608  *out_bfree = bfree;
609  *out_bavail = bavail;
610}
611