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