s3:smbd - support streams larger than 64 KiB
authorAndrew Walker <awalker@ixsystems.com>
Fri, 7 May 2021 10:37:25 +0000 (06:37 -0400)
committerJeremy Allison <jra@samba.org>
Mon, 10 May 2021 20:16:21 +0000 (20:16 +0000)
Add support for streams that are larger than 64 KiB in size. Upper
and lower bound are controlled by the parameters smbd max_xattr_size.
Testing against ReFS on Windows (where ADS size is limited in size
shows the server responding with STATUS_FILESYSTEM_LIMITATION.
Do the same in samba for this case.

Currently, large xattrs are supported in FreeBSD.

Signed-off-by: Andrew Walker <awalker@ixsystems.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Mon May 10 20:16:21 UTC 2021 on sn-devel-184

docs-xml/smbdotconf/misc/smbdmaxxattrsize.xml [new file with mode: 0644]
lib/param/loadparm.c
source3/modules/vfs_streams_xattr.c
source3/param/loadparm.c
source3/smbd/smb2_write.c
source3/smbd/trans2.c

diff --git a/docs-xml/smbdotconf/misc/smbdmaxxattrsize.xml b/docs-xml/smbdotconf/misc/smbdmaxxattrsize.xml
new file mode 100644 (file)
index 0000000..3ae91a3
--- /dev/null
@@ -0,0 +1,28 @@
+<samba:parameter name="smbd max xattr size"
+                 context="S"
+                 type="integer"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+       <para>
+         This parameter controls the maximum size of extended attributes
+         that may be written to the server as EAs or as alternate data
+         streams if vfs_streams_xattr is enabled. The maximum size of
+         extended attributes depends on the Samba server's operating system
+         and the underlying filesystem. The Linux VFS currently sets an
+         upper boundary of 64 KiB per extended attribute. FreeBSD does not
+         set a practical upper limit, but since pread() and pwrite() are not
+         possible via the extattr on FreeBSD, it is not recommended to
+         increase this value above a few MiB.
+
+         If a client attempts to write an overly-large alternate datastream,
+         the Samba server will return STATUS_FILESYSTEM_LIMITATION.
+         If this error is encountered, users may try increasing the maximum
+         size supported for xattr writes. If this is not possible, and
+         writes are from a MacOS client and to an AFP_Resource extended
+         attribute, the user may enable the vfs_fruit module and configure
+         to allow stream writes for AFP_Resource to an alternative storage
+         location. See vfs_fruit documentation for further details.
+       </para>
+</description>
+<value type="default">65536</value>
+</samba:parameter>
index adfba67652e43230baf0ce31d9cbff2149a4590f..b674858e70652492694c41ece69053f71c805fa3 100644 (file)
@@ -2956,6 +2956,10 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
                                  "client protection",
                                  "default");
 
+       lpcfg_do_global_parameter(lp_ctx,
+                                 "smbd max xattr size",
+                                 "65536");
+
        for (i = 0; parm_table[i].label; i++) {
                if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
                        lp_ctx->flags[i] |= FLAG_DEFAULT;
index cbe82ad6f33587b018bb7f6a6ce2914d4e3f1a95..78a2baf40d57014214827b6b0541254d74feb867 100644 (file)
@@ -957,6 +957,27 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
                return -1;
        }
 
+       if ((offset + n) >= lp_smbd_max_xattr_size(SNUM(handle->conn))) {
+               /*
+                * Requested write is beyond what can be read based on
+                * samba configuration.
+                * ReFS returns STATUS_FILESYSTEM_LIMITATION, which causes
+                * entire file to be skipped by File Explorer. VFAT returns
+                * NT_STATUS_OBJECT_NAME_COLLISION causes user to be prompted
+                * to skip writing metadata, but copy data.
+                */
+               DBG_ERR("Write to xattr [%s] on file [%s] exceeds maximum "
+                       "supported extended attribute size. "
+                       "Depending on filesystem type and operating system "
+                       "(OS) specifics, this value may be increased using "
+                       "the value of the parameter: "
+                       "smbd max xattr size = <bytes>. Consult OS and "
+                       "filesystem manpages prior to increasing this limit.\n",
+                       sio->xattr_name, sio->base);
+               errno = EOVERFLOW;
+               return -1;
+       }
+
        /* Create an smb_filename with stream_name == NULL. */
        smb_fname_base = synthetic_smb_fname(talloc_tos(),
                                        sio->base,
index 55184e9b7981c76033bc49ffb022c2746a2015d1..85e578eda9eb9cd2fa9229fcf3609cfaf50fb6e9 100644 (file)
@@ -197,6 +197,7 @@ static const struct loadparm_service _sDefault =
        .map_hidden = false,
        .map_archive = true,
        .store_dos_attributes = true,
+       .smbd_max_xattr_size = 65536,
        .dmapi_support = false,
        .locking = true,
        .strict_locking = Auto,
index e49e623d79686a5efe2c6f265b0fd5c8e81963a9..612c89d59d1b79228542b92201a57e574cb1d317 100644 (file)
@@ -193,7 +193,12 @@ static NTSTATUS smb2_write_complete_internal(struct tevent_req *req,
        files_struct *fsp = state->fsp;
 
        if (nwritten == -1) {
-               status = map_nt_error_from_unix(err);
+               if (err == EOVERFLOW &&
+                   is_ntfs_stream_smb_fname(fsp->fsp_name)) {
+                       status = NT_STATUS_FILE_SYSTEM_LIMITATION;
+               } else {
+                       status = map_nt_error_from_unix(err);
+               }
 
                DEBUG(2, ("smb2_write failed: %s, file %s, "
                          "length=%lu offset=%lu nwritten=-1: %s\n",
index de8431175812f0591aeb3bedc6162bdabe6fc73f..9aa2495792eedcf2d4daa7ddd4f3e2fe45ecb880 100644 (file)
@@ -249,6 +249,7 @@ NTSTATUS get_ea_value(TALLOC_CTX *mem_ctx,
        size_t attr_size = 256;
        char *val = NULL;
        ssize_t sizeret;
+       size_t max_xattr_size = lp_smbd_max_xattr_size(SNUM(conn));
 
  again:
 
@@ -264,8 +265,8 @@ NTSTATUS get_ea_value(TALLOC_CTX *mem_ctx,
                                ea_name, val, attr_size);
        }
 
-       if (sizeret == -1 && errno == ERANGE && attr_size != 65536) {
-               attr_size = 65536;
+       if (sizeret == -1 && errno == ERANGE && attr_size < max_xattr_size) {
+               attr_size = max_xattr_size;
                goto again;
        }
 
@@ -501,6 +502,17 @@ static NTSTATUS get_ea_list_from_fsp(TALLOC_CTX *mem_ctx,
                         */
                        TALLOC_FREE(listp);
                        continue;
+               } else if (listp->ea.value.length > 65536) {
+                       /*
+                        * SMB clients may report error with file
+                        * if large EA is presented to them.
+                        */
+                       DBG_ERR("EA [%s] on file [%s] exceeds "
+                               "maximum permitted EA size of 64KiB: %zu\n.",
+                               listp->ea.name, fsp_str_dbg(fsp),
+                               listp->ea.value.length);
+                       TALLOC_FREE(listp);
+                       continue;
                }
 
                push_ascii_fstring(dos_ea_name, listp->ea.name);