Home | History | Annotate | Line # | Download | only in amd
      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 *
     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
    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 *
    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 *
    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
    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 *
    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
    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 *
    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 *
    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
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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
    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 *
    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 *
    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 *
    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 *
    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 *
    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
    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
    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
    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 
    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 
    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 
    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 
    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 
    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 
   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
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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