Commit 9f867ba2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.15-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull more smb client updates from Steve French:

 - reconnect fixes: three for updating rsize/wsize and an SMB1 reconnect
   fix

 - RFC1001 fixes: fixing connections to nonstandard ports, and negprot
   retries

 - fix mfsymlinks to old servers

 - make mapping of open flags for SMB1 more accurate

 - permission fixes: adding retry on open for write, and one for stat to
   workaround unexpected access denied

 - add two new xattrs, one for retrieving SACL and one for retrieving
   owner (without having to retrieve the whole ACL)

 - fix mount parm validation for echo_interval

 - minor cleanup (including removing now unneeded cifs_truncate_page)

* tag '6.15-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: update internal version number
  cifs: Implement is_network_name_deleted for SMB1
  cifs: Remove cifs_truncate_page() as it should be superfluous
  cifs: Do not add FILE_READ_ATTRIBUTES when using GENERIC_READ/EXECUTE/ALL
  cifs: Improve SMB2+ stat() to work also without FILE_READ_ATTRIBUTES
  cifs: Add fallback for SMB2 CREATE without FILE_READ_ATTRIBUTES
  cifs: Fix querying and creating MF symlinks over SMB1
  cifs: Fix access_flags_to_smbopen_mode
  cifs: Fix negotiate retry functionality
  cifs: Improve handling of NetBIOS packets
  cifs: Allow to disable or force initialization of NetBIOS session
  cifs: Add a new xattr system.smb3_ntsd_owner for getting or setting owner
  cifs: Add a new xattr system.smb3_ntsd_sacl for getting or setting SACLs
  smb: client: Update IO sizes after reconnection
  smb: client: Store original IO parameters and prevent zero IO sizes
  smb:client: smb: client: Add reverse mapping from tcon to superblocks
  cifs: remove unreachable code in cifs_get_tcp_session()
  cifs: fix integer overflow in match_server()
parents a52a3c18 827a1bd9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@

struct cifs_sb_info {
	struct rb_root tlink_tree;
	struct list_head tcon_sb_link;
	spinlock_t tlink_tree_lock;
	struct tcon_link *master_tlink;
	struct nls_table *local_nls;
+2 −3
Original line number Diff line number Diff line
@@ -135,7 +135,6 @@ extern ssize_t cifs_file_copychunk_range(unsigned int xid,

extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern void cifs_setsize(struct inode *inode, loff_t offset);
extern int cifs_truncate_page(struct address_space *mapping, loff_t from);

struct smb3_fs_context;
extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type,
@@ -146,6 +145,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */

/* when changing internal version - update following two lines at same time */
#define SMB3_PRODUCT_BUILD 53
#define CIFS_VERSION   "2.53"
#define SMB3_PRODUCT_BUILD 54
#define CIFS_VERSION   "2.54"
#endif				/* _CIFSFS_H */
+6 −1
Original line number Diff line number Diff line
@@ -714,6 +714,8 @@ struct TCP_Server_Info {
	spinlock_t srv_lock;  /* protect anything here that is not protected */
	__u64 conn_id; /* connection identifier (useful for debugging) */
	int srv_count; /* reference counter */
	int rfc1001_sessinit; /* whether to estasblish netbios session */
	bool with_rfc1001; /* if netbios session is used */
	/* 15 character server name + 0x20 16th byte indicating type = srv */
	char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
	struct smb_version_operations	*ops;
@@ -1321,7 +1323,8 @@ struct cifs_tcon {
#endif
	struct list_head pending_opens;	/* list of incomplete opens */
	struct cached_fids *cfids;
	/* BB add field for back pointer to sb struct(s)? */
	struct list_head cifs_sb_list;
	spinlock_t sb_list_lock;
#ifdef CONFIG_CIFS_DFS_UPCALL
	struct delayed_work dfs_cache_work;
	struct list_head dfs_ses_list;
@@ -1718,6 +1721,7 @@ struct mid_q_entry {
	void *resp_buf;		/* pointer to received SMB header */
	unsigned int resp_buf_size;
	int mid_state;	/* wish this were enum but can not pass to wait_event */
	int mid_rc;		/* rc for MID_RC */
	unsigned int mid_flags;
	__le16 command;		/* smb command code */
	unsigned int optype;	/* operation type */
@@ -1880,6 +1884,7 @@ static inline bool is_replayable_error(int error)
#define   MID_RESPONSE_MALFORMED 0x10
#define   MID_SHUTDOWN		 0x20
#define   MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
#define   MID_RC             0x80 /* mid_rc contains custom rc */

/* Flags */
#define   MID_WAIT_CANCELLED	 1 /* Cancelled while waiting for response */
+24 −8
Original line number Diff line number Diff line
@@ -1041,15 +1041,31 @@ static __u16 convert_disposition(int disposition)
static int
access_flags_to_smbopen_mode(const int access_flags)
{
	int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);

	if (masked_flags == GENERIC_READ)
		return SMBOPEN_READ;
	else if (masked_flags == GENERIC_WRITE)
		return SMBOPEN_WRITE;

	/* just go for read/write */
	/*
	 * SYSTEM_SECURITY grants both read and write access to SACL, treat is as read/write.
	 * MAXIMUM_ALLOWED grants as many access as possible, so treat it as read/write too.
	 * SYNCHRONIZE as is does not grant any specific access, so do not check its mask.
	 * If only SYNCHRONIZE bit is specified then fallback to read access.
	 */
	bool with_write_flags = access_flags & (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA |
						FILE_DELETE_CHILD | FILE_WRITE_ATTRIBUTES | DELETE |
						WRITE_DAC | WRITE_OWNER | SYSTEM_SECURITY |
						MAXIMUM_ALLOWED | GENERIC_WRITE | GENERIC_ALL);
	bool with_read_flags = access_flags & (FILE_READ_DATA | FILE_READ_EA | FILE_EXECUTE |
						FILE_READ_ATTRIBUTES | READ_CONTROL |
						SYSTEM_SECURITY | MAXIMUM_ALLOWED | GENERIC_ALL |
						GENERIC_EXECUTE | GENERIC_READ);
	bool with_execute_flags = access_flags & (FILE_EXECUTE | MAXIMUM_ALLOWED | GENERIC_ALL |
						GENERIC_EXECUTE);

	if (with_write_flags && with_read_flags)
		return SMBOPEN_READWRITE;
	else if (with_write_flags)
		return SMBOPEN_WRITE;
	else if (with_execute_flags)
		return SMBOPEN_EXECUTE;
	else
		return SMBOPEN_READ;
}

int
+157 −23
Original line number Diff line number Diff line
@@ -371,7 +371,7 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
 *
 */
static int __cifs_reconnect(struct TCP_Server_Info *server,
			    bool mark_smb_session)
			    bool mark_smb_session, bool once)
{
	int rc = 0;

@@ -399,6 +399,9 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
		if (rc) {
			cifs_server_unlock(server);
			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
			/* If was asked to reconnect only once, do not try it more times */
			if (once)
				break;
			msleep(3000);
		} else {
			atomic_inc(&tcpSesReconnectCount);
@@ -564,19 +567,33 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
	return rc;
}

int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
static int
_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
{
	if (!server->leaf_fullpath)
		return __cifs_reconnect(server, mark_smb_session);
		return __cifs_reconnect(server, mark_smb_session, once);
	return reconnect_dfs_server(server);
}
#else
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
static int
_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
{
	return __cifs_reconnect(server, mark_smb_session);
	return __cifs_reconnect(server, mark_smb_session, once);
}
#endif

int
cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{
	return _cifs_reconnect(server, mark_smb_session, false);
}

static int
cifs_reconnect_once(struct TCP_Server_Info *server)
{
	return _cifs_reconnect(server, true, true);
}

static void
cifs_echo_request(struct work_struct *work)
{
@@ -803,26 +820,110 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
		/* Regular SMB response */
		return true;
	case RFC1002_SESSION_KEEP_ALIVE:
		/*
		 * RFC 1002 session keep alive can sent by the server only when
		 * we established a RFC 1002 session. But Samba servers send
		 * RFC 1002 session keep alive also over port 445 on which
		 * RFC 1002 session is not established.
		 */
		cifs_dbg(FYI, "RFC 1002 session keep alive\n");
		break;
	case RFC1002_POSITIVE_SESSION_RESPONSE:
		cifs_dbg(FYI, "RFC 1002 positive session response\n");
		/*
		 * RFC 1002 positive session response cannot be returned
		 * for SMB request. RFC 1002 session response is handled
		 * exclusively in ip_rfc1001_connect() function.
		 */
		cifs_server_dbg(VFS, "RFC 1002 positive session response (unexpected)\n");
		cifs_reconnect(server, true);
		break;
	case RFC1002_NEGATIVE_SESSION_RESPONSE:
		/*
		 * We get this from Windows 98 instead of an error on
		 * SMB negprot response.
		 * SMB negprot response, when we have not established
		 * RFC 1002 session (which means ip_rfc1001_connect()
		 * was skipped). Note that same still happens with
		 * Windows Server 2022 when connecting via port 139.
		 * So for this case when mount option -o nonbsessinit
		 * was not specified, try to reconnect with establishing
		 * RFC 1002 session. If new socket establishment with
		 * RFC 1002 session was successful then return to the
		 * mid's caller -EAGAIN, so it can retry the request.
		 */
		cifs_dbg(FYI, "RFC 1002 negative session response\n");
		/* give server a second to clean up */
		msleep(1000);
		if (!cifs_rdma_enabled(server) &&
		    server->tcpStatus == CifsInNegotiate &&
		    !server->with_rfc1001 &&
		    server->rfc1001_sessinit != 0) {
			int rc, mid_rc;
			struct mid_q_entry *mid, *nmid;
			LIST_HEAD(dispose_list);

			cifs_dbg(FYI, "RFC 1002 negative session response during SMB Negotiate, retrying with NetBIOS session\n");

			/*
			 * Before reconnect, delete all pending mids for this
			 * server, so reconnect would not signal connection
			 * aborted error to mid's callbacks. Note that for this
			 * server there should be exactly one pending mid
			 * corresponding to SMB1/SMB2 Negotiate packet.
			 */
			spin_lock(&server->mid_lock);
			list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
				kref_get(&mid->refcount);
				list_move(&mid->qhead, &dispose_list);
				mid->mid_flags |= MID_DELETED;
			}
			spin_unlock(&server->mid_lock);

			/* Now try to reconnect once with NetBIOS session. */
			server->with_rfc1001 = true;
			rc = cifs_reconnect_once(server);

			/*
		 * Always try 445 first on reconnect since we get NACK
		 * on some if we ever connected to port 139 (the NACK
		 * is since we do not begin with RFC1001 session
		 * initialize frame).
			 * If reconnect was successful then indicate -EAGAIN
			 * to mid's caller. If reconnect failed with -EAGAIN
			 * then mask it as -EHOSTDOWN, so mid's caller would
			 * know that it failed.
			 */
		cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
			if (rc == 0)
				mid_rc = -EAGAIN;
			else if (rc == -EAGAIN)
				mid_rc = -EHOSTDOWN;
			else
				mid_rc = rc;

			/*
			 * After reconnect (either successful or unsuccessful)
			 * deliver reconnect status to mid's caller via mid's
			 * callback. Use MID_RC state which indicates that the
			 * return code should be read from mid_rc member.
			 */
			list_for_each_entry_safe(mid, nmid, &dispose_list, qhead) {
				list_del_init(&mid->qhead);
				mid->mid_rc = mid_rc;
				mid->mid_state = MID_RC;
				mid->callback(mid);
				release_mid(mid);
			}

			/*
			 * If reconnect failed then wait two seconds. In most
			 * cases we were been called from the mount context and
			 * delivered failure to mid's callback will stop this
			 * receiver task thread and fails the mount process.
			 * So wait two seconds to prevent another reconnect
			 * in this task thread, which would be useless as the
			 * mount context will fail at all.
			 */
			if (rc != 0)
				msleep(2000);
		} else {
			cifs_server_dbg(VFS, "RFC 1002 negative session response (unexpected)\n");
			cifs_reconnect(server, true);
		}
		break;
	case RFC1002_RETARGET_SESSION_RESPONSE:
		cifs_server_dbg(VFS, "RFC 1002 retarget session response (unexpected)\n");
		cifs_reconnect(server, true);
		break;
	default:
@@ -1701,6 +1802,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
		ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
	memcpy(tcp_ses->server_RFC1001_name,
		ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
	tcp_ses->rfc1001_sessinit = ctx->rfc1001_sessinit;
	tcp_ses->with_rfc1001 = false;
	tcp_ses->session_estab = false;
	tcp_ses->sequence_number = 0;
	tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
@@ -1731,12 +1834,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
	 */
	tcp_ses->tcpStatus = CifsNew;
	++tcp_ses->srv_count;

	if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
		ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX)
	tcp_ses->echo_interval = ctx->echo_interval * HZ;
	else
		tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;

	if (tcp_ses->rdma) {
#ifndef CONFIG_CIFS_SMB_DIRECT
		cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
@@ -3221,6 +3320,7 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
		return -EIO;
	}

	server->with_rfc1001 = true;
	return 0;
}

@@ -3332,7 +3432,16 @@ generic_ip_connect(struct TCP_Server_Info *server)
		return rc;
	}
	trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr);
	if (sport == htons(RFC1001_PORT))

	/*
	 * Establish RFC1001 NetBIOS session when it was explicitly requested
	 * by mount option -o nbsessinit, or when connecting to default RFC1001
	 * server port (139) and it was not explicitly disabled by mount option
	 * -o nonbsessinit.
	 */
	if (server->with_rfc1001 ||
	    server->rfc1001_sessinit == 1 ||
	    (server->rfc1001_sessinit == -1 && sport == htons(RFC1001_PORT)))
		rc = ip_rfc1001_connect(server);

	return rc;
@@ -3481,6 +3590,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
	struct smb3_fs_context *ctx = cifs_sb->ctx;

	INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
	INIT_LIST_HEAD(&cifs_sb->tcon_sb_link);

	spin_lock_init(&cifs_sb->tlink_tree_lock);
	cifs_sb->tlink_tree = RB_ROOT;
@@ -3713,6 +3823,10 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
	tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
	spin_unlock(&cifs_sb->tlink_tree_lock);

	spin_lock(&tcon->sb_list_lock);
	list_add(&cifs_sb->tcon_sb_link, &tcon->cifs_sb_list);
	spin_unlock(&tcon->sb_list_lock);

	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
				TLINK_IDLE_EXPIRE);
	return 0;
@@ -4054,9 +4168,19 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
	struct rb_root *root = &cifs_sb->tlink_tree;
	struct rb_node *node;
	struct tcon_link *tlink;
	struct cifs_tcon *tcon = NULL;

	cancel_delayed_work_sync(&cifs_sb->prune_tlinks);

	if (cifs_sb->master_tlink) {
		tcon = cifs_sb->master_tlink->tl_tcon;
		if (tcon) {
			spin_lock(&tcon->sb_list_lock);
			list_del_init(&cifs_sb->tcon_sb_link);
			spin_unlock(&tcon->sb_list_lock);
		}
	}

	spin_lock(&cifs_sb->tlink_tree_lock);
	while ((node = rb_first(root))) {
		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
@@ -4078,11 +4202,13 @@ int
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
			struct TCP_Server_Info *server)
{
	bool in_retry = false;
	int rc = 0;

	if (!server->ops->need_neg || !server->ops->negotiate)
		return -ENOSYS;

retry:
	/* only send once per connect */
	spin_lock(&server->srv_lock);
	if (server->tcpStatus != CifsGood &&
@@ -4102,6 +4228,14 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
	spin_unlock(&server->srv_lock);

	rc = server->ops->negotiate(xid, ses, server);
	if (rc == -EAGAIN) {
		/* Allow one retry attempt */
		if (!in_retry) {
			in_retry = true;
			goto retry;
		}
		rc = -EHOSTDOWN;
	}
	if (rc == 0) {
		spin_lock(&server->srv_lock);
		if (server->tcpStatus == CifsInNegotiate)
Loading