]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/amd/amd/nfs_subr.c
MFC r308493, r308619: Update amd from am-utils 6.1.5 to 6.2.
[FreeBSD/stable/10.git] / contrib / amd / amd / nfs_subr.c
1 /*
2  * Copyright (c) 1997-2014 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. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *
36  * File: am-utils/amd/nfs_subr.c
37  *
38  */
39
40 #ifdef HAVE_CONFIG_H
41 # include <config.h>
42 #endif /* HAVE_CONFIG_H */
43 #include <am_defs.h>
44 #include <amd.h>
45
46 /*
47  * Convert from UN*X to NFS error code.
48  * Some systems like linux define their own (see
49  * conf/mount/mount_linux.h).
50  */
51 #ifndef nfs_error
52 # define nfs_error(e) ((nfsstat)(e))
53 #endif /* nfs_error */
54
55 /*
56  * File Handle structure
57  *
58  * This is interpreted by indexing the exported array
59  * by fhh_id (for old-style filehandles), or by retrieving
60  * the node name from fhh_path (for new-style filehandles).
61  *
62  * The whole structure is mapped onto a standard fhandle_t
63  * when transmitted.
64  */
65 struct am_fh {
66   u_int fhh_gen;                                /* generation number */
67   union {
68     struct {
69       int fhh_type;                             /* old or new am_fh */
70       pid_t fhh_pid;                            /* process id */
71       int fhh_id;                               /* map id */
72     } s;
73     char fhh_path[NFS_FHSIZE-sizeof(u_int)];    /* path to am_node */
74   } u;
75 };
76
77 struct am_fh3 {
78   u_int fhh_gen;                                /* generation number */
79   union {
80     struct {
81       int fhh_type;                             /* old or new am_fh */
82       pid_t fhh_pid;                            /* process id */
83       int fhh_id;                               /* map id */
84     } s;
85     char fhh_path[AM_FHSIZE3-sizeof(u_int)];    /* path to am_node */
86   } u;
87 };
88
89 /* forward declarations */
90 /* converting am-filehandles to mount-points */
91 static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop);
92 static am_node *fh_to_mp(am_nfs_fh *fhp);
93 static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
94
95
96 static char *
97 do_readlink(am_node *mp, int *error_return)
98 {
99   char *ln;
100
101   /*
102    * If there is a readlink method then use it,
103    * otherwise if a link exists use that,
104    * otherwise use the mount point.
105    */
106   if (mp->am_al->al_mnt->mf_ops->readlink) {
107     int retry = 0;
108     mp = (*mp->am_al->al_mnt->mf_ops->readlink) (mp, &retry);
109     if (mp == NULL) {
110       *error_return = retry;
111       return 0;
112     }
113     /* reschedule_timeout_mp(); */
114   }
115
116   if (mp->am_link) {
117     ln = mp->am_link;
118   } else {
119     ln = mp->am_al->al_mnt->mf_mount;
120   }
121
122   return ln;
123 }
124
125
126 voidp
127 nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
128 {
129   static char res;
130
131   return (voidp) &res;
132 }
133
134
135 nfsattrstat *
136 nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
137 {
138   static nfsattrstat res;
139   am_node *mp;
140   int retry = 0;
141   time_t now = clocktime(NULL);
142
143   if (amuDebug(D_TRACE))
144     plog(XLOG_DEBUG, "getattr:");
145
146   mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
147   if (mp == NULL) {
148     if (amuDebug(D_TRACE))
149       plog(XLOG_DEBUG, "\tretry=%d", retry);
150
151     if (retry < 0) {
152       amd_stats.d_drops++;
153       return 0;
154     }
155     res.ns_status = nfs_error(retry);
156     return &res;
157   }
158
159   res = mp->am_attr;
160   if (amuDebug(D_TRACE))
161     plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld",
162          mp->am_path,
163          (int) res.ns_u.ns_attr_u.na_size,
164          (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds,
165          (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds);
166
167   /* Delay unmount of what was looked up */
168   if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
169     mp->am_timeo_w += gopt.am_timeo_w;
170   mp->am_ttl = now + mp->am_timeo_w;
171
172   mp->am_stats.s_getattr++;
173   return &res;
174 }
175
176
177 nfsattrstat *
178 nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
179 {
180   static nfsattrstat res;
181
182   if (!fh_to_mp(&argp->sag_fhandle))
183     res.ns_status = nfs_error(ESTALE);
184   else
185     res.ns_status = nfs_error(EROFS);
186
187   return &res;
188 }
189
190
191 voidp
192 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
193 {
194   static char res;
195
196   return (voidp) &res;
197 }
198
199
200 nfsdiropres *
201 nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
202 {
203   static nfsdiropres res;
204   am_node *mp;
205   int retry;
206   uid_t uid;
207   gid_t gid;
208
209   if (amuDebug(D_TRACE))
210     plog(XLOG_DEBUG, "lookup:");
211
212   /* finally, find the effective uid/gid from RPC request */
213   if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
214     plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
215   xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid);
216   xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid);
217
218   mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE);
219   if (mp == NULL) {
220     if (retry < 0) {
221       amd_stats.d_drops++;
222       return 0;
223     }
224     res.dr_status = nfs_error(retry);
225   } else {
226     int error;
227     am_node *ap;
228     if (amuDebug(D_TRACE))
229       plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name);
230     ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE);
231     if (ap && error < 0)
232       ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error);
233     if (ap == 0) {
234       if (error < 0) {
235         amd_stats.d_drops++;
236         return 0;
237       }
238       res.dr_status = nfs_error(error);
239     } else {
240       /*
241        * XXX: EXPERIMENTAL! Delay unmount of what was looked up.  This
242        * should reduce the chance for race condition between unmounting an
243        * entry synchronously, and re-mounting it asynchronously.
244        */
245       if (ap->am_ttl < mp->am_ttl)
246         ap->am_ttl = mp->am_ttl;
247       mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
248       res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
249       res.dr_status = NFS_OK;
250     }
251     mp->am_stats.s_lookup++;
252     /* reschedule_timeout_mp(); */
253   }
254
255   return &res;
256 }
257
258
259 void
260 nfs_quick_reply(am_node *mp, int error)
261 {
262   SVCXPRT *transp = mp->am_transp;
263   nfsdiropres res;
264   xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
265
266   /*
267    * If there's a transp structure then we can reply to the client's
268    * nfs lookup request.
269    */
270   if (transp) {
271     if (error == 0) {
272       /*
273        * Construct a valid reply to a lookup request.  Same
274        * code as in nfsproc_lookup_2_svc() above.
275        */
276       mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
277       res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
278       res.dr_status = NFS_OK;
279     } else
280       /*
281        * Return the error that was passed to us.
282        */
283       res.dr_status = nfs_error(error);
284
285     /*
286      * Send off our reply
287      */
288     if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
289       svcerr_systemerr(transp);
290
291     /*
292      * Free up transp.  It's only used for one reply.
293      */
294     XFREE(mp->am_transp);
295     dlog("Quick reply sent for %s", mp->am_al->al_mnt->mf_mount);
296   }
297 }
298
299
300 nfsreadlinkres *
301 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
302 {
303   static nfsreadlinkres res;
304   am_node *mp;
305   int retry;
306
307   if (amuDebug(D_TRACE))
308     plog(XLOG_DEBUG, "readlink:");
309
310   mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
311   if (mp == NULL) {
312   readlink_retry:
313     if (retry < 0) {
314       amd_stats.d_drops++;
315       return 0;
316     }
317     res.rlr_status = nfs_error(retry);
318   } else {
319     char *ln = do_readlink(mp, &retry);
320     if (ln == 0)
321       goto readlink_retry;
322     res.rlr_status = NFS_OK;
323     if (amuDebug(D_TRACE) && ln)
324       plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
325     res.rlr_u.rlr_data_u = ln;
326     mp->am_stats.s_readlink++;
327   }
328
329   return &res;
330 }
331
332
333 nfsreadres *
334 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
335 {
336   static nfsreadres res;
337
338   memset((char *) &res, 0, sizeof(res));
339   res.rr_status = nfs_error(EACCES);
340
341   return &res;
342 }
343
344
345 voidp
346 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
347 {
348   static char res;
349
350   return (voidp) &res;
351 }
352
353
354 nfsattrstat *
355 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
356 {
357   static nfsattrstat res;
358
359   if (!fh_to_mp(&argp->wra_fhandle))
360     res.ns_status = nfs_error(ESTALE);
361   else
362     res.ns_status = nfs_error(EROFS);
363
364   return &res;
365 }
366
367
368 nfsdiropres *
369 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
370 {
371   static nfsdiropres res;
372
373   if (!fh_to_mp(&argp->ca_where.da_fhandle))
374     res.dr_status = nfs_error(ESTALE);
375   else
376     res.dr_status = nfs_error(EROFS);
377
378   return &res;
379 }
380
381
382 static nfsstat *
383 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
384 {
385   static nfsstat res;
386   int retry;
387
388   am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
389   if (mp == NULL) {
390     if (retry < 0) {
391       amd_stats.d_drops++;
392       return 0;
393     }
394     res = nfs_error(retry);
395     goto out;
396   }
397
398   if (mp->am_fattr.na_type != NFDIR) {
399     res = nfs_error(ENOTDIR);
400     goto out;
401   }
402
403   if (amuDebug(D_TRACE))
404     plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
405
406   mp = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE);
407   if (mp == NULL) {
408     /*
409      * Ignore retries...
410      */
411     if (retry < 0)
412       retry = 0;
413     /*
414      * Usual NFS workaround...
415      */
416     else if (retry == ENOENT)
417       retry = 0;
418     res = nfs_error(retry);
419   } else {
420     forcibly_timeout_mp(mp);
421     res = NFS_OK;
422   }
423
424 out:
425   return &res;
426 }
427
428
429 nfsstat *
430 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
431 {
432   return unlink_or_rmdir(argp, rqstp, TRUE);
433 }
434
435
436 nfsstat *
437 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
438 {
439   static nfsstat res;
440
441   if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
442     res = nfs_error(ESTALE);
443   /*
444    * If the kernel is doing clever things with referenced files
445    * then let it pretend...
446    */
447   else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
448     res = NFS_OK;
449   /*
450    * otherwise a failure
451    */
452   else
453     res = nfs_error(EROFS);
454
455   return &res;
456 }
457
458
459 nfsstat *
460 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
461 {
462   static nfsstat res;
463
464   if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
465     res = nfs_error(ESTALE);
466   else
467     res = nfs_error(EROFS);
468
469   return &res;
470 }
471
472
473 nfsstat *
474 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
475 {
476   static nfsstat res;
477
478   if (!fh_to_mp(&argp->sla_from.da_fhandle))
479     res = nfs_error(ESTALE);
480   else
481     res = nfs_error(EROFS);
482
483   return &res;
484 }
485
486
487 nfsdiropres *
488 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
489 {
490   static nfsdiropres res;
491
492   if (!fh_to_mp(&argp->ca_where.da_fhandle))
493     res.dr_status = nfs_error(ESTALE);
494   else
495     res.dr_status = nfs_error(EROFS);
496
497   return &res;
498 }
499
500
501 nfsstat *
502 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
503 {
504   return unlink_or_rmdir(argp, rqstp, FALSE);
505 }
506
507
508 nfsreaddirres *
509 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
510 {
511   static nfsreaddirres res;
512   static nfsentry e_res[MAX_READDIR_ENTRIES];
513   am_node *mp;
514   int retry;
515
516   if (amuDebug(D_TRACE))
517     plog(XLOG_DEBUG, "readdir:");
518
519   mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE);
520   if (mp == NULL) {
521     if (retry < 0) {
522       amd_stats.d_drops++;
523       return 0;
524     }
525     res.rdr_status = nfs_error(retry);
526   } else {
527     if (amuDebug(D_TRACE))
528       plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
529     res.rdr_status = nfs_error((*mp->am_al->al_mnt->mf_ops->readdir)
530                            (mp, argp->rda_cookie,
531                             &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
532     mp->am_stats.s_readdir++;
533   }
534
535   return &res;
536 }
537
538
539 nfsstatfsres *
540 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
541 {
542   static nfsstatfsres res;
543   am_node *mp;
544   int retry;
545   mntent_t mnt;
546
547   if (amuDebug(D_TRACE))
548     plog(XLOG_DEBUG, "statfs:");
549
550   mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
551   if (mp == NULL) {
552     if (retry < 0) {
553       amd_stats.d_drops++;
554       return 0;
555     }
556     res.sfr_status = nfs_error(retry);
557   } else {
558     nfsstatfsokres *fp;
559     if (amuDebug(D_TRACE))
560       plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
561
562     /*
563      * just return faked up file system information
564      */
565     fp = &res.sfr_u.sfr_reply_u;
566
567     fp->sfrok_tsize = 1024;
568     fp->sfrok_bsize = 1024;
569
570     /* check if map is browsable and show_statfs_entries=yes  */
571     if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
572         mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) {
573       mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts;
574       if (amu_hasmntopt(&mnt, "browsable")) {
575         count_map_entries(mp,
576                           &fp->sfrok_blocks,
577                           &fp->sfrok_bfree,
578                           &fp->sfrok_bavail);
579       }
580     } else {
581       fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
582       fp->sfrok_bfree = 0;
583       fp->sfrok_bavail = 0;
584     }
585
586     res.sfr_status = NFS_OK;
587     mp->am_stats.s_statfs++;
588   }
589
590   return &res;
591 }
592
593
594 /*
595  * count how many total entries there are in a map, and how many
596  * of them are in use.
597  */
598 static void
599 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
600 {
601   u_int blocks, bfree, bavail, i;
602   mntfs *mf;
603   mnt_map *mmp;
604   kv *k;
605
606   blocks = bfree = bavail = 0;
607   if (!mp)
608     goto out;
609   mf = mp->am_al->al_mnt;
610   if (!mf)
611     goto out;
612   mmp = (mnt_map *) mf->mf_private;
613   if (!mmp)
614     goto out;
615
616   /* iterate over keys */
617   for (i = 0; i < NKVHASH; i++) {
618     for (k = mmp->kvhash[i]; k ; k = k->next) {
619       if (!k->key)
620         continue;
621       blocks++;
622       /*
623        * XXX: Need to count how many are actively in use and recompute
624        * bfree and bavail based on it.
625        */
626     }
627   }
628
629 out:
630   *out_blocks = blocks;
631   *out_bfree = bfree;
632   *out_bavail = bavail;
633 }
634
635 static am_node *
636 validate_ap(am_node *node, int *rp, u_int fhh_gen)
637 {
638   am_node *ap = node;
639   /*
640    * Check the generation number in the node
641    * matches the one from the kernel.  If not
642    * then the old node has been timed out and
643    * a new one allocated.
644    */
645   if (node != NULL && node->am_gen != fhh_gen)
646     ap = NULL;
647
648   /*
649    * If it doesn't exists then drop the request
650    */
651   if (!ap)
652     goto drop;
653
654 #if 0
655   /*
656    * If the node is hung then locate a new node
657    * for it.  This implements the replicated filesystem
658    * retries.
659    */
660   if (ap->am_al->al_mnt && FSRV_ISDOWN(ap->am_al->al_mnt->mf_server) && ap->am_parent) {
661     int error;
662     am_node *orig_ap = ap;
663
664     dlog("%s: %s (%s) is hung: lookup alternative file server", __func__,
665          orig_ap->am_path, orig_ap->am_al->al_mnt->mf_info);
666
667     /*
668      * Update modify time of parent node.
669      * With any luck the kernel will re-stat
670      * the child node and get new information.
671      */
672     clocktime(&orig_ap->am_fattr.na_mtime);
673
674     /*
675      * Call the parent's lookup routine for an object
676      * with the same name.  This may return -1 in error
677      * if a mount is in progress.  In any case, if no
678      * mount node is returned the error code is propagated
679      * to the caller.
680      */
681     if (vop == VLOOK_CREATE) {
682       ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop);
683       if (ap && error < 0)
684         ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->mount_child(ap, &error);
685     } else {
686       ap = NULL;
687       error = ESTALE;
688     }
689     if (ap == 0) {
690       if (error < 0 && amd_state == Finishing)
691         error = ENOENT;
692       *rp = error;
693       return 0;
694     }
695
696     /*
697      * Update last access to original node.  This
698      * avoids timing it out and so sending ESTALE
699      * back to the kernel.
700      * XXX - Not sure we need this anymore (jsp, 90/10/6).
701      */
702     new_ttl(orig_ap);
703
704   }
705 #endif /* 0 */
706
707   /*
708    * Disallow references to objects being unmounted, unless
709    * they are automount points.
710    */
711   if (ap->am_al->al_mnt && (ap->am_al->al_mnt->mf_flags & MFF_UNMOUNTING) &&
712       !(ap->am_flags & AMF_ROOT)) {
713     if (amd_state == Finishing)
714       *rp = ENOENT;
715     else
716       *rp = -1;
717     return 0;
718   }
719   new_ttl(ap);
720
721 drop:
722   if (!ap || !ap->am_al->al_mnt) {
723     /*
724      * If we are shutting down then it is likely
725      * that this node has disappeared because of
726      * a fast timeout.  To avoid things thrashing
727      * just pretend it doesn't exist at all.  If
728      * ESTALE is returned, some NFS clients just
729      * keep retrying (stupid or what - if it's
730      * stale now, what's it going to be in 5 minutes?)
731      */
732     if (amd_state == Finishing)
733       *rp = ENOENT;
734     else {
735       *rp = ESTALE;
736       amd_stats.d_stale++;
737     }
738   }
739
740   return ap;
741 }
742
743 /*
744  * Convert from file handle to automount node.
745  */
746 static am_node *
747 fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop)
748 {
749   struct am_fh *fp = (struct am_fh *) fhp;
750   am_node *ap = NULL;
751
752   if (fp->u.s.fhh_type != 0) {
753     /* New filehandle type */
754     int len = sizeof(*fhp) - sizeof(fp->fhh_gen);
755     char *path = xmalloc(len+1);
756     /*
757      * Because fhp is treated as a filehandle we use memcpy
758      * instead of xstrlcpy.
759      */
760     memcpy(path, (char *) fp->u.fhh_path, len);
761     path[len] = '\0';
762     dlog("%s: new filehandle: %s", __func__, path);
763
764     ap = path_to_exported_ap(path);
765     XFREE(path);
766   } else {
767     dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id);
768     /*
769      * Check process id matches
770      * If it doesn't then it is probably
771      * from an old kernel-cached filehandle
772      * which is now out of date.
773      */
774     if (fp->u.s.fhh_pid != get_server_pid()) {
775       dlog("%s: wrong pid %ld != my pid %ld", __func__,
776            (long) fp->u.s.fhh_pid, get_server_pid());
777       goto done;
778     }
779
780     /*
781      * Get hold of the supposed mount node
782      */
783     ap = get_exported_ap(fp->u.s.fhh_id);
784   }
785 done:
786   return validate_ap(ap, rp, fp->fhh_gen);
787 }
788
789 static am_node *
790 fh_to_mp(am_nfs_fh *fhp)
791 {
792   int dummy;
793
794   return fh_to_mp3(fhp, &dummy, VLOOK_CREATE);
795 }
796
797 static am_node *
798 fh3_to_mp3(am_nfs_fh3 *fhp, int *rp, int vop)
799 {
800   struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data;
801   am_node *ap = NULL;
802
803   if (fp->u.s.fhh_type != 0) {
804     /* New filehandle type */
805     int len = sizeof(*fp) - sizeof(fp->fhh_gen);
806     char *path = xmalloc(len+1);
807     /*
808      * Because fhp is treated as a filehandle we use memcpy
809      * instead of xstrlcpy.
810      */
811     memcpy(path, (char *) fp->u.fhh_path, len);
812     path[len] = '\0';
813     dlog("%s: new filehandle: %s", __func__, path);
814
815     ap = path_to_exported_ap(path);
816     XFREE(path);
817   } else {
818     dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id);
819     /*
820      * Check process id matches
821      * If it doesn't then it is probably
822      * from an old kernel-cached filehandle
823      * which is now out of date.
824      */
825     if (fp->u.s.fhh_pid != get_server_pid()) {
826       dlog("%s: wrong pid %ld != my pid %ld", __func__,
827            (long) fp->u.s.fhh_pid, get_server_pid());
828       goto done;
829     }
830
831     /*
832      * Get hold of the supposed mount node
833      */
834     ap = get_exported_ap(fp->u.s.fhh_id);
835   }
836 done:
837   return validate_ap(ap, rp, fp->fhh_gen);
838 }
839
840 static am_node *
841 fh3_to_mp(am_nfs_fh3 *fhp)
842 {
843   int dummy;
844
845   return fh3_to_mp3(fhp, &dummy, VLOOK_CREATE);
846 }
847
848 /*
849  * Convert from automount node to file handle.
850  */
851 void
852 mp_to_fh(am_node *mp, am_nfs_fh *fhp)
853 {
854   u_int pathlen;
855   struct am_fh *fp = (struct am_fh *) fhp;
856
857   memset((char *) fhp, 0, sizeof(am_nfs_fh));
858
859   /* Store the generation number */
860   fp->fhh_gen = mp->am_gen;
861
862   pathlen = strlen(mp->am_path);
863   if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) {
864     /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */
865
866     /*
867      * Because fhp is treated as a filehandle we use memcpy instead of
868      * xstrlcpy.
869      */
870     memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */
871   } else {
872     /*
873      * Take the process id
874      */
875     fp->u.s.fhh_pid = get_server_pid();
876
877     /*
878      * ... the map number
879      */
880     fp->u.s.fhh_id = mp->am_mapno;
881
882     /*
883      * ... and the generation number (previously stored)
884      * to make a "unique" triple that will never
885      * be reallocated except across reboots (which doesn't matter)
886      * or if we are unlucky enough to be given the same
887      * pid as a previous amd (very unlikely).
888      */
889     /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */
890   }
891 }
892 void
893 mp_to_fh3(am_node *mp, am_nfs_fh3 *fhp)
894 {
895   u_int pathlen;
896   struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data;
897
898   memset((char *) fhp, 0, sizeof(am_nfs_fh3));
899   fhp->am_fh3_length = AM_FHSIZE3;
900
901   /* Store the generation number */
902   fp->fhh_gen = mp->am_gen;
903
904   pathlen = strlen(mp->am_path);
905   if (pathlen <= sizeof(*fp) - sizeof(fp->fhh_gen)) {
906     /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */
907
908     /*
909      * Because fhp is treated as a filehandle we use memcpy instead of
910      * xstrlcpy.
911      */
912     memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */
913   } else {
914     /*
915      * Take the process id
916      */
917     fp->u.s.fhh_pid = get_server_pid();
918
919     /*
920      * ... the map number
921      */
922     fp->u.s.fhh_id = mp->am_mapno;
923
924     /*
925      * ... and the generation number (previously stored)
926      * to make a "unique" triple that will never
927      * be reallocated except across reboots (which doesn't matter)
928      * or if we are unlucky enough to be given the same
929      * pid as a previous amd (very unlikely).
930      */
931     /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */
932   }
933 }
934
935 #ifdef HAVE_FS_NFS3
936 static am_ftype3 ftype_to_ftype3(nfsftype ftype)
937 {
938   if (ftype == NFFIFO)
939     return AM_NF3FIFO;
940   else
941     return ftype;
942 }
943
944 static void nfstime_to_am_nfstime3(nfstime *time, am_nfstime3 *time3)
945 {
946   time3->seconds = time->seconds;
947   time3->nseconds = time->useconds * 1000;
948 }
949
950 static void rdev_to_am_specdata3(u_int rdev, am_specdata3 *rdev3)
951 {
952   /* No device node here */
953   rdev3->specdata1 = (u_int) -1;
954   rdev3->specdata2 = (u_int) -1;
955 }
956
957 static void fattr_to_fattr3(nfsfattr *fattr, am_fattr3 *fattr3)
958 {
959   fattr3->type = ftype_to_ftype3(fattr->na_type);
960   fattr3->mode = (am_mode3) fattr->na_mode;
961   fattr3->nlink = fattr->na_nlink;
962   fattr3->uid = (am_uid3) fattr->na_uid;
963   fattr3->gid = (am_uid3) fattr->na_gid;
964   fattr3->size = (am_size3) fattr->na_size;
965   fattr3->used = (am_size3) fattr->na_size;
966   rdev_to_am_specdata3(fattr->na_rdev, &fattr3->rdev);
967   fattr3->fsid = (uint64) fattr->na_fsid;
968   fattr3->fileid = (uint64) fattr->na_fileid;
969   nfstime_to_am_nfstime3(&fattr->na_atime, &fattr3->atime);
970   nfstime_to_am_nfstime3(&fattr->na_mtime, &fattr3->mtime);
971   nfstime_to_am_nfstime3(&fattr->na_ctime, &fattr3->ctime);
972 }
973
974 static void fattr_to_wcc_attr(nfsfattr *fattr, am_wcc_attr *wcc_attr)
975 {
976   wcc_attr->size = (am_size3) fattr->na_size;
977   nfstime_to_am_nfstime3(&fattr->na_mtime, &wcc_attr->mtime);
978   nfstime_to_am_nfstime3(&fattr->na_ctime, &wcc_attr->ctime);
979 }
980
981 static am_nfsstat3 return_estale_or_rofs(am_nfs_fh3 *fh,
982                                          am_pre_op_attr *pre_op,
983                                          am_post_op_attr *post_op)
984 {
985   am_node *mp;
986
987   mp = fh3_to_mp(fh);
988   if (!mp) {
989     pre_op->attributes_follow = 0;
990     post_op->attributes_follow = 0;
991     return  nfs_error(ESTALE);
992   } else {
993     am_fattr3 *fattr3 = &post_op->am_post_op_attr_u.attributes;
994     am_wcc_attr *wcc_attr = &pre_op->am_pre_op_attr_u.attributes;
995     nfsfattr *fattr = &mp->am_fattr;
996     pre_op->attributes_follow = 1;
997     fattr_to_wcc_attr(fattr, wcc_attr);
998     post_op->attributes_follow = 1;
999     fattr_to_fattr3(fattr, fattr3);
1000     return nfs_error(EROFS);
1001   }
1002 }
1003
1004 static am_nfsstat3 unlink3_or_rmdir3(am_diropargs3 *argp,
1005                                      am_wcc_data *wcc_data, int unlinkp)
1006 {
1007   static am_nfsstat3 res;
1008   am_nfs_fh3 *dir = &argp->dir;
1009   am_filename3 name = argp->name;
1010   am_pre_op_attr *pre_op_dir = &wcc_data->before;
1011   am_post_op_attr *post_op_dir = &wcc_data->after;
1012   nfsfattr *fattr;
1013   am_wcc_attr *wcc_attr;
1014   am_node *mp, *ap;
1015   int retry;
1016
1017   post_op_dir->attributes_follow = 0;
1018
1019   mp = fh3_to_mp3(dir, &retry, VLOOK_DELETE);
1020   if (!mp) {
1021     pre_op_dir->attributes_follow = 0;
1022     if (retry < 0) {
1023       amd_stats.d_drops++;
1024       return 0;
1025     }
1026     res = nfs_error(retry);
1027     goto out;
1028   }
1029
1030   pre_op_dir->attributes_follow = 1;
1031   fattr = &mp->am_fattr;
1032   wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes;
1033   fattr_to_wcc_attr(fattr, wcc_attr);
1034
1035   if (mp->am_fattr.na_type != NFDIR) {
1036     res = nfs_error(ENOTDIR);
1037     goto out;
1038   }
1039
1040   if (amuDebug(D_TRACE))
1041     plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, name);
1042
1043   ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &retry, VLOOK_DELETE);
1044   if (!ap) {
1045     /*
1046      * Ignore retries...
1047      */
1048     if (retry < 0)
1049       retry = 0;
1050     /*
1051      * Usual NFS workaround...
1052      */
1053     else if (retry == ENOENT)
1054       retry = 0;
1055     res = nfs_error(retry);
1056   } else {
1057     forcibly_timeout_mp(mp);
1058     res = AM_NFS3_OK;
1059   }
1060
1061 out:
1062   return res;
1063 }
1064
1065 voidp
1066 am_nfs3_null_3_svc(voidp argp, struct svc_req *rqstp)
1067 {
1068   static char * result;
1069
1070   return (voidp) &result;
1071 }
1072
1073 am_GETATTR3res *
1074 am_nfs3_getattr_3_svc(am_GETATTR3args *argp, struct svc_req *rqstp)
1075 {
1076   static am_GETATTR3res  result;
1077   am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object;
1078   am_fattr3 *fattr3;
1079   nfsfattr *fattr;
1080   am_node *mp;
1081   int retry = 0;
1082   time_t now = clocktime(NULL);
1083
1084   if (amuDebug(D_TRACE))
1085     plog(XLOG_DEBUG, "getattr_3:");
1086
1087   mp = fh3_to_mp3(fh, &retry, VLOOK_CREATE);
1088   if (!mp) {
1089     if (amuDebug(D_TRACE))
1090       plog(XLOG_DEBUG, "\tretry=%d", retry);
1091
1092     if (retry < 0) {
1093       amd_stats.d_drops++;
1094       return 0;
1095     }
1096     result.status = nfs_error(retry);
1097     return &result;
1098   }
1099
1100   fattr = &mp->am_fattr;
1101   fattr3 = (am_fattr3 *) &result.res_u.ok.obj_attributes;
1102   fattr_to_fattr3(fattr, fattr3);
1103
1104   result.status = AM_NFS3_OK;
1105
1106   if (amuDebug(D_TRACE))
1107     plog(XLOG_DEBUG, "\tstat(%s), size = %lu, mtime=%d.%d",
1108          mp->am_path,
1109          (u_long) fattr3->size,
1110          (u_int) fattr3->mtime.seconds,
1111          (u_int) fattr3->mtime.nseconds);
1112
1113   /* Delay unmount of what was looked up */
1114   if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
1115     mp->am_timeo_w += gopt.am_timeo_w;
1116   mp->am_ttl = now + mp->am_timeo_w;
1117
1118   mp->am_stats.s_getattr++;
1119
1120   return &result;
1121 }
1122
1123 am_SETATTR3res *
1124 am_nfs3_setattr_3_svc(am_SETATTR3args *argp, struct svc_req *rqstp)
1125 {
1126   static am_SETATTR3res  result;
1127   am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object;
1128   am_pre_op_attr *pre_op_obj = &result.res_u.fail.obj_wcc.before;
1129   am_post_op_attr *post_op_obj = &result.res_u.fail.obj_wcc.after;
1130
1131   if (amuDebug(D_TRACE))
1132     plog(XLOG_DEBUG, "setattr_3:");
1133
1134   result.status = return_estale_or_rofs(fh, pre_op_obj, post_op_obj);
1135
1136   return &result;
1137 }
1138
1139 am_LOOKUP3res *
1140 am_nfs3_lookup_3_svc(am_LOOKUP3args *argp, struct svc_req *rqstp)
1141 {
1142   static am_LOOKUP3res  result;
1143   am_nfs_fh3 *dir = &argp->what.dir;
1144   am_post_op_attr *post_op_dir;
1145   am_post_op_attr *post_op_obj;
1146   am_node *mp;
1147   int retry;
1148   uid_t uid;
1149   gid_t gid;
1150
1151   if (amuDebug(D_TRACE))
1152     plog(XLOG_DEBUG, "lookup_3:");
1153
1154   /* finally, find the effective uid/gid from RPC request */
1155   if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
1156     plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
1157   xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid);
1158   xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid);
1159
1160   mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE);
1161   if (!mp) {
1162     post_op_dir = &result.res_u.fail.dir_attributes;
1163     post_op_dir->attributes_follow = 0;
1164     if (retry < 0) {
1165       amd_stats.d_drops++;
1166       return 0;
1167     }
1168     result.status = nfs_error(retry);
1169   } else {
1170     post_op_dir = &result.res_u.ok.dir_attributes;
1171     post_op_obj = &result.res_u.ok.obj_attributes;
1172     am_filename3 name;
1173     am_fattr3 *fattr3;
1174     nfsfattr *fattr;
1175     am_node *ap;
1176     int error;
1177
1178     /* dir attributes */
1179     post_op_dir->attributes_follow = 1;
1180     fattr = &mp->am_fattr;
1181     fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1182     fattr_to_fattr3(fattr, fattr3);
1183
1184     post_op_obj->attributes_follow = 0;
1185
1186     name = argp->what.name;
1187
1188     if (amuDebug(D_TRACE))
1189       plog(XLOG_DEBUG, "\tlookup_3(%s, %s)", mp->am_path, name);
1190
1191     ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &error, VLOOK_CREATE);
1192     if (ap && error < 0)
1193       ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error);
1194     if (ap == 0) {
1195       if (error < 0) {
1196         amd_stats.d_drops++;
1197         return 0;
1198       }
1199       result.status = nfs_error(error);
1200     } else {
1201       /*
1202        * XXX: EXPERIMENTAL! Delay unmount of what was looked up.  This
1203        * should reduce the chance for race condition between unmounting an
1204        * entry synchronously, and re-mounting it asynchronously.
1205        */
1206       if (ap->am_ttl < mp->am_ttl)
1207         ap->am_ttl = mp->am_ttl;
1208
1209       mp_to_fh3(ap, &result.res_u.ok.object);
1210
1211       /* mount attributes */
1212       post_op_obj->attributes_follow = 1;
1213       fattr = &ap->am_fattr;
1214       fattr3 = &post_op_obj->am_post_op_attr_u.attributes;
1215       fattr_to_fattr3(fattr, fattr3);
1216
1217       result.status = AM_NFS3_OK;
1218     }
1219     mp->am_stats.s_lookup++;
1220   }
1221   return &result;
1222 }
1223
1224 am_ACCESS3res *
1225 am_nfs3_access_3_svc(am_ACCESS3args *argp, struct svc_req *rqstp)
1226 {
1227   static am_ACCESS3res  result;
1228
1229   am_nfs_fh3 *obj = &argp->object;
1230   u_int accessbits = argp->access;
1231   u_int accessmask = AM_ACCESS3_LOOKUP|AM_ACCESS3_READ;
1232   am_post_op_attr *post_op_obj;
1233   am_node *mp;
1234
1235   if (amuDebug(D_TRACE))
1236     plog(XLOG_DEBUG, "access_3:");
1237
1238   mp = fh3_to_mp(obj);
1239   if (!mp) {
1240     post_op_obj = &result.res_u.fail.obj_attributes;
1241     post_op_obj->attributes_follow = 0;
1242     result.status = nfs_error(ENOENT);
1243     if (amuDebug(D_TRACE))
1244       plog(XLOG_DEBUG, "access_3: ENOENT");
1245   } else {
1246     nfsfattr *fattr = &mp->am_fattr;
1247     am_fattr3 *fattr3;
1248     post_op_obj = &result.res_u.ok.obj_attributes;
1249     fattr3 = &post_op_obj->am_post_op_attr_u.attributes;
1250     post_op_obj->attributes_follow = 1;
1251     fattr_to_fattr3(fattr, fattr3);
1252
1253     result.res_u.ok.access = accessbits & accessmask;
1254     if (amuDebug(D_TRACE))
1255       plog(XLOG_DEBUG, "access_3: b=%x m=%x", accessbits, accessmask);
1256
1257     result.status = AM_NFS3_OK;
1258   }
1259
1260   return &result;
1261 }
1262
1263 am_READLINK3res *
1264 am_nfs3_readlink_3_svc(am_READLINK3args *argp, struct svc_req *rqstp)
1265 {
1266   static am_READLINK3res  result;
1267
1268   am_nfs_fh3 *symlink = (am_nfs_fh3 *) &argp->symlink;
1269   am_post_op_attr *post_op_sym;
1270   am_node *mp;
1271   int retry = 0;
1272
1273   if (amuDebug(D_TRACE))
1274     plog(XLOG_DEBUG, "readlink_3:");
1275
1276   mp = fh3_to_mp3(symlink, &retry, VLOOK_CREATE);
1277   if (!mp) {
1278   readlink_retry:
1279     if (retry < 0) {
1280       amd_stats.d_drops++;
1281       return 0;
1282     }
1283     post_op_sym = &result.res_u.fail.symlink_attributes;
1284     post_op_sym->attributes_follow = 0;
1285     result.status = nfs_error(retry);
1286   } else {
1287     nfsfattr *fattr;
1288     am_fattr3 *fattr3;
1289     char *ln;
1290
1291     ln = do_readlink(mp, &retry);
1292     if (!ln)
1293       goto readlink_retry;
1294
1295     if (amuDebug(D_TRACE) && ln)
1296       plog(XLOG_DEBUG, "\treadlink_3(%s) = %s", mp->am_path, ln);
1297
1298     result.res_u.ok.data = ln;
1299
1300     post_op_sym = &result.res_u.ok.symlink_attributes;
1301     post_op_sym->attributes_follow = 1;
1302     fattr = &mp->am_fattr;
1303     fattr3 = &post_op_sym->am_post_op_attr_u.attributes;
1304     fattr_to_fattr3(fattr, fattr3);
1305
1306     mp->am_stats.s_readlink++;
1307     result.status = AM_NFS3_OK;
1308   }
1309
1310   return &result;
1311 }
1312
1313 am_READ3res *
1314 am_nfs3_read_3_svc(am_READ3args *argp, struct svc_req *rqstp)
1315 {
1316   static am_READ3res  result;
1317
1318   am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1319   am_post_op_attr *post_op_file;
1320   am_node *mp;
1321
1322   if (amuDebug(D_TRACE))
1323     plog(XLOG_DEBUG, "read_3:");
1324
1325   post_op_file = &result.res_u.fail.file_attributes;
1326   result.status = nfs_error(EACCES);
1327
1328   mp = fh3_to_mp(file);
1329   if (!mp)
1330     post_op_file->attributes_follow = 0;
1331   else {
1332     nfsfattr *fattr = &mp->am_fattr;
1333     am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes;
1334     post_op_file->attributes_follow = 1;
1335     fattr_to_fattr3(fattr, fattr3);
1336   }
1337
1338   return &result;
1339 }
1340
1341 am_WRITE3res *
1342 am_nfs3_write_3_svc(am_WRITE3args *argp, struct svc_req *rqstp)
1343 {
1344   static am_WRITE3res  result;
1345
1346   am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1347   am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before;
1348   am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after;
1349
1350   if (amuDebug(D_TRACE))
1351     plog(XLOG_DEBUG, "write_3:");
1352
1353   result.status = return_estale_or_rofs(file, pre_op_file, post_op_file);
1354
1355   return &result;
1356 }
1357
1358 am_CREATE3res *
1359 am_nfs3_create_3_svc(am_CREATE3args *argp, struct svc_req *rqstp)
1360 {
1361   static am_CREATE3res  result;
1362
1363   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1364   am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1365   am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1366
1367   if (amuDebug(D_TRACE))
1368     plog(XLOG_DEBUG, "create_3:");
1369
1370   result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1371
1372   return &result;
1373 }
1374
1375 am_MKDIR3res *
1376 am_nfs3_mkdir_3_svc(am_MKDIR3args *argp, struct svc_req *rqstp)
1377 {
1378   static am_MKDIR3res  result;
1379
1380   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1381   am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1382   am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1383
1384   if (amuDebug(D_TRACE))
1385     plog(XLOG_DEBUG, "mkdir_3:");
1386
1387   result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1388
1389   return &result;
1390 }
1391
1392 am_SYMLINK3res *
1393 am_nfs3_symlink_3_svc(am_SYMLINK3args *argp, struct svc_req *rqstp)
1394 {
1395   static am_SYMLINK3res  result;
1396
1397   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1398   am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1399   am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1400
1401   if (amuDebug(D_TRACE))
1402     plog(XLOG_DEBUG, "symlink_3:");
1403
1404   result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1405
1406   return &result;
1407 }
1408
1409 am_MKNOD3res *
1410 am_nfs3_mknod_3_svc(am_MKNOD3args *argp, struct svc_req *rqstp)
1411 {
1412   static am_MKNOD3res  result;
1413
1414   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1415   am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1416   am_post_op_attr *post_op_dir =  &result.res_u.fail.dir_wcc.after;
1417
1418   if (amuDebug(D_TRACE))
1419     plog(XLOG_DEBUG, "mknod_3:");
1420
1421   result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1422   return &result;
1423 }
1424
1425 am_REMOVE3res *
1426 am_nfs3_remove_3_svc(am_REMOVE3args *argp, struct svc_req *rqstp)
1427 {
1428   static am_REMOVE3res  result;
1429
1430   am_diropargs3 *obj = &argp->object;
1431   am_wcc_data dir_wcc;
1432
1433   if (amuDebug(D_TRACE))
1434     plog(XLOG_DEBUG, "remove_3:");
1435
1436   result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE);
1437
1438   result.res_u.ok.dir_wcc = dir_wcc;
1439  
1440   return &result;
1441 }
1442
1443 am_RMDIR3res *
1444 am_nfs3_rmdir_3_svc(am_RMDIR3args *argp, struct svc_req *rqstp)
1445 {
1446   static am_RMDIR3res  result;
1447
1448   am_diropargs3 *obj = &argp->object;
1449   am_wcc_data dir_wcc;
1450
1451   if (amuDebug(D_TRACE))
1452     plog(XLOG_DEBUG, "rmdir_3:");
1453
1454   result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE);
1455
1456   result.res_u.ok.dir_wcc = dir_wcc;
1457  
1458   return &result;
1459 }
1460
1461 am_RENAME3res *
1462 am_nfs3_rename_3_svc(am_RENAME3args *argp, struct svc_req *rqstp)
1463 {
1464   static am_RENAME3res  result;
1465
1466   am_nfs_fh3 *fromdir = (am_nfs_fh3 *) &argp->from.dir;
1467   am_nfs_fh3 *todir = (am_nfs_fh3 *) &argp->to.dir;
1468   am_filename3 name = argp->to.name;
1469   am_node *to_mp, *from_mp;
1470
1471   if (amuDebug(D_TRACE))
1472     plog(XLOG_DEBUG, "rename_3:");
1473
1474   if (!(from_mp = fh3_to_mp(fromdir)) || !(to_mp = fh3_to_mp(todir)))
1475     result.status = nfs_error(ESTALE);
1476   /*
1477    * If the kernel is doing clever things with referenced files
1478    * then let it pretend...
1479    */
1480   else {
1481     am_wcc_attr *wcc_attr;
1482     am_fattr3 *fattr3;
1483     am_wcc_data *to_wcc_data, *from_wcc_data;
1484     am_pre_op_attr *pre_op_to, *pre_op_from;
1485     am_post_op_attr *post_op_to, *post_op_from;
1486     nfsfattr *fattr;
1487
1488     to_wcc_data = &result.res_u.ok.todir_wcc;
1489
1490     pre_op_to = &to_wcc_data->before;
1491     post_op_to = &to_wcc_data->after;
1492
1493     pre_op_to->attributes_follow = 1;
1494     fattr = &to_mp->am_fattr;
1495     wcc_attr = &pre_op_to->am_pre_op_attr_u.attributes;
1496     fattr_to_wcc_attr(fattr, wcc_attr);
1497     post_op_to->attributes_follow = 1;
1498     fattr3 = &post_op_to->am_post_op_attr_u.attributes;
1499     fattr_to_fattr3(fattr, fattr3);
1500
1501     from_wcc_data = &result.res_u.ok.fromdir_wcc;
1502
1503     pre_op_from = &from_wcc_data->before;
1504     post_op_from = &from_wcc_data->after;
1505
1506     pre_op_from->attributes_follow = 1;
1507     fattr = &from_mp->am_fattr;
1508     wcc_attr = &pre_op_from->am_pre_op_attr_u.attributes;
1509     fattr_to_wcc_attr(fattr, wcc_attr);
1510     post_op_from->attributes_follow = 1;
1511     fattr3 = &post_op_from->am_post_op_attr_u.attributes;
1512     fattr_to_fattr3(fattr, fattr3);
1513
1514     if (NSTREQ(name, ".nfs", 4))
1515       result.status = AM_NFS3_OK;
1516     /*
1517      * otherwise a failure
1518      */
1519     else
1520       result.status = nfs_error(EROFS);
1521   }
1522
1523   return &result;
1524 }
1525
1526 am_LINK3res *
1527 am_nfs3_link_3_svc(am_LINK3args *argp, struct svc_req *rqstp)
1528 {
1529   static am_LINK3res  result;
1530
1531   am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1532   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->link.dir;
1533   am_post_op_attr *post_op_file;
1534   am_pre_op_attr *pre_op_dir;
1535   am_post_op_attr *post_op_dir;
1536   am_node *mp_file, *mp_dir;
1537
1538   if (amuDebug(D_TRACE))
1539     plog(XLOG_DEBUG, "link_3:");
1540
1541   post_op_file = &result.res_u.fail.file_attributes;
1542   post_op_file->attributes_follow = 0;
1543
1544   mp_file = fh3_to_mp(file);
1545   if (mp_file) {
1546     nfsfattr *fattr = &mp_file->am_fattr;
1547     am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes;
1548     fattr_to_fattr3(fattr, fattr3);
1549   }
1550
1551   pre_op_dir = &result.res_u.fail.linkdir_wcc.before;
1552   pre_op_dir->attributes_follow = 0;
1553   post_op_dir = &result.res_u.fail.linkdir_wcc.after;
1554   post_op_dir->attributes_follow = 0;
1555
1556   mp_dir = fh3_to_mp(dir);
1557   if (mp_dir) {
1558     nfsfattr *fattr = &mp_dir->am_fattr;
1559     am_fattr3 *fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1560     am_wcc_attr *wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes;
1561
1562     pre_op_dir->attributes_follow = 1;
1563     fattr_to_wcc_attr(fattr, wcc_attr);
1564     post_op_dir->attributes_follow = 1;
1565     fattr_to_fattr3(fattr, fattr3);
1566   }
1567
1568   if (!mp_file || !mp_dir)
1569     result.status = nfs_error(ESTALE);
1570   else
1571     result.status = nfs_error(EROFS);
1572
1573   return &result;
1574 }
1575
1576 am_READDIR3res *
1577 am_nfs3_readdir_3_svc(am_READDIR3args *argp, struct svc_req *rqstp)
1578 {
1579   static am_READDIR3res  result;
1580   static am_entry3 entries[MAX_READDIR_ENTRIES];
1581   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir;
1582   am_cookie3 cookie = argp->cookie;
1583   am_cookieverf3 cookieverf;
1584   am_count3 count = argp->count;
1585   am_post_op_attr *post_op_dir;
1586   am_node *mp;
1587   int retry;
1588
1589   if (amuDebug(D_TRACE))
1590     plog(XLOG_DEBUG, "readdir_3:");
1591
1592   memcpy(&cookieverf, &argp->cookieverf, sizeof(am_cookieverf3));
1593
1594   mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE);
1595   if (mp == NULL) {
1596     if (retry < 0) {
1597       amd_stats.d_drops++;
1598       return 0;
1599     }
1600     post_op_dir = &result.res_u.fail.dir_attributes;
1601     post_op_dir->attributes_follow = 0;
1602     result.status = nfs_error(retry);
1603   } else {
1604     am_dirlist3 *list = &result.res_u.ok.reply;
1605     am_nfsstat3 status;
1606
1607     if (amuDebug(D_TRACE))
1608       plog(XLOG_DEBUG, "\treaddir_3(%s)", mp->am_path);
1609
1610     status = mp->am_al->al_mnt->mf_ops->readdir(mp,
1611                                         (voidp)&cookie, list, entries, count);
1612     if (status == 0) {
1613       post_op_dir = &result.res_u.ok.dir_attributes;
1614       nfsfattr *fattr;
1615       am_fattr3 *fattr3;
1616
1617       fattr = &mp->am_fattr;
1618       fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1619       post_op_dir->attributes_follow = 1;
1620       fattr_to_fattr3(fattr, fattr3);
1621       result.status = AM_NFS3_OK;
1622     } else {
1623       post_op_dir = &result.res_u.fail.dir_attributes;
1624       post_op_dir->attributes_follow = 0;
1625       result.status = nfs_error(status);
1626     }
1627
1628     mp->am_stats.s_readdir++;
1629   }
1630
1631   return &result;
1632 }
1633
1634 am_READDIRPLUS3res *
1635 am_nfs3_readdirplus_3_svc(am_READDIRPLUS3args *argp, struct svc_req *rqstp)
1636 {
1637   static am_READDIRPLUS3res  result;
1638   am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir;
1639   am_post_op_attr *post_op_dir;
1640   nfsfattr *fattr;
1641   am_fattr3 *fattr3;
1642   am_node *mp;
1643   int retry;
1644
1645   mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE);
1646   if (mp == NULL) {
1647     if (retry < 0) {
1648       amd_stats.d_drops++;
1649       return 0;
1650     }
1651     post_op_dir = &result.res_u.fail.dir_attributes;
1652     post_op_dir->attributes_follow = 0;
1653     result.status = nfs_error(retry);
1654   } else {
1655       post_op_dir = &result.res_u.ok.dir_attributes;
1656       fattr = &mp->am_fattr;
1657       fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1658       post_op_dir->attributes_follow = 1;
1659       fattr_to_fattr3(fattr, fattr3);
1660       result.status = AM_NFS3ERR_NOTSUPP;
1661   }
1662
1663   return &result;
1664 }
1665
1666 am_FSSTAT3res *
1667 am_nfs3_fsstat_3_svc(am_FSSTAT3args *argp, struct svc_req *rqstp)
1668 {
1669   static am_FSSTAT3res  result;
1670
1671   am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot;
1672   am_post_op_attr *post_op_fsroot;
1673   am_node *mp;
1674   int retry;
1675  
1676   if (amuDebug(D_TRACE))
1677     plog(XLOG_DEBUG, "fsstat_3:");
1678
1679   mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE);
1680   if (!mp) {
1681     if (retry < 0) {
1682       amd_stats.d_drops++;
1683       return 0;
1684     }
1685     post_op_fsroot = &result.res_u.fail.obj_attributes;
1686     post_op_fsroot->attributes_follow = 0;
1687     result.status = nfs_error(retry);
1688   } else {
1689     am_FSSTAT3resok *ok = &result.res_u.ok;
1690     u_int blocks, bfree, bavail;
1691     nfsfattr *fattr;
1692     am_fattr3 *fattr3;
1693     mntent_t mnt;
1694
1695     if (amuDebug(D_TRACE))
1696       plog(XLOG_DEBUG, "\tfsstat_3(%s)", mp->am_path);
1697
1698     fattr = &mp->am_fattr;
1699     post_op_fsroot = &ok->obj_attributes;
1700     post_op_fsroot->attributes_follow = 1;
1701     fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes;
1702     fattr_to_fattr3(fattr, fattr3);
1703
1704     /*
1705      * just return faked up file system information
1706      */
1707     ok->tbytes = 1024;
1708     ok->invarsec = 0;
1709
1710     /* check if map is browsable and show_statfs_entries=yes  */
1711     if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
1712         mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) {
1713       mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts;
1714       if (amu_hasmntopt(&mnt, "browsable")) {
1715         count_map_entries(mp, &blocks, &bfree, &bavail);
1716       }
1717       ok->fbytes = bfree;
1718       ok->abytes = bavail;
1719       ok->ffiles = bfree;
1720       ok->afiles = bavail;
1721       ok->tfiles = blocks;
1722     } else {
1723       ok->fbytes = 0;
1724       ok->abytes = 0;
1725       ok->ffiles = 0;
1726       ok->afiles = 0;
1727       ok->tfiles = 0; /* set to 1 if you don't want empty automounts */
1728     }
1729
1730     result.status = AM_NFS3_OK;
1731     mp->am_stats.s_statfs++;
1732   }
1733
1734   return &result;
1735 }
1736
1737 #define FSF3_HOMOGENEOUS 0x0008
1738
1739 am_FSINFO3res *
1740 am_nfs3_fsinfo_3_svc(am_FSINFO3args *argp, struct svc_req *rqstp)
1741 {
1742   static am_FSINFO3res  result;
1743
1744   am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot;
1745   am_post_op_attr *post_op_fsroot;
1746   am_node *mp;
1747   int retry;
1748
1749   if (amuDebug(D_TRACE))
1750     plog(XLOG_DEBUG, "fsinfo_3:");
1751
1752   mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE);
1753   if (!mp) {
1754     if (retry < 0) {
1755       amd_stats.d_drops++;
1756       return 0;
1757     }
1758     post_op_fsroot = &result.res_u.fail.obj_attributes;
1759     post_op_fsroot->attributes_follow = 0;
1760     result.status = nfs_error(retry);
1761   } else {
1762     am_FSINFO3resok *ok = &result.res_u.ok;
1763     nfsfattr *fattr;
1764     am_fattr3 *fattr3;
1765
1766     if (amuDebug(D_TRACE))
1767       plog(XLOG_DEBUG, "\tfsinfo_3(%s)", mp->am_path);
1768
1769     fattr = &mp->am_fattr;
1770     post_op_fsroot = &ok->obj_attributes;
1771     post_op_fsroot->attributes_follow = 1;
1772     fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes;
1773     fattr_to_fattr3(fattr, fattr3);
1774
1775     /*
1776      * just return faked up file system information
1777      */
1778     ok->rtmax = 0;
1779     ok->rtpref = 0;
1780     ok->rtmult = 0;
1781     ok->wtmax = 0;
1782     ok->wtpref = 0;
1783     ok->wtmult = 0;
1784     ok->dtpref = 1024;
1785     ok->maxfilesize = 0;
1786     ok->time_delta.seconds = 1;
1787     ok->time_delta.nseconds = 0;
1788     ok->properties = FSF3_HOMOGENEOUS;
1789
1790     result.status = AM_NFS3_OK;
1791     mp->am_stats.s_fsinfo++;
1792   }
1793
1794   return &result;
1795 }
1796
1797 am_PATHCONF3res *
1798 am_nfs3_pathconf_3_svc(am_PATHCONF3args *argp, struct svc_req *rqstp)
1799 {
1800   static am_PATHCONF3res  result;
1801
1802   am_nfs_fh3 *obj = (am_nfs_fh3 *) &argp->object;
1803   am_post_op_attr *post_op_obj;
1804   am_node *mp;
1805   int retry;
1806
1807   if (amuDebug(D_TRACE))
1808     plog(XLOG_DEBUG, "pathconf_3:");
1809
1810   mp = fh3_to_mp3(obj, &retry, VLOOK_CREATE);
1811   if (!mp) {
1812     if (retry < 0) {
1813       amd_stats.d_drops++;
1814       return 0;
1815     }
1816     post_op_obj = &result.res_u.fail.obj_attributes;
1817     post_op_obj->attributes_follow = 0;
1818     result.status = nfs_error(retry);
1819   } else {
1820     am_PATHCONF3resok *ok = &result.res_u.ok;
1821     nfsfattr *fattr;
1822     am_fattr3 *fattr3;
1823
1824     if (amuDebug(D_TRACE))
1825       plog(XLOG_DEBUG, "\tpathconf_3(%s)", mp->am_path);
1826
1827     fattr = &mp->am_fattr;
1828     post_op_obj = &ok->obj_attributes;
1829     post_op_obj->attributes_follow = 1;
1830     fattr3 = &post_op_obj->am_post_op_attr_u.attributes;
1831     fattr_to_fattr3(fattr, fattr3);
1832
1833     ok->linkmax = 0;
1834     ok->name_max = NAME_MAX;
1835     ok->no_trunc = 1;
1836     ok->chown_restricted = 1;
1837     ok->case_insensitive = 0;
1838     ok->case_preserving = 1;
1839
1840     result.status = AM_NFS3_OK;
1841     mp->am_stats.s_pathconf++;
1842   }
1843
1844   return &result;
1845 }
1846
1847 am_COMMIT3res *
1848 am_nfs3_commit_3_svc(am_COMMIT3args *argp, struct svc_req *rqstp)
1849 {
1850   static am_COMMIT3res  result;
1851
1852   am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1853   am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before;
1854   am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after;
1855
1856   if (amuDebug(D_TRACE))
1857     plog(XLOG_DEBUG, "commit_3:");
1858
1859   result.status = return_estale_or_rofs(file, pre_op_file, post_op_file);
1860
1861   return &result;
1862 }
1863 #endif /* HAVE_FS_NFS3 */