vfs_acl_xattr: avoid needlessly supplying a large buffer to getxattr()
authorUri Simchoni <uri@samba.org>
Thu, 13 Apr 2017 09:44:58 +0000 (12:44 +0300)
committerJeremy Allison <jra@samba.org>
Tue, 18 Apr 2017 02:41:16 +0000 (04:41 +0200)
When obtaining the security descriptor via getxattr(), first try
optimistically to supply a buffer of 4K, and if that turns out
to be too small, determine the correct buffer size.

The previous behavior of falling back to a 64K buffer encountered
problem with Linux prior to version 3.6, due to pyisical memory
fragmentation. With those kernels, as long as the buffer is 8K or
smaller, getting the xattr is much less prone to failure due to
memory fragmentation.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12737

Signed-off-by: Uri Simchoni <uri@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Tue Apr 18 04:41:16 CEST 2017 on sn-devel-144

source3/modules/vfs_acl_xattr.c

index 85b9a0d660a57bbbced5637c0d4a38fcb16946d4..421860b2adf64dc54c2b7329f270ddbef0927b8b 100644 (file)
@@ -72,7 +72,7 @@ static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
                        const struct smb_filename *smb_fname,
                        DATA_BLOB *pblob)
 {
-       size_t size = 1024;
+       size_t size = 4096;
        uint8_t *val = NULL;
        uint8_t *tmp;
        ssize_t sizeret;
@@ -91,22 +91,38 @@ static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
        sizeret =
            getxattr_do(handle, fsp, smb_fname, XATTR_NTACL_NAME, val, size);
 
-       /* Max ACL size is 65536 bytes. */
-       if (sizeret == -1) {
-               if ((errno == ERANGE) && (size != 65536)) {
-                       /* Too small, try again. */
-                       size = 65536;
-                       goto again;
-               }
+       if (sizeret >= 0) {
+               pblob->data = val;
+               pblob->length = sizeret;
+               return NT_STATUS_OK;
+       }
 
-               /* Real error - exit here. */
-               TALLOC_FREE(val);
-               return map_nt_error_from_unix(errno);
+       if (errno != ERANGE) {
+               goto err;
        }
 
-       pblob->data = val;
-       pblob->length = sizeret;
-       return NT_STATUS_OK;
+       /* Too small, try again. */
+       sizeret =
+           getxattr_do(handle, fsp, smb_fname, XATTR_NTACL_NAME, NULL, 0);
+       if (sizeret < 0) {
+               goto err;
+       }
+
+       if (size < sizeret) {
+               size = sizeret;
+       }
+
+       if (size > 65536) {
+               /* Max ACL size is 65536 bytes. */
+               errno = ERANGE;
+               goto err;
+       }
+
+       goto again;
+  err:
+       /* Real error - exit here. */
+       TALLOC_FREE(val);
+       return map_nt_error_from_unix(errno);
 }
 
 /*******************************************************************