The branch, master has been updated via 0ec8fe4... s4/torture: Ported SMBv1 RAW-STREAMS tests to SMB2-STREAMS from 3703890... AD-Bench: A first go at an Active Directory benchmark.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 0ec8fe420f65fabce9d6b0a998ef892201f3e395 Author: Aravind Srinivasan <aravind.sriniva...@isilon.com> Date: Tue Oct 6 20:25:15 2009 -0700 s4/torture: Ported SMBv1 RAW-STREAMS tests to SMB2-STREAMS ----------------------------------------------------------------------- Summary of changes: source4/torture/smb2/config.mk | 3 +- source4/torture/smb2/smb2.c | 1 + source4/torture/smb2/streams.c | 1767 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1770 insertions(+), 1 deletions(-) create mode 100644 source4/torture/smb2/streams.c Changeset truncated at 500 lines: diff --git a/source4/torture/smb2/config.mk b/source4/torture/smb2/config.mk index f617f70..e26ad26 100644 --- a/source4/torture/smb2/config.mk +++ b/source4/torture/smb2/config.mk @@ -25,7 +25,8 @@ TORTURE_SMB2_OBJ_FILES = $(addprefix $(torturesrcdir)/smb2/, \ lease.o \ create.o \ read.o \ - compound.o) + compound.o \ + streams.o) $(eval $(call proto_header_template,$(torturesrcdir)/smb2/proto.h,$(TORTURE_SMB2_OBJ_FILES:.o=.c))) diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c index 2430b09..f1d8fbb 100644 --- a/source4/torture/smb2/smb2.c +++ b/source4/torture/smb2/smb2.c @@ -143,6 +143,7 @@ NTSTATUS torture_smb2_init(void) torture_suite_add_suite(suite, torture_smb2_lease_init()); torture_suite_add_suite(suite, torture_smb2_compound_init()); torture_suite_add_suite(suite, torture_smb2_oplocks_init()); + torture_suite_add_suite(suite, torture_smb2_streams_init()); torture_suite_add_1smb2_test(suite, "BENCH-OPLOCK", test_smb2_bench_oplock); torture_suite_add_1smb2_test(suite, "HOLD-OPLOCK", test_smb2_hold_oplock); diff --git a/source4/torture/smb2/streams.c b/source4/torture/smb2/streams.c new file mode 100644 index 0000000..50f27a8 --- /dev/null +++ b/source4/torture/smb2/streams.c @@ -0,0 +1,1767 @@ +/* + Unix SMB/CIFS implementation. + + test alternate data streams + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" + +#include "system/filesys.h" + +#define DNAME "teststreams" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%d - should be %d\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%llu - should be %llu\n", \ + __location__, #v, (unsigned long long)v, \ + (unsigned long long)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_STR(v, correct) do { \ + bool ok; \ + if ((v) && !(correct)) { \ + ok = false; \ + } else if (!(v) && (correct)) { \ + ok = false; \ + } else if (!(v) && !(correct)) { \ + ok = true; \ + } else if (strcmp((v), (correct)) == 0) { \ + ok = true; \ + } else { \ + ok = false; \ + } \ + if (!ok) { \ + torture_comment(tctx,"(%s) Incorrect value %s='%s' - " \ + "should be '%s'\n", \ + __location__, #v, (v)?(v):"NULL", \ + (correct)?(correct):"NULL"); \ + ret = false; \ + }} while (0) + + +static int qsort_string(const void *v1, + const void *v2) +{ + char * const *s1 = v1; + char * const *s2 = v2; + return strcmp(*s1, *s2); +} + +static int qsort_stream(const void *v1, + const void *v2) +{ + const struct stream_struct * s1 = v1; + const struct stream_struct * s2 = v2; + return strcmp(s1->stream_name.s, s2->stream_name.s); +} + +static bool check_stream(struct smb2_tree *tree, + const char *location, + TALLOC_CTX *mem_ctx, + const char *fname, + const char *sname, + const char *value) +{ + struct smb2_handle handle; + struct smb2_create create; + struct smb2_read r; + NTSTATUS status; + const char *full_name; + + full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = full_name; + + status = smb2_create(tree, mem_ctx, &create); + if (!NT_STATUS_IS_OK(status)) { + if (value == NULL) { + return true; + } else { + torture_comment(mem_ctx, "Unable to open stream %s\n", + full_name); + return false; + } + } + + handle = create.out.file.handle; + if (value == NULL) { + return true; + } + + + ZERO_STRUCT(r); + r.in.file.handle = handle; + r.in.length = strlen(value)+11; + r.in.offset = 0; + + status = smb2_read(tree, tree, &r); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(mem_ctx, "(%s) Failed to read %lu bytes from " + "stream '%s'\n", location, (long)strlen(value), full_name); + return false; + } + + if (memcmp(r.out.data.data, value, strlen(value)) != 0) { + torture_comment(mem_ctx, "(%s) Bad data in stream\n", location); + return false; + } + + smb2_util_close(tree, handle); + 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; + } + + qsort(exp_sort, num_exp, sizeof(*exp_sort), 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; + } + + qsort(stream_sort, finfo.stream_info.out.num_streams, + sizeof(*stream_sort), qsort_stream); + + for (i=0; i<num_exp; i++) { + if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) { + torture_comment(tctx, + "(%s) expected stream name %s, got %s\n", + __location__, exp_sort[i], + stream_sort[i].stream_name.s); + goto fail; + } + } + + ret = true; + fail: + talloc_free(tmp_ctx); + return ret; +} + + +static bool test_stream_dir(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_open io; + const char *fname = DNAME "\\stream.txt"; + const char *sname1; + bool ret = true; + const char *basedir_data; + struct smb2_handle h; + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", DNAME); + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One"); + torture_comment(tctx, "%s\n", sname1); + + torture_comment(tctx, "(%s) opening non-existant directory stream\n", + __location__); + ZERO_STRUCT(io.smb2); + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.desired_access = SEC_FILE_WRITE_DATA; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = sname1; + io.smb2.in.create_flags = 0; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + torture_comment(tctx, "(%s) opening basedir stream\n", __location__); + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = basedir_data; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + torture_comment(tctx, "(%s) opening basedir ::$DATA stream\n", + __location__); + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0x10; + io.smb2.in.desired_access = SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = basedir_data; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + torture_comment(tctx, "(%s) list the streams on the basedir\n", + __location__); + ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h); +done: + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_stream_io(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_open io; + const char *fname = DNAME "\\stream.txt"; + const char *sname1, *sname2; + bool ret = true; + struct smb2_handle h, h2; + + const char *one[] = { "::$DATA" }; + const char *two[] = { "::$DATA", ":Second Stream:$DATA" }; + const char *three[] = { "::$DATA", ":Stream One:$DATA", + ":Second Stream:$DATA" }; + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, + "Second Stream"); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "(%s) creating a stream on a non-existant file\n", + __location__); + + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_WRITE_DATA; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = sname1; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Stream One", NULL); + + torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.fname = fname; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, io.smb2.out.file.handle); + + torture_comment(tctx, "(%s) writing to stream\n", __location__); + status = smb2_util_write(tree, h2, "test data", 0, 9); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h2); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Stream One", "test data"); + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.fname = sname1; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + + torture_comment(tctx, "(%s) modifying stream\n", __location__); + status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h2); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Stream One:$FOO", NULL); + + torture_comment(tctx, "(%s) creating a stream2 on a existing file\n", + __location__); + io.smb2.in.fname = sname2; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + + torture_comment(tctx, "(%s) modifying stream\n", __location__); + status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h2); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Stream One", "test MORE DATA "); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Stream One:$DATA", "test MORE DATA "); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Stream One:", NULL); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Second Stream", "SECOND STREAM"); + + if (!torture_setting_bool(tctx, "samba4", false)) { + ret &= check_stream(tree, __location__, mem_ctx, fname, + "SECOND STREAM:$DATA", "SECOND STREAM"); + } + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Second Stream:$DATA", "SECOND STREAM"); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Second Stream:", NULL); + + ret &= check_stream(tree, __location__, mem_ctx, fname, + "Second Stream:$FOO", NULL); + + io.smb2.in.fname = sname2; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + check_stream_list(tree, tctx, fname, 3, three, h2); + + smb2_util_close(tree, h2); + + torture_comment(tctx, "(%s) deleting stream\n", __location__); + status = smb2_util_unlink(tree, sname1); + CHECK_STATUS(status, NT_STATUS_OK); + + io.smb2.in.fname = sname2; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + check_stream_list(tree, tctx, fname, 2, two, h2); + smb2_util_close(tree, h2); + + torture_comment(tctx, "(%s) delete a stream via delete-on-close\n", + __location__); + io.smb2.in.fname = sname2; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.smb2.out.file.handle; + + smb2_util_close(tree, h2); + status = smb2_util_unlink(tree, sname2); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; -- Samba Shared Repository