Re: [PATCH] CIFS: Fix mounting share on non-standard ports

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue,  9 Nov 2010 19:50:25 +0300
Pavel Shilovsky <piastryyy@xxxxxxxxx> wrote:

> This patch fixes the following situation: we have the share (1) mounted
> with a non-standard port and try to mount another share (2) with the standard
> one (not specifying the port), the client doesn't connect to the new share (2)
> and returns with existing connection (1). The patch also makes the client
> prevent connecting to a wrong share on a reconnect if the client fails during
> the re-establishing connection to a lost share.
> 
> Signed-off-by: Pavel Shilovsky <piastryyy@xxxxxxxxx>
> ---
>  fs/cifs/connect.c |   86 ++++++++++++++++++++++++++---------------------------
>  1 files changed, 42 insertions(+), 44 deletions(-)
> 
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 251a17c..6edf5ed 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -1446,25 +1446,32 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
>  {
>  	struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
>  	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
> +	unsigned short int port;
> +
> +	/* search for a connection with the default port if user doesn't
> +	   specify the port manually */
> +	port = htons(CIFS_PORT);
>  

I don't think this patch deals correctly with servers that are
listening on RFC1001_PORT but not CIFS_PORT. With two mounts to the
same server that don't specify a port, you'll end up with two sockets,
right?

Also, the logic in match_address is getting to be rather convoluted. It
might be good to break that out into a match_port function.

>  	switch (addr->sa_family) {
>  	case AF_INET:
> +		if (addr4->sin_port)
> +			port = addr4->sin_port;
>  		if (addr4->sin_addr.s_addr !=
>  		    server->addr.sockAddr.sin_addr.s_addr)
>  			return false;
> -		if (addr4->sin_port &&
> -		    addr4->sin_port != server->addr.sockAddr.sin_port)
> +		if (port != server->addr.sockAddr.sin_port)
>  			return false;
>  		break;
>  	case AF_INET6:
> +		if (addr6->sin6_port)
> +			port = addr6->sin6_port;
>  		if (!ipv6_addr_equal(&addr6->sin6_addr,
>  				     &server->addr.sockAddr6.sin6_addr))
>  			return false;
>  		if (addr6->sin6_scope_id !=
>  		    server->addr.sockAddr6.sin6_scope_id)
>  			return false;
> -		if (addr6->sin6_port &&
> -		    addr6->sin6_port != server->addr.sockAddr6.sin6_port)
> +		if (port != server->addr.sockAddr6.sin6_port)
>  			return false;
>  		break;
>  	}
> @@ -2126,7 +2133,7 @@ ipv4_connect(struct TCP_Server_Info *server)
>  	int rc = 0;
>  	int val;
>  	bool connected = false;
> -	__be16 orig_port = 0;
> +	bool orig_port_error = false;
>  	struct socket *socket = server->ssocket;
>  
>  	if (socket == NULL) {
> @@ -2148,32 +2155,30 @@ ipv4_connect(struct TCP_Server_Info *server)
>  	if (rc < 0)
>  		return rc;
>  
> -	/* user overrode default port */
> +	/* user overrode default port or we perform reconnect */
>  	if (server->addr.sockAddr.sin_port) {
>  		rc = socket->ops->connect(socket, (struct sockaddr *)
>  					  &server->addr.sockAddr,
>  					  sizeof(struct sockaddr_in), 0);
>  		if (rc >= 0)
>  			connected = true;
> +		else
> +			orig_port_error = true;
> +		/* in this case we don't have to try other port
> +		   because we can mount another share */
>  	}
>  
> -	if (!connected) {
> -		/* save original port so we can retry user specified port
> -			later if fall back ports fail this time  */
> -		orig_port = server->addr.sockAddr.sin_port;
> -
> -		/* do not retry on the same port we just failed on */
> -		if (server->addr.sockAddr.sin_port != htons(CIFS_PORT)) {
> -			server->addr.sockAddr.sin_port = htons(CIFS_PORT);
> -			rc = socket->ops->connect(socket,
> -						(struct sockaddr *)
> -						&server->addr.sockAddr,
> -						sizeof(struct sockaddr_in), 0);
> -			if (rc >= 0)
> -				connected = true;
> -		}
> +	if (!orig_port_error && !connected) {
> +		server->addr.sockAddr.sin_port = htons(CIFS_PORT);
> +		rc = socket->ops->connect(socket,
> +					(struct sockaddr *)
> +					&server->addr.sockAddr,
> +					sizeof(struct sockaddr_in), 0);
> +		if (rc >= 0)
> +			connected = true;
>  	}
> -	if (!connected) {
> +
> +	if (!orig_port_error && !connected) {
>  		server->addr.sockAddr.sin_port = htons(RFC1001_PORT);
>  		rc = socket->ops->connect(socket, (struct sockaddr *)
>  					      &server->addr.sockAddr,
> @@ -2185,15 +2190,12 @@ ipv4_connect(struct TCP_Server_Info *server)
>  	/* give up here - unless we want to retry on different
>  		protocol families some day */
>  	if (!connected) {
> -		if (orig_port)
> -			server->addr.sockAddr.sin_port = orig_port;
>  		cFYI(1, "Error %d connecting to server via ipv4", rc);
>  		sock_release(socket);
>  		server->ssocket = NULL;
>  		return rc;
>  	}
>  
> -
>  	/*
>  	 * Eventually check for other socket options to change from
>  	 *  the default. sock_setsockopt not used because it expects
> @@ -2291,7 +2293,7 @@ ipv6_connect(struct TCP_Server_Info *server)
>  	int rc = 0;
>  	int val;
>  	bool connected = false;
> -	__be16 orig_port = 0;
> +	bool orig_port_error = false;
>  	struct socket *socket = server->ssocket;
>  
>  	if (socket == NULL) {
> @@ -2314,31 +2316,29 @@ ipv6_connect(struct TCP_Server_Info *server)
>  	if (rc < 0)
>  		return rc;
>  
> -	/* user overrode default port */
> +	/* user overrode default port or we perform reconnect */
>  	if (server->addr.sockAddr6.sin6_port) {
>  		rc = socket->ops->connect(socket,
>  				(struct sockaddr *) &server->addr.sockAddr6,
>  				sizeof(struct sockaddr_in6), 0);
>  		if (rc >= 0)
>  			connected = true;
> +		else
> +			orig_port_error = true;
> +		/* in this case we don't have to try other port
> +		   because we can mount another share */
>  	}
>  
> -	if (!connected) {
> -		/* save original port so we can retry user specified port
> -			later if fall back ports fail this time  */
> -
> -		orig_port = server->addr.sockAddr6.sin6_port;
> -		/* do not retry on the same port we just failed on */
> -		if (server->addr.sockAddr6.sin6_port != htons(CIFS_PORT)) {
> -			server->addr.sockAddr6.sin6_port = htons(CIFS_PORT);
> -			rc = socket->ops->connect(socket, (struct sockaddr *)
> -					&server->addr.sockAddr6,
> -					sizeof(struct sockaddr_in6), 0);
> -			if (rc >= 0)
> -				connected = true;
> -		}
> +	if (!orig_port_error && !connected) {
> +		server->addr.sockAddr6.sin6_port = htons(CIFS_PORT);
> +		rc = socket->ops->connect(socket, (struct sockaddr *)
> +				&server->addr.sockAddr6,
> +				sizeof(struct sockaddr_in6), 0);
> +		if (rc >= 0)
> +			connected = true;
>  	}
> -	if (!connected) {
> +
> +	if (!orig_port_error && !connected) {
>  		server->addr.sockAddr6.sin6_port = htons(RFC1001_PORT);
>  		rc = socket->ops->connect(socket, (struct sockaddr *)
>  				&server->addr.sockAddr6,
> @@ -2350,8 +2350,6 @@ ipv6_connect(struct TCP_Server_Info *server)
>  	/* give up here - unless we want to retry on different
>  		protocol families some day */
>  	if (!connected) {
> -		if (orig_port)
> -			server->addr.sockAddr6.sin6_port = orig_port;
>  		cFYI(1, "Error %d connecting to server via ipv6", rc);
>  		sock_release(socket);
>  		server->ssocket = NULL;


ipv4_connect and ipv6_connect ought to be sharing a lot of code. It
would be good to do some cleanup here and try to consolidate these
functions more than they are today.

I envision a function that just handles setting up the socket and
leaves the caller to determine the port to use. Also, breaking out the
RFC1001 stuff into a separate function would be good too.

-- 
Jeff Layton <jlayton@xxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux