Beispiel #1
0
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;
}
Beispiel #2
0
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);
}
Beispiel #3
0
/*
 * 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;
}
Beispiel #4
0
/*
 * 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;
}