vfs_streams_xattr: stream name prefix and type suffix

Ralph Böhme rb at sernet.de
Wed May 21 05:47:53 MDT 2014


Hi all!

I'm trying to figure out a proper solution such that xattrs sent by an
OS X SMB client as named streams are stored on disk on the server by
vfs_streams_xattr in a way that's compatible with the way other
protocols use xattr names.

Using vfs_streams_xattr for named streams support in Samba results in
all (this is simplified) streams stored on disk with a name
"user.DosStream.NAME:$DATA" where NAME is the name of the stream.

As the OS X SMB client sends xattrs as named streams across the wire,
an xattr that is created by the client with a name "foo" is stored on
the server as "user.DosStream.foo:$DATA".

Accessing the same filesystem with other protocols, eg AFP, would
expect "user.foo" as xattr name.

It would be nice to make the module behaviour configurable:

1) a string option for the prefix (with the previous value as default:
   "user.DosStream.")

2) a boolean that controls whether the stream type ("$DATA") name is
   appended to the xattr name (with a default of true which resembles
   previous behaviour)

Attached patches implement this. The initial patch makes a private
function public. I'm then calling that function from the VFS module
for checking againt the private Samba xattr namespace.

With the patches applied, adding the following to smb.conf

      streams_xattr:prefix = user.
      streams_xattr:store_stream_type = no

gives the desired behaviour.

Thoughts?

Thanks!
-Ralph

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de,mailto:kontakt@sernet.de
-------------- next part --------------
>From 066fcf3c5bb414b84a349a1a5fda07e3e0308593 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Wed, 21 May 2014 11:52:27 +0200
Subject: [PATCH 1/2] Convert samba_private_attr_name() to a public function

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source3/modules/vfs_streams_xattr.c | 3 +++
 source3/smbd/proto.h                | 1 +
 source3/smbd/trans2.c               | 2 +-
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 5e9bd3e..5ff4f82 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -674,6 +674,9 @@ static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
 			    prefix_len) != 0) {
 			continue;
 		}
+		if (samba_private_attr_name(names[i])) {
+			continue;
+		}
 
 		status = get_ea_value(names, conn, fsp, fname, names[i], &ea);
 		if (!NT_STATUS_IS_OK(status)) {
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index f598816..3c6accf 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -1056,6 +1056,7 @@ NTSTATUS check_access(connection_struct *conn,
 				uint32_t access_mask);
 uint64_t smb_roundup(connection_struct *conn, uint64_t val);
 uint64_t get_FileIndex(connection_struct *conn, const SMB_STRUCT_STAT *psbuf);
+bool samba_private_attr_name(const char *unix_ea_name);
 NTSTATUS get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn,
 		      files_struct *fsp, const char *fname,
 		      const char *ea_name, struct ea_struct *pea);
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 4afb27e..aeada68 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -120,7 +120,7 @@ uint64_t get_FileIndex(connection_struct *conn, const SMB_STRUCT_STAT *psbuf)
  Refuse to allow clients to overwrite our private xattrs.
 ****************************************************************************/
 
-static bool samba_private_attr_name(const char *unix_ea_name)
+bool samba_private_attr_name(const char *unix_ea_name)
 {
 	static const char * const prohibited_ea_names[] = {
 		SAMBA_POSIX_INHERITANCE_EA_NAME,
-- 
1.8.5.3

-------------- next part --------------
>From 65c09e51770d77f88f37233383038b56d0ffb031 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <rb at sernet.de>
Date: Tue, 20 May 2014 15:17:01 +0200
Subject: [PATCH 2/2] vfs_streams_xattr: add options "prefix" and 
 "store_stream_type"

Add module options that can be used to configure the stream prefix the
module uses (option "prefix", a string) and whether the stream type
"$DATA" is appended to the xattr name on disk (option
"store_stream_type", a boolean).

The default "prefix" is "user.DosStream" and the default for
"store_stream_type" is true, this gives unchanged default behaviour
when not specifying this option.

OS X SMB clients will send xattrs as named streams over the wire, by
setting the options to the following values

  streams_xattr:prefix = user.
  streams_xattr:store_stream_type = no

OS X xattrs will be stored on disk on the server with their unmodified
names and as such provide interoperability with other protocols like
AFP.

Signed-off-by: Ralph Boehme <rb at sernet.de>
---
 source3/modules/vfs_streams_xattr.c | 93 +++++++++++++++++++++++++++++--------
 1 file changed, 73 insertions(+), 20 deletions(-)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 5ff4f82..50f227d 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -29,6 +29,12 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
 
+struct streams_xattr_config {
+	const char *prefix;
+	size_t prefix_len;
+	bool store_stream_type;
+};
+
 struct stream_io {
 	char *base;
 	char *xattr_name;
@@ -95,16 +101,21 @@ static ssize_t get_xattr_size(connection_struct *conn,
  * Given a stream name, populate xattr_name with the xattr name to use for
  * accessing the stream.
  */
-static NTSTATUS streams_xattr_get_name(TALLOC_CTX *ctx,
+static NTSTATUS streams_xattr_get_name(vfs_handle_struct *handle,
+				       TALLOC_CTX *ctx,
 				       const char *stream_name,
 				       char **xattr_name)
 {
 	char *stype;
+	struct streams_xattr_config *config;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config,
+				return NT_STATUS_UNSUCCESSFUL);
 
 	stype = strchr_m(stream_name + 1, ':');
 
 	*xattr_name = talloc_asprintf(ctx, "%s%s",
-				      SAMBA_XATTR_DOSSTREAM_PREFIX,
+				      config->prefix,
 				      stream_name + 1);
 	if (*xattr_name == NULL) {
 		return NT_STATUS_NO_MEMORY;
@@ -112,8 +123,9 @@ static NTSTATUS streams_xattr_get_name(TALLOC_CTX *ctx,
 
 	if (stype == NULL) {
 		/* Append an explicit stream type if one wasn't specified. */
-		*xattr_name = talloc_asprintf(ctx, "%s:$DATA",
-					       *xattr_name);
+		*xattr_name = talloc_asprintf(ctx, "%s%s",
+					      *xattr_name,
+					      config->store_stream_type ? ":$DATA" : "");
 		if (*xattr_name == NULL) {
 			return NT_STATUS_NO_MEMORY;
 		}
@@ -145,7 +157,7 @@ static bool streams_xattr_recheck(struct stream_io *sio)
 		return false;
 	}
 
-	status = streams_xattr_get_name(talloc_tos(),
+	status = streams_xattr_get_name(sio->handle, talloc_tos(),
 					sio->fsp->fsp_name->stream_name,
 					&xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -271,7 +283,7 @@ static int streams_xattr_stat(vfs_handle_struct *handle,
 	}
 
 	/* Derive the xattr name to lookup. */
-	status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
+	status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name,
 					&xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
 		errno = map_errno_from_nt_status(status);
@@ -322,7 +334,7 @@ static int streams_xattr_lstat(vfs_handle_struct *handle,
 	}
 
 	/* Derive the xattr name to lookup. */
-	status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
+	status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name,
 					&xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
 		errno = map_errno_from_nt_status(status);
@@ -386,7 +398,7 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 		return ret;
 	}
 
-	status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
+	status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name,
 					&xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
 		errno = map_errno_from_nt_status(status);
@@ -541,7 +553,7 @@ static int streams_xattr_unlink(vfs_handle_struct *handle,
 		return ret;
 	}
 
-	status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
+	status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name,
 					&xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
 		errno = map_errno_from_nt_status(status);
@@ -597,14 +609,14 @@ static int streams_xattr_rename(vfs_handle_struct *handle,
 	}
 
 	/* Get the xattr names. */
-	status = streams_xattr_get_name(talloc_tos(),
+	status = streams_xattr_get_name(handle, talloc_tos(),
 					smb_fname_src->stream_name,
 					&src_xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
 		errno = map_errno_from_nt_status(status);
 		goto fail;
 	}
-	status = streams_xattr_get_name(talloc_tos(),
+	status = streams_xattr_get_name(handle, talloc_tos(),
 					smb_fname_dst->stream_name,
 					&dst_xattr_name);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -650,7 +662,7 @@ static int streams_xattr_rename(vfs_handle_struct *handle,
 	return ret;
 }
 
-static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
+static NTSTATUS walk_xattr_streams(vfs_handle_struct *handle, files_struct *fsp,
 				   const char *fname,
 				   bool (*fn)(struct ea_struct *ea,
 					      void *private_data),
@@ -659,9 +671,12 @@ static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
 	NTSTATUS status;
 	char **names;
 	size_t i, num_names;
-	size_t prefix_len = strlen(SAMBA_XATTR_DOSSTREAM_PREFIX);
+	struct streams_xattr_config *config;
 
-	status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config,
+				return NT_STATUS_UNSUCCESSFUL);
+
+	status = get_ea_names_from_file(talloc_tos(), handle->conn, fsp, fname,
 					&names, &num_names);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
@@ -670,23 +685,24 @@ static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
 	for (i=0; i<num_names; i++) {
 		struct ea_struct ea;
 
-		if (strncmp(names[i], SAMBA_XATTR_DOSSTREAM_PREFIX,
-			    prefix_len) != 0) {
+		if (strncmp(names[i], config->prefix,
+			    config->prefix_len) != 0) {
 			continue;
 		}
 		if (samba_private_attr_name(names[i])) {
 			continue;
 		}
 
-		status = get_ea_value(names, conn, fsp, fname, names[i], &ea);
+		status = get_ea_value(names, handle->conn, fsp, fname, names[i], &ea);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(10, ("Could not get ea %s for file %s: %s\n",
 				   names[i], fname, nt_errstr(status)));
 			continue;
 		}
 
-		ea.name = talloc_asprintf(ea.value.data, ":%s",
-					  names[i] + prefix_len);
+		ea.name = talloc_asprintf(ea.value.data, ":%s%s",
+					  names[i] + config->prefix_len,
+					  config->store_stream_type ? "" : ":$DATA");
 		if (ea.name == NULL) {
 			DEBUG(0, ("talloc failed\n"));
 			continue;
@@ -806,7 +822,7 @@ static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
 		 */
 		status = NT_STATUS_OK;
 	} else {
-		status = walk_xattr_streams(handle->conn, fsp, fname,
+		status = walk_xattr_streams(handle, fsp, fname,
 				    collect_one_stream, &state);
 	}
 
@@ -832,6 +848,42 @@ static uint32_t streams_xattr_fs_capabilities(struct vfs_handle_struct *handle,
 	return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_NAMED_STREAMS;
 }
 
+static int streams_xattr_connect(vfs_handle_struct *handle, const char *service, const char *user)
+{
+	struct streams_xattr_config *config;
+	const char *default_prefix = SAMBA_XATTR_DOSSTREAM_PREFIX;
+	const char **prefix;
+
+	config = talloc_zero(handle->conn, struct streams_xattr_config);
+	if (!config) {
+		DEBUG(1, ("talloc_zero() failed\n"));
+		errno = ENOMEM;
+		return -1;
+	}
+
+	prefix = lp_parm_string_list(SNUM(handle->conn),
+				     "streams_xattr", "prefix",
+				     &default_prefix);
+	if (prefix) {
+		config->prefix = *prefix;
+	} else {
+		config->prefix = talloc_strdup(config, "");
+	}
+	config->prefix_len = strlen(config->prefix);
+	DEBUG(10, ("streams_xattr using stream prefix: %s\n", config->prefix));
+
+	config->store_stream_type = lp_parm_bool(SNUM(handle->conn),
+						 "streams_xattr",
+						 "store_stream_type",
+						 true);
+
+	SMB_VFS_HANDLE_SET_DATA(handle, config,
+				NULL, struct stream_xattr_config,
+				return -1);
+
+	return 0;
+}
+
 static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
 				    files_struct *fsp, const void *data,
 				    size_t n, off_t offset)
@@ -1034,6 +1086,7 @@ static int streams_xattr_fallocate(struct vfs_handle_struct *handle,
 
 static struct vfs_fn_pointers vfs_streams_xattr_fns = {
 	.fs_capabilities_fn = streams_xattr_fs_capabilities,
+	.connect_fn = streams_xattr_connect,
 	.open_fn = streams_xattr_open,
 	.stat_fn = streams_xattr_stat,
 	.fstat_fn = streams_xattr_fstat,
-- 
1.8.5.3



More information about the samba-technical mailing list