cifs: handle large EA requests more gracefully in smb2+

Update reading the EA using increasingly larger buffer sizes
until the response will fit in the buffer, or we exceed the
(arbitrary) maximum set to 64kb.

Without this change, a user is able to add more and more EAs using
setfattr until the point where the total space of all EAs exceed 2kb
at which point the user can no longer list the EAs at all
and getfattr will abort with an error.

The same issue still exists for EAs in SMB1.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Reported-by: Xiaoli Feng <xifeng@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>

authored by Ronnie Sahlberg and committed by Steve French 7cb3def4 06e22908

+32 -11
+1 -1
fs/cifs/smb2maperror.c
··· 214 {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, 215 {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, 216 {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, 217 - {STATUS_BUFFER_OVERFLOW, -EIO, "STATUS_BUFFER_OVERFLOW"}, 218 {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, 219 {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, 220 {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"},
··· 214 {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, 215 {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, 216 {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, 217 + {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"}, 218 {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, 219 {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, 220 {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"},
+25 -6
fs/cifs/smb2ops.c
··· 522 struct cifs_open_parms oparms; 523 struct cifs_fid fid; 524 struct smb2_file_full_ea_info *smb2_data; 525 526 utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 527 if (!utf16_path) ··· 542 return rc; 543 } 544 545 - smb2_data = kzalloc(SMB2_MAX_EA_BUF, GFP_KERNEL); 546 - if (smb2_data == NULL) { 547 - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 548 - return -ENOMEM; 549 } 550 551 - rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, fid.volatile_fid, 552 - smb2_data); 553 SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 554 555 if (!rc)
··· 522 struct cifs_open_parms oparms; 523 struct cifs_fid fid; 524 struct smb2_file_full_ea_info *smb2_data; 525 + int ea_buf_size = SMB2_MIN_EA_BUF; 526 527 utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 528 if (!utf16_path) ··· 541 return rc; 542 } 543 544 + while (1) { 545 + smb2_data = kzalloc(ea_buf_size, GFP_KERNEL); 546 + if (smb2_data == NULL) { 547 + SMB2_close(xid, tcon, fid.persistent_fid, 548 + fid.volatile_fid); 549 + return -ENOMEM; 550 + } 551 + 552 + rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, 553 + fid.volatile_fid, 554 + ea_buf_size, smb2_data); 555 + 556 + if (rc != -E2BIG) 557 + break; 558 + 559 + kfree(smb2_data); 560 + ea_buf_size <<= 1; 561 + 562 + if (ea_buf_size > SMB2_MAX_EA_BUF) { 563 + cifs_dbg(VFS, "EA size is too large\n"); 564 + SMB2_close(xid, tcon, fid.persistent_fid, 565 + fid.volatile_fid); 566 + return -ENOMEM; 567 + } 568 } 569 570 SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 571 572 if (!rc)
+3 -3
fs/cifs/smb2pdu.c
··· 2233 } 2234 2235 int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, 2236 - u64 persistent_fid, u64 volatile_fid, 2237 - struct smb2_file_full_ea_info *data) 2238 { 2239 return query_info(xid, tcon, persistent_fid, volatile_fid, 2240 FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0, 2241 - SMB2_MAX_EA_BUF, 2242 sizeof(struct smb2_file_full_ea_info), 2243 (void **)&data, 2244 NULL);
··· 2233 } 2234 2235 int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, 2236 + u64 persistent_fid, u64 volatile_fid, 2237 + int ea_buf_size, struct smb2_file_full_ea_info *data) 2238 { 2239 return query_info(xid, tcon, persistent_fid, volatile_fid, 2240 FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0, 2241 + ea_buf_size, 2242 sizeof(struct smb2_file_full_ea_info), 2243 (void **)&data, 2244 NULL);
+2 -1
fs/cifs/smb2pdu.h
··· 1178 char FileName[0]; /* Name to be assigned to new link */ 1179 } __packed; /* level 11 Set */ 1180 1181 - #define SMB2_MAX_EA_BUF 2048 1182 1183 struct smb2_file_full_ea_info { /* encoding of response for level 15 */ 1184 __le32 next_entry_offset;
··· 1178 char FileName[0]; /* Name to be assigned to new link */ 1179 } __packed; /* level 11 Set */ 1180 1181 + #define SMB2_MIN_EA_BUF 2048 1182 + #define SMB2_MAX_EA_BUF 65536 1183 1184 struct smb2_file_full_ea_info { /* encoding of response for level 15 */ 1185 __le32 next_entry_offset;
+1
fs/cifs/smb2proto.h
··· 134 u64 persistent_file_id, u64 volatile_file_id); 135 extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, 136 u64 persistent_file_id, u64 volatile_file_id, 137 struct smb2_file_full_ea_info *data); 138 extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, 139 u64 persistent_file_id, u64 volatile_file_id,
··· 134 u64 persistent_file_id, u64 volatile_file_id); 135 extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, 136 u64 persistent_file_id, u64 volatile_file_id, 137 + int ea_buf_size, 138 struct smb2_file_full_ea_info *data); 139 extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, 140 u64 persistent_file_id, u64 volatile_file_id,