138494Sobrien/* 2310490Scy * Copyright (c) 1997-2014 Erez Zadok 338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1990 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 19310490Scy * 3. Neither the name of the University nor the names of its contributors 2038494Sobrien * may be used to endorse or promote products derived from this software 2138494Sobrien * without specific prior written permission. 2238494Sobrien * 2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2638494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3338494Sobrien * SUCH DAMAGE. 3438494Sobrien * 3538494Sobrien * 36174294Sobrien * File: am-utils/amd/ops_nfs.c 3738494Sobrien * 3838494Sobrien */ 3938494Sobrien 4038494Sobrien/* 4138494Sobrien * Network file system 4238494Sobrien */ 4338494Sobrien 4438494Sobrien#ifdef HAVE_CONFIG_H 4538494Sobrien# include <config.h> 4638494Sobrien#endif /* HAVE_CONFIG_H */ 4738494Sobrien#include <am_defs.h> 4838494Sobrien#include <amd.h> 4938494Sobrien 5038494Sobrien/* 5138494Sobrien * Convert from nfsstat to UN*X error code 5238494Sobrien */ 5338494Sobrien#define unx_error(e) ((int)(e)) 5438494Sobrien 5538494Sobrien/* 5638494Sobrien * FH_TTL is the time a file handle will remain in the cache since 5738494Sobrien * last being used. If the file handle becomes invalid, then it 5838494Sobrien * will be flushed anyway. 5938494Sobrien */ 6038494Sobrien#define FH_TTL (5 * 60) /* five minutes */ 6138494Sobrien#define FH_TTL_ERROR (30) /* 30 seconds */ 62174294Sobrien#define FHID_ALLOC() (++fh_id) 6338494Sobrien 6438494Sobrien/* 6538494Sobrien * The NFS layer maintains a cache of file handles. 6638494Sobrien * This is *fundamental* to the implementation and 6738494Sobrien * also allows quick remounting when a filesystem 6838494Sobrien * is accessed soon after timing out. 6938494Sobrien * 7038494Sobrien * The NFS server layer knows to flush this cache 7138494Sobrien * when a server goes down so avoiding stale handles. 7238494Sobrien * 7338494Sobrien * Each cache entry keeps a hard reference to 7438494Sobrien * the corresponding server. This ensures that 7538494Sobrien * the server keepalive information is maintained. 7638494Sobrien * 7738494Sobrien * The copy of the sockaddr_in here is taken so 7838494Sobrien * that the port can be twiddled to talk to mountd 7938494Sobrien * instead of portmap or the NFS server as used 8038494Sobrien * elsewhere. 8138494Sobrien * The port# is flushed if a server goes down. 8238494Sobrien * The IP address is never flushed - we assume 8338494Sobrien * that the address of a mounted machine never 8438494Sobrien * changes. If it does, then you have other 8538494Sobrien * problems... 8638494Sobrien */ 8738494Sobrientypedef struct fh_cache fh_cache; 8838494Sobrienstruct fh_cache { 8938494Sobrien qelem fh_q; /* List header */ 90174294Sobrien wchan_t fh_wchan; /* Wait channel */ 9138494Sobrien int fh_error; /* Valid data? */ 9238494Sobrien int fh_id; /* Unique id */ 9338494Sobrien int fh_cid; /* Callout id */ 9438494Sobrien u_long fh_nfs_version; /* highest NFS version on host */ 9538494Sobrien am_nfs_handle_t fh_nfs_handle; /* Handle on filesystem */ 96174294Sobrien int fh_status; /* Status of last rpc */ 9738494Sobrien struct sockaddr_in fh_sin; /* Address of mountd */ 9838494Sobrien fserver *fh_fs; /* Server holding filesystem */ 9938494Sobrien char *fh_path; /* Filesystem on host */ 10038494Sobrien}; 10138494Sobrien 10238494Sobrien/* forward definitions */ 103174294Sobrienstatic int nfs_init(mntfs *mf); 104174294Sobrienstatic char *nfs_match(am_opts *fo); 105174294Sobrienstatic int nfs_mount(am_node *am, mntfs *mf); 106174294Sobrienstatic int nfs_umount(am_node *am, mntfs *mf); 107174294Sobrienstatic void nfs_umounted(mntfs *mf); 108174294Sobrienstatic int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, wchan_t wchan); 109174294Sobrienstatic int webnfs_lookup(fh_cache *fp, fwd_fun f, wchan_t wchan); 11038494Sobrienstatic int fh_id = 0; 11138494Sobrien 112310490Scy/* 113310490Scy * clamp the filehandle version to 3, so that we can fail back to nfsv3 114310490Scy * since nfsv4 does not have file handles 115310490Scy */ 116310490Scy#define SET_FH_VERSION(fs) \ 117310490Scy (fs)->fs_version > NFS_VERSION3 ? NFS_VERSION3 : (fs)->fs_version; 118310490Scy 11938494Sobrien/* globals */ 12038494SobrienAUTH *nfs_auth; 12138494Sobrienqelem fh_head = {&fh_head, &fh_head}; 12238494Sobrien 12338494Sobrien/* 12438494Sobrien * Network file system operations 12538494Sobrien */ 12638494Sobrienam_ops nfs_ops = 12738494Sobrien{ 12838494Sobrien "nfs", 12938494Sobrien nfs_match, 13038494Sobrien nfs_init, 131174294Sobrien nfs_mount, 132174294Sobrien nfs_umount, 133174294Sobrien amfs_error_lookup_child, 134174294Sobrien amfs_error_mount_child, 13538494Sobrien amfs_error_readdir, 13638494Sobrien 0, /* nfs_readlink */ 13738494Sobrien 0, /* nfs_mounted */ 13838494Sobrien nfs_umounted, 13938494Sobrien find_nfs_srvr, 140174294Sobrien 0, /* nfs_get_wchan */ 141174294Sobrien FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ 142174294Sobrien#ifdef HAVE_FS_AUTOFS 143174294Sobrien AUTOFS_NFS_FS_FLAGS, 144174294Sobrien#endif /* HAVE_FS_AUTOFS */ 14538494Sobrien}; 14638494Sobrien 14738494Sobrien 14838494Sobrienstatic fh_cache * 149174294Sobrienfind_nfs_fhandle_cache(opaque_t arg, int done) 15038494Sobrien{ 151310490Scy fh_cache *fp, *fp2 = NULL; 152174294Sobrien int id = (long) arg; /* for 64-bit archs */ 15338494Sobrien 15438494Sobrien ITER(fp, fh_cache, &fh_head) { 15538494Sobrien if (fp->fh_id == id) { 15638494Sobrien fp2 = fp; 15738494Sobrien break; 15838494Sobrien } 15938494Sobrien } 16038494Sobrien 16138494Sobrien if (fp2) { 16251292Sobrien dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path); 16338494Sobrien } else { 16438494Sobrien dlog("fh cache search failed"); 16538494Sobrien } 16638494Sobrien 16738494Sobrien if (fp2 && !done) { 16838494Sobrien fp2->fh_error = ETIMEDOUT; 16938494Sobrien return 0; 17038494Sobrien } 17138494Sobrien 17238494Sobrien return fp2; 17338494Sobrien} 17438494Sobrien 17538494Sobrien 17638494Sobrien/* 177174294Sobrien * Called when a filehandle appears via the mount protocol 17838494Sobrien */ 17938494Sobrienstatic void 180174294Sobriengot_nfs_fh_mount(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) 18138494Sobrien{ 18238494Sobrien fh_cache *fp; 183174294Sobrien struct fhstatus res; 184174294Sobrien#ifdef HAVE_FS_NFS3 185174294Sobrien struct am_mountres3 res3; 186174294Sobrien#endif /* HAVE_FS_NFS3 */ 18738494Sobrien 188174294Sobrien fp = find_nfs_fhandle_cache(arg, done); 18938494Sobrien if (!fp) 19038494Sobrien return; 19138494Sobrien 19238494Sobrien /* 19338494Sobrien * retrieve the correct RPC reply for the file handle, based on the 19438494Sobrien * NFS protocol version. 19538494Sobrien */ 19638494Sobrien#ifdef HAVE_FS_NFS3 197174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 198174294Sobrien memset(&res3, 0, sizeof(res3)); 199174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, 200174294Sobrien (XDRPROC_T_TYPE) xdr_am_mountres3); 201174294Sobrien fp->fh_status = unx_error(res3.fhs_status); 202174294Sobrien memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); 203174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; 204174294Sobrien memmove(fp->fh_nfs_handle.v3.am_fh3_data, 205174294Sobrien res3.mountres3_u.mountinfo.fhandle.fhandle3_val, 206174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length); 207310490Scy 208310490Scy XFREE(res3.mountres3_u.mountinfo.fhandle.fhandle3_val); 209310490Scy if (res3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val) 210310490Scy XFREE(res3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val); 211174294Sobrien } else { 21238494Sobrien#endif /* HAVE_FS_NFS3 */ 213174294Sobrien memset(&res, 0, sizeof(res)); 214174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, 21538494Sobrien (XDRPROC_T_TYPE) xdr_fhstatus); 216174294Sobrien fp->fh_status = unx_error(res.fhs_status); 217174294Sobrien memmove(&fp->fh_nfs_handle.v2, &res.fhs_fh, NFS_FHSIZE); 218174294Sobrien#ifdef HAVE_FS_NFS3 219174294Sobrien } 220174294Sobrien#endif /* HAVE_FS_NFS3 */ 22138494Sobrien 22238494Sobrien if (!fp->fh_error) { 22338494Sobrien dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 224174294Sobrien } else { 225174294Sobrien plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 226174294Sobrien /* 227174294Sobrien * Force the error to be EACCES. It's debatable whether it should be 228174294Sobrien * ENOENT instead, but the server really doesn't give us any clues, and 229174294Sobrien * EACCES is more in line with the "filehandle denied" message. 230174294Sobrien */ 231174294Sobrien fp->fh_error = EACCES; 232174294Sobrien } 23338494Sobrien 234174294Sobrien /* 235174294Sobrien * Wakeup anything sleeping on this filehandle 236174294Sobrien */ 237174294Sobrien if (fp->fh_wchan) { 238174294Sobrien dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); 239174294Sobrien wakeup(fp->fh_wchan); 240174294Sobrien } 241174294Sobrien} 242174294Sobrien 243174294Sobrien 244174294Sobrien/* 245174294Sobrien * Called when a filehandle appears via WebNFS 246174294Sobrien */ 247174294Sobrienstatic void 248174294Sobriengot_nfs_fh_webnfs(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) 249174294Sobrien{ 250174294Sobrien fh_cache *fp; 251174294Sobrien nfsdiropres res; 252174294Sobrien#ifdef HAVE_FS_NFS3 253174294Sobrien am_LOOKUP3res res3; 254174294Sobrien#endif /* HAVE_FS_NFS3 */ 255174294Sobrien 256174294Sobrien fp = find_nfs_fhandle_cache(arg, done); 257174294Sobrien if (!fp) 258174294Sobrien return; 259174294Sobrien 260174294Sobrien /* 261174294Sobrien * retrieve the correct RPC reply for the file handle, based on the 262174294Sobrien * NFS protocol version. 263174294Sobrien */ 264174294Sobrien#ifdef HAVE_FS_NFS3 265174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 266174294Sobrien memset(&res3, 0, sizeof(res3)); 267174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, 268174294Sobrien (XDRPROC_T_TYPE) xdr_am_LOOKUP3res); 269174294Sobrien fp->fh_status = unx_error(res3.status); 270174294Sobrien memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); 271174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length = res3.res_u.ok.object.am_fh3_length; 272174294Sobrien memmove(fp->fh_nfs_handle.v3.am_fh3_data, 273174294Sobrien res3.res_u.ok.object.am_fh3_data, 274174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length); 275174294Sobrien } else { 276174294Sobrien#endif /* HAVE_FS_NFS3 */ 277174294Sobrien memset(&res, 0, sizeof(res)); 278174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, 279174294Sobrien (XDRPROC_T_TYPE) xdr_diropres); 280174294Sobrien fp->fh_status = unx_error(res.dr_status); 281174294Sobrien memmove(&fp->fh_nfs_handle.v2, &res.dr_u.dr_drok_u.drok_fhandle, NFS_FHSIZE); 282174294Sobrien#ifdef HAVE_FS_NFS3 283174294Sobrien } 284174294Sobrien#endif /* HAVE_FS_NFS3 */ 285174294Sobrien 286174294Sobrien if (!fp->fh_error) { 287174294Sobrien dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 288174294Sobrien } else { 289174294Sobrien plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 29038494Sobrien /* 291174294Sobrien * Force the error to be EACCES. It's debatable whether it should be 292174294Sobrien * ENOENT instead, but the server really doesn't give us any clues, and 293174294Sobrien * EACCES is more in line with the "filehandle denied" message. 29438494Sobrien */ 295174294Sobrien fp->fh_error = EACCES; 29638494Sobrien } 297174294Sobrien 298174294Sobrien /* 299174294Sobrien * Wakeup anything sleeping on this filehandle 300174294Sobrien */ 301174294Sobrien if (fp->fh_wchan) { 302174294Sobrien dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); 303174294Sobrien wakeup(fp->fh_wchan); 304174294Sobrien } 30538494Sobrien} 30638494Sobrien 30738494Sobrien 30838494Sobrienvoid 30938494Sobrienflush_nfs_fhandle_cache(fserver *fs) 31038494Sobrien{ 31138494Sobrien fh_cache *fp; 31238494Sobrien 31338494Sobrien ITER(fp, fh_cache, &fh_head) { 314174294Sobrien if (fp->fh_fs == fs || fs == NULL) { 315174294Sobrien /* 316174294Sobrien * Only invalidate port info for non-WebNFS servers 317174294Sobrien */ 318174294Sobrien if (!(fp->fh_fs->fs_flags & FSF_WEBNFS)) 319174294Sobrien fp->fh_sin.sin_port = (u_short) 0; 32038494Sobrien fp->fh_error = -1; 32138494Sobrien } 32238494Sobrien } 32338494Sobrien} 32438494Sobrien 32538494Sobrien 32638494Sobrienstatic void 327174294Sobriendiscard_fh(opaque_t arg) 32838494Sobrien{ 329174294Sobrien fh_cache *fp = (fh_cache *) arg; 33038494Sobrien 33138494Sobrien rem_que(&fp->fh_q); 33238494Sobrien if (fp->fh_fs) { 33338494Sobrien dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 33438494Sobrien free_srvr(fp->fh_fs); 33538494Sobrien } 336310490Scy XFREE(fp->fh_path); 33738494Sobrien XFREE(fp); 33838494Sobrien} 33938494Sobrien 34038494Sobrien 34138494Sobrien/* 34238494Sobrien * Determine the file handle for a node 34338494Sobrien */ 34438494Sobrienstatic int 345174294Sobrienprime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, mntfs *mf) 34638494Sobrien{ 347310490Scy fh_cache *fp, *fp_save = NULL; 34838494Sobrien int error; 34938494Sobrien int reuse_id = FALSE; 35038494Sobrien 35138494Sobrien dlog("Searching cache for %s:%s", fs->fs_host, path); 35238494Sobrien 35338494Sobrien /* 35438494Sobrien * First search the cache 35538494Sobrien */ 35638494Sobrien ITER(fp, fh_cache, &fh_head) { 357174294Sobrien if (fs != fp->fh_fs || !STREQ(path, fp->fh_path)) 358174294Sobrien continue; /* skip to next ITER item */ 359174294Sobrien /* else we got a match */ 360174294Sobrien switch (fp->fh_error) { 361174294Sobrien case 0: 362174294Sobrien plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version); 363174294Sobrien 364174294Sobrien error = fp->fh_error = fp->fh_status; 365174294Sobrien 366174294Sobrien if (error == 0) { 367174294Sobrien if (mf->mf_flags & MFF_NFS_SCALEDOWN) { 368174294Sobrien fp_save = fp; 369174294Sobrien /* XXX: why reuse the ID? */ 370174294Sobrien reuse_id = TRUE; 371174294Sobrien break; 372174294Sobrien } 373174294Sobrien 374174294Sobrien if (fhbuf) { 37538494Sobrien#ifdef HAVE_FS_NFS3 376174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 377174294Sobrien memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3), 378174294Sobrien sizeof(fp->fh_nfs_handle.v3)); 379174294Sobrien } else 38038494Sobrien#endif /* HAVE_FS_NFS3 */ 381174294Sobrien { 38238494Sobrien memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2), 38338494Sobrien sizeof(fp->fh_nfs_handle.v2)); 384174294Sobrien } 38538494Sobrien } 386174294Sobrien if (fp->fh_cid) 387174294Sobrien untimeout(fp->fh_cid); 388174294Sobrien fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); 389174294Sobrien } else if (error == EACCES) { 39038494Sobrien /* 391174294Sobrien * Now decode the file handle return code. 39238494Sobrien */ 393174294Sobrien plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"", 394174294Sobrien fs->fs_host, path); 395174294Sobrien } else { 396174294Sobrien errno = error; /* XXX */ 397174294Sobrien plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m", 398174294Sobrien fs->fs_host, path); 399174294Sobrien } 40038494Sobrien 401174294Sobrien /* 402174294Sobrien * The error was returned from the remote mount daemon. 403174294Sobrien * Policy: this error will be cached for now... 404174294Sobrien */ 405174294Sobrien return error; 40638494Sobrien 407174294Sobrien case -1: 408174294Sobrien /* 409174294Sobrien * Still thinking about it, but we can re-use. 410174294Sobrien */ 411174294Sobrien fp_save = fp; 412174294Sobrien reuse_id = TRUE; 41338494Sobrien break; 41438494Sobrien 415174294Sobrien default: 416174294Sobrien /* 417174294Sobrien * Return the error. 418174294Sobrien * Policy: make sure we recompute if required again 419174294Sobrien * in case this was caused by a network failure. 420174294Sobrien * This can thrash mountd's though... If you find 421174294Sobrien * your mountd going slowly then: 422174294Sobrien * 1. Add a fork() loop to main. 423174294Sobrien * 2. Remove the call to innetgr() and don't use 424174294Sobrien * netgroups, especially if you don't use YP. 425174294Sobrien */ 426174294Sobrien error = fp->fh_error; 427174294Sobrien fp->fh_error = -1; 428174294Sobrien return error; 429174294Sobrien } /* end of switch statement */ 430174294Sobrien } /* end of ITER loop */ 431174294Sobrien 43238494Sobrien /* 43338494Sobrien * Not in cache 43438494Sobrien */ 43538494Sobrien if (fp_save) { 43638494Sobrien fp = fp_save; 43738494Sobrien /* 43838494Sobrien * Re-use existing slot 43938494Sobrien */ 44038494Sobrien untimeout(fp->fh_cid); 44138494Sobrien free_srvr(fp->fh_fs); 44238494Sobrien XFREE(fp->fh_path); 44338494Sobrien } else { 44438494Sobrien fp = ALLOC(struct fh_cache); 44538494Sobrien memset((voidp) fp, 0, sizeof(struct fh_cache)); 44638494Sobrien ins_que(&fp->fh_q, &fh_head); 44738494Sobrien } 44838494Sobrien if (!reuse_id) 449174294Sobrien fp->fh_id = FHID_ALLOC(); 450174294Sobrien fp->fh_wchan = get_mntfs_wchan(mf); 45138494Sobrien fp->fh_error = -1; 452174294Sobrien fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); 45338494Sobrien 45438494Sobrien /* 455174294Sobrien * If fs->fs_ip is null, remote server is probably down. 45638494Sobrien */ 45738494Sobrien if (!fs->fs_ip) { 45838494Sobrien /* Mark the fileserver down and invalid again */ 45938494Sobrien fs->fs_flags &= ~FSF_VALID; 46038494Sobrien fs->fs_flags |= FSF_DOWN; 46138494Sobrien error = AM_ERRNO_HOST_DOWN; 46238494Sobrien return error; 46338494Sobrien } 46438494Sobrien 46538494Sobrien /* 466174294Sobrien * Either fp has been freshly allocated or the address has changed. 467174294Sobrien * Initialize address and nfs version. Don't try to re-use the port 468174294Sobrien * information unless using WebNFS where the port is fixed either by 469174294Sobrien * the spec or the "port" mount option. 47038494Sobrien */ 47138494Sobrien if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { 47238494Sobrien fp->fh_sin = *fs->fs_ip; 473174294Sobrien if (!(mf->mf_flags & MFF_WEBNFS)) 474174294Sobrien fp->fh_sin.sin_port = 0; 475310490Scy fp->fh_nfs_version = SET_FH_VERSION(fs); 47638494Sobrien } 477174294Sobrien 47838494Sobrien fp->fh_fs = dup_srvr(fs); 479310490Scy fp->fh_path = xstrdup(path); 48038494Sobrien 481174294Sobrien if (mf->mf_flags & MFF_WEBNFS) 482174294Sobrien error = webnfs_lookup(fp, got_nfs_fh_webnfs, get_mntfs_wchan(mf)); 483174294Sobrien else 484174294Sobrien error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh_mount, get_mntfs_wchan(mf)); 48538494Sobrien if (error) { 48638494Sobrien /* 48738494Sobrien * Local error - cache for a short period 48838494Sobrien * just to prevent thrashing. 48938494Sobrien */ 49038494Sobrien untimeout(fp->fh_cid); 49138494Sobrien fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, 492174294Sobrien discard_fh, (opaque_t) fp); 49338494Sobrien fp->fh_error = error; 49438494Sobrien } else { 49538494Sobrien error = fp->fh_error; 49638494Sobrien } 49738494Sobrien 49838494Sobrien return error; 49938494Sobrien} 50038494Sobrien 50138494Sobrien 50238494Sobrienint 50338494Sobrienmake_nfs_auth(void) 50438494Sobrien{ 50538494Sobrien AUTH_CREATE_GIDLIST_TYPE group_wheel = 0; 50638494Sobrien 50738494Sobrien /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */ 50838494Sobrien 50938494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 51038494Sobrien if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { 51182794Sobrien plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); 51238494Sobrien nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel); 51338494Sobrien } else { 51438494Sobrien nfs_auth = authsys_create_default(); 51538494Sobrien } 51638494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */ 51738494Sobrien if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { 51882794Sobrien plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); 51938494Sobrien nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel); 52038494Sobrien } else { 52138494Sobrien nfs_auth = authunix_create_default(); 52238494Sobrien } 52338494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 52438494Sobrien 52538494Sobrien if (!nfs_auth) 52638494Sobrien return ENOBUFS; 52738494Sobrien 52838494Sobrien return 0; 52938494Sobrien} 53038494Sobrien 53138494Sobrien 53238494Sobrienstatic int 533174294Sobriencall_mountd(fh_cache *fp, u_long proc, fwd_fun fun, wchan_t wchan) 53438494Sobrien{ 53538494Sobrien struct rpc_msg mnt_msg; 53638494Sobrien int len; 537174294Sobrien char iobuf[UDPMSGSIZE]; 53838494Sobrien int error; 53938494Sobrien u_long mnt_version; 54038494Sobrien 54138494Sobrien if (!nfs_auth) { 54238494Sobrien error = make_nfs_auth(); 54338494Sobrien if (error) 54438494Sobrien return error; 54538494Sobrien } 54638494Sobrien 54738494Sobrien if (fp->fh_sin.sin_port == 0) { 548174294Sobrien u_short mountd_port; 549174294Sobrien error = get_mountd_port(fp->fh_fs, &mountd_port, wchan); 55038494Sobrien if (error) 55138494Sobrien return error; 552174294Sobrien fp->fh_sin.sin_port = mountd_port; 553310490Scy dlog("%s: New %d mountd port", __func__, fp->fh_sin.sin_port); 554310490Scy } else 555310490Scy dlog("%s: Already had %d mountd port", __func__, fp->fh_sin.sin_port); 55638494Sobrien 55738494Sobrien /* find the right version of the mount protocol */ 55838494Sobrien#ifdef HAVE_FS_NFS3 55938494Sobrien if (fp->fh_nfs_version == NFS_VERSION3) 560174294Sobrien mnt_version = AM_MOUNTVERS3; 56138494Sobrien else 56238494Sobrien#endif /* HAVE_FS_NFS3 */ 56338494Sobrien mnt_version = MOUNTVERS; 56438494Sobrien plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d", 56551292Sobrien (int) fp->fh_nfs_version, (int) mnt_version); 56638494Sobrien 56738494Sobrien rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL); 56838494Sobrien len = make_rpc_packet(iobuf, 56938494Sobrien sizeof(iobuf), 57038494Sobrien proc, 57138494Sobrien &mnt_msg, 57238494Sobrien (voidp) &fp->fh_path, 57338494Sobrien (XDRPROC_T_TYPE) xdr_nfspath, 57438494Sobrien nfs_auth); 57538494Sobrien 57638494Sobrien if (len > 0) { 57738494Sobrien error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), 578174294Sobrien iobuf, 57938494Sobrien len, 58038494Sobrien &fp->fh_sin, 58138494Sobrien &fp->fh_sin, 582174294Sobrien (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ 583174294Sobrien fun); 58438494Sobrien } else { 58538494Sobrien error = -len; 58638494Sobrien } 58738494Sobrien 588174294Sobrien /* 589174294Sobrien * It may be the case that we're sending to the wrong MOUNTD port. This 590174294Sobrien * occurs if mountd is restarted on the server after the port has been 591174294Sobrien * looked up and stored in the filehandle cache somewhere. The correct 592174294Sobrien * solution, if we're going to cache port numbers is to catch the ICMP 593174294Sobrien * port unreachable reply from the server and cause the portmap request 594174294Sobrien * to be redone. The quick solution here is to invalidate the MOUNTD 595174294Sobrien * port. 596174294Sobrien */ 59738494Sobrien fp->fh_sin.sin_port = 0; 59838494Sobrien 59938494Sobrien return error; 60038494Sobrien} 60138494Sobrien 60238494Sobrien 603174294Sobrienstatic int 604174294Sobrienwebnfs_lookup(fh_cache *fp, fwd_fun fun, wchan_t wchan) 605174294Sobrien{ 606174294Sobrien struct rpc_msg wnfs_msg; 607174294Sobrien int len; 608174294Sobrien char iobuf[UDPMSGSIZE]; 609174294Sobrien int error; 610174294Sobrien u_long proc; 611174294Sobrien XDRPROC_T_TYPE xdr_fn; 612174294Sobrien voidp argp; 613174294Sobrien nfsdiropargs args; 614174294Sobrien#ifdef HAVE_FS_NFS3 615174294Sobrien am_LOOKUP3args args3; 616310490Scy#endif /* HAVE_FS_NFS3 */ 617174294Sobrien char *wnfs_path; 618174294Sobrien size_t l; 619174294Sobrien 620174294Sobrien if (!nfs_auth) { 621174294Sobrien error = make_nfs_auth(); 622174294Sobrien if (error) 623174294Sobrien return error; 624174294Sobrien } 625174294Sobrien 626174294Sobrien if (fp->fh_sin.sin_port == 0) { 627174294Sobrien /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */ 628174294Sobrien plog(XLOG_WARNING, "webnfs_lookup: port == 0 for nfs on %s, fixed", 629174294Sobrien fp->fh_fs->fs_host); 630174294Sobrien fp->fh_sin.sin_port = htons(NFS_PORT); 631174294Sobrien } 632174294Sobrien 633174294Sobrien /* 634174294Sobrien * Use native path like the rest of amd (cf. RFC 2054, 6.1). 635174294Sobrien */ 636174294Sobrien l = strlen(fp->fh_path) + 2; 637174294Sobrien wnfs_path = (char *) xmalloc(l); 638174294Sobrien wnfs_path[0] = 0x80; 639174294Sobrien xstrlcpy(wnfs_path + 1, fp->fh_path, l - 1); 640174294Sobrien 641174294Sobrien /* find the right program and lookup procedure */ 642174294Sobrien#ifdef HAVE_FS_NFS3 643174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 644174294Sobrien proc = AM_NFSPROC3_LOOKUP; 645174294Sobrien xdr_fn = (XDRPROC_T_TYPE) xdr_am_LOOKUP3args; 646174294Sobrien argp = &args3; 647174294Sobrien /* WebNFS public file handle */ 648174294Sobrien args3.what.dir.am_fh3_length = 0; 649174294Sobrien args3.what.name = wnfs_path; 650174294Sobrien } else { 651174294Sobrien#endif /* HAVE_FS_NFS3 */ 652174294Sobrien proc = NFSPROC_LOOKUP; 653174294Sobrien xdr_fn = (XDRPROC_T_TYPE) xdr_diropargs; 654174294Sobrien argp = &args; 655174294Sobrien /* WebNFS public file handle */ 656174294Sobrien memset(&args.da_fhandle, 0, NFS_FHSIZE); 657174294Sobrien args.da_name = wnfs_path; 658174294Sobrien#ifdef HAVE_FS_NFS3 659174294Sobrien } 660174294Sobrien#endif /* HAVE_FS_NFS3 */ 661174294Sobrien 662174294Sobrien plog(XLOG_INFO, "webnfs_lookup: NFS version %d", (int) fp->fh_nfs_version); 663174294Sobrien 664174294Sobrien rpc_msg_init(&wnfs_msg, NFS_PROGRAM, fp->fh_nfs_version, proc); 665174294Sobrien len = make_rpc_packet(iobuf, 666174294Sobrien sizeof(iobuf), 667174294Sobrien proc, 668174294Sobrien &wnfs_msg, 669174294Sobrien argp, 670174294Sobrien (XDRPROC_T_TYPE) xdr_fn, 671174294Sobrien nfs_auth); 672174294Sobrien 673174294Sobrien if (len > 0) { 674174294Sobrien error = fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS, fp->fh_id), 675174294Sobrien iobuf, 676174294Sobrien len, 677174294Sobrien &fp->fh_sin, 678174294Sobrien &fp->fh_sin, 679174294Sobrien (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ 680174294Sobrien fun); 681174294Sobrien } else { 682174294Sobrien error = -len; 683174294Sobrien } 684174294Sobrien 685174294Sobrien XFREE(wnfs_path); 686174294Sobrien return error; 687174294Sobrien} 688174294Sobrien 689174294Sobrien 69038494Sobrien/* 69138494Sobrien * NFS needs the local filesystem, remote filesystem 69238494Sobrien * remote hostname. 69338494Sobrien * Local filesystem defaults to remote and vice-versa. 69438494Sobrien */ 695174294Sobrienstatic char * 69638494Sobriennfs_match(am_opts *fo) 69738494Sobrien{ 69838494Sobrien char *xmtab; 699174294Sobrien size_t l; 70038494Sobrien 70138494Sobrien if (fo->opt_fs && !fo->opt_rfs) 70238494Sobrien fo->opt_rfs = fo->opt_fs; 70338494Sobrien if (!fo->opt_rfs) { 70438494Sobrien plog(XLOG_USER, "nfs: no remote filesystem specified"); 70538494Sobrien return NULL; 70638494Sobrien } 70738494Sobrien if (!fo->opt_rhost) { 70838494Sobrien plog(XLOG_USER, "nfs: no remote host specified"); 70938494Sobrien return NULL; 71038494Sobrien } 71138494Sobrien 71238494Sobrien /* 71338494Sobrien * Determine magic cookie to put in mtab 71438494Sobrien */ 715174294Sobrien l = strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2; 716174294Sobrien xmtab = (char *) xmalloc(l); 717174294Sobrien xsnprintf(xmtab, l, "%s:%s", fo->opt_rhost, fo->opt_rfs); 71838494Sobrien dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 71938494Sobrien fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 72038494Sobrien 72138494Sobrien return xmtab; 72238494Sobrien} 72338494Sobrien 72438494Sobrien 72538494Sobrien/* 72638494Sobrien * Initialize am structure for nfs 72738494Sobrien */ 728174294Sobrienstatic int 72938494Sobriennfs_init(mntfs *mf) 73038494Sobrien{ 73138494Sobrien int error; 73238494Sobrien am_nfs_handle_t fhs; 73338494Sobrien char *colon; 73438494Sobrien 735310490Scy#ifdef NO_FALLBACK 736310490Scy /* 737310490Scy * We don't need file handles for NFS version 4, but we can fall back to 738310490Scy * version 3, so we allocate anyway 739310490Scy */ 740310490Scy#ifdef HAVE_FS_NFS4 741310490Scy if (mf->mf_server->fs_version == NFS_VERSION4) 742310490Scy return 0; 743310490Scy#endif /* HAVE_FS_NFS4 */ 744310490Scy#endif /* NO_FALLBACK */ 745310490Scy 746174294Sobrien if (mf->mf_private) { 747174294Sobrien if (mf->mf_flags & MFF_NFS_SCALEDOWN) { 748174294Sobrien fserver *fs; 74938494Sobrien 750174294Sobrien /* tell remote mountd that we're done with this filehandle */ 751174294Sobrien mf->mf_ops->umounted(mf); 752174294Sobrien 753174294Sobrien mf->mf_prfree(mf->mf_private); 754310490Scy mf->mf_private = NULL; 755310490Scy mf->mf_prfree = NULL; 756310490Scy 757174294Sobrien fs = mf->mf_ops->ffserver(mf); 758174294Sobrien free_srvr(mf->mf_server); 759174294Sobrien mf->mf_server = fs; 760174294Sobrien } else 761174294Sobrien return 0; 762174294Sobrien } 763174294Sobrien 76438494Sobrien colon = strchr(mf->mf_info, ':'); 76538494Sobrien if (colon == 0) 76638494Sobrien return ENOENT; 76738494Sobrien 768174294Sobrien error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, mf); 76938494Sobrien if (!error) { 770174294Sobrien mf->mf_private = (opaque_t) ALLOC(am_nfs_handle_t); 771174294Sobrien mf->mf_prfree = (void (*)(opaque_t)) free; 77238494Sobrien memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs)); 77338494Sobrien } 77438494Sobrien return error; 77538494Sobrien} 77638494Sobrien 77738494Sobrien 77838494Sobrienint 779174294Sobrienmount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) 78038494Sobrien{ 78138494Sobrien MTYPE_TYPE type; 78238494Sobrien char *colon; 783174294Sobrien char *xopts=NULL, transp_timeo_opts[40], transp_retrans_opts[40]; 78438494Sobrien char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; 78538494Sobrien fserver *fs = mf->mf_server; 78638494Sobrien u_long nfs_version = fs->fs_version; 78738494Sobrien char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */ 788174294Sobrien int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; 78938494Sobrien int error; 79038494Sobrien int genflags; 79138494Sobrien int retry; 792174294Sobrien int proto = AMU_TYPE_NONE; 79338494Sobrien mntent_t mnt; 794310490Scy void *argsp; 79538494Sobrien nfs_args_t nfs_args; 796310490Scy#ifdef HAVE_FS_NFS4 797310490Scy nfs4_args_t nfs4_args; 798310490Scy#endif /* HAVE_FS_NFS4 */ 79938494Sobrien 80038494Sobrien /* 80138494Sobrien * Extract HOST name to give to kernel. 80238494Sobrien * Some systems like osf1/aix3/bsd44 variants may need old code 80338494Sobrien * for NFS_ARGS_NEEDS_PATH. 80438494Sobrien */ 80538494Sobrien if (!(colon = strchr(fs_name, ':'))) 80638494Sobrien return ENOENT; 80738494Sobrien#ifdef MOUNT_TABLE_ON_FILE 80838494Sobrien *colon = '\0'; 80938494Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 810174294Sobrien xstrlcpy(host, fs_name, sizeof(host)); 81138494Sobrien#ifdef MOUNT_TABLE_ON_FILE 81238494Sobrien *colon = ':'; 81338494Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 81438494Sobrien#ifdef MAXHOSTNAMELEN 81538494Sobrien /* most kernels have a name length restriction */ 81638494Sobrien if (strlen(host) >= MAXHOSTNAMELEN) 817174294Sobrien xstrlcpy(host + MAXHOSTNAMELEN - 3, "..", 818174294Sobrien sizeof(host) - MAXHOSTNAMELEN + 3); 81938494Sobrien#endif /* MAXHOSTNAMELEN */ 82038494Sobrien 821174294Sobrien /* 822174294Sobrien * Create option=VAL for udp/tcp specific timeouts and retrans values, but 823174294Sobrien * only if these options were specified. 824174294Sobrien */ 825174294Sobrien 826174294Sobrien transp_timeo_opts[0] = transp_retrans_opts[0] = '\0'; /* initialize */ 827174294Sobrien if (STREQ(nfs_proto, "udp")) 828174294Sobrien proto = AMU_TYPE_UDP; 829174294Sobrien else if (STREQ(nfs_proto, "tcp")) 830174294Sobrien proto = AMU_TYPE_TCP; 831174294Sobrien if (proto != AMU_TYPE_NONE) { 832174294Sobrien if (gopt.amfs_auto_timeo[proto] > 0) 833174294Sobrien xsnprintf(transp_timeo_opts, sizeof(transp_timeo_opts), "%s=%d,", 834174294Sobrien MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[proto]); 835174294Sobrien if (gopt.amfs_auto_retrans[proto] > 0) 836174294Sobrien xsnprintf(transp_retrans_opts, sizeof(transp_retrans_opts), "%s=%d,", 837174294Sobrien MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[proto]); 838174294Sobrien } 839174294Sobrien 84051292Sobrien if (mf->mf_remopts && *mf->mf_remopts && 84151292Sobrien !islocalnet(fs->fs_ip->sin_addr.s_addr)) { 84251292Sobrien plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts); 843174294Sobrien /* use transp_opts first, so map-specific opts will override */ 844174294Sobrien xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_remopts); 84551292Sobrien } else { 846174294Sobrien /* use transp_opts first, so map-specific opts will override */ 847174294Sobrien xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_mopts); 84851292Sobrien } 84938494Sobrien 85038494Sobrien memset((voidp) &mnt, 0, sizeof(mnt)); 851174294Sobrien mnt.mnt_dir = mntdir; 85238494Sobrien mnt.mnt_fsname = fs_name; 85338494Sobrien mnt.mnt_opts = xopts; 85438494Sobrien 85538494Sobrien /* 85638494Sobrien * Set mount types accordingly 85738494Sobrien */ 858310490Scy#ifdef HAVE_FS_NFS3 85938494Sobrien if (nfs_version == NFS_VERSION3) { 86038494Sobrien type = MOUNT_TYPE_NFS3; 86138494Sobrien /* 86238494Sobrien * Systems that include the mount table "vers" option generally do not 86338494Sobrien * set the mnttab entry to "nfs3", but to "nfs" and then they set 86438494Sobrien * "vers=3". Setting it to "nfs3" works, but it may break some things 86538494Sobrien * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix). 86638494Sobrien * So on those systems, set it to "nfs". 86738494Sobrien * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h). 86838494Sobrien */ 869310490Scy argsp = &nfs_args; 87038494Sobrien# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) 87138494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS; 87238494Sobrien# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ 87338494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS3; 87438494Sobrien# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ 875310490Scy# ifdef HAVE_FS_NFS4 876310490Scy } else if (nfs_version == NFS_VERSION4) { 877310490Scy argsp = &nfs4_args; 878310490Scy type = MOUNT_TYPE_NFS4; 879310490Scy mnt.mnt_type = MNTTAB_TYPE_NFS4; 880310490Scy# endif /* HAVE_FS_NFS4 */ 881310490Scy } else 882310490Scy#endif /* HAVE_FS_NFS3 */ 883310490Scy { 884310490Scy argsp = &nfs_args; 88538494Sobrien type = MOUNT_TYPE_NFS; 88638494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS; 88738494Sobrien } 88851292Sobrien plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version); 88938494Sobrien plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto); 89038494Sobrien 89138494Sobrien retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); 89238494Sobrien if (retry <= 0) 89338494Sobrien retry = 1; /* XXX */ 89438494Sobrien 89538494Sobrien genflags = compute_mount_flags(&mnt); 896174294Sobrien#ifdef HAVE_FS_AUTOFS 897174294Sobrien if (on_autofs) 898174294Sobrien genflags |= autofs_compute_mount_flags(&mnt); 899174294Sobrien#endif /* HAVE_FS_AUTOFS */ 90038494Sobrien 901310490Scy /* setup the many fields and flags within nfs_args */ 902310490Scy compute_nfs_args(argsp, 903310490Scy &mnt, 904310490Scy genflags, 905310490Scy NULL, /* struct netconfig *nfsncp */ 906310490Scy fs->fs_ip, 907310490Scy nfs_version, 908310490Scy nfs_proto, 909310490Scy fhp, 910310490Scy host, 911310490Scy fs_name); 91238494Sobrien 91338494Sobrien /* finally call the mounting function */ 914174294Sobrien if (amuDebug(D_TRACE)) { 915310490Scy print_nfs_args(argsp, nfs_version); 91682794Sobrien plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags); 91751292Sobrien } 918310490Scy error = mount_fs(&mnt, genflags, argsp, retry, type, 919310490Scy nfs_version, nfs_proto, mnttab_file_name, on_autofs); 920310490Scy XFREE(mnt.mnt_opts); 921310490Scy discard_nfs_args(argsp, nfs_version); 92238494Sobrien 923310490Scy#ifdef HAVE_FS_NFS4 924310490Scy# ifndef NO_FALLBACK 925310490Scy /* 926310490Scy * If we are using a v4 file handle, we try a v3 if we get back: 927310490Scy * ENOENT: NFS v4 has a different export list than v3 928310490Scy * EPERM: Kernels <= 2.6.18 return that, instead of ENOENT 929310490Scy */ 930310490Scy if ((error == ENOENT || error == EPERM) && nfs_version == NFS_VERSION4) { 931310490Scy plog(XLOG_DEBUG, "Could not find NFS 4 mount, trying again with NFS 3"); 932310490Scy fs->fs_version = NFS_VERSION3; 933310490Scy error = mount_nfs_fh(fhp, mntdir, fs_name, mf); 934310490Scy if (error) 935310490Scy fs->fs_version = NFS_VERSION4; 936310490Scy } 937310490Scy# endif /* NO_FALLBACK */ 938310490Scy#endif /* HAVE_FS_NFS4 */ 93938494Sobrien 94038494Sobrien return error; 94138494Sobrien} 94238494Sobrien 94338494Sobrien 94438494Sobrienstatic int 945174294Sobriennfs_mount(am_node *am, mntfs *mf) 94638494Sobrien{ 947174294Sobrien int error = 0; 948174294Sobrien mntent_t mnt; 949174294Sobrien 950310490Scy if (!mf->mf_private && mf->mf_server->fs_version != 4) { 951174294Sobrien plog(XLOG_ERROR, "Missing filehandle for %s", mf->mf_info); 95238494Sobrien return EINVAL; 95338494Sobrien } 95438494Sobrien 955310490Scy if (mf->mf_mopts == NULL) { 956310490Scy plog(XLOG_ERROR, "Missing mount options for %s", mf->mf_info); 957310490Scy return EINVAL; 958310490Scy } 959310490Scy 960174294Sobrien mnt.mnt_opts = mf->mf_mopts; 961174294Sobrien if (amu_hasmntopt(&mnt, "softlookup") || 962174294Sobrien (amu_hasmntopt(&mnt, "soft") && !amu_hasmntopt(&mnt, "nosoftlookup"))) 963174294Sobrien am->am_flags |= AMF_SOFTLOOKUP; 96438494Sobrien 965174294Sobrien error = mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, 966174294Sobrien mf->mf_mount, 967174294Sobrien mf->mf_info, 968174294Sobrien mf); 96938494Sobrien 97038494Sobrien if (error) { 97138494Sobrien errno = error; 97238494Sobrien dlog("mount_nfs: %m"); 97338494Sobrien } 97438494Sobrien 97538494Sobrien return error; 97638494Sobrien} 97738494Sobrien 97838494Sobrien 979174294Sobrienstatic int 980174294Sobriennfs_umount(am_node *am, mntfs *mf) 98138494Sobrien{ 982174294Sobrien int unmount_flags, new_unmount_flags, error; 98338494Sobrien 984310490Scy dlog("attempting nfs umount"); 985174294Sobrien unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 986174294Sobrien error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); 987174294Sobrien 988174294Sobrien#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) 98938494Sobrien /* 990174294Sobrien * If the attempt to unmount failed with EBUSY, and this fserver was 991174294Sobrien * marked for forced unmounts, then use forced/lazy unmounts. 992174294Sobrien */ 993174294Sobrien if (error == EBUSY && 994174294Sobrien gopt.flags & CFM_FORCED_UNMOUNTS && 995174294Sobrien mf->mf_server->fs_flags & FSF_FORCE_UNMOUNT) { 996174294Sobrien plog(XLOG_INFO, "EZK: nfs_umount: trying forced/lazy unmounts"); 997174294Sobrien /* 998174294Sobrien * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly 999174294Sobrien * incorrect. Multiple nodes may need to be timed out and restarted for 1000174294Sobrien * a single hung fserver. 1001174294Sobrien */ 1002174294Sobrien mf->mf_server->fs_flags &= ~FSF_FORCE_UNMOUNT; 1003174294Sobrien new_unmount_flags = unmount_flags | AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH; 1004174294Sobrien error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, new_unmount_flags); 1005174294Sobrien } 1006174294Sobrien#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */ 1007174294Sobrien 1008174294Sobrien /* 100938494Sobrien * Here is some code to unmount 'restarted' file systems. 101038494Sobrien * The restarted file systems are marked as 'nfs', not 101138494Sobrien * 'host', so we only have the map information for the 101238494Sobrien * the top-level mount. The unmount will fail (EBUSY) 101338494Sobrien * if there are anything else from the NFS server mounted 101438494Sobrien * below the mount-point. This code checks to see if there 101538494Sobrien * is anything mounted with the same prefix as the 101638494Sobrien * file system to be unmounted ("/a/b/c" when unmounting "/a/b"). 101738494Sobrien * If there is, and it is a 'restarted' file system, we unmount 101838494Sobrien * it. 101938494Sobrien * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93 102038494Sobrien */ 102138494Sobrien if (error == EBUSY) { 102238494Sobrien mntfs *new_mf; 102338494Sobrien int len = strlen(mf->mf_mount); 102438494Sobrien int didsome = 0; 102538494Sobrien 102638494Sobrien ITER(new_mf, mntfs, &mfhead) { 102738494Sobrien if (new_mf->mf_ops != mf->mf_ops || 102838494Sobrien new_mf->mf_refc > 1 || 102938494Sobrien mf == new_mf || 103038494Sobrien ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART))) 103138494Sobrien continue; 103238494Sobrien 103338494Sobrien if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) && 103438494Sobrien new_mf->mf_mount[len] == '/') { 1035174294Sobrien new_unmount_flags = 1036174294Sobrien (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 1037174294Sobrien UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags); 103838494Sobrien didsome = 1; 103938494Sobrien } 104038494Sobrien } 104138494Sobrien if (didsome) 1042174294Sobrien error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); 104338494Sobrien } 104438494Sobrien if (error) 104538494Sobrien return error; 104638494Sobrien 104738494Sobrien return 0; 104838494Sobrien} 104938494Sobrien 105038494Sobrien 1051174294Sobrienstatic void 1052174294Sobriennfs_umounted(mntfs *mf) 105338494Sobrien{ 105438494Sobrien fserver *fs; 105538494Sobrien char *colon, *path; 105638494Sobrien 105738494Sobrien if (mf->mf_error || mf->mf_refc > 1) 105838494Sobrien return; 105938494Sobrien 1060174294Sobrien /* 1061174294Sobrien * No need to inform mountd when WebNFS is in use. 1062174294Sobrien */ 1063174294Sobrien if (mf->mf_flags & MFF_WEBNFS) 1064174294Sobrien return; 106538494Sobrien 106638494Sobrien /* 106738494Sobrien * Call the mount daemon on the server to announce that we are not using 106838494Sobrien * the fs any more. 106938494Sobrien * 1070174294Sobrien * XXX: This is *wrong*. The mountd should be called when the fhandle is 107138494Sobrien * flushed from the cache, and a reference held to the cached entry while 107238494Sobrien * the fs is mounted... 107338494Sobrien */ 1074174294Sobrien fs = mf->mf_server; 107538494Sobrien colon = path = strchr(mf->mf_info, ':'); 107638494Sobrien if (fs && colon) { 107738494Sobrien fh_cache f; 107838494Sobrien 107938494Sobrien dlog("calling mountd for %s", mf->mf_info); 108038494Sobrien *path++ = '\0'; 108138494Sobrien f.fh_path = path; 108238494Sobrien f.fh_sin = *fs->fs_ip; 108338494Sobrien f.fh_sin.sin_port = (u_short) 0; 1084310490Scy f.fh_nfs_version = SET_FH_VERSION(fs); 108538494Sobrien f.fh_fs = fs; 108638494Sobrien f.fh_id = 0; 108738494Sobrien f.fh_error = 0; 1088310490Scy prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) NULL, mf); 1089310490Scy call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun *) NULL, (wchan_t) NULL); 109038494Sobrien *colon = ':'; 109138494Sobrien } 109238494Sobrien} 1093