]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/amd/amd/nfs_subr.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / amd / amd / nfs_subr.c
1 /*
2  * Copyright (c) 1997-2006 Erez Zadok
3  * Copyright (c) 1990 Jan-Simon Pendry
4  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgment:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *
40  * File: am-utils/amd/nfs_subr.c
41  *
42  */
43
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif /* HAVE_CONFIG_H */
47 #include <am_defs.h>
48 #include <amd.h>
49
50 /*
51  * Convert from UN*X to NFS error code.
52  * Some systems like linux define their own (see
53  * conf/mount/mount_linux.h).
54  */
55 #ifndef nfs_error
56 # define nfs_error(e) ((nfsstat)(e))
57 #endif /* nfs_error */
58
59 /*
60  * File Handle structure
61  *
62  * This is interpreted by indexing the exported array
63  * by fhh_id (for old-style filehandles), or by retrieving
64  * the node name from fhh_path (for new-style filehandles).
65  *
66  * The whole structure is mapped onto a standard fhandle_t
67  * when transmitted.
68  */
69 struct am_fh {
70   u_int fhh_gen;                                /* generation number */
71   union {
72     struct {
73       int fhh_type;                             /* old or new am_fh */
74       pid_t fhh_pid;                            /* process id */
75       int fhh_id;                               /* map id */
76     } s;
77     char fhh_path[NFS_FHSIZE-sizeof(u_int)];    /* path to am_node */
78   } u;
79 };
80
81
82 /* forward declarations */
83 /* converting am-filehandles to mount-points */
84 static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop);
85 static am_node *fh_to_mp(am_nfs_fh *fhp);
86 static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
87
88
89 static char *
90 do_readlink(am_node *mp, int *error_return)
91 {
92   char *ln;
93
94   /*
95    * If there is a readlink method then use it,
96    * otherwise if a link exists use that,
97    * otherwise use the mount point.
98    */
99   if (mp->am_mnt->mf_ops->readlink) {
100     int retry = 0;
101     mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry);
102     if (mp == 0) {
103       *error_return = retry;
104       return 0;
105     }
106     /* reschedule_timeout_mp(); */
107   }
108
109   if (mp->am_link) {
110     ln = mp->am_link;
111   } else {
112     ln = mp->am_mnt->mf_mount;
113   }
114
115   return ln;
116 }
117
118
119 voidp
120 nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
121 {
122   static char res;
123
124   return (voidp) &res;
125 }
126
127
128 nfsattrstat *
129 nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
130 {
131   static nfsattrstat res;
132   am_node *mp;
133   int retry;
134   time_t now = clocktime(NULL);
135
136   if (amuDebug(D_TRACE))
137     plog(XLOG_DEBUG, "getattr:");
138
139   mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
140   if (mp == 0) {
141     if (amuDebug(D_TRACE))
142       plog(XLOG_DEBUG, "\tretry=%d", retry);
143
144     if (retry < 0) {
145       amd_stats.d_drops++;
146       return 0;
147     }
148     res.ns_status = nfs_error(retry);
149     return &res;
150   }
151
152   res = mp->am_attr;
153   if (amuDebug(D_TRACE))
154     plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld",
155          mp->am_path,
156          (int) res.ns_u.ns_attr_u.na_size,
157          (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds,
158          (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds);
159
160   /* Delay unmount of what was looked up */
161   if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
162     mp->am_timeo_w += gopt.am_timeo_w;
163   mp->am_ttl = now + mp->am_timeo_w;
164
165   mp->am_stats.s_getattr++;
166   return &res;
167 }
168
169
170 nfsattrstat *
171 nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
172 {
173   static nfsattrstat res;
174
175   if (!fh_to_mp(&argp->sag_fhandle))
176     res.ns_status = nfs_error(ESTALE);
177   else
178     res.ns_status = nfs_error(EROFS);
179
180   return &res;
181 }
182
183
184 voidp
185 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
186 {
187   static char res;
188
189   return (voidp) &res;
190 }
191
192
193 nfsdiropres *
194 nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
195 {
196   static nfsdiropres res;
197   am_node *mp;
198   int retry;
199   uid_t uid;
200   gid_t gid;
201
202   if (amuDebug(D_TRACE))
203     plog(XLOG_DEBUG, "lookup:");
204
205   /* finally, find the effective uid/gid from RPC request */
206   if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
207     plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
208   xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid);
209   xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid);
210
211   mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE);
212   if (mp == 0) {
213     if (retry < 0) {
214       amd_stats.d_drops++;
215       return 0;
216     }
217     res.dr_status = nfs_error(retry);
218   } else {
219     int error;
220     am_node *ap;
221     if (amuDebug(D_TRACE))
222       plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name);
223     ap = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE);
224     if (ap && error < 0)
225       ap = mp->am_mnt->mf_ops->mount_child(ap, &error);
226     if (ap == 0) {
227       if (error < 0) {
228         amd_stats.d_drops++;
229         return 0;
230       }
231       res.dr_status = nfs_error(error);
232     } else {
233       /*
234        * XXX: EXPERIMENTAL! Delay unmount of what was looked up.  This
235        * should reduce the chance for race condition between unmounting an
236        * entry synchronously, and re-mounting it asynchronously.
237        */
238       if (ap->am_ttl < mp->am_ttl)
239         ap->am_ttl = mp->am_ttl;
240       mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
241       res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
242       res.dr_status = NFS_OK;
243     }
244     mp->am_stats.s_lookup++;
245     /* reschedule_timeout_mp(); */
246   }
247
248   return &res;
249 }
250
251
252 void
253 nfs_quick_reply(am_node *mp, int error)
254 {
255   SVCXPRT *transp = mp->am_transp;
256   nfsdiropres res;
257   xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
258
259   /*
260    * If there's a transp structure then we can reply to the client's
261    * nfs lookup request.
262    */
263   if (transp) {
264     if (error == 0) {
265       /*
266        * Construct a valid reply to a lookup request.  Same
267        * code as in nfsproc_lookup_2_svc() above.
268        */
269       mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
270       res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
271       res.dr_status = NFS_OK;
272     } else
273       /*
274        * Return the error that was passed to us.
275        */
276       res.dr_status = nfs_error(error);
277
278     /*
279      * Send off our reply
280      */
281     if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
282       svcerr_systemerr(transp);
283
284     /*
285      * Free up transp.  It's only used for one reply.
286      */
287     XFREE(mp->am_transp);
288     dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
289   }
290 }
291
292
293 nfsreadlinkres *
294 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
295 {
296   static nfsreadlinkres res;
297   am_node *mp;
298   int retry;
299
300   if (amuDebug(D_TRACE))
301     plog(XLOG_DEBUG, "readlink:");
302
303   mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
304   if (mp == 0) {
305   readlink_retry:
306     if (retry < 0) {
307       amd_stats.d_drops++;
308       return 0;
309     }
310     res.rlr_status = nfs_error(retry);
311   } else {
312     char *ln = do_readlink(mp, &retry);
313     if (ln == 0)
314       goto readlink_retry;
315     res.rlr_status = NFS_OK;
316     if (amuDebug(D_TRACE) && ln)
317       plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
318     res.rlr_u.rlr_data_u = ln;
319     mp->am_stats.s_readlink++;
320   }
321
322   return &res;
323 }
324
325
326 nfsreadres *
327 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
328 {
329   static nfsreadres res;
330
331   memset((char *) &res, 0, sizeof(res));
332   res.rr_status = nfs_error(EACCES);
333
334   return &res;
335 }
336
337
338 voidp
339 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
340 {
341   static char res;
342
343   return (voidp) &res;
344 }
345
346
347 nfsattrstat *
348 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
349 {
350   static nfsattrstat res;
351
352   if (!fh_to_mp(&argp->wra_fhandle))
353     res.ns_status = nfs_error(ESTALE);
354   else
355     res.ns_status = nfs_error(EROFS);
356
357   return &res;
358 }
359
360
361 nfsdiropres *
362 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
363 {
364   static nfsdiropres res;
365
366   if (!fh_to_mp(&argp->ca_where.da_fhandle))
367     res.dr_status = nfs_error(ESTALE);
368   else
369     res.dr_status = nfs_error(EROFS);
370
371   return &res;
372 }
373
374
375 static nfsstat *
376 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
377 {
378   static nfsstat res;
379   int retry;
380
381   am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
382   if (mp == 0) {
383     if (retry < 0) {
384       amd_stats.d_drops++;
385       return 0;
386     }
387     res = nfs_error(retry);
388     goto out;
389   }
390
391   if (mp->am_fattr.na_type != NFDIR) {
392     res = nfs_error(ENOTDIR);
393     goto out;
394   }
395
396   if (amuDebug(D_TRACE))
397     plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
398
399   mp = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE);
400   if (mp == 0) {
401     /*
402      * Ignore retries...
403      */
404     if (retry < 0)
405       retry = 0;
406     /*
407      * Usual NFS workaround...
408      */
409     else if (retry == ENOENT)
410       retry = 0;
411     res = nfs_error(retry);
412   } else {
413     forcibly_timeout_mp(mp);
414     res = NFS_OK;
415   }
416
417 out:
418   return &res;
419 }
420
421
422 nfsstat *
423 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
424 {
425   return unlink_or_rmdir(argp, rqstp, TRUE);
426 }
427
428
429 nfsstat *
430 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
431 {
432   static nfsstat res;
433
434   if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
435     res = nfs_error(ESTALE);
436   /*
437    * If the kernel is doing clever things with referenced files
438    * then let it pretend...
439    */
440   else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
441     res = NFS_OK;
442   /*
443    * otherwise a failure
444    */
445   else
446     res = nfs_error(EROFS);
447
448   return &res;
449 }
450
451
452 nfsstat *
453 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
454 {
455   static nfsstat res;
456
457   if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
458     res = nfs_error(ESTALE);
459   else
460     res = nfs_error(EROFS);
461
462   return &res;
463 }
464
465
466 nfsstat *
467 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
468 {
469   static nfsstat res;
470
471   if (!fh_to_mp(&argp->sla_from.da_fhandle))
472     res = nfs_error(ESTALE);
473   else
474     res = nfs_error(EROFS);
475
476   return &res;
477 }
478
479
480 nfsdiropres *
481 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
482 {
483   static nfsdiropres res;
484
485   if (!fh_to_mp(&argp->ca_where.da_fhandle))
486     res.dr_status = nfs_error(ESTALE);
487   else
488     res.dr_status = nfs_error(EROFS);
489
490   return &res;
491 }
492
493
494 nfsstat *
495 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
496 {
497   return unlink_or_rmdir(argp, rqstp, FALSE);
498 }
499
500
501 nfsreaddirres *
502 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
503 {
504   static nfsreaddirres res;
505   static nfsentry e_res[MAX_READDIR_ENTRIES];
506   am_node *mp;
507   int retry;
508
509   if (amuDebug(D_TRACE))
510     plog(XLOG_DEBUG, "readdir:");
511
512   mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE);
513   if (mp == 0) {
514     if (retry < 0) {
515       amd_stats.d_drops++;
516       return 0;
517     }
518     res.rdr_status = nfs_error(retry);
519   } else {
520     if (amuDebug(D_TRACE))
521       plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
522     res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir)
523                            (mp, argp->rda_cookie,
524                             &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
525     mp->am_stats.s_readdir++;
526   }
527
528   return &res;
529 }
530
531
532 nfsstatfsres *
533 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
534 {
535   static nfsstatfsres res;
536   am_node *mp;
537   int retry;
538   mntent_t mnt;
539
540   if (amuDebug(D_TRACE))
541     plog(XLOG_DEBUG, "statfs:");
542
543   mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
544   if (mp == 0) {
545     if (retry < 0) {
546       amd_stats.d_drops++;
547       return 0;
548     }
549     res.sfr_status = nfs_error(retry);
550   } else {
551     nfsstatfsokres *fp;
552     if (amuDebug(D_TRACE))
553       plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
554
555     /*
556      * just return faked up file system information
557      */
558     fp = &res.sfr_u.sfr_reply_u;
559
560     fp->sfrok_tsize = 1024;
561     fp->sfrok_bsize = 1024;
562
563     /* check if map is browsable and show_statfs_entries=yes  */
564     if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
565         mp->am_mnt && mp->am_mnt->mf_mopts) {
566       mnt.mnt_opts = mp->am_mnt->mf_mopts;
567       if (amu_hasmntopt(&mnt, "browsable")) {
568         count_map_entries(mp,
569                           &fp->sfrok_blocks,
570                           &fp->sfrok_bfree,
571                           &fp->sfrok_bavail);
572       }
573     } else {
574       fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
575       fp->sfrok_bfree = 0;
576       fp->sfrok_bavail = 0;
577     }
578
579     res.sfr_status = NFS_OK;
580     mp->am_stats.s_statfs++;
581   }
582
583   return &res;
584 }
585
586
587 /*
588  * count how many total entries there are in a map, and how many
589  * of them are in use.
590  */
591 static void
592 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
593 {
594   u_int blocks, bfree, bavail, i;
595   mntfs *mf;
596   mnt_map *mmp;
597   kv *k;
598
599   blocks = bfree = bavail = 0;
600   if (!mp)
601     goto out;
602   mf = mp->am_mnt;
603   if (!mf)
604     goto out;
605   mmp = (mnt_map *) mf->mf_private;
606   if (!mmp)
607     goto out;
608
609   /* iterate over keys */
610   for (i = 0; i < NKVHASH; i++) {
611     for (k = mmp->kvhash[i]; k ; k = k->next) {
612       if (!k->key)
613         continue;
614       blocks++;
615       /*
616        * XXX: Need to count how many are actively in use and recompute
617        * bfree and bavail based on it.
618        */
619     }
620   }
621
622 out:
623   *out_blocks = blocks;
624   *out_bfree = bfree;
625   *out_bavail = bavail;
626 }
627
628
629 /*
630  * Convert from file handle to automount node.
631  */
632 static am_node *
633 fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop)
634 {
635   struct am_fh *fp = (struct am_fh *) fhp;
636   am_node *ap = 0;
637
638   if (fp->u.s.fhh_type != 0) {
639     /* New filehandle type */
640     int len = sizeof(*fhp) - sizeof(fp->fhh_gen);
641     char *path = xmalloc(len+1);
642     /*
643      * Because fhp is treated as a filehandle we use memcpy
644      * instead of xstrlcpy.
645      */
646     memcpy(path, (char *) fp->u.fhh_path, len);
647     path[len] = '\0';
648     /* dlog("fh_to_mp3: new filehandle: %s", path); */
649
650     ap = path_to_exported_ap(path);
651     XFREE(path);
652   } else {
653     /* dlog("fh_to_mp3: old filehandle: %d", fp->u.s.fhh_id); */
654     /*
655      * Check process id matches
656      * If it doesn't then it is probably
657      * from an old kernel-cached filehandle
658      * which is now out of date.
659      */
660     if (fp->u.s.fhh_pid != get_server_pid()) {
661       dlog("fh_to_mp3: wrong pid %ld != my pid %ld",
662            (long) fp->u.s.fhh_pid, get_server_pid());
663       goto drop;
664     }
665
666     /*
667      * Get hold of the supposed mount node
668      */
669     ap = get_exported_ap(fp->u.s.fhh_id);
670   }
671
672   /*
673    * Check the generation number in the node
674    * matches the one from the kernel.  If not
675    * then the old node has been timed out and
676    * a new one allocated.
677    */
678   if (ap != NULL && ap->am_gen != fp->fhh_gen)
679     ap = 0;
680
681   /*
682    * If it doesn't exists then drop the request
683    */
684   if (!ap)
685     goto drop;
686
687 #if 0
688   /*
689    * If the node is hung then locate a new node
690    * for it.  This implements the replicated filesystem
691    * retries.
692    */
693   if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) {
694     int error;
695     am_node *orig_ap = ap;
696
697     dlog("fh_to_mp3: %s (%s) is hung: lookup alternative file server",
698          orig_ap->am_path, orig_ap->am_mnt->mf_info);
699
700     /*
701      * Update modify time of parent node.
702      * With any luck the kernel will re-stat
703      * the child node and get new information.
704      */
705     clocktime(&orig_ap->am_fattr.na_mtime);
706
707     /*
708      * Call the parent's lookup routine for an object
709      * with the same name.  This may return -1 in error
710      * if a mount is in progress.  In any case, if no
711      * mount node is returned the error code is propagated
712      * to the caller.
713      */
714     if (vop == VLOOK_CREATE) {
715       ap = orig_ap->am_parent->am_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop);
716       if (ap && error < 0)
717         ap = orig_ap->am_parent->am_mnt->mf_ops->mount_child(ap, &error);
718     } else {
719       ap = 0;
720       error = ESTALE;
721     }
722     if (ap == 0) {
723       if (error < 0 && amd_state == Finishing)
724         error = ENOENT;
725       *rp = error;
726       return 0;
727     }
728
729     /*
730      * Update last access to original node.  This
731      * avoids timing it out and so sending ESTALE
732      * back to the kernel.
733      * XXX - Not sure we need this anymore (jsp, 90/10/6).
734      */
735     new_ttl(orig_ap);
736
737   }
738 #endif
739
740   /*
741    * Disallow references to objects being unmounted, unless
742    * they are automount points.
743    */
744   if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) &&
745       !(ap->am_flags & AMF_ROOT)) {
746     if (amd_state == Finishing)
747       *rp = ENOENT;
748     else
749       *rp = -1;
750     return 0;
751   }
752   new_ttl(ap);
753
754 drop:
755   if (!ap || !ap->am_mnt) {
756     /*
757      * If we are shutting down then it is likely
758      * that this node has disappeared because of
759      * a fast timeout.  To avoid things thrashing
760      * just pretend it doesn't exist at all.  If
761      * ESTALE is returned, some NFS clients just
762      * keep retrying (stupid or what - if it's
763      * stale now, what's it going to be in 5 minutes?)
764      */
765     if (amd_state == Finishing)
766       *rp = ENOENT;
767     else
768       *rp = ESTALE;
769     amd_stats.d_stale++;
770   }
771
772   return ap;
773 }
774
775
776 static am_node *
777 fh_to_mp(am_nfs_fh *fhp)
778 {
779   int dummy;
780
781   return fh_to_mp3(fhp, &dummy, VLOOK_CREATE);
782 }
783
784
785 /*
786  * Convert from automount node to file handle.
787  */
788 void
789 mp_to_fh(am_node *mp, am_nfs_fh *fhp)
790 {
791   u_int pathlen;
792   struct am_fh *fp = (struct am_fh *) fhp;
793
794   memset((char *) fhp, 0, sizeof(am_nfs_fh));
795
796   /* Store the generation number */
797   fp->fhh_gen = mp->am_gen;
798
799   pathlen = strlen(mp->am_path);
800   if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) {
801     /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */
802
803     /*
804      * Because fhp is treated as a filehandle we use memcpy instead of
805      * xstrlcpy.
806      */
807     memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */
808   } else {
809     /*
810      * Take the process id
811      */
812     fp->u.s.fhh_pid = get_server_pid();
813
814     /*
815      * ... the map number
816      */
817     fp->u.s.fhh_id = mp->am_mapno;
818
819     /*
820      * ... and the generation number (previously stored)
821      * to make a "unique" triple that will never
822      * be reallocated except across reboots (which doesn't matter)
823      * or if we are unlucky enough to be given the same
824      * pid as a previous amd (very unlikely).
825      */
826     /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */
827   }
828 }