Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8dc5b3a6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '4.14-smb3-xattr-enable' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs update from Steve French:
 "Enable xattr support for smb3 and also a bugfix"

* tag '4.14-smb3-xattr-enable' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Check for timeout on Negotiate stage
  cifs: Add support for writing attributes on SMB2+
  cifs: Add support for reading attributes on SMB2+
parents 2500e287 76e75270
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -421,7 +421,7 @@ struct smb_version_operations {
			size_t, struct cifs_sb_info *);
	int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *,
			const char *, const void *, const __u16,
			const struct nls_table *, int);
			const struct nls_table *, struct cifs_sb_info *);
	struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *,
			const char *, u32 *);
	struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *,
+2 −1
Original line number Diff line number Diff line
@@ -484,7 +484,8 @@ extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
		const char *fileName, const char *ea_name,
		const void *ea_value, const __u16 ea_value_len,
		const struct nls_table *nls_codepage, int remap_special_chars);
		const struct nls_table *nls_codepage,
		struct cifs_sb_info *cifs_sb);
extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon,
			__u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16,
+14 −1
Original line number Diff line number Diff line
@@ -178,6 +178,18 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
	 * reconnect the same SMB session
	 */
	mutex_lock(&ses->session_mutex);

	/*
	 * Recheck after acquire mutex. If another thread is negotiating
	 * and the server never sends an answer the socket will be closed
	 * and tcpStatus set to reconnect.
	 */
	if (server->tcpStatus == CifsNeedReconnect) {
		rc = -EHOSTDOWN;
		mutex_unlock(&ses->session_mutex);
		goto out;
	}

	rc = cifs_negotiate_protocol(0, ses);
	if (rc == 0 && ses->need_reconnect)
		rc = cifs_setup_session(0, ses, nls_codepage);
@@ -6264,7 +6276,7 @@ int
CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
	     const char *fileName, const char *ea_name, const void *ea_value,
	     const __u16 ea_value_len, const struct nls_table *nls_codepage,
	     int remap)
	     struct cifs_sb_info *cifs_sb)
{
	struct smb_com_transaction2_spi_req *pSMB = NULL;
	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
@@ -6273,6 +6285,7 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, param_offset, byte_count, offset, count;
	int remap = cifs_remap(cifs_sb);

	cifs_dbg(FYI, "In SetEA\n");
SetEARetry:
+2 −1
Original line number Diff line number Diff line
@@ -509,7 +509,8 @@ server_unresponsive(struct TCP_Server_Info *server)
	 * 65s kernel_recvmsg times out, and we see that we haven't gotten
	 *     a response in >60s.
	 */
	if (server->tcpStatus == CifsGood &&
	if ((server->tcpStatus == CifsGood ||
	    server->tcpStatus == CifsNeedNegotiate) &&
	    time_after(jiffies, server->lstrp + 2 * server->echo_interval)) {
		cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n",
			 server->hostname, (2 * server->echo_interval) / HZ);
+204 −0
Original line number Diff line number Diff line
@@ -426,6 +426,194 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
	return rc;
}

static ssize_t
move_smb2_ea_to_cifs(char *dst, size_t dst_size,
		     struct smb2_file_full_ea_info *src, size_t src_size,
		     const unsigned char *ea_name)
{
	int rc = 0;
	unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
	char *name, *value;
	size_t name_len, value_len, user_name_len;

	while (src_size > 0) {
		name = &src->ea_data[0];
		name_len = (size_t)src->ea_name_length;
		value = &src->ea_data[src->ea_name_length + 1];
		value_len = (size_t)le16_to_cpu(src->ea_value_length);

		if (name_len == 0) {
			break;
		}

		if (src_size < 8 + name_len + 1 + value_len) {
			cifs_dbg(FYI, "EA entry goes beyond length of list\n");
			rc = -EIO;
			goto out;
		}

		if (ea_name) {
			if (ea_name_len == name_len &&
			    memcmp(ea_name, name, name_len) == 0) {
				rc = value_len;
				if (dst_size == 0)
					goto out;
				if (dst_size < value_len) {
					rc = -ERANGE;
					goto out;
				}
				memcpy(dst, value, value_len);
				goto out;
			}
		} else {
			/* 'user.' plus a terminating null */
			user_name_len = 5 + 1 + name_len;

			rc += user_name_len;

			if (dst_size >= user_name_len) {
				dst_size -= user_name_len;
				memcpy(dst, "user.", 5);
				dst += 5;
				memcpy(dst, src->ea_data, name_len);
				dst += name_len;
				*dst = 0;
				++dst;
			} else if (dst_size == 0) {
				/* skip copy - calc size only */
			} else {
				/* stop before overrun buffer */
				rc = -ERANGE;
				break;
			}
		}

		if (!src->next_entry_offset)
			break;

		if (src_size < le32_to_cpu(src->next_entry_offset)) {
			/* stop before overrun buffer */
			rc = -ERANGE;
			break;
		}
		src_size -= le32_to_cpu(src->next_entry_offset);
		src = (void *)((char *)src +
			       le32_to_cpu(src->next_entry_offset));
	}

	/* didn't find the named attribute */
	if (ea_name)
		rc = -ENODATA;

out:
	return (ssize_t)rc;
}

static ssize_t
smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
	       const unsigned char *path, const unsigned char *ea_name,
	       char *ea_data, size_t buf_size,
	       struct cifs_sb_info *cifs_sb)
{
	int rc;
	__le16 *utf16_path;
	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
	struct cifs_open_parms oparms;
	struct cifs_fid fid;
	struct smb2_file_full_ea_info *smb2_data;

	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
	if (!utf16_path)
		return -ENOMEM;

	oparms.tcon = tcon;
	oparms.desired_access = FILE_READ_EA;
	oparms.disposition = FILE_OPEN;
	oparms.create_options = 0;
	oparms.fid = &fid;
	oparms.reconnect = false;

	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
	kfree(utf16_path);
	if (rc) {
		cifs_dbg(FYI, "open failed rc=%d\n", rc);
		return rc;
	}

	smb2_data = kzalloc(SMB2_MAX_EA_BUF, GFP_KERNEL);
	if (smb2_data == NULL) {
		SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
		return -ENOMEM;
	}

	rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, fid.volatile_fid,
			    smb2_data);
	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);

	if (!rc)
		rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data,
					  SMB2_MAX_EA_BUF, ea_name);

	kfree(smb2_data);
	return rc;
}


static int
smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
	    const char *path, const char *ea_name, const void *ea_value,
	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
	    struct cifs_sb_info *cifs_sb)
{
	int rc;
	__le16 *utf16_path;
	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
	struct cifs_open_parms oparms;
	struct cifs_fid fid;
	struct smb2_file_full_ea_info *ea;
	int ea_name_len = strlen(ea_name);
	int len;

	if (ea_name_len > 255)
		return -EINVAL;

	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
	if (!utf16_path)
		return -ENOMEM;

	oparms.tcon = tcon;
	oparms.desired_access = FILE_WRITE_EA;
	oparms.disposition = FILE_OPEN;
	oparms.create_options = 0;
	oparms.fid = &fid;
	oparms.reconnect = false;

	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
	kfree(utf16_path);
	if (rc) {
		cifs_dbg(FYI, "open failed rc=%d\n", rc);
		return rc;
	}

	len = sizeof(ea) + ea_name_len + ea_value_len + 1;
	ea = kzalloc(len, GFP_KERNEL);
	if (ea == NULL) {
		SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
		return -ENOMEM;
	}

	ea->ea_name_length = ea_name_len;
	ea->ea_value_length = cpu_to_le16(ea_value_len);
	memcpy(ea->ea_data, ea_name, ea_name_len + 1);
	memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len);

	rc = SMB2_set_ea(xid, tcon, fid.persistent_fid, fid.volatile_fid, ea,
			 len);
	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);

	return rc;
}

static bool
smb2_can_echo(struct TCP_Server_Info *server)
{
@@ -2572,6 +2760,10 @@ struct smb_version_operations smb20_operations = {
	.dir_needs_close = smb2_dir_needs_close,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_XATTR
	.query_all_EAs = smb2_query_eas,
	.set_EA = smb2_set_ea,
#endif /* CIFS_XATTR */
#ifdef CONFIG_CIFS_ACL
	.get_acl = get_smb2_acl,
	.get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2662,6 +2854,10 @@ struct smb_version_operations smb21_operations = {
	.enum_snapshots = smb3_enum_snapshots,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_XATTR
	.query_all_EAs = smb2_query_eas,
	.set_EA = smb2_set_ea,
#endif /* CIFS_XATTR */
#ifdef CONFIG_CIFS_ACL
	.get_acl = get_smb2_acl,
	.get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2762,6 +2958,10 @@ struct smb_version_operations smb30_operations = {
	.receive_transform = smb3_receive_transform,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_XATTR
	.query_all_EAs = smb2_query_eas,
	.set_EA = smb2_set_ea,
#endif /* CIFS_XATTR */
#ifdef CONFIG_CIFS_ACL
	.get_acl = get_smb2_acl,
	.get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2863,6 +3063,10 @@ struct smb_version_operations smb311_operations = {
	.receive_transform = smb3_receive_transform,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_XATTR
	.query_all_EAs = smb2_query_eas,
	.set_EA = smb2_set_ea,
#endif /* CIFS_XATTR */
};
#endif /* CIFS_SMB311 */

Loading