static int nfs_verify_lock_option(struct mount_options *options) { if (po_rightmost(options, nfs_lock_opttbl) == 0) return 1; if (!start_statd()) { nfs_error(_("%s: rpc.statd is not running but is " "required for remote locking."), progname); nfs_error(_("%s: Either use '-o nolock' to keep " "locks local, or start statd."), progname); return 0; } return 1; }
static int nfsmount_start(struct nfsmount_info *mi) { if (!nfs_validate_options(mi)) return EX_FAIL; /* * Avoid retry and negotiation logic when remounting */ if (mi->flags & MS_REMOUNT) return nfs_remount(mi); if (po_rightmost(mi->options, nfs_background_opttbl) == 0) return nfsmount_bg(mi); else return nfsmount_fg(mi); }
/* * Returns the NFS transport protocol specified by the given mount options * * Returns the IPPROTO_ value specified by the given mount options, or * IPPROTO_UDP if all fails. */ static unsigned short nfs_nfs_protocol(struct mount_options *options) { char *option; switch (po_rightmost(options, nfs_transport_opttbl)) { case 1: /* tcp */ return IPPROTO_TCP; case 2: /* proto */ option = po_get(options, "proto"); if (option) { if (strcmp(option, "tcp") == 0) return IPPROTO_TCP; if (strcmp(option, "udp") == 0) return IPPROTO_UDP; } } return IPPROTO_UDP; }
/* * Try a getsockname() on a connected datagram socket. * * Returns 1 and fills in @buf if successful; otherwise, zero. * * A connected datagram socket prevents leaving a socket in TIME_WAIT. * This conserves the ephemeral port number space, helping reduce failed * socket binds during mount storms. */ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen, struct sockaddr *buf, socklen_t *buflen) { struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), }; struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, }; int sock; sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) return 0; switch (sap->sa_family) { case AF_INET: if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { close(sock); return 0; } break; case AF_INET6: if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) { close(sock); return 0; } break; default: errno = EAFNOSUPPORT; return 0; } if (connect(sock, sap, salen) < 0) { close(sock); return 0; } return !getsockname(sock, buf, buflen); } /* * Try to generate an address that prevents the server from calling us. * * Returns 1 and fills in @buf if successful; otherwise, zero. */ static int nfs_ca_gai(const struct sockaddr *sap, const socklen_t salen, struct sockaddr *buf, socklen_t *buflen) { struct addrinfo *gai_results; struct addrinfo gai_hint = { .ai_family = sap->sa_family, .ai_flags = AI_PASSIVE, /* ANYADDR */ }; if (getaddrinfo(NULL, "", &gai_hint, &gai_results)) return 0; *buflen = gai_results->ai_addrlen; memcpy(buf, gai_results->ai_addr, *buflen); freeaddrinfo(gai_results); return 1; } /** * nfs_callback_address - acquire our local network address * @sap: pointer to address of remote * @sap_len: length of address * @buf: pointer to buffer to be filled in with local network address * @buflen: IN: length of buffer to fill in; OUT: length of filled-in address * * Discover a network address that an NFSv4 server can use to call us back. * On multi-homed clients, this address depends on which NIC we use to * route requests to the server. * * Returns 1 and fills in @buf if an unambiguous local address is * available; returns 1 and fills in an appropriate ANYADDR address * if a local address isn't available; otherwise, returns zero. */ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, struct sockaddr *buf, socklen_t *buflen) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf; if (nfs_ca_sockname(sap, salen, buf, buflen) == 0) if (nfs_ca_gai(sap, salen, buf, buflen) == 0) goto out_failed; /* * The server can't use an interface ID that was generated * here on the client, so always clear sin6_scope_id. */ if (sin6->sin6_family == AF_INET6) sin6->sin6_scope_id = 0; return 1; out_failed: *buflen = 0; if (verbose) nfs_error(_("%s: failed to construct callback address")); return 0; } /* * "nfsprog" is only supported by the legacy mount command. The * kernel mount client does not support this option. * * Returns the value set by the nfsprog= option, the value of * the RPC NFS program specified in /etc/rpc, or a baked-in * default program number, if all fails. */ static rpcprog_t nfs_nfs_program(struct mount_options *options) { long tmp; if (po_get_numeric(options, "nfsprog", &tmp) == PO_FOUND) if (tmp >= 0) return tmp; return nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl); } /* * Returns the RPC version number specified by the given mount * options for the NFS service, or zero if all fails. */ static rpcvers_t nfs_nfs_version(struct mount_options *options) { long tmp; switch (po_rightmost(options, nfs_version_opttbl)) { case 0: /* v2 */ return 2; case 1: /* v3 */ return 3; case 2: /* vers */ if (po_get_numeric(options, "vers", &tmp) == PO_FOUND) if (tmp >= 2 && tmp <= 3) return tmp; break; case 3: /* nfsvers */ if (po_get_numeric(options, "nfsvers", &tmp) == PO_FOUND) if (tmp >= 2 && tmp <= 3) return tmp; break; } return 0; }