[3/3] cifs: check how much EA space we already use before adding more EAs

Message ID 20181114224020.6100-4-lsahlber@redhat.com
State New
Headers show
  • cifs: moar compounding
Related show

Commit Message

Ronnie Sahlberg Nov. 14, 2018, 10:40 p.m.
When setting/adding an EA, check how much EA space we arelady have on this
file and make sure that if we add this new EA that the result will still
fit inside a SMB2_MAX_EA_BUF.

In the client we limit the maximum buffer to currently 64k.
When we store these on the server we have no way of knowing if/what the
servers limit for EAs are. They could be, and often are, much larger than
This change is to return an error and not set the EA if we suspect that
the result could overflow the buffer.

This prevents the very annoying behaviour that we can add EAs well
beyond the maximum clientside buffer and thus, at that stage make
it is no longer query or list the existing EAs any more.

The check for overflow is overly conservative :
        if (len + rsp_iov[0].iov_len > SMB2_MAX_EA_BUF) {
since the actual size of the EAs are a bit less than iov_len
but this is simpler to read.

The only effect of this is that we will stop allowing adding new EAs ~100
bytes earlier than stricly needed which does not matter much for 64k

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
 fs/cifs/smb2ops.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)


diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index cea942056252..65e5d5521a6c 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -915,10 +915,30 @@  smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 	if (!utf16_path)
 		return -ENOMEM;
+	len = sizeof(ea) + ea_name_len + ea_value_len + 1;
 	memset(rqst, 0, sizeof(rqst));
 	resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
 	memset(rsp_iov, 0, sizeof(rsp_iov));
+	/*
+	 * Make sure we have enough space to store the new EA.
+	 */
+	rc = smb2_query_info_compound(xid, tcon, utf16_path,
+				      FILE_READ_EA,
+				      SMB2_O_INFO_FILE,
+				      SMB2_MAX_EA_BUF,
+				      &rsp_iov[0], &resp_buftype[0], cifs_sb);
+	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+	resp_buftype[0] = CIFS_NO_BUFFER;
+	rsp_iov[0].iov_base = NULL;
+	if (len + rsp_iov[0].iov_len > SMB2_MAX_EA_BUF) {
+		rc = -ERANGE;
+		goto sea_exit;
+	}
 	/* Open */
 	memset(&open_iov, 0, sizeof(open_iov));
 	rqst[0].rq_iov = open_iov;