]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
cifs: handle large EA requests more gracefully in smb2+
authorRonnie Sahlberg <lsahlber@redhat.com>
Wed, 27 Sep 2017 23:39:58 +0000 (09:39 +1000)
committerSteve French <smfrench@gmail.com>
Wed, 18 Oct 2017 16:52:39 +0000 (11:52 -0500)
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>
fs/cifs/smb2maperror.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/smb2proto.h

index 7ca9808a0daa01bfb149690964bbd165d9c6af20..62c88dfed57b3f66fff931f4abbcc8126ca4cf11 100644 (file)
@@ -214,7 +214,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
        {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"},
        {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"},
        {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"},
-       {STATUS_BUFFER_OVERFLOW, -EIO, "STATUS_BUFFER_OVERFLOW"},
+       {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"},
        {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"},
        {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"},
        {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"},
index 0dafdbae1f8cb35539a138e73330a214f7f72da3..bdb963d0ba32069035bdd23c9046985b41feb2bb 100644 (file)
@@ -522,6 +522,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
        struct cifs_open_parms oparms;
        struct cifs_fid fid;
        struct smb2_file_full_ea_info *smb2_data;
+       int ea_buf_size = SMB2_MIN_EA_BUF;
 
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path)
@@ -541,14 +542,32 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
                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;
+       while (1) {
+               smb2_data = kzalloc(ea_buf_size, 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,
+                                   ea_buf_size, smb2_data);
+
+               if (rc != -E2BIG)
+                       break;
+
+               kfree(smb2_data);
+               ea_buf_size <<= 1;
+
+               if (ea_buf_size > SMB2_MAX_EA_BUF) {
+                       cifs_dbg(VFS, "EA size is too large\n");
+                       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)
index 6f0e6343c15e7e329e6de896220c693f337d9829..ba3865b338d870c371b46431640405f064545eb3 100644 (file)
@@ -2233,12 +2233,12 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
-       u64 persistent_fid, u64 volatile_fid,
-       struct smb2_file_full_ea_info *data)
+                  u64 persistent_fid, u64 volatile_fid,
+                  int ea_buf_size, struct smb2_file_full_ea_info *data)
 {
        return query_info(xid, tcon, persistent_fid, volatile_fid,
                          FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0,
-                         SMB2_MAX_EA_BUF,
+                         ea_buf_size,
                          sizeof(struct smb2_file_full_ea_info),
                          (void **)&data,
                          NULL);
index 6c9653a130c8bf38f5c38d6965cea32d105e708d..4c155b95b5584069beab75a4eda198c00b3e0f74 100644 (file)
@@ -1178,7 +1178,8 @@ struct smb2_file_link_info { /* encoding of request for level 11 */
        char   FileName[0];     /* Name to be assigned to new link */
 } __packed; /* level 11 Set */
 
-#define SMB2_MAX_EA_BUF 2048
+#define SMB2_MIN_EA_BUF  2048
+#define SMB2_MAX_EA_BUF 65536
 
 struct smb2_file_full_ea_info { /* encoding of response for level 15 */
        __le32 next_entry_offset;
index 003217099ef3e6831a36ed81f7c13f1f611efb9d..e9ab5227e7a8ac3d69e69afe678cee78566056a8 100644 (file)
@@ -134,6 +134,7 @@ extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
                      u64 persistent_file_id, u64 volatile_file_id);
 extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
                          u64 persistent_file_id, u64 volatile_file_id,
+                         int ea_buf_size,
                          struct smb2_file_full_ea_info *data);
 extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
                           u64 persistent_file_id, u64 volatile_file_id,