diff options
Diffstat (limited to 'contrib/amd/amd/srvr_nfs.c')
-rw-r--r-- | contrib/amd/amd/srvr_nfs.c | 588 |
1 files changed, 372 insertions, 216 deletions
diff --git a/contrib/amd/amd/srvr_nfs.c b/contrib/amd/amd/srvr_nfs.c index 48e0789..0c74a65 100644 --- a/contrib/amd/amd/srvr_nfs.c +++ b/contrib/amd/amd/srvr_nfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: srvr_nfs.c,v 1.7.2.11 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/srvr_nfs.c * */ @@ -72,8 +71,6 @@ */ #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */ -#define NPXID_ALLOC(struct ) (++np_xid) - /* structures and typedefs */ typedef struct nfs_private { u_short np_mountd; /* Mount daemon port number */ @@ -88,9 +85,16 @@ typedef struct nfs_private { qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list}; /* statics */ -static int np_xid; /* For NFS pings */ -static int ping_len; -static char ping_buf[sizeof(struct rpc_msg) + 32]; +static int global_xid; /* For NFS pings */ +#define XID_ALLOC() (++global_xid) + +#ifdef HAVE_FS_NFS3 +# define NUM_NFS_VERS 2 +#else /* not HAVE_FS_NFS3 */ +# define NUM_NFS_VERS 1 +#endif /* not HAVE_FS_NFS3 */ +static int ping_len[NUM_NFS_VERS]; +static char ping_buf[NUM_NFS_VERS][sizeof(struct rpc_msg) + 32]; #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) /* @@ -99,6 +103,8 @@ static char ping_buf[sizeof(struct rpc_msg) + 32]; * Note that Solaris 8 and newer NetBSD systems are switching to UDP first, * so this order may have to be adjusted for Amd in the future once more * vendors make that change. -Erez 11/24/2000 + * + * Or we might simply make this is a platform-specific order. -Ion 09/13/2003 */ static char *protocols[] = { "tcp", "udp", NULL }; #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ @@ -107,20 +113,21 @@ static char *protocols[] = { "tcp", "udp", NULL }; static void nfs_keepalive(voidp); - /* - * Flush any cached data + * Flush cached data for an fserver (or for all, if fs==NULL) */ void -flush_srvr_nfs_cache(void) +flush_srvr_nfs_cache(fserver *fs) { - fserver *fs = 0; + fserver *fs2 = NULL; - ITER(fs, fserver, &nfs_srvr_list) { - nfs_private *np = (nfs_private *) fs->fs_private; - if (np) { - np->np_mountd_inval = TRUE; - np->np_error = -1; + ITER(fs2, fserver, &nfs_srvr_list) { + if (fs == NULL || fs == fs2) { + nfs_private *np = (nfs_private *) fs2->fs_private; + if (np) { + np->np_mountd_inval = TRUE; + np->np_error = -1; + } } } } @@ -130,7 +137,7 @@ flush_srvr_nfs_cache(void) * Startup the NFS ping for a particular version. */ static void -start_ping(u_long nfs_version) +create_ping_payload(u_long nfs_version) { XDR ping_xdr; struct rpc_msg ping_msg; @@ -140,16 +147,16 @@ start_ping(u_long nfs_version) */ if (nfs_version == 0) { nfs_version = NFS_VERSION; - plog(XLOG_WARNING, "start_ping: nfs_version = 0 fixed"); - } - plog(XLOG_INFO, "start_ping: nfs_version: %d", (int) nfs_version); + plog(XLOG_WARNING, "create_ping_payload: nfs_version = 0, changed to 2"); + } else + plog(XLOG_INFO, "create_ping_payload: nfs_version: %d", (int) nfs_version); rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL); /* * Create an XDR endpoint */ - xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE); + xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE); /* * Create the NFS ping message @@ -161,7 +168,7 @@ start_ping(u_long nfs_version) /* * Find out how long it is */ - ping_len = xdr_getpos(&ping_xdr); + ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr); /* * Destroy the XDR endpoint - we don't need it anymore @@ -183,7 +190,7 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, * Find which fileserver we are talking about */ ITER(fs, fserver, &nfs_srvr_list) - if (fs == fs2) + if (fs == fs2) break; if (fs == fs2) { @@ -192,9 +199,7 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, nfs_private *np = (nfs_private *) fs->fs_private; if (!error && port) { -#ifdef DEBUG dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host); -#endif /* DEBUG */ /* * Grab the port number. Portmap sends back * an u_long in native ordering, so it @@ -205,10 +210,8 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, np->np_mountd_inval = FALSE; np->np_error = 0; } else { -#ifdef DEBUG dlog("Error fetching port for mountd on %s", fs->fs_host); dlog("\t error=%d, port=%d", error, (int) port); -#endif /* DEBUG */ /* * Almost certainly no mountd running on remote host */ @@ -218,13 +221,9 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, if (fs->fs_flags & FSF_WANT) wakeup_srvr(fs); } else if (done) { -#ifdef DEBUG dlog("Got portmap for old port request"); -#endif /* DEBUG */ } else { -#ifdef DEBUG dlog("portmap request timed out"); -#endif /* DEBUG */ } } @@ -258,7 +257,7 @@ call_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot) memset((voidp) &sin, 0, sizeof(sin)); sin = *fs->fs_ip; sin.sin_port = htons(PMAPPORT); - error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len, + error = fwd_packet(RPC_XID_PORTMAP, iobuf, len, &sin, &sin, (voidp) fs, got_portmap); } else { error = -len; @@ -274,6 +273,12 @@ recompute_portmap(fserver *fs) int error; u_long mnt_version; + /* + * No portmap calls for pure WebNFS servers. + */ + if (fs->fs_flags & FSF_WEBNFS) + return; + if (nfs_auth) error = 0; else @@ -288,10 +293,11 @@ recompute_portmap(fserver *fs) if (fs->fs_version == 0) plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed"); - plog(XLOG_INFO, "recompute_portmap: NFS version %d", (int) fs->fs_version); + plog(XLOG_INFO, "recompute_portmap: NFS version %d on %s", + (int) fs->fs_version, fs->fs_host); #ifdef HAVE_FS_NFS3 if (fs->fs_version == NFS_VERSION3) - mnt_version = MOUNTVERS3; + mnt_version = AM_MOUNTVERS3; else #endif /* HAVE_FS_NFS3 */ mnt_version = MOUNTVERS; @@ -301,19 +307,61 @@ recompute_portmap(fserver *fs) } +int +get_mountd_port(fserver *fs, u_short *port, wchan_t wchan) +{ + int error = -1; + if (FSRV_ISDOWN(fs)) + return EWOULDBLOCK; + + if (FSRV_ISUP(fs)) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np->np_error == 0) { + *port = np->np_mountd; + error = 0; + } else { + error = np->np_error; + } + /* + * Now go get the port mapping again in case it changed. + * Note that it is used even if (np_mountd_inval) + * is True. The flag is used simply as an + * indication that the mountd may be invalid, not + * that it is known to be invalid. + */ + if (np->np_mountd_inval) + recompute_portmap(fs); + else + np->np_mountd_inval = TRUE; + } + if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { + /* + * If a wait channel is supplied, and no + * error has yet occurred, then arrange + * that a wakeup is done on the wait channel, + * whenever a wakeup is done on this fs node. + * Wakeup's are done on the fs node whenever + * it changes state - thus causing control to + * come back here and new, better things to happen. + */ + fs->fs_flags |= FSF_WANT; + sched_task(wakeup_task, wchan, (wchan_t) fs); + } + return error; +} + + /* * This is called when we get a reply to an RPC ping. * The value of id was taken from the nfs_private * structure when the ping was transmitted. */ static void -nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done) +nfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done) { - int xid = (long) idv; /* for 64-bit archs */ + int xid = (long) idv; /* cast needed for 64-bit archs */ fserver *fs; -#ifdef DEBUG int found_map = 0; -#endif /* DEBUG */ if (!done) return; @@ -336,19 +384,15 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, } else { if (np->np_ping > 1) srvrlog(fs, "ok"); -#ifdef DEBUG else srvrlog(fs, "starts up"); -#endif /* DEBUG */ fs->fs_flags |= FSF_VALID; } map_flush_srvr(fs); } else { if (fs->fs_flags & FSF_VALID) { -#ifdef DEBUG dlog("file server %s type nfs is still up", fs->fs_host); -#endif /* DEBUG */ } else { if (np->np_ping > 1) srvrlog(fs, "ok"); @@ -365,13 +409,13 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, /* * Update ttl for this server */ - np->np_ttl = clocktime() + + np->np_ttl = clocktime(NULL) + (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; /* * New RPC xid... */ - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); /* * Failed pings is zero... @@ -384,17 +428,53 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, if (np->np_mountd_inval) recompute_portmap(fs); -#ifdef DEBUG found_map++; -#endif /* DEBUG */ break; } } -#ifdef DEBUG if (found_map == 0) dlog("Spurious ping packet"); -#endif /* DEBUG */ +} + + +static void +check_fs_addr_change(fserver *fs) +{ + struct hostent *hp = NULL; + struct in_addr ia; + char *old_ipaddr, *new_ipaddr; + + hp = gethostbyname(fs->fs_host); + if (!hp || + hp->h_addrtype != AF_INET || + !STREQ((char *) hp->h_name, fs->fs_host) || + memcmp((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)) == 0) + return; + /* if got here: downed server changed IP address */ + old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); + memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); + new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ + plog(XLOG_WARNING, "EZK: down fileserver %s changed ip: %s -> %s", + fs->fs_host, old_ipaddr, new_ipaddr); + XFREE(old_ipaddr); + /* copy new IP addr */ + memmove((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)); + /* XXX: do we need to un/set these flags? */ + fs->fs_flags &= ~FSF_DOWN; + fs->fs_flags |= FSF_VALID | FSF_WANT; + map_flush_srvr(fs); /* XXX: a race with flush_srvr_nfs_cache? */ + flush_srvr_nfs_cache(fs); + fs->fs_flags |= FSF_FORCE_UNMOUNT; + +#if 0 + flush_nfs_fhandle_cache(fs); /* done in caller: nfs_keepalive_timeout */ + /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */ +#endif } @@ -402,7 +482,7 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, * Called when no ping-reply received */ static void -nfs_timed_out(voidp v) +nfs_keepalive_timeout(voidp v) { fserver *fs = v; nfs_private *np = (nfs_private *) fs->fs_private; @@ -423,11 +503,9 @@ nfs_timed_out(voidp v) /* * If ttl has expired then guess that it is dead */ - if (np->np_ttl < clocktime()) { + if (np->np_ttl < clocktime(NULL)) { int oflags = fs->fs_flags; -#ifdef DEBUG dlog("ttl has expired"); -#endif /* DEBUG */ if ((fs->fs_flags & FSF_DOWN) == 0) { /* * Server was up, but is now down. @@ -441,14 +519,13 @@ nfs_timed_out(voidp v) */ flush_nfs_fhandle_cache(fs); np->np_error = -1; + check_fs_addr_change(fs); /* check if IP addr of fserver changed */ } else { /* * Known to be down */ -#ifdef DEBUG if ((fs->fs_flags & FSF_VALID) == 0) srvrlog(fs, "starts down"); -#endif /* DEBUG */ fs->fs_flags |= FSF_VALID; } if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT)) @@ -458,17 +535,15 @@ nfs_timed_out(voidp v) */ np->np_ping = 0; } else { -#ifdef DEBUG if (np->np_ping > 1) dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS); -#endif /* DEBUG */ } /* * New RPC xid, so any late responses to the previous ping * get ignored... */ - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); /* * Run keepalive again @@ -492,19 +567,19 @@ nfs_keepalive(voidp v) * Send an NFS ping to this node */ - if (ping_len == 0) - start_ping(fs->fs_version); + if (ping_len[fs->fs_version - NFS_VERSION] == 0) + create_ping_payload(fs->fs_version); /* * Queue the packet... */ error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), - (voidp) ping_buf, - ping_len, + ping_buf[fs->fs_version - NFS_VERSION], + ping_len[fs->fs_version - NFS_VERSION], fs->fs_ip, (struct sockaddr_in *) 0, - (voidp) ((long) np->np_xid), /* for 64-bit archs */ - nfs_pinged); + (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */ + nfs_keepalive_callback); /* * See if a hard error occurred @@ -517,7 +592,7 @@ nfs_keepalive(voidp v) np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ np->np_ttl = (time_t) 0; /* - * This causes an immediate call to nfs_timed_out + * This causes an immediate call to nfs_keepalive_timeout * whenever the server was thought to be up. * See +++ below. */ @@ -525,15 +600,13 @@ nfs_keepalive(voidp v) break; case 0: -#ifdef DEBUG dlog("Sent NFS ping to %s", fs->fs_host); -#endif /* DEBUG */ break; } /* * Back off the ping interval if we are not getting replies and - * the remote system is know to be down. + * the remote system is known to be down. */ switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) { case FSF_VALID: /* Up */ @@ -550,73 +623,41 @@ nfs_keepalive(voidp v) break; } -#ifdef DEBUG dlog("NFS timeout in %d seconds", fstimeo); -#endif /* DEBUG */ - fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs); -} - - -int -nfs_srvr_port(fserver *fs, u_short *port, voidp wchan) -{ - int error = -1; - if ((fs->fs_flags & FSF_VALID) == FSF_VALID) { - if ((fs->fs_flags & FSF_DOWN) == 0) { - nfs_private *np = (nfs_private *) fs->fs_private; - if (np->np_error == 0) { - *port = np->np_mountd; - error = 0; - } else { - error = np->np_error; - } - /* - * Now go get the port mapping again in case it changed. - * Note that it is used even if (np_mountd_inval) - * is True. The flag is used simply as an - * indication that the mountd may be invalid, not - * that it is known to be invalid. - */ - if (np->np_mountd_inval) - recompute_portmap(fs); - else - np->np_mountd_inval = TRUE; - } else { - error = EWOULDBLOCK; - } - } - if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { - /* - * If a wait channel is supplied, and no - * error has yet occurred, then arrange - * that a wakeup is done on the wait channel, - * whenever a wakeup is done on this fs node. - * Wakeup's are done on the fs node whenever - * it changes state - thus causing control to - * come back here and new, better things to happen. - */ - fs->fs_flags |= FSF_WANT; - sched_task(wakeup_task, wchan, (voidp) fs); - } - return error; + fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs); } static void start_nfs_pings(fserver *fs, int pingval) { - if (fs->fs_flags & FSF_PINGING) { -#ifdef DEBUG - dlog("Already running pings to %s", fs->fs_host); -#endif /* DEBUG */ + if (pingval == 0) /* could be because ping mnt option not found */ + pingval = AM_PINGER; + /* if pings haven't been initalized, then init them for first time */ + if (fs->fs_flags & FSF_PING_UNINIT) { + fs->fs_flags &= ~FSF_PING_UNINIT; + plog(XLOG_INFO, "initializing %s's pinger to %d sec", fs->fs_host, pingval); + goto do_pings; + } + + if ((fs->fs_flags & FSF_PINGING) && fs->fs_pinger == pingval) { + dlog("already running pings to %s", fs->fs_host); return; } + /* if got here, then we need to update the ping value */ + plog(XLOG_INFO, "changing %s's ping value from %d%s to %d%s", + fs->fs_host, + fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""), + pingval, (pingval < 0 ? " (off)" : "")); + do_pings: + fs->fs_pinger = pingval; + if (fs->fs_cid) untimeout(fs->fs_cid); if (pingval < 0) { - srvrlog(fs, "wired up"); + srvrlog(fs, "wired up (pings disabled)"); fs->fs_flags |= FSF_VALID; fs->fs_flags &= ~FSF_DOWN; } else { @@ -633,17 +674,18 @@ fserver * find_nfs_srvr(mntfs *mf) { char *host = mf->mf_fo->opt_rhost; - char *nfs_proto = NULL; fserver *fs; int pingval; mntent_t mnt; nfs_private *np; - struct hostent *hp = 0; - struct sockaddr_in *ip; + struct hostent *hp = NULL; + struct sockaddr_in *ip = NULL; u_long nfs_version = 0; /* default is no version specified */ -#ifdef MNTTAB_OPT_PROTO - char *rfsname = mf->mf_fo->opt_rfs; -#endif /* MNTTAB_OPT_PROTO */ + u_long best_nfs_version = 0; + char *nfs_proto = NULL; /* no IP protocol either */ + int nfs_port = 0; + int nfs_port_opt = 0; + int fserver_is_down = 0; /* * Get ping interval from mount options. @@ -653,49 +695,60 @@ find_nfs_srvr(mntfs *mf) mnt.mnt_opts = mf->mf_mopts; pingval = hasmntval(&mnt, "ping"); - /* - * Get the NFS version from the mount options. This is used - * to decide the highest NFS version to try. - */ + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + /* + * the server granted us a filehandle, but we were unable to mount it. + * therefore, scale down to NFSv2/UDP and try again. + */ + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + plog(XLOG_WARNING, "find_nfs_srvr: NFS mount failed, trying again with NFSv2/UDP"); + mf->mf_flags &= ~MFF_NFS_SCALEDOWN; + } else { + /* + * Get the NFS version from the mount options. This is used + * to decide the highest NFS version to try. + */ #ifdef MNTTAB_OPT_VERS - nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS); + nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS); #endif /* MNTTAB_OPT_VERS */ #ifdef MNTTAB_OPT_PROTO - { - char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO); - if (proto_opt) { - char **p; - for (p = protocols; *p; p ++) - if (NSTREQ(proto_opt, *p, strlen(*p))) { - nfs_proto = *p; - break; - } - if (*p == NULL) - plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s", - host, rfsname); + { + char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO); + if (proto_opt) { + char **p; + for (p = protocols; *p; p++) + if (NSTREQ(proto_opt, *p, strlen(*p))) { + nfs_proto = *p; + break; + } + if (*p == NULL) + plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s", + host, mf->mf_fo->opt_rfs); + } } - } #endif /* MNTTAB_OPT_PROTO */ #ifdef HAVE_NFS_NFSV2_H - /* allow overriding if nfsv2 option is specified in mount options */ - if (hasmntopt(&mnt, "nfsv2")) { - nfs_version = (u_long) 2; /* nullify any ``vers=X'' statements */ - nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */ - plog(XLOG_WARNING, "found compatiblity option \"nfsv2\": set options vers=2,proto=udp for host %s", host); - } + /* allow overriding if nfsv2 option is specified in mount options */ + if (amu_hasmntopt(&mnt, "nfsv2")) { + nfs_version = NFS_VERSION;/* nullify any ``vers=X'' statements */ + nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */ + plog(XLOG_WARNING, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host); + } #endif /* HAVE_NFS_NFSV2_H */ - /* check if we globally overridden the NFS version/protocol */ - if (gopt.nfs_vers) { - nfs_version = gopt.nfs_vers; - plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d", - (int) nfs_version); - } - if (gopt.nfs_proto) { - nfs_proto = gopt.nfs_proto; - plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto); + /* check if we've globally overridden the NFS version/protocol */ + if (gopt.nfs_vers) { + nfs_version = gopt.nfs_vers; + plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d", + (int) nfs_version); + } + if (gopt.nfs_proto) { + nfs_proto = gopt.nfs_proto; + plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto); + } } /* @@ -718,72 +771,148 @@ find_nfs_srvr(mntfs *mf) case AF_INET: ip = ALLOC(struct sockaddr_in); memset((voidp) ip, 0, sizeof(*ip)); + /* as per POSIX, sin_len need not be set (used internally by kernel) */ ip->sin_family = AF_INET; memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr)); - - ip->sin_port = htons(NFS_PORT); break; default: - ip = 0; - break; + plog(XLOG_USER, "No IP address for host %s", host); + goto no_dns; } } else { plog(XLOG_USER, "Unknown host: %s", host); - ip = 0; + goto no_dns; } /* - * Get the NFS Version, and verify server is up. Probably no - * longer need to start server down below. + * This may not be the best way to do things, but it really doesn't make + * sense to query a file server which is marked as 'down' for any + * version/proto combination. */ - if (ip) { + ITER(fs, fserver, &nfs_srvr_list) { + if (FSRV_ISDOWN(fs) && + STREQ(host, fs->fs_host)) { + plog(XLOG_WARNING, "fileserver %s is already hung - not running NFS proto/version discovery", host); + fs->fs_refc++; + if (ip) + XFREE(ip); + return fs; + } + } + + /* + * Get the NFS Version, and verify server is up. + * If the client only supports NFSv2, hardcode it but still try to + * contact the remote portmapper to see if the service is running. + */ +#ifndef HAVE_FS_NFS3 + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + plog(XLOG_INFO, "The client supports only NFS(2,udp)"); +#endif /* not HAVE_FS_NFS3 */ + + + if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) { + /* + * Use WebNFS to obtain file handles. + */ + mf->mf_flags |= MFF_WEBNFS; + plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s", + MNTTAB_OPT_PUBLIC, host); + /* + * Prefer NFSv3/tcp if the client supports it (cf. RFC 2054, 7). + */ + if (!nfs_version) { #ifdef HAVE_FS_NFS3 + nfs_version = NFS_VERSION3; +#else /* not HAVE_FS_NFS3 */ + nfs_version = NFS_VERSION; +#endif /* not HAVE_FS_NFS3 */ + plog(XLOG_INFO, "No NFS version specified, will use NFSv%d", + (int) nfs_version); + } + if (!nfs_proto) { +#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) + nfs_proto = "tcp"; +#else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + nfs_proto = "udp"; +#endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + plog(XLOG_INFO, "No NFS protocol transport specified, will use %s", + nfs_proto); + } + } else { /* * Find the best combination of NFS version and protocol. * When given a choice, use the highest available version, * and use TCP over UDP if available. */ - if (nfs_proto) - nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto); - else { - int best_nfs_version = 0; - int proto_nfs_version; - char **p; - - for (p = protocols; *p; p++) { - proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p); - - if (proto_nfs_version > best_nfs_version) { - best_nfs_version = proto_nfs_version; - nfs_proto = *p; + if (check_pmap_up(host, ip)) { + if (nfs_proto) { + best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto); + nfs_port = ip->sin_port; + } +#ifdef MNTTAB_OPT_PROTO + else { + u_int proto_nfs_version; + char **p; + + for (p = protocols; *p; p++) { + proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p); + + if (proto_nfs_version > best_nfs_version) { + best_nfs_version = proto_nfs_version; + nfs_proto = *p; + nfs_port = ip->sin_port; + } } } - nfs_version = best_nfs_version; +#endif /* MNTTAB_OPT_PROTO */ + } else { + plog(XLOG_INFO, "portmapper service not running on %s", host); } - if (!nfs_version) { + /* use the portmapper results only nfs_version is not set yet */ + if (!best_nfs_version) { /* * If the NFS server is down or does not support the portmapper call * (such as certain Novell NFS servers) we mark it as version 2 and we - * let the nfs code deal with the case that is down. If when the - * server comes back up, it can support NFS V.3 and/or TCP, it will + * let the nfs code deal with the case when it is down. If/when the + * server comes back up and it can support NFSv3 and/or TCP, it will * use those. */ - nfs_version = NFS_VERSION; - nfs_proto = "udp"; + if (nfs_version == 0) { + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + } + plog(XLOG_INFO, "NFS service not running on %s", host); + fserver_is_down = 1; + } else { + if (nfs_version == 0) + nfs_version = best_nfs_version; + plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s", + (int) nfs_version, nfs_proto, host); } -#else /* not HAVE_FS_NFS3 */ - nfs_version = NFS_VERSION; -#endif /* not HAVE_FS_NFS3 */ } - if (!nfs_proto) - nfs_proto = "udp"; + /* + * Determine the NFS port. + * + * A valid "port" mount option overrides anything else. + * If the port has been determined from the portmapper, use that. + * Default to NFS_PORT otherwise (cf. RFC 2054, 3). + */ + nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT); + if (nfs_port_opt > 0) + nfs_port = htons(nfs_port_opt); + if (!nfs_port) + nfs_port = htons(NFS_PORT); - plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s", - (int) nfs_version, nfs_proto, host); + dlog("find_nfs_srvr: using port %d for nfs on %s", + (int) ntohs(nfs_port), host); + ip->sin_port = nfs_port; +no_dns: /* * Try to find an existing fs server structure for this host. * Note that differing versions or protocols have their own structures. @@ -795,16 +924,46 @@ find_nfs_srvr(mntfs *mf) nfs_version == fs->fs_version && STREQ(nfs_proto, fs->fs_proto)) { /* - * following if statement from Mike Mitchell - * <mcm@unx.sas.com> - * Initialize the ping data if we aren't pinging - * now. The np_ttl and np_ping fields are - * especially important. + * fill in the IP address -- this is only needed + * if there is a chance an IP address will change + * between mounts. + * Mike Mitchell, mcm@unx.sas.com, 09/08/93 + */ + if (hp && fs->fs_ip && + memcmp((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)) != 0) { + struct in_addr ia; + char *old_ipaddr, *new_ipaddr; + old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); + memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); + new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ + plog(XLOG_WARNING, "fileserver %s changed ip: %s -> %s", + fs->fs_host, old_ipaddr, new_ipaddr); + XFREE(old_ipaddr); + flush_nfs_fhandle_cache(fs); + memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr)); + } + + /* + * If the new file systems doesn't use WebNFS, the nfs pings may + * try to contact the portmapper. + */ + if (!(mf->mf_flags & MFF_WEBNFS)) + fs->fs_flags &= ~FSF_WEBNFS; + + /* check if pingval needs to be updated/set/reset */ + start_nfs_pings(fs, pingval); + + /* + * Following if statement from Mike Mitchell <mcm@unx.sas.com> + * Initialize the ping data if we aren't pinging now. The np_ttl and + * np_ping fields are especially important. */ if (!(fs->fs_flags & FSF_PINGING)) { np = (nfs_private *) fs->fs_private; np->np_mountd_inval = TRUE; - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); np->np_error = -1; np->np_ping = 0; /* @@ -812,18 +971,12 @@ find_nfs_srvr(mntfs *mf) * after MAX_ALLOWED_PINGS of the fast variety * have failed. */ - np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime() - 1; + np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1; + start_nfs_pings(fs, pingval); + if (fserver_is_down) + fs->fs_flags |= FSF_VALID | FSF_DOWN; } - /* - * fill in the IP address -- this is only needed - * if there is a chance an IP address will change - * between mounts. - * Mike Mitchell, mcm@unx.sas.com, 09/08/93 - */ - if (hp && fs->fs_ip) - memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr)); - start_nfs_pings(fs, pingval); fs->fs_refc++; if (ip) XFREE(ip); @@ -852,29 +1005,32 @@ find_nfs_srvr(mntfs *mf) mf->mf_flags |= MFF_ERROR; mf->mf_error = ENOENT; } + if (mf->mf_flags & MFF_WEBNFS) + fs->fs_flags |= FSF_WEBNFS; fs->fs_version = nfs_version; fs->fs_proto = nfs_proto; fs->fs_type = MNTTAB_TYPE_NFS; fs->fs_pinger = AM_PINGER; + fs->fs_flags |= FSF_PING_UNINIT; /* pinger hasn't been initialized */ np = ALLOC(struct nfs_private); memset((voidp) np, 0, sizeof(*np)); np->np_mountd_inval = TRUE; - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); np->np_error = -1; /* * Initially the server will be deemed dead after * MAX_ALLOWED_PINGS of the fast variety have failed. */ - np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; + np->np_ttl = clocktime(NULL) + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; fs->fs_private = (voidp) np; fs->fs_prfree = (void (*)(voidp)) free; - if (!(fs->fs_flags & FSF_ERROR)) { - /* - * Start of keepalive timer - */ + if (!FSRV_ERROR(fs)) { + /* start of keepalive timer, first updating pingval */ start_nfs_pings(fs, pingval); + if (fserver_is_down) + fs->fs_flags |= FSF_VALID | FSF_DOWN; } /* |