From 54db3bf590758c195efb90b74d9f50e2771146c4 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 9 May 2015 15:02:03 +0200 Subject: [PATCH 1/3] vfs_streams_xattr: stream names may contain colons With vfs_fruit option "fruit:encoding = native" we're already converting stream names that contain illegal NTFS characters from their on-the-wire Unicode Private Range encoding to their native ASCII representation. As as result the name of xattrs storing the streams (via vfs_streams_xattr) may contain a colon, so we have to use strrchr_m() instead of strchr_m() for matching the stream type suffix. Signed-off-by: Ralph Boehme --- source3/modules/vfs_streams_xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index d149fc8..0b01f3b 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -112,7 +112,7 @@ static NTSTATUS streams_xattr_get_name(vfs_handle_struct *handle, SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config, return NT_STATUS_UNSUCCESSFUL); - stype = strchr_m(stream_name + 1, ':'); + stype = strrchr_m(stream_name + 1, ':'); if (stype) { if (strcasecmp_m(stype, ":$DATA") != 0) { -- 2.1.0 From 6e371bac3fa3d22b8de6577b33a62e74521b03f2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 9 May 2015 15:12:41 +0200 Subject: [PATCH 2/3] vfs_catia: run translation on stream names With vfs_fruit option "fruit:encoding = native" we're already converting stream names that contain illegal NTFS characters from their on-the-wire Unicode Private Range encoding to their native ASCII representation. Unfortunately the reverse mapping for stream names was not perfomed. Signed-off-by: Ralph Boehme --- source3/modules/vfs_catia.c | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c index f2769a9..4fb4ae8 100644 --- a/source3/modules/vfs_catia.c +++ b/source3/modules/vfs_catia.c @@ -710,7 +710,11 @@ catia_streaminfo(struct vfs_handle_struct *handle, struct stream_struct **streams) { char *mapped_name = NULL; + char *stream_name = NULL; NTSTATUS status; + int i; + struct stream_struct *pstream; + size_t sname_len; status = catia_string_replace_allocate(handle->conn, path, &mapped_name, vfs_translate_to_unix); @@ -723,6 +727,62 @@ catia_streaminfo(struct vfs_handle_struct *handle, mem_ctx, num_streams,streams); TALLOC_FREE(mapped_name); + pstream = *streams; + /* + * Translate stream names just like the base names + */ + for (i = 0; i < *num_streams; i++) { + /* + * Strip ":" prefix and ":$DATA" suffix to get a + * "pure" stream name. We need the string buffer + * length, not the glyph count, which is why I call + * strlen not strlen_m. + */ + sname_len = strlen(pstream[i].name + 1); + + /* Compiler will optimize out strlen(":$DATA") */ + if (sname_len < strlen(":$DATA")) { + DEBUG(1,("%s: unexpected stream: %s\n", __func__, pstream[i].name)); + return NT_STATUS_UNSUCCESSFUL; + } + if (sname_len == strlen(":$DATA")) { + /* + * sname_len started at streamname+1 so we + * skip the first ':' in the canonical default + * stream name "::$DATA" + */ + continue; + } + sname_len -= strlen(":$DATA"); + if (strcasecmp_m(pstream[i].name + 1 + sname_len, ":$DATA") != 0) { + /* We're not expecting any other streamtype then $DATA */ + DEBUG(1,("%s: unexpected stream: %s\n", __func__, pstream[i].name)); + return NT_STATUS_UNSUCCESSFUL; + } + stream_name = talloc_strndup(mem_ctx, + pstream[i].name + 1, + sname_len); + if (stream_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = catia_string_replace_allocate(handle->conn, stream_name, + &mapped_name, vfs_translate_to_windows); + TALLOC_FREE(stream_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + TALLOC_FREE(pstream[i].name); + pstream[i].name = talloc_asprintf(mem_ctx, ":%s:$DATA", mapped_name); + if (pstream[i].name == NULL) { + TALLOC_FREE(mapped_name); + return NT_STATUS_NO_MEMORY; + } + TALLOC_FREE(mapped_name); + DEBUG(10,("%s: stream: %s\n", __func__, pstream[i].name)); + } + return status; } -- 2.1.0 From 8a6129da688963b251e5b7adc5749d31a6aa44cf Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sun, 10 May 2015 11:58:32 +0200 Subject: [PATCH 3/3] s4:torture:vfs_fruit: add a test for stream names Signed-off-by: Ralph Boehme --- source4/torture/vfs/fruit.c | 221 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 4de21b2..8f34845 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -29,6 +29,7 @@ #include "param/param.h" #include "libcli/resolve/resolve.h" #include "MacExtensions.h" +#include "lib/util/tsort.h" #include "torture/torture.h" #include "torture/util.h" @@ -58,6 +59,16 @@ goto done; \ }} while (0) +static int qsort_string(char * const *s1, char * const *s2) +{ + return strcmp(*s1, *s2); +} + +static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2) +{ + return strcmp(s1->stream_name.s, s2->stream_name.s); +} + /* * REVIEW: * This is hokey, but what else can we do? @@ -2163,6 +2174,215 @@ done: return true; } +static bool check_stream_list(struct smb2_tree *tree, + struct torture_context *tctx, + const char *fname, + int num_exp, + const char **exp, + struct smb2_handle h) +{ + union smb_fileinfo finfo; + NTSTATUS status; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char **exp_sort; + struct stream_struct *stream_sort; + bool ret = false; + + finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION; + finfo.generic.in.file.handle = h; + + status = smb2_getinfo_file(tree, tctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n", + __location__, nt_errstr(status)); + goto fail; + } + + if (finfo.stream_info.out.num_streams != num_exp) { + torture_comment(tctx, "(%s) expected %d streams, got %d\n", + __location__, num_exp, finfo.stream_info.out.num_streams); + goto fail; + } + + if (num_exp == 0) { + ret = true; + goto fail; + } + + exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp)); + + if (exp_sort == NULL) { + goto fail; + } + + TYPESAFE_QSORT(exp_sort, num_exp, qsort_string); + + stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams, + finfo.stream_info.out.num_streams * + sizeof(*stream_sort)); + + if (stream_sort == NULL) { + goto fail; + } + + TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream); + + for (i=0; i