138494Sobrien/* 2174294Sobrien * Copyright (c) 1997-2006 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. 1938494Sobrien * 3. All advertising materials mentioning features or use of this software 2042629Sobrien * must display the following acknowledgment: 2138494Sobrien * This product includes software developed by the University of 2238494Sobrien * California, Berkeley and its contributors. 2338494Sobrien * 4. Neither the name of the University nor the names of its contributors 2438494Sobrien * may be used to endorse or promote products derived from this software 2538494Sobrien * without specific prior written permission. 2638494Sobrien * 2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3038494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3738494Sobrien * SUCH DAMAGE. 3838494Sobrien * 3938494Sobrien * 40174294Sobrien * File: am-utils/amd/ops_nfs.c 4138494Sobrien * 4238494Sobrien */ 4338494Sobrien 4438494Sobrien/* 4538494Sobrien * Network file system 4638494Sobrien */ 4738494Sobrien 4838494Sobrien#ifdef HAVE_CONFIG_H 4938494Sobrien# include <config.h> 5038494Sobrien#endif /* HAVE_CONFIG_H */ 5138494Sobrien#include <am_defs.h> 5238494Sobrien#include <amd.h> 5338494Sobrien 5438494Sobrien/* 5538494Sobrien * Convert from nfsstat to UN*X error code 5638494Sobrien */ 5738494Sobrien#define unx_error(e) ((int)(e)) 5838494Sobrien 5938494Sobrien/* 6038494Sobrien * FH_TTL is the time a file handle will remain in the cache since 6138494Sobrien * last being used. If the file handle becomes invalid, then it 6238494Sobrien * will be flushed anyway. 6338494Sobrien */ 6438494Sobrien#define FH_TTL (5 * 60) /* five minutes */ 6538494Sobrien#define FH_TTL_ERROR (30) /* 30 seconds */ 66174294Sobrien#define FHID_ALLOC() (++fh_id) 6738494Sobrien 6838494Sobrien/* 6938494Sobrien * The NFS layer maintains a cache of file handles. 7038494Sobrien * This is *fundamental* to the implementation and 7138494Sobrien * also allows quick remounting when a filesystem 7238494Sobrien * is accessed soon after timing out. 7338494Sobrien * 7438494Sobrien * The NFS server layer knows to flush this cache 7538494Sobrien * when a server goes down so avoiding stale handles. 7638494Sobrien * 7738494Sobrien * Each cache entry keeps a hard reference to 7838494Sobrien * the corresponding server. This ensures that 7938494Sobrien * the server keepalive information is maintained. 8038494Sobrien * 8138494Sobrien * The copy of the sockaddr_in here is taken so 8238494Sobrien * that the port can be twiddled to talk to mountd 8338494Sobrien * instead of portmap or the NFS server as used 8438494Sobrien * elsewhere. 8538494Sobrien * The port# is flushed if a server goes down. 8638494Sobrien * The IP address is never flushed - we assume 8738494Sobrien * that the address of a mounted machine never 8838494Sobrien * changes. If it does, then you have other 8938494Sobrien * problems... 9038494Sobrien */ 9138494Sobrientypedef struct fh_cache fh_cache; 9238494Sobrienstruct fh_cache { 9338494Sobrien qelem fh_q; /* List header */ 94174294Sobrien wchan_t fh_wchan; /* Wait channel */ 9538494Sobrien int fh_error; /* Valid data? */ 9638494Sobrien int fh_id; /* Unique id */ 9738494Sobrien int fh_cid; /* Callout id */ 9838494Sobrien u_long fh_nfs_version; /* highest NFS version on host */ 9938494Sobrien am_nfs_handle_t fh_nfs_handle; /* Handle on filesystem */ 100174294Sobrien int fh_status; /* Status of last rpc */ 10138494Sobrien struct sockaddr_in fh_sin; /* Address of mountd */ 10238494Sobrien fserver *fh_fs; /* Server holding filesystem */ 10338494Sobrien char *fh_path; /* Filesystem on host */ 10438494Sobrien}; 10538494Sobrien 10638494Sobrien/* forward definitions */ 107174294Sobrienstatic int nfs_init(mntfs *mf); 108174294Sobrienstatic char *nfs_match(am_opts *fo); 109174294Sobrienstatic int nfs_mount(am_node *am, mntfs *mf); 110174294Sobrienstatic int nfs_umount(am_node *am, mntfs *mf); 111174294Sobrienstatic void nfs_umounted(mntfs *mf); 112174294Sobrienstatic int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, wchan_t wchan); 113174294Sobrienstatic int webnfs_lookup(fh_cache *fp, fwd_fun f, wchan_t wchan); 11438494Sobrienstatic int fh_id = 0; 11538494Sobrien 11638494Sobrien/* globals */ 11738494SobrienAUTH *nfs_auth; 11838494Sobrienqelem fh_head = {&fh_head, &fh_head}; 11938494Sobrien 12038494Sobrien/* 12138494Sobrien * Network file system operations 12238494Sobrien */ 12338494Sobrienam_ops nfs_ops = 12438494Sobrien{ 12538494Sobrien "nfs", 12638494Sobrien nfs_match, 12738494Sobrien nfs_init, 128174294Sobrien nfs_mount, 129174294Sobrien nfs_umount, 130174294Sobrien amfs_error_lookup_child, 131174294Sobrien amfs_error_mount_child, 13238494Sobrien amfs_error_readdir, 13338494Sobrien 0, /* nfs_readlink */ 13438494Sobrien 0, /* nfs_mounted */ 13538494Sobrien nfs_umounted, 13638494Sobrien find_nfs_srvr, 137174294Sobrien 0, /* nfs_get_wchan */ 138174294Sobrien FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ 139174294Sobrien#ifdef HAVE_FS_AUTOFS 140174294Sobrien AUTOFS_NFS_FS_FLAGS, 141174294Sobrien#endif /* HAVE_FS_AUTOFS */ 14238494Sobrien}; 14338494Sobrien 14438494Sobrien 14538494Sobrienstatic fh_cache * 146174294Sobrienfind_nfs_fhandle_cache(opaque_t arg, int done) 14738494Sobrien{ 14838494Sobrien fh_cache *fp, *fp2 = 0; 149174294Sobrien int id = (long) arg; /* for 64-bit archs */ 15038494Sobrien 15138494Sobrien ITER(fp, fh_cache, &fh_head) { 15238494Sobrien if (fp->fh_id == id) { 15338494Sobrien fp2 = fp; 15438494Sobrien break; 15538494Sobrien } 15638494Sobrien } 15738494Sobrien 15838494Sobrien if (fp2) { 15951292Sobrien dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path); 16038494Sobrien } else { 16138494Sobrien dlog("fh cache search failed"); 16238494Sobrien } 16338494Sobrien 16438494Sobrien if (fp2 && !done) { 16538494Sobrien fp2->fh_error = ETIMEDOUT; 16638494Sobrien return 0; 16738494Sobrien } 16838494Sobrien 16938494Sobrien return fp2; 17038494Sobrien} 17138494Sobrien 17238494Sobrien 17338494Sobrien/* 174174294Sobrien * Called when a filehandle appears via the mount protocol 17538494Sobrien */ 17638494Sobrienstatic void 177174294Sobriengot_nfs_fh_mount(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) 17838494Sobrien{ 17938494Sobrien fh_cache *fp; 180174294Sobrien struct fhstatus res; 181174294Sobrien#ifdef HAVE_FS_NFS3 182174294Sobrien struct am_mountres3 res3; 183174294Sobrien#endif /* HAVE_FS_NFS3 */ 18438494Sobrien 185174294Sobrien fp = find_nfs_fhandle_cache(arg, done); 18638494Sobrien if (!fp) 18738494Sobrien return; 18838494Sobrien 18938494Sobrien /* 19038494Sobrien * retrieve the correct RPC reply for the file handle, based on the 19138494Sobrien * NFS protocol version. 19238494Sobrien */ 19338494Sobrien#ifdef HAVE_FS_NFS3 194174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 195174294Sobrien memset(&res3, 0, sizeof(res3)); 196174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, 197174294Sobrien (XDRPROC_T_TYPE) xdr_am_mountres3); 198174294Sobrien fp->fh_status = unx_error(res3.fhs_status); 199174294Sobrien memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); 200174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; 201174294Sobrien memmove(fp->fh_nfs_handle.v3.am_fh3_data, 202174294Sobrien res3.mountres3_u.mountinfo.fhandle.fhandle3_val, 203174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length); 204174294Sobrien } else { 20538494Sobrien#endif /* HAVE_FS_NFS3 */ 206174294Sobrien memset(&res, 0, sizeof(res)); 207174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, 20838494Sobrien (XDRPROC_T_TYPE) xdr_fhstatus); 209174294Sobrien fp->fh_status = unx_error(res.fhs_status); 210174294Sobrien memmove(&fp->fh_nfs_handle.v2, &res.fhs_fh, NFS_FHSIZE); 211174294Sobrien#ifdef HAVE_FS_NFS3 212174294Sobrien } 213174294Sobrien#endif /* HAVE_FS_NFS3 */ 21438494Sobrien 21538494Sobrien if (!fp->fh_error) { 21638494Sobrien dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 217174294Sobrien } else { 218174294Sobrien plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 219174294Sobrien /* 220174294Sobrien * Force the error to be EACCES. It's debatable whether it should be 221174294Sobrien * ENOENT instead, but the server really doesn't give us any clues, and 222174294Sobrien * EACCES is more in line with the "filehandle denied" message. 223174294Sobrien */ 224174294Sobrien fp->fh_error = EACCES; 225174294Sobrien } 22638494Sobrien 227174294Sobrien /* 228174294Sobrien * Wakeup anything sleeping on this filehandle 229174294Sobrien */ 230174294Sobrien if (fp->fh_wchan) { 231174294Sobrien dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); 232174294Sobrien wakeup(fp->fh_wchan); 233174294Sobrien } 234174294Sobrien} 235174294Sobrien 236174294Sobrien 237174294Sobrien/* 238174294Sobrien * Called when a filehandle appears via WebNFS 239174294Sobrien */ 240174294Sobrienstatic void 241174294Sobriengot_nfs_fh_webnfs(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) 242174294Sobrien{ 243174294Sobrien fh_cache *fp; 244174294Sobrien nfsdiropres res; 245174294Sobrien#ifdef HAVE_FS_NFS3 246174294Sobrien am_LOOKUP3res res3; 247174294Sobrien#endif /* HAVE_FS_NFS3 */ 248174294Sobrien 249174294Sobrien fp = find_nfs_fhandle_cache(arg, done); 250174294Sobrien if (!fp) 251174294Sobrien return; 252174294Sobrien 253174294Sobrien /* 254174294Sobrien * retrieve the correct RPC reply for the file handle, based on the 255174294Sobrien * NFS protocol version. 256174294Sobrien */ 257174294Sobrien#ifdef HAVE_FS_NFS3 258174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 259174294Sobrien memset(&res3, 0, sizeof(res3)); 260174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, 261174294Sobrien (XDRPROC_T_TYPE) xdr_am_LOOKUP3res); 262174294Sobrien fp->fh_status = unx_error(res3.status); 263174294Sobrien memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); 264174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length = res3.res_u.ok.object.am_fh3_length; 265174294Sobrien memmove(fp->fh_nfs_handle.v3.am_fh3_data, 266174294Sobrien res3.res_u.ok.object.am_fh3_data, 267174294Sobrien fp->fh_nfs_handle.v3.am_fh3_length); 268174294Sobrien } else { 269174294Sobrien#endif /* HAVE_FS_NFS3 */ 270174294Sobrien memset(&res, 0, sizeof(res)); 271174294Sobrien fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, 272174294Sobrien (XDRPROC_T_TYPE) xdr_diropres); 273174294Sobrien fp->fh_status = unx_error(res.dr_status); 274174294Sobrien memmove(&fp->fh_nfs_handle.v2, &res.dr_u.dr_drok_u.drok_fhandle, NFS_FHSIZE); 275174294Sobrien#ifdef HAVE_FS_NFS3 276174294Sobrien } 277174294Sobrien#endif /* HAVE_FS_NFS3 */ 278174294Sobrien 279174294Sobrien if (!fp->fh_error) { 280174294Sobrien dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 281174294Sobrien } else { 282174294Sobrien plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 28338494Sobrien /* 284174294Sobrien * Force the error to be EACCES. It's debatable whether it should be 285174294Sobrien * ENOENT instead, but the server really doesn't give us any clues, and 286174294Sobrien * EACCES is more in line with the "filehandle denied" message. 28738494Sobrien */ 288174294Sobrien fp->fh_error = EACCES; 28938494Sobrien } 290174294Sobrien 291174294Sobrien /* 292174294Sobrien * Wakeup anything sleeping on this filehandle 293174294Sobrien */ 294174294Sobrien if (fp->fh_wchan) { 295174294Sobrien dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); 296174294Sobrien wakeup(fp->fh_wchan); 297174294Sobrien } 29838494Sobrien} 29938494Sobrien 30038494Sobrien 30138494Sobrienvoid 30238494Sobrienflush_nfs_fhandle_cache(fserver *fs) 30338494Sobrien{ 30438494Sobrien fh_cache *fp; 30538494Sobrien 30638494Sobrien ITER(fp, fh_cache, &fh_head) { 307174294Sobrien if (fp->fh_fs == fs || fs == NULL) { 308174294Sobrien /* 309174294Sobrien * Only invalidate port info for non-WebNFS servers 310174294Sobrien */ 311174294Sobrien if (!(fp->fh_fs->fs_flags & FSF_WEBNFS)) 312174294Sobrien fp->fh_sin.sin_port = (u_short) 0; 31338494Sobrien fp->fh_error = -1; 31438494Sobrien } 31538494Sobrien } 31638494Sobrien} 31738494Sobrien 31838494Sobrien 31938494Sobrienstatic void 320174294Sobriendiscard_fh(opaque_t arg) 32138494Sobrien{ 322174294Sobrien fh_cache *fp = (fh_cache *) arg; 32338494Sobrien 32438494Sobrien rem_que(&fp->fh_q); 32538494Sobrien if (fp->fh_fs) { 32638494Sobrien dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 32738494Sobrien free_srvr(fp->fh_fs); 32838494Sobrien } 32938494Sobrien if (fp->fh_path) 33038494Sobrien XFREE(fp->fh_path); 33138494Sobrien XFREE(fp); 33238494Sobrien} 33338494Sobrien 33438494Sobrien 33538494Sobrien/* 33638494Sobrien * Determine the file handle for a node 33738494Sobrien */ 33838494Sobrienstatic int 339174294Sobrienprime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, mntfs *mf) 34038494Sobrien{ 34138494Sobrien fh_cache *fp, *fp_save = 0; 34238494Sobrien int error; 34338494Sobrien int reuse_id = FALSE; 34438494Sobrien 34538494Sobrien dlog("Searching cache for %s:%s", fs->fs_host, path); 34638494Sobrien 34738494Sobrien /* 34838494Sobrien * First search the cache 34938494Sobrien */ 35038494Sobrien ITER(fp, fh_cache, &fh_head) { 351174294Sobrien if (fs != fp->fh_fs || !STREQ(path, fp->fh_path)) 352174294Sobrien continue; /* skip to next ITER item */ 353174294Sobrien /* else we got a match */ 354174294Sobrien switch (fp->fh_error) { 355174294Sobrien case 0: 356174294Sobrien plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version); 357174294Sobrien 358174294Sobrien error = fp->fh_error = fp->fh_status; 359174294Sobrien 360174294Sobrien if (error == 0) { 361174294Sobrien if (mf->mf_flags & MFF_NFS_SCALEDOWN) { 362174294Sobrien fp_save = fp; 363174294Sobrien /* XXX: why reuse the ID? */ 364174294Sobrien reuse_id = TRUE; 365174294Sobrien break; 366174294Sobrien } 367174294Sobrien 368174294Sobrien if (fhbuf) { 36938494Sobrien#ifdef HAVE_FS_NFS3 370174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 371174294Sobrien memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3), 372174294Sobrien sizeof(fp->fh_nfs_handle.v3)); 373174294Sobrien } else 37438494Sobrien#endif /* HAVE_FS_NFS3 */ 375174294Sobrien { 37638494Sobrien memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2), 37738494Sobrien sizeof(fp->fh_nfs_handle.v2)); 378174294Sobrien } 37938494Sobrien } 380174294Sobrien if (fp->fh_cid) 381174294Sobrien untimeout(fp->fh_cid); 382174294Sobrien fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); 383174294Sobrien } else if (error == EACCES) { 38438494Sobrien /* 385174294Sobrien * Now decode the file handle return code. 38638494Sobrien */ 387174294Sobrien plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"", 388174294Sobrien fs->fs_host, path); 389174294Sobrien } else { 390174294Sobrien errno = error; /* XXX */ 391174294Sobrien plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m", 392174294Sobrien fs->fs_host, path); 393174294Sobrien } 39438494Sobrien 395174294Sobrien /* 396174294Sobrien * The error was returned from the remote mount daemon. 397174294Sobrien * Policy: this error will be cached for now... 398174294Sobrien */ 399174294Sobrien return error; 40038494Sobrien 401174294Sobrien case -1: 402174294Sobrien /* 403174294Sobrien * Still thinking about it, but we can re-use. 404174294Sobrien */ 405174294Sobrien fp_save = fp; 406174294Sobrien reuse_id = TRUE; 40738494Sobrien break; 40838494Sobrien 409174294Sobrien default: 410174294Sobrien /* 411174294Sobrien * Return the error. 412174294Sobrien * Policy: make sure we recompute if required again 413174294Sobrien * in case this was caused by a network failure. 414174294Sobrien * This can thrash mountd's though... If you find 415174294Sobrien * your mountd going slowly then: 416174294Sobrien * 1. Add a fork() loop to main. 417174294Sobrien * 2. Remove the call to innetgr() and don't use 418174294Sobrien * netgroups, especially if you don't use YP. 419174294Sobrien */ 420174294Sobrien error = fp->fh_error; 421174294Sobrien fp->fh_error = -1; 422174294Sobrien return error; 423174294Sobrien } /* end of switch statement */ 424174294Sobrien } /* end of ITER loop */ 425174294Sobrien 42638494Sobrien /* 42738494Sobrien * Not in cache 42838494Sobrien */ 42938494Sobrien if (fp_save) { 43038494Sobrien fp = fp_save; 43138494Sobrien /* 43238494Sobrien * Re-use existing slot 43338494Sobrien */ 43438494Sobrien untimeout(fp->fh_cid); 43538494Sobrien free_srvr(fp->fh_fs); 43638494Sobrien XFREE(fp->fh_path); 43738494Sobrien } else { 43838494Sobrien fp = ALLOC(struct fh_cache); 43938494Sobrien memset((voidp) fp, 0, sizeof(struct fh_cache)); 44038494Sobrien ins_que(&fp->fh_q, &fh_head); 44138494Sobrien } 44238494Sobrien if (!reuse_id) 443174294Sobrien fp->fh_id = FHID_ALLOC(); 444174294Sobrien fp->fh_wchan = get_mntfs_wchan(mf); 44538494Sobrien fp->fh_error = -1; 446174294Sobrien fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); 44738494Sobrien 44838494Sobrien /* 449174294Sobrien * If fs->fs_ip is null, remote server is probably down. 45038494Sobrien */ 45138494Sobrien if (!fs->fs_ip) { 45238494Sobrien /* Mark the fileserver down and invalid again */ 45338494Sobrien fs->fs_flags &= ~FSF_VALID; 45438494Sobrien fs->fs_flags |= FSF_DOWN; 45538494Sobrien error = AM_ERRNO_HOST_DOWN; 45638494Sobrien return error; 45738494Sobrien } 45838494Sobrien 45938494Sobrien /* 460174294Sobrien * Either fp has been freshly allocated or the address has changed. 461174294Sobrien * Initialize address and nfs version. Don't try to re-use the port 462174294Sobrien * information unless using WebNFS where the port is fixed either by 463174294Sobrien * the spec or the "port" mount option. 46438494Sobrien */ 46538494Sobrien if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { 46638494Sobrien fp->fh_sin = *fs->fs_ip; 467174294Sobrien if (!(mf->mf_flags & MFF_WEBNFS)) 468174294Sobrien fp->fh_sin.sin_port = 0; 46938494Sobrien fp->fh_nfs_version = fs->fs_version; 47038494Sobrien } 471174294Sobrien 47238494Sobrien fp->fh_fs = dup_srvr(fs); 47338494Sobrien fp->fh_path = strdup(path); 47438494Sobrien 475174294Sobrien if (mf->mf_flags & MFF_WEBNFS) 476174294Sobrien error = webnfs_lookup(fp, got_nfs_fh_webnfs, get_mntfs_wchan(mf)); 477174294Sobrien else 478174294Sobrien error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh_mount, get_mntfs_wchan(mf)); 47938494Sobrien if (error) { 48038494Sobrien /* 48138494Sobrien * Local error - cache for a short period 48238494Sobrien * just to prevent thrashing. 48338494Sobrien */ 48438494Sobrien untimeout(fp->fh_cid); 48538494Sobrien fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, 486174294Sobrien discard_fh, (opaque_t) fp); 48738494Sobrien fp->fh_error = error; 48838494Sobrien } else { 48938494Sobrien error = fp->fh_error; 49038494Sobrien } 49138494Sobrien 49238494Sobrien return error; 49338494Sobrien} 49438494Sobrien 49538494Sobrien 49638494Sobrienint 49738494Sobrienmake_nfs_auth(void) 49838494Sobrien{ 49938494Sobrien AUTH_CREATE_GIDLIST_TYPE group_wheel = 0; 50038494Sobrien 50138494Sobrien /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */ 50238494Sobrien 50338494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 50438494Sobrien if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { 50582794Sobrien plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); 50638494Sobrien nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel); 50738494Sobrien } else { 50838494Sobrien nfs_auth = authsys_create_default(); 50938494Sobrien } 51038494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */ 51138494Sobrien if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { 51282794Sobrien plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); 51338494Sobrien nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel); 51438494Sobrien } else { 51538494Sobrien nfs_auth = authunix_create_default(); 51638494Sobrien } 51738494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 51838494Sobrien 51938494Sobrien if (!nfs_auth) 52038494Sobrien return ENOBUFS; 52138494Sobrien 52238494Sobrien return 0; 52338494Sobrien} 52438494Sobrien 52538494Sobrien 52638494Sobrienstatic int 527174294Sobriencall_mountd(fh_cache *fp, u_long proc, fwd_fun fun, wchan_t wchan) 52838494Sobrien{ 52938494Sobrien struct rpc_msg mnt_msg; 53038494Sobrien int len; 531174294Sobrien char iobuf[UDPMSGSIZE]; 53238494Sobrien int error; 53338494Sobrien u_long mnt_version; 53438494Sobrien 53538494Sobrien if (!nfs_auth) { 53638494Sobrien error = make_nfs_auth(); 53738494Sobrien if (error) 53838494Sobrien return error; 53938494Sobrien } 54038494Sobrien 54138494Sobrien if (fp->fh_sin.sin_port == 0) { 542174294Sobrien u_short mountd_port; 543174294Sobrien error = get_mountd_port(fp->fh_fs, &mountd_port, wchan); 54438494Sobrien if (error) 54538494Sobrien return error; 546174294Sobrien fp->fh_sin.sin_port = mountd_port; 54738494Sobrien } 54838494Sobrien 54938494Sobrien /* find the right version of the mount protocol */ 55038494Sobrien#ifdef HAVE_FS_NFS3 55138494Sobrien if (fp->fh_nfs_version == NFS_VERSION3) 552174294Sobrien mnt_version = AM_MOUNTVERS3; 55338494Sobrien else 55438494Sobrien#endif /* HAVE_FS_NFS3 */ 55538494Sobrien mnt_version = MOUNTVERS; 55638494Sobrien plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d", 55751292Sobrien (int) fp->fh_nfs_version, (int) mnt_version); 55838494Sobrien 55938494Sobrien rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL); 56038494Sobrien len = make_rpc_packet(iobuf, 56138494Sobrien sizeof(iobuf), 56238494Sobrien proc, 56338494Sobrien &mnt_msg, 56438494Sobrien (voidp) &fp->fh_path, 56538494Sobrien (XDRPROC_T_TYPE) xdr_nfspath, 56638494Sobrien nfs_auth); 56738494Sobrien 56838494Sobrien if (len > 0) { 56938494Sobrien error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), 570174294Sobrien iobuf, 57138494Sobrien len, 57238494Sobrien &fp->fh_sin, 57338494Sobrien &fp->fh_sin, 574174294Sobrien (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ 575174294Sobrien fun); 57638494Sobrien } else { 57738494Sobrien error = -len; 57838494Sobrien } 57938494Sobrien 580174294Sobrien /* 581174294Sobrien * It may be the case that we're sending to the wrong MOUNTD port. This 582174294Sobrien * occurs if mountd is restarted on the server after the port has been 583174294Sobrien * looked up and stored in the filehandle cache somewhere. The correct 584174294Sobrien * solution, if we're going to cache port numbers is to catch the ICMP 585174294Sobrien * port unreachable reply from the server and cause the portmap request 586174294Sobrien * to be redone. The quick solution here is to invalidate the MOUNTD 587174294Sobrien * port. 588174294Sobrien */ 58938494Sobrien fp->fh_sin.sin_port = 0; 59038494Sobrien 59138494Sobrien return error; 59238494Sobrien} 59338494Sobrien 59438494Sobrien 595174294Sobrienstatic int 596174294Sobrienwebnfs_lookup(fh_cache *fp, fwd_fun fun, wchan_t wchan) 597174294Sobrien{ 598174294Sobrien struct rpc_msg wnfs_msg; 599174294Sobrien int len; 600174294Sobrien char iobuf[UDPMSGSIZE]; 601174294Sobrien int error; 602174294Sobrien u_long proc; 603174294Sobrien XDRPROC_T_TYPE xdr_fn; 604174294Sobrien voidp argp; 605174294Sobrien nfsdiropargs args; 606174294Sobrien#ifdef HAVE_FS_NFS3 607174294Sobrien am_LOOKUP3args args3; 608174294Sobrien#endif 609174294Sobrien char *wnfs_path; 610174294Sobrien size_t l; 611174294Sobrien 612174294Sobrien if (!nfs_auth) { 613174294Sobrien error = make_nfs_auth(); 614174294Sobrien if (error) 615174294Sobrien return error; 616174294Sobrien } 617174294Sobrien 618174294Sobrien if (fp->fh_sin.sin_port == 0) { 619174294Sobrien /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */ 620174294Sobrien plog(XLOG_WARNING, "webnfs_lookup: port == 0 for nfs on %s, fixed", 621174294Sobrien fp->fh_fs->fs_host); 622174294Sobrien fp->fh_sin.sin_port = htons(NFS_PORT); 623174294Sobrien } 624174294Sobrien 625174294Sobrien /* 626174294Sobrien * Use native path like the rest of amd (cf. RFC 2054, 6.1). 627174294Sobrien */ 628174294Sobrien l = strlen(fp->fh_path) + 2; 629174294Sobrien wnfs_path = (char *) xmalloc(l); 630174294Sobrien wnfs_path[0] = 0x80; 631174294Sobrien xstrlcpy(wnfs_path + 1, fp->fh_path, l - 1); 632174294Sobrien 633174294Sobrien /* find the right program and lookup procedure */ 634174294Sobrien#ifdef HAVE_FS_NFS3 635174294Sobrien if (fp->fh_nfs_version == NFS_VERSION3) { 636174294Sobrien proc = AM_NFSPROC3_LOOKUP; 637174294Sobrien xdr_fn = (XDRPROC_T_TYPE) xdr_am_LOOKUP3args; 638174294Sobrien argp = &args3; 639174294Sobrien /* WebNFS public file handle */ 640174294Sobrien args3.what.dir.am_fh3_length = 0; 641174294Sobrien args3.what.name = wnfs_path; 642174294Sobrien } else { 643174294Sobrien#endif /* HAVE_FS_NFS3 */ 644174294Sobrien proc = NFSPROC_LOOKUP; 645174294Sobrien xdr_fn = (XDRPROC_T_TYPE) xdr_diropargs; 646174294Sobrien argp = &args; 647174294Sobrien /* WebNFS public file handle */ 648174294Sobrien memset(&args.da_fhandle, 0, NFS_FHSIZE); 649174294Sobrien args.da_name = wnfs_path; 650174294Sobrien#ifdef HAVE_FS_NFS3 651174294Sobrien } 652174294Sobrien#endif /* HAVE_FS_NFS3 */ 653174294Sobrien 654174294Sobrien plog(XLOG_INFO, "webnfs_lookup: NFS version %d", (int) fp->fh_nfs_version); 655174294Sobrien 656174294Sobrien rpc_msg_init(&wnfs_msg, NFS_PROGRAM, fp->fh_nfs_version, proc); 657174294Sobrien len = make_rpc_packet(iobuf, 658174294Sobrien sizeof(iobuf), 659174294Sobrien proc, 660174294Sobrien &wnfs_msg, 661174294Sobrien argp, 662174294Sobrien (XDRPROC_T_TYPE) xdr_fn, 663174294Sobrien nfs_auth); 664174294Sobrien 665174294Sobrien if (len > 0) { 666174294Sobrien error = fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS, fp->fh_id), 667174294Sobrien iobuf, 668174294Sobrien len, 669174294Sobrien &fp->fh_sin, 670174294Sobrien &fp->fh_sin, 671174294Sobrien (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ 672174294Sobrien fun); 673174294Sobrien } else { 674174294Sobrien error = -len; 675174294Sobrien } 676174294Sobrien 677174294Sobrien XFREE(wnfs_path); 678174294Sobrien return error; 679174294Sobrien} 680174294Sobrien 681174294Sobrien 68238494Sobrien/* 68338494Sobrien * NFS needs the local filesystem, remote filesystem 68438494Sobrien * remote hostname. 68538494Sobrien * Local filesystem defaults to remote and vice-versa. 68638494Sobrien */ 687174294Sobrienstatic char * 68838494Sobriennfs_match(am_opts *fo) 68938494Sobrien{ 69038494Sobrien char *xmtab; 691174294Sobrien size_t l; 69238494Sobrien 69338494Sobrien if (fo->opt_fs && !fo->opt_rfs) 69438494Sobrien fo->opt_rfs = fo->opt_fs; 69538494Sobrien if (!fo->opt_rfs) { 69638494Sobrien plog(XLOG_USER, "nfs: no remote filesystem specified"); 69738494Sobrien return NULL; 69838494Sobrien } 69938494Sobrien if (!fo->opt_rhost) { 70038494Sobrien plog(XLOG_USER, "nfs: no remote host specified"); 70138494Sobrien return NULL; 70238494Sobrien } 70338494Sobrien 70438494Sobrien /* 70538494Sobrien * Determine magic cookie to put in mtab 70638494Sobrien */ 707174294Sobrien l = strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2; 708174294Sobrien xmtab = (char *) xmalloc(l); 709174294Sobrien xsnprintf(xmtab, l, "%s:%s", fo->opt_rhost, fo->opt_rfs); 71038494Sobrien dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 71138494Sobrien fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 71238494Sobrien 71338494Sobrien return xmtab; 71438494Sobrien} 71538494Sobrien 71638494Sobrien 71738494Sobrien/* 71838494Sobrien * Initialize am structure for nfs 71938494Sobrien */ 720174294Sobrienstatic int 72138494Sobriennfs_init(mntfs *mf) 72238494Sobrien{ 72338494Sobrien int error; 72438494Sobrien am_nfs_handle_t fhs; 72538494Sobrien char *colon; 72638494Sobrien 727174294Sobrien if (mf->mf_private) { 728174294Sobrien if (mf->mf_flags & MFF_NFS_SCALEDOWN) { 729174294Sobrien fserver *fs; 73038494Sobrien 731174294Sobrien /* tell remote mountd that we're done with this filehandle */ 732174294Sobrien mf->mf_ops->umounted(mf); 733174294Sobrien 734174294Sobrien mf->mf_prfree(mf->mf_private); 735174294Sobrien fs = mf->mf_ops->ffserver(mf); 736174294Sobrien free_srvr(mf->mf_server); 737174294Sobrien mf->mf_server = fs; 738174294Sobrien } else 739174294Sobrien return 0; 740174294Sobrien } 741174294Sobrien 74238494Sobrien colon = strchr(mf->mf_info, ':'); 74338494Sobrien if (colon == 0) 74438494Sobrien return ENOENT; 74538494Sobrien 746174294Sobrien error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, mf); 74738494Sobrien if (!error) { 748174294Sobrien mf->mf_private = (opaque_t) ALLOC(am_nfs_handle_t); 749174294Sobrien mf->mf_prfree = (void (*)(opaque_t)) free; 75038494Sobrien memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs)); 75138494Sobrien } 75238494Sobrien return error; 75338494Sobrien} 75438494Sobrien 75538494Sobrien 75638494Sobrienint 757174294Sobrienmount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) 75838494Sobrien{ 75938494Sobrien MTYPE_TYPE type; 76038494Sobrien char *colon; 761174294Sobrien char *xopts=NULL, transp_timeo_opts[40], transp_retrans_opts[40]; 76238494Sobrien char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; 76338494Sobrien fserver *fs = mf->mf_server; 76438494Sobrien u_long nfs_version = fs->fs_version; 76538494Sobrien char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */ 766174294Sobrien int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; 76738494Sobrien int error; 76838494Sobrien int genflags; 76938494Sobrien int retry; 770174294Sobrien int proto = AMU_TYPE_NONE; 77138494Sobrien mntent_t mnt; 77238494Sobrien nfs_args_t nfs_args; 77338494Sobrien 77438494Sobrien /* 77538494Sobrien * Extract HOST name to give to kernel. 77638494Sobrien * Some systems like osf1/aix3/bsd44 variants may need old code 77738494Sobrien * for NFS_ARGS_NEEDS_PATH. 77838494Sobrien */ 77938494Sobrien if (!(colon = strchr(fs_name, ':'))) 78038494Sobrien return ENOENT; 78138494Sobrien#ifdef MOUNT_TABLE_ON_FILE 78238494Sobrien *colon = '\0'; 78338494Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 784174294Sobrien xstrlcpy(host, fs_name, sizeof(host)); 78538494Sobrien#ifdef MOUNT_TABLE_ON_FILE 78638494Sobrien *colon = ':'; 78738494Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 78838494Sobrien#ifdef MAXHOSTNAMELEN 78938494Sobrien /* most kernels have a name length restriction */ 79038494Sobrien if (strlen(host) >= MAXHOSTNAMELEN) 791174294Sobrien xstrlcpy(host + MAXHOSTNAMELEN - 3, "..", 792174294Sobrien sizeof(host) - MAXHOSTNAMELEN + 3); 79338494Sobrien#endif /* MAXHOSTNAMELEN */ 79438494Sobrien 795174294Sobrien /* 796174294Sobrien * Create option=VAL for udp/tcp specific timeouts and retrans values, but 797174294Sobrien * only if these options were specified. 798174294Sobrien */ 799174294Sobrien 800174294Sobrien transp_timeo_opts[0] = transp_retrans_opts[0] = '\0'; /* initialize */ 801174294Sobrien if (STREQ(nfs_proto, "udp")) 802174294Sobrien proto = AMU_TYPE_UDP; 803174294Sobrien else if (STREQ(nfs_proto, "tcp")) 804174294Sobrien proto = AMU_TYPE_TCP; 805174294Sobrien if (proto != AMU_TYPE_NONE) { 806174294Sobrien if (gopt.amfs_auto_timeo[proto] > 0) 807174294Sobrien xsnprintf(transp_timeo_opts, sizeof(transp_timeo_opts), "%s=%d,", 808174294Sobrien MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[proto]); 809174294Sobrien if (gopt.amfs_auto_retrans[proto] > 0) 810174294Sobrien xsnprintf(transp_retrans_opts, sizeof(transp_retrans_opts), "%s=%d,", 811174294Sobrien MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[proto]); 812174294Sobrien } 813174294Sobrien 81451292Sobrien if (mf->mf_remopts && *mf->mf_remopts && 81551292Sobrien !islocalnet(fs->fs_ip->sin_addr.s_addr)) { 81651292Sobrien plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts); 817174294Sobrien /* use transp_opts first, so map-specific opts will override */ 818174294Sobrien xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_remopts); 81951292Sobrien } else { 820174294Sobrien /* use transp_opts first, so map-specific opts will override */ 821174294Sobrien xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_mopts); 82251292Sobrien } 82338494Sobrien 82438494Sobrien memset((voidp) &mnt, 0, sizeof(mnt)); 825174294Sobrien mnt.mnt_dir = mntdir; 82638494Sobrien mnt.mnt_fsname = fs_name; 82738494Sobrien mnt.mnt_opts = xopts; 82838494Sobrien 82938494Sobrien /* 83038494Sobrien * Set mount types accordingly 83138494Sobrien */ 83238494Sobrien#ifndef HAVE_FS_NFS3 83338494Sobrien type = MOUNT_TYPE_NFS; 83438494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS; 83538494Sobrien#else /* HAVE_FS_NFS3 */ 83638494Sobrien if (nfs_version == NFS_VERSION3) { 83738494Sobrien type = MOUNT_TYPE_NFS3; 83838494Sobrien /* 83938494Sobrien * Systems that include the mount table "vers" option generally do not 84038494Sobrien * set the mnttab entry to "nfs3", but to "nfs" and then they set 84138494Sobrien * "vers=3". Setting it to "nfs3" works, but it may break some things 84238494Sobrien * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix). 84338494Sobrien * So on those systems, set it to "nfs". 84438494Sobrien * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h). 84538494Sobrien */ 84638494Sobrien# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) 84738494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS; 84838494Sobrien# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ 84938494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS3; 85038494Sobrien# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ 85138494Sobrien } else { 85238494Sobrien type = MOUNT_TYPE_NFS; 85338494Sobrien mnt.mnt_type = MNTTAB_TYPE_NFS; 85438494Sobrien } 85538494Sobrien#endif /* HAVE_FS_NFS3 */ 85651292Sobrien plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version); 85738494Sobrien plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto); 85838494Sobrien 85938494Sobrien retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); 86038494Sobrien if (retry <= 0) 86138494Sobrien retry = 1; /* XXX */ 86238494Sobrien 86338494Sobrien genflags = compute_mount_flags(&mnt); 864174294Sobrien#ifdef HAVE_FS_AUTOFS 865174294Sobrien if (on_autofs) 866174294Sobrien genflags |= autofs_compute_mount_flags(&mnt); 867174294Sobrien#endif /* HAVE_FS_AUTOFS */ 86838494Sobrien 86938494Sobrien /* setup the many fields and flags within nfs_args */ 87038494Sobrien compute_nfs_args(&nfs_args, 87138494Sobrien &mnt, 87238494Sobrien genflags, 87338494Sobrien NULL, /* struct netconfig *nfsncp */ 87438494Sobrien fs->fs_ip, 87538494Sobrien nfs_version, 87638494Sobrien nfs_proto, 87738494Sobrien fhp, 87838494Sobrien host, 87938494Sobrien fs_name); 88038494Sobrien 88138494Sobrien /* finally call the mounting function */ 882174294Sobrien if (amuDebug(D_TRACE)) { 88338494Sobrien print_nfs_args(&nfs_args, nfs_version); 88482794Sobrien plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags); 88551292Sobrien } 88638494Sobrien error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 887174294Sobrien nfs_version, nfs_proto, mnttab_file_name, on_autofs); 88838494Sobrien XFREE(xopts); 88938494Sobrien 89038494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI 89138494Sobrien free_knetconfig(nfs_args.knconf); 89238494Sobrien if (nfs_args.addr) 89338494Sobrien XFREE(nfs_args.addr); /* allocated in compute_nfs_args() */ 89438494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */ 89538494Sobrien 89638494Sobrien return error; 89738494Sobrien} 89838494Sobrien 89938494Sobrien 90038494Sobrienstatic int 901174294Sobriennfs_mount(am_node *am, mntfs *mf) 90238494Sobrien{ 903174294Sobrien int error = 0; 904174294Sobrien mntent_t mnt; 905174294Sobrien 90638494Sobrien if (!mf->mf_private) { 907174294Sobrien plog(XLOG_ERROR, "Missing filehandle for %s", mf->mf_info); 90838494Sobrien return EINVAL; 90938494Sobrien } 91038494Sobrien 911174294Sobrien mnt.mnt_opts = mf->mf_mopts; 912174294Sobrien if (amu_hasmntopt(&mnt, "softlookup") || 913174294Sobrien (amu_hasmntopt(&mnt, "soft") && !amu_hasmntopt(&mnt, "nosoftlookup"))) 914174294Sobrien am->am_flags |= AMF_SOFTLOOKUP; 91538494Sobrien 916174294Sobrien error = mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, 917174294Sobrien mf->mf_mount, 918174294Sobrien mf->mf_info, 919174294Sobrien mf); 92038494Sobrien 92138494Sobrien if (error) { 92238494Sobrien errno = error; 92338494Sobrien dlog("mount_nfs: %m"); 92438494Sobrien } 92538494Sobrien 92638494Sobrien return error; 92738494Sobrien} 92838494Sobrien 92938494Sobrien 930174294Sobrienstatic int 931174294Sobriennfs_umount(am_node *am, mntfs *mf) 93238494Sobrien{ 933174294Sobrien int unmount_flags, new_unmount_flags, error; 93438494Sobrien 935174294Sobrien unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 936174294Sobrien error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); 937174294Sobrien 938174294Sobrien#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) 93938494Sobrien /* 940174294Sobrien * If the attempt to unmount failed with EBUSY, and this fserver was 941174294Sobrien * marked for forced unmounts, then use forced/lazy unmounts. 942174294Sobrien */ 943174294Sobrien if (error == EBUSY && 944174294Sobrien gopt.flags & CFM_FORCED_UNMOUNTS && 945174294Sobrien mf->mf_server->fs_flags & FSF_FORCE_UNMOUNT) { 946174294Sobrien plog(XLOG_INFO, "EZK: nfs_umount: trying forced/lazy unmounts"); 947174294Sobrien /* 948174294Sobrien * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly 949174294Sobrien * incorrect. Multiple nodes may need to be timed out and restarted for 950174294Sobrien * a single hung fserver. 951174294Sobrien */ 952174294Sobrien mf->mf_server->fs_flags &= ~FSF_FORCE_UNMOUNT; 953174294Sobrien new_unmount_flags = unmount_flags | AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH; 954174294Sobrien error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, new_unmount_flags); 955174294Sobrien } 956174294Sobrien#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */ 957174294Sobrien 958174294Sobrien /* 95938494Sobrien * Here is some code to unmount 'restarted' file systems. 96038494Sobrien * The restarted file systems are marked as 'nfs', not 96138494Sobrien * 'host', so we only have the map information for the 96238494Sobrien * the top-level mount. The unmount will fail (EBUSY) 96338494Sobrien * if there are anything else from the NFS server mounted 96438494Sobrien * below the mount-point. This code checks to see if there 96538494Sobrien * is anything mounted with the same prefix as the 96638494Sobrien * file system to be unmounted ("/a/b/c" when unmounting "/a/b"). 96738494Sobrien * If there is, and it is a 'restarted' file system, we unmount 96838494Sobrien * it. 96938494Sobrien * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93 97038494Sobrien */ 97138494Sobrien if (error == EBUSY) { 97238494Sobrien mntfs *new_mf; 97338494Sobrien int len = strlen(mf->mf_mount); 97438494Sobrien int didsome = 0; 97538494Sobrien 97638494Sobrien ITER(new_mf, mntfs, &mfhead) { 97738494Sobrien if (new_mf->mf_ops != mf->mf_ops || 97838494Sobrien new_mf->mf_refc > 1 || 97938494Sobrien mf == new_mf || 98038494Sobrien ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART))) 98138494Sobrien continue; 98238494Sobrien 98338494Sobrien if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) && 98438494Sobrien new_mf->mf_mount[len] == '/') { 985174294Sobrien new_unmount_flags = 986174294Sobrien (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 987174294Sobrien UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags); 98838494Sobrien didsome = 1; 98938494Sobrien } 99038494Sobrien } 99138494Sobrien if (didsome) 992174294Sobrien error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); 99338494Sobrien } 99438494Sobrien if (error) 99538494Sobrien return error; 99638494Sobrien 99738494Sobrien return 0; 99838494Sobrien} 99938494Sobrien 100038494Sobrien 1001174294Sobrienstatic void 1002174294Sobriennfs_umounted(mntfs *mf) 100338494Sobrien{ 100438494Sobrien fserver *fs; 100538494Sobrien char *colon, *path; 100638494Sobrien 100738494Sobrien if (mf->mf_error || mf->mf_refc > 1) 100838494Sobrien return; 100938494Sobrien 1010174294Sobrien /* 1011174294Sobrien * No need to inform mountd when WebNFS is in use. 1012174294Sobrien */ 1013174294Sobrien if (mf->mf_flags & MFF_WEBNFS) 1014174294Sobrien return; 101538494Sobrien 101638494Sobrien /* 101738494Sobrien * Call the mount daemon on the server to announce that we are not using 101838494Sobrien * the fs any more. 101938494Sobrien * 1020174294Sobrien * XXX: This is *wrong*. The mountd should be called when the fhandle is 102138494Sobrien * flushed from the cache, and a reference held to the cached entry while 102238494Sobrien * the fs is mounted... 102338494Sobrien */ 1024174294Sobrien fs = mf->mf_server; 102538494Sobrien colon = path = strchr(mf->mf_info, ':'); 102638494Sobrien if (fs && colon) { 102738494Sobrien fh_cache f; 102838494Sobrien 102938494Sobrien dlog("calling mountd for %s", mf->mf_info); 103038494Sobrien *path++ = '\0'; 103138494Sobrien f.fh_path = path; 103238494Sobrien f.fh_sin = *fs->fs_ip; 103338494Sobrien f.fh_sin.sin_port = (u_short) 0; 103438494Sobrien f.fh_nfs_version = fs->fs_version; 103538494Sobrien f.fh_fs = fs; 103638494Sobrien f.fh_id = 0; 103738494Sobrien f.fh_error = 0; 1038174294Sobrien prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, mf); 1039174294Sobrien call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun *) 0, (wchan_t) 0); 104038494Sobrien *colon = ':'; 104138494Sobrien } 104238494Sobrien} 1043