Merge branch 'vendor/TNFTP'
[dragonfly.git] / contrib / amd / amd / nfs_subr.c
1 /*
2  * Copyright (c) 1997-1999 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.3 1999/01/13 23:31:00 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
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            (int) 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-modified 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
156 nfsattrstat *
157 nfsproc_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
170 voidp
171 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
172 {
173   static char res;
174
175   return (voidp) &res;
176 }
177
178
179 nfsdiropres *
180 nfsproc_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       /*
215        * XXX: EXPERIMENTAL! Delay unmount of what was looked up.  This
216        * should reduce the chance for race condition between unmounting an
217        * entry synchronously, and re-mounting it asynchronously.
218        */
219       if (ap->am_ttl < mp->am_ttl)
220         ap->am_ttl = mp->am_ttl;
221       mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
222       res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
223       res.dr_status = NFS_OK;
224     }
225     mp->am_stats.s_lookup++;
226     /* reschedule_timeout_mp(); */
227   }
228
229   return &res;
230 }
231
232
233 void
234 quick_reply(am_node *mp, int error)
235 {
236   SVCXPRT *transp = mp->am_transp;
237   nfsdiropres res;
238   xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
239
240   /*
241    * If there's a transp structure then we can reply to the client's
242    * nfs lookup request.
243    */
244   if (transp) {
245     if (error == 0) {
246       /*
247        * Construct a valid reply to a lookup request.  Same
248        * code as in nfsproc_lookup_2_svc() above.
249        */
250       mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
251       res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
252       res.dr_status = NFS_OK;
253     } else
254       /*
255        * Return the error that was passed to us.
256        */
257       res.dr_status = nfs_error(error);
258
259     /*
260      * Send off our reply
261      */
262     if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
263       svcerr_systemerr(transp);
264
265     /*
266      * Free up transp.  It's only used for one reply.
267      */
268     XFREE(transp);
269     mp->am_transp = NULL;
270 #ifdef DEBUG
271     dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
272 #endif /* DEBUG */
273   }
274 }
275
276
277 nfsreadlinkres *
278 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
279 {
280   static nfsreadlinkres res;
281   am_node *mp;
282   int retry;
283
284 #ifdef DEBUG
285   amuDebug(D_TRACE)
286     plog(XLOG_DEBUG, "readlink:");
287 #endif /* DEBUG */
288
289   mp = fh_to_mp2(argp, &retry);
290   if (mp == 0) {
291   readlink_retry:
292     if (retry < 0)
293       return 0;
294     res.rlr_status = nfs_error(retry);
295   } else {
296     char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0);
297     if (ln == 0)
298       goto readlink_retry;
299     res.rlr_status = NFS_OK;
300 #ifdef DEBUG
301     amuDebug(D_TRACE)
302       if (ln)
303         plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
304 #endif /* DEBUG */
305     res.rlr_u.rlr_data_u = ln;
306     mp->am_stats.s_readlink++;
307   }
308
309   return &res;
310 }
311
312
313 nfsreadres *
314 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
315 {
316   static nfsreadres res;
317
318   memset((char *) &res, 0, sizeof(res));
319   res.rr_status = nfs_error(EACCES);
320
321   return &res;
322 }
323
324
325 voidp
326 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
327 {
328   static char res;
329
330   return (voidp) &res;
331 }
332
333
334 nfsattrstat *
335 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
336 {
337   static nfsattrstat res;
338
339   if (!fh_to_mp(&argp->wra_fhandle))
340     res.ns_status = nfs_error(ESTALE);
341   else
342     res.ns_status = nfs_error(EROFS);
343
344   return &res;
345 }
346
347
348 nfsdiropres *
349 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
350 {
351   static nfsdiropres res;
352
353   if (!fh_to_mp(&argp->ca_where.da_fhandle))
354     res.dr_status = nfs_error(ESTALE);
355   else
356     res.dr_status = nfs_error(EROFS);
357
358   return &res;
359 }
360
361
362 static nfsstat *
363 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
364 {
365   static nfsstat res;
366   int retry;
367
368   am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
369   if (mp == 0) {
370     if (retry < 0)
371       return 0;
372     res = nfs_error(retry);
373     goto out;
374   }
375
376   if (mp->am_fattr.na_type != NFDIR) {
377     res = nfs_error(ENOTDIR);
378     goto out;
379   }
380
381 #ifdef DEBUG
382   amuDebug(D_TRACE)
383     plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
384 #endif /* DEBUG */
385
386   mp = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &retry, VLOOK_DELETE);
387   if (mp == 0) {
388     /*
389      * Ignore retries...
390      */
391     if (retry < 0)
392       retry = 0;
393     /*
394      * Usual NFS workaround...
395      */
396     else if (retry == ENOENT)
397       retry = 0;
398     res = nfs_error(retry);
399   } else {
400     forcibly_timeout_mp(mp);
401     res = NFS_OK;
402   }
403
404 out:
405   return &res;
406 }
407
408
409 nfsstat *
410 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
411 {
412   return unlink_or_rmdir(argp, rqstp, TRUE);
413 }
414
415
416 nfsstat *
417 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
418 {
419   static nfsstat res;
420
421   if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
422     res = nfs_error(ESTALE);
423   /*
424    * If the kernel is doing clever things with referenced files
425    * then let it pretend...
426    */
427   else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
428     res = NFS_OK;
429   /*
430    * otherwise a failure
431    */
432   else
433     res = nfs_error(EROFS);
434
435   return &res;
436 }
437
438
439 nfsstat *
440 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
441 {
442   static nfsstat res;
443
444   if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
445     res = nfs_error(ESTALE);
446   else
447     res = nfs_error(EROFS);
448
449   return &res;
450 }
451
452
453 nfsstat *
454 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
455 {
456   static nfsstat res;
457
458   if (!fh_to_mp(&argp->sla_from.da_fhandle))
459     res = nfs_error(ESTALE);
460   else
461     res = nfs_error(EROFS);
462
463   return &res;
464 }
465
466
467 nfsdiropres *
468 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
469 {
470   static nfsdiropres res;
471
472   if (!fh_to_mp(&argp->ca_where.da_fhandle))
473     res.dr_status = nfs_error(ESTALE);
474   else
475     res.dr_status = nfs_error(EROFS);
476
477   return &res;
478 }
479
480
481 nfsstat *
482 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
483 {
484   return unlink_or_rmdir(argp, rqstp, FALSE);
485 }
486
487
488 nfsreaddirres *
489 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
490 {
491   static nfsreaddirres res;
492   static nfsentry e_res[MAX_READDIR_ENTRIES];
493   am_node *mp;
494   int retry;
495
496 #ifdef DEBUG
497   amuDebug(D_TRACE)
498     plog(XLOG_DEBUG, "readdir:");
499 #endif /* DEBUG */
500
501   mp = fh_to_mp2(&argp->rda_fhandle, &retry);
502   if (mp == 0) {
503     if (retry < 0)
504       return 0;
505     res.rdr_status = nfs_error(retry);
506   } else {
507 #ifdef DEBUG
508     amuDebug(D_TRACE)
509       plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
510 #endif /* DEBUG */
511     res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir)
512                            (mp, argp->rda_cookie,
513                             &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
514     mp->am_stats.s_readdir++;
515   }
516
517   return &res;
518 }
519
520
521 nfsstatfsres *
522 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
523 {
524   static nfsstatfsres res;
525   am_node *mp;
526   int retry;
527   mntent_t mnt;
528
529 #ifdef DEBUG
530   amuDebug(D_TRACE)
531     plog(XLOG_DEBUG, "statfs:");
532 #endif /* DEBUG */
533
534   mp = fh_to_mp2(argp, &retry);
535   if (mp == 0) {
536     if (retry < 0)
537       return 0;
538     res.sfr_status = nfs_error(retry);
539   } else {
540     nfsstatfsokres *fp;
541 #ifdef DEBUG
542     amuDebug(D_TRACE)
543       plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
544 #endif /* DEBUG */
545
546     /*
547      * just return faked up file system information
548      */
549     fp = &res.sfr_u.sfr_reply_u;
550
551     fp->sfrok_tsize = 1024;
552     fp->sfrok_bsize = 1024;
553
554     /* check if map is browsable and show_statfs_entries=yes  */
555     if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
556         mp->am_mnt && mp->am_mnt->mf_mopts) {
557       mnt.mnt_opts = mp->am_mnt->mf_mopts;
558       if (hasmntopt(&mnt, "browsable")) {
559         count_map_entries(mp,
560                           &fp->sfrok_blocks,
561                           &fp->sfrok_bfree,
562                           &fp->sfrok_bavail);
563       }
564     } else {
565       fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
566       fp->sfrok_bfree = 0;
567       fp->sfrok_bavail = 0;
568     }
569
570     res.sfr_status = NFS_OK;
571     mp->am_stats.s_statfs++;
572   }
573
574   return &res;
575 }
576
577
578 /*
579  * count how many total entries there are in a map, and how many
580  * of them are in use.
581  */
582 static void
583 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
584 {
585   u_int blocks, bfree, bavail, i;
586   mntfs *mf;
587   mnt_map *mmp;
588   kv *k;
589
590   blocks = bfree = bavail = 0;
591   if (!mp)
592     goto out;
593   mf = mp->am_mnt;
594   if (!mf)
595     goto out;
596   mmp = (mnt_map *) mf->mf_private;
597   if (!mmp)
598     goto out;
599
600   /* iterate over keys */
601   for (i = 0; i < NKVHASH; i++) {
602     for (k = mmp->kvhash[i]; k ; k = k->next) {
603       if (!k->key)
604         continue;
605       blocks++;
606       /*
607        * XXX: Need to count how many are actively in use and recompute
608        * bfree and bavail based on it.
609        */
610     }
611   }
612
613 out:
614   *out_blocks = blocks;
615   *out_bfree = bfree;
616   *out_bavail = bavail;
617 }