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

Commit 4846546f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  [CIFS] pSesInfo->sesSem is used as mutex. Rename it to session_mutex and
  [CIFS] Use unsigned ea length for clarity
  cifs: set server_eof in cifs_fattr_to_inode
  [CIFS] Minor cleanup to EA patch
  cifs: merge CIFSSMBQueryEA with CIFSSMBQAllEAs
  cifs: verify lengths of QueryAllEAs reply
  cifs: increase maximum buffer size in CIFSSMBQAllEAs
  cifs: rename name_len to list_len in CIFSSMBQAllEAs
  cifs: clean up indentation in CIFSSMBQAllEAs
  cifs: add parens around smb_var in BCC macros
parents 832d30ca d7b619cf
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
Version 1.62
------------
Add sockopt=TCP_NODELAY mount option.
Add sockopt=TCP_NODELAY mount option. EA (xattr) routines hardened
to more strictly handle corrupt frames.

Version 1.61
------------
+1 −1
Original line number Diff line number Diff line
@@ -205,7 +205,7 @@ struct cifsUidInfo {
struct cifsSesInfo {
	struct list_head smb_ses_list;
	struct list_head tcon_list;
	struct semaphore sesSem;
	struct mutex session_mutex;
#if 0
	struct cifsUidInfo *uidInfo;	/* pointer to user info */
#endif
+3 −3
Original line number Diff line number Diff line
@@ -415,10 +415,10 @@ struct smb_hdr {
	__u8 WordCount;
} __attribute__((packed));
/* given a pointer to an smb_hdr retrieve the value of byte count */
#define BCC(smb_var) (*(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount)))
#define BCC_LE(smb_var) (*(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount)))
#define BCC(smb_var) (*(__u16 *)((char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount)))
#define BCC_LE(smb_var) (*(__le16 *)((char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount)))
/* given a pointer to an smb_hdr retrieve the pointer to the byte area */
#define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount) + 2)
#define pByteArea(smb_var) ((unsigned char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount) + 2)

/*
 * Computer Name Length (since Netbios name was length 16 with last byte 0x20)
+2 −5
Original line number Diff line number Diff line
@@ -363,13 +363,10 @@ extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
			__u32 filter, struct file *file, int multishot,
			const struct nls_table *nls_codepage);
extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
			const unsigned char *searchName, char *EAData,
			const unsigned char *searchName,
			const unsigned char *ea_name, char *EAData,
			size_t bufsize, const struct nls_table *nls_codepage,
			int remap_special_chars);
extern ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
		const unsigned char *searchName, const unsigned char *ea_name,
		unsigned char *ea_value, size_t buf_size,
		const struct nls_table *nls_codepage, int remap_special_chars);
extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon,
		const char *fileName, const char *ea_name,
		const void *ea_value, const __u16 ea_value_len,
+126 −234
Original line number Diff line number Diff line
@@ -170,19 +170,19 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
	 * need to prevent multiple threads trying to simultaneously
	 * reconnect the same SMB session
	 */
	down(&ses->sesSem);
	mutex_lock(&ses->session_mutex);
	if (ses->need_reconnect)
		rc = cifs_setup_session(0, ses, nls_codepage);

	/* do we need to reconnect tcon? */
	if (rc || !tcon->need_reconnect) {
		up(&ses->sesSem);
		mutex_unlock(&ses->session_mutex);
		goto out;
	}

	mark_open_files_invalid(tcon);
	rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
	up(&ses->sesSem);
	mutex_unlock(&ses->session_mutex);
	cFYI(1, ("reconnect tcon rc = %d", rc));

	if (rc)
@@ -700,13 +700,13 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
	if (!ses || !ses->server)
		return -EIO;

	down(&ses->sesSem);
	mutex_lock(&ses->session_mutex);
	if (ses->need_reconnect)
		goto session_already_dead; /* no need to send SMBlogoff if uid
					      already closed due to reconnect */
	rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
	if (rc) {
		up(&ses->sesSem);
		mutex_unlock(&ses->session_mutex);
		return rc;
	}

@@ -721,7 +721,7 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
	pSMB->AndXCommand = 0xFF;
	rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
session_already_dead:
	up(&ses->sesSem);
	mutex_unlock(&ses->session_mutex);

	/* if session dead then we do not need to do ulogoff,
		since server closed smb session, no sense reporting
@@ -5269,10 +5269,20 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
	cifs_buf_release(pSMB);
	return rc;
}

#ifdef CONFIG_CIFS_XATTR
/*
 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
 * function used by listxattr and getxattr type calls. When ea_name is set,
 * it looks for that attribute name and stuffs that value into the EAData
 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
 * buffer. In both cases, the return value is either the length of the
 * resulting data or a negative error code. If EAData is a NULL pointer then
 * the data isn't copied to it, but the length is returned.
 */
ssize_t
CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
		 const unsigned char *searchName,
		const unsigned char *searchName, const unsigned char *ea_name,
		char *EAData, size_t buf_size,
		const struct nls_table *nls_codepage, int remap)
{
@@ -5281,10 +5291,12 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	int list_len;
	struct fealist *ea_response_data;
	struct fea *temp_fea;
	char *temp_ptr;
	__u16 params, byte_count;
	char *end_of_smb;
	__u16 params, byte_count, data_offset;

	cFYI(1, ("In Query All EAs path %s", searchName));
QAllEAsRetry:
@@ -5294,22 +5306,22 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		list_len =
		    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
				     PATH_MAX, nls_codepage, remap);
		name_len++;	/* trailing null */
		name_len *= 2;
		list_len++;	/* trailing null */
		list_len *= 2;
	} else {	/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, PATH_MAX);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
		list_len = strnlen(searchName, PATH_MAX);
		list_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, list_len);
	}

	params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
	params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxDataCount = cpu_to_le16(4000);
	pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
@@ -5334,58 +5346,97 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Send error in QueryAllEAs = %d", rc));
	} else {		/* decode response */
		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
		goto QAllEAsOut;
	}


	/* BB also check enough total bytes returned */
	/* BB we need to improve the validity checking
	of these trans2 responses */
		if (rc || (pSMBr->ByteCount < 4))

	rc = validate_t2((struct smb_t2_rsp *)pSMBr);
	if (rc || (pSMBr->ByteCount < 4)) {
		rc = -EIO;	/* bad smb */
	   /* else if (pFindData){
			memcpy((char *) pFindData,
			       (char *) &pSMBr->hdr.Protocol +
			       data_offset, kl);
		}*/ else {
		goto QAllEAsOut;
	}

	/* check that length of list is not more than bcc */
	/* check that each entry does not go beyond length
	   of list */
	/* check that each element of each entry does not
	   go beyond end of list */
			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
			struct fealist *ea_response_data;
			rc = 0;
	/* validate_trans2_offsets() */
	/* BB check if start of smb + data_offset > &bcc+ bcc */

	data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
	ea_response_data = (struct fealist *)
				(((char *) &pSMBr->hdr.Protocol) +
				data_offset);
			name_len = le32_to_cpu(ea_response_data->list_len);
			cFYI(1, ("ea length %d", name_len));
			if (name_len <= 8) {
			/* returned EA size zeroed at top of function */
				(((char *) &pSMBr->hdr.Protocol) + data_offset);

	list_len = le32_to_cpu(ea_response_data->list_len);
	cFYI(1, ("ea length %d", list_len));
	if (list_len <= 8) {
		cFYI(1, ("empty EA list returned from server"));
			} else {
		goto QAllEAsOut;
	}

	/* make sure list_len doesn't go past end of SMB */
	end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
	if ((char *)ea_response_data + list_len > end_of_smb) {
		cFYI(1, ("EA list appears to go beyond SMB"));
		rc = -EIO;
		goto QAllEAsOut;
	}

	/* account for ea list len */
				name_len -= 4;
	list_len -= 4;
	temp_fea = ea_response_data->list;
	temp_ptr = (char *)temp_fea;
				while (name_len > 0) {
	while (list_len > 0) {
		unsigned int name_len;
		__u16 value_len;
					name_len -= 4;

		list_len -= 4;
		temp_ptr += 4;
					rc += temp_fea->name_len;
		/* make sure we can read name_len and value_len */
		if (list_len < 0) {
			cFYI(1, ("EA entry goes beyond length of list"));
			rc = -EIO;
			goto QAllEAsOut;
		}

		name_len = temp_fea->name_len;
		value_len = le16_to_cpu(temp_fea->value_len);
		list_len -= name_len + 1 + value_len;
		if (list_len < 0) {
			cFYI(1, ("EA entry goes beyond length of list"));
			rc = -EIO;
			goto QAllEAsOut;
		}

		if (ea_name) {
			if (strncmp(ea_name, temp_ptr, name_len) == 0) {
				temp_ptr += name_len + 1;
				rc = value_len;
				if (buf_size == 0)
					goto QAllEAsOut;
				if ((size_t)value_len > buf_size) {
					rc = -ERANGE;
					goto QAllEAsOut;
				}
				memcpy(EAData, temp_ptr, value_len);
				goto QAllEAsOut;
			}
		} else {
			/* account for prefix user. and trailing null */
					rc = rc + 5 + 1;
			rc += (5 + 1 + name_len);
			if (rc < (int) buf_size) {
				memcpy(EAData, "user.", 5);
				EAData += 5;
						memcpy(EAData, temp_ptr,
						       temp_fea->name_len);
						EAData += temp_fea->name_len;
				memcpy(EAData, temp_ptr, name_len);
				EAData += name_len;
				/* null terminate name */
				*EAData = 0;
						EAData = EAData + 1;
				++EAData;
			} else if (buf_size == 0) {
				/* skip copy - calc size only */
			} else {
@@ -5393,178 +5444,19 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
				rc = -ERANGE;
				break;
			}
					name_len -= temp_fea->name_len;
					temp_ptr += temp_fea->name_len;
					/* account for trailing null */
					name_len--;
					temp_ptr++;
					value_len =
					      le16_to_cpu(temp_fea->value_len);
					name_len -= value_len;
					temp_ptr += value_len;
					/* BB check that temp_ptr is still
					      within the SMB BB*/

					/* no trailing null to account for
					   in value len */
					/* go on to next EA */
					temp_fea = (struct fea *)temp_ptr;
				}
			}
		}
	}
	cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto QAllEAsRetry;

	return (ssize_t)rc;
		}

ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
		const unsigned char *searchName, const unsigned char *ea_name,
		unsigned char *ea_value, size_t buf_size,
		const struct nls_table *nls_codepage, int remap)
{
	TRANSACTION2_QPI_REQ *pSMB = NULL;
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	struct fea *temp_fea;
	char *temp_ptr;
	__u16 params, byte_count;

	cFYI(1, ("In Query EA path %s", searchName));
QEARetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
				     PATH_MAX, nls_codepage, remap);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {	/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, PATH_MAX);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
		temp_ptr += name_len + 1 + value_len;
		temp_fea = (struct fea *)temp_ptr;
	}

	params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxDataCount = cpu_to_le16(4000);
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(
		struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
	pSMB->Reserved4 = 0;
	pSMB->hdr.smb_buf_length += byte_count;
	pSMB->ByteCount = cpu_to_le16(byte_count);

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Send error in Query EA = %d", rc));
	} else {		/* decode response */
		rc = validate_t2((struct smb_t2_rsp *)pSMBr);

		/* BB also check enough total bytes returned */
		/* BB we need to improve the validity checking
		of these trans2 responses */
		if (rc || (pSMBr->ByteCount < 4))
			rc = -EIO;	/* bad smb */
	   /* else if (pFindData){
			memcpy((char *) pFindData,
			       (char *) &pSMBr->hdr.Protocol +
			       data_offset, kl);
		}*/ else {
			/* check that length of list is not more than bcc */
			/* check that each entry does not go beyond length
			   of list */
			/* check that each element of each entry does not
			   go beyond end of list */
			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
			struct fealist *ea_response_data;
	/* didn't find the named attribute */
	if (ea_name)
		rc = -ENODATA;
			/* validate_trans2_offsets() */
			/* BB check if start of smb + data_offset > &bcc+ bcc*/
			ea_response_data = (struct fealist *)
				(((char *) &pSMBr->hdr.Protocol) +
				data_offset);
			name_len = le32_to_cpu(ea_response_data->list_len);
			cFYI(1, ("ea length %d", name_len));
			if (name_len <= 8) {
			/* returned EA size zeroed at top of function */
				cFYI(1, ("empty EA list returned from server"));
			} else {
				/* account for ea list len */
				name_len -= 4;
				temp_fea = ea_response_data->list;
				temp_ptr = (char *)temp_fea;
				/* loop through checking if we have a matching
				name and then return the associated value */
				while (name_len > 0) {
					__u16 value_len;
					name_len -= 4;
					temp_ptr += 4;
					value_len =
					      le16_to_cpu(temp_fea->value_len);
				/* BB validate that value_len falls within SMB,
				even though maximum for name_len is 255 */
					if (memcmp(temp_fea->name, ea_name,
						  temp_fea->name_len) == 0) {
						/* found a match */
						rc = value_len;
				/* account for prefix user. and trailing null */
						if (rc <= (int)buf_size) {
							memcpy(ea_value,
								temp_fea->name+temp_fea->name_len+1,
								rc);
							/* ea values, unlike ea
							   names, are not null
							   terminated */
						} else if (buf_size == 0) {
						/* skip copy - calc size only */
						} else {
						/* stop before overrun buffer */
							rc = -ERANGE;
						}
						break;
					}
					name_len -= temp_fea->name_len;
					temp_ptr += temp_fea->name_len;
					/* account for trailing null */
					name_len--;
					temp_ptr++;
					name_len -= value_len;
					temp_ptr += value_len;
					/* No trailing null to account for in
					   value_len.  Go on to next EA */
					temp_fea = (struct fea *)temp_ptr;
				}
			}
		}
	}

QAllEAsOut:
	cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto QEARetry;
		goto QAllEAsRetry;

	return (ssize_t)rc;
}
Loading