summaryrefslogtreecommitdiffstats
path: root/source4/torture/smb2/create.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/torture/smb2/create.c')
-rw-r--r--source4/torture/smb2/create.c3629
1 files changed, 3629 insertions, 0 deletions
diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c
new file mode 100644
index 0000000..c1d132d
--- /dev/null
+++ b/source4/torture/smb2/create.c
@@ -0,0 +1,3629 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 create test suite
+
+ Copyright (C) Andrew Tridgell 2008
+
+ 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/smbXcli_base.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "torture/smb2/proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+
+#include "system/filesys.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/cmdline.h"
+#include "librpc/gen_ndr/security.h"
+#include "lib/events/events.h"
+
+#define FNAME "test_create.dat"
+#define DNAME "smb2_open"
+
+#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)); \
+ return false; \
+ }} while (0)
+
+#define CHECK_EQUAL(v, correct) do { \
+ if (v != correct) { \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) Incorrect value for %s 0x%08llx - " \
+ "should be 0x%08llx\n", \
+ __location__, #v, \
+ (unsigned long long)v, \
+ (unsigned long long)correct); \
+ return false; \
+ }} while (0)
+
+#define CHECK_TIME(t, field) do { \
+ time_t t1, t2; \
+ finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \
+ finfo.all_info.in.file.handle = h1; \
+ status = smb2_getinfo_file(tree, tctx, &finfo); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ t1 = t & ~1; \
+ t2 = nt_time_to_unix(finfo.all_info.out.field) & ~1; \
+ if (abs(t1-t2) > 2) { \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) wrong time for field %s %s - %s\n", \
+ __location__, #field, \
+ timestring(tctx, t1), \
+ timestring(tctx, t2)); \
+ dump_all_info(tctx, &finfo); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_NTTIME(t, field) do { \
+ NTTIME t2; \
+ finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \
+ finfo.all_info.in.file.handle = h1; \
+ status = smb2_getinfo_file(tree, tctx, &finfo); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ t2 = finfo.all_info.out.field; \
+ if (llabs((int64_t)(t-t2)) > 20000) { \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) wrong time for field %s %s - %s\n", \
+ __location__, #field, \
+ nt_time_string(tctx, t), \
+ nt_time_string(tctx, t2)); \
+ dump_all_info(tctx, &finfo); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_ALL_INFO(v, field) do { \
+ finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \
+ finfo.all_info.in.file.handle = h1; \
+ status = smb2_getinfo_file(tree, tctx, &finfo); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ if ((v) != (finfo.all_info.out.field)) { \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) wrong value for field %s 0x%x - 0x%x\n", \
+ __location__, #field, (int)v,\
+ (int)(finfo.all_info.out.field)); \
+ dump_all_info(tctx, &finfo); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_VAL(v, correct) do { \
+ if ((v) != (correct)) { \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) wrong value for %s 0x%x - should be 0x%x\n", \
+ __location__, #v, (int)(v), (int)correct); \
+ ret = false; \
+ }} while (0)
+
+#define SET_ATTRIB(sattrib) do { \
+ union smb_setfileinfo sfinfo; \
+ ZERO_STRUCT(sfinfo.basic_info.in); \
+ sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; \
+ sfinfo.basic_info.in.file.handle = h1; \
+ sfinfo.basic_info.in.attrib = sattrib; \
+ status = smb2_setinfo_file(tree, &sfinfo); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, \
+ "(%s) Failed to set attrib 0x%x on %s\n", \
+ __location__, (unsigned int)(sattrib), fname); \
+ }} while (0)
+
+/*
+ test some interesting combinations found by gentest
+ */
+static bool test_create_gentest(struct torture_context *tctx, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ uint32_t access_mask, file_attributes_set;
+ uint32_t ok_mask, not_supported_mask, invalid_parameter_mask;
+ uint32_t not_a_directory_mask, unexpected_mask;
+ union smb_fileinfo q;
+
+ ZERO_STRUCT(io);
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = FNAME;
+
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.in.create_options = 0xF0000000;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.create_options = 0;
+
+ io.in.file_attributes = FILE_ATTRIBUTE_DEVICE;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.file_attributes = FILE_ATTRIBUTE_VOLUME;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+ io.in.file_attributes = FILE_ATTRIBUTE_VOLUME;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.in.desired_access = 0x08000000;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.in.desired_access = 0x04000000;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ ok_mask = 0;
+ not_supported_mask = 0;
+ invalid_parameter_mask = 0;
+ not_a_directory_mask = 0;
+ unexpected_mask = 0;
+ {
+ int i;
+ for (i=0;i<32;i++) {
+ io.in.create_options = (uint32_t)1<<i;
+ if (io.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+ continue;
+ }
+ status = smb2_create(tree, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ not_supported_mask |= 1<<i;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ invalid_parameter_mask |= 1<<i;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ not_a_directory_mask |= 1<<i;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ ok_mask |= 1<<i;
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ unexpected_mask |= 1<<i;
+ torture_comment(tctx,
+ "create option 0x%08x returned %s\n",
+ 1<<i, nt_errstr(status));
+ }
+ }
+ }
+ io.in.create_options = 0;
+
+ CHECK_EQUAL(ok_mask, 0x00efcf7e);
+ CHECK_EQUAL(not_a_directory_mask, 0x00000001);
+ CHECK_EQUAL(not_supported_mask, 0x00102080);
+ CHECK_EQUAL(invalid_parameter_mask, 0xff000000);
+ CHECK_EQUAL(unexpected_mask, 0x00000000);
+
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.file_attributes = 0;
+ access_mask = 0;
+ {
+ int i;
+ for (i=0;i<32;i++) {
+ io.in.desired_access = (uint32_t)1<<i;
+ status = smb2_create(tree, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_PRIVILEGE_NOT_HELD)) {
+ access_mask |= io.in.desired_access;
+ } else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+ }
+ }
+
+ if (TARGET_IS_WIN7(tctx)) {
+ CHECK_EQUAL(access_mask, 0x0de0fe00);
+ } else if (torture_setting_bool(tctx, "samba4", false)) {
+ CHECK_EQUAL(access_mask, 0x0cf0fe00);
+ } else {
+ CHECK_EQUAL(access_mask, 0x0df0fe00);
+ }
+
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = 0;
+ ok_mask = 0;
+ invalid_parameter_mask = 0;
+ unexpected_mask = 0;
+ file_attributes_set = 0;
+ {
+ int i;
+ for (i=0;i<32;i++) {
+ io.in.file_attributes = (uint32_t)1<<i;
+ if (io.in.file_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ continue;
+ }
+ smb2_deltree(tree, FNAME);
+ status = smb2_create(tree, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ invalid_parameter_mask |= 1<<i;
+ } else if (NT_STATUS_IS_OK(status)) {
+ uint32_t expected;
+ ok_mask |= 1<<i;
+
+ expected = (io.in.file_attributes | FILE_ATTRIBUTE_ARCHIVE) & 0x00005127;
+ io.out.file_attr &= ~FILE_ATTRIBUTE_NONINDEXED;
+ CHECK_EQUAL(io.out.file_attr, expected);
+ file_attributes_set |= io.out.file_attr;
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ unexpected_mask |= 1<<i;
+ torture_comment(tctx,
+ "file attribute 0x%08x returned %s\n",
+ 1<<i, nt_errstr(status));
+ }
+ }
+ }
+
+ CHECK_EQUAL(ok_mask, 0x00003fb7);
+ CHECK_EQUAL(invalid_parameter_mask, 0xffff8048);
+ CHECK_EQUAL(unexpected_mask, 0x00000000);
+ CHECK_EQUAL(file_attributes_set, 0x00001127);
+
+ smb2_deltree(tree, FNAME);
+
+ /*
+ * Standalone servers doesn't support encryption
+ */
+ io.in.file_attributes = FILE_ATTRIBUTE_ENCRYPTED;
+ status = smb2_create(tree, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx,
+ "FILE_ATTRIBUTE_ENCRYPTED returned %s\n",
+ nt_errstr(status));
+ } else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.file_attr, (FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_ARCHIVE));
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ smb2_deltree(tree, FNAME);
+
+ ZERO_STRUCT(io);
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = FNAME ":stream1";
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.in.fname = FNAME;
+ io.in.file_attributes = 0x8040;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.fname = FNAME;
+ io.in.file_attributes = 0;
+ io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA;
+ io.in.query_maximal_access = true;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.maximal_access, 0x001f01ff);
+
+ q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION;
+ q.access_information.in.file.handle = io.out.file.handle;
+ status = smb2_getinfo_file(tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(q.access_information.out.access_flags, io.in.desired_access);
+
+ io.in.file_attributes = 0;
+ io.in.desired_access = 0;
+ io.in.query_maximal_access = false;
+ io.in.share_access = 0;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ smb2_deltree(tree, FNAME);
+
+ return true;
+}
+
+
+/*
+ try the various request blobs
+ */
+static bool test_create_blob(struct torture_context *tctx, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+
+ smb2_deltree(tree, FNAME);
+
+ ZERO_STRUCT(io);
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+ NTCREATEX_OPTIONS_ASYNC_ALERT |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ 0x00200000;
+ io.in.fname = FNAME;
+
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing alloc size\n");
+ /* FIXME We use 1M cause that's the rounded size of Samba.
+ * We should ask the server for the cluster size and calculate it
+ * correctly. */
+ io.in.alloc_size = 0x00100000;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.alloc_size, io.in.alloc_size);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing durable open\n");
+ io.in.durable_open = true;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing query maximal access\n");
+ io.in.query_maximal_access = true;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.maximal_access, 0x001f01ff);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing timewarp\n");
+ io.in.timewarp = 10000;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ io.in.timewarp = 0;
+
+ torture_comment(tctx, "Testing query_on_disk\n");
+ io.in.query_on_disk_id = true;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing unknown tag\n");
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "FooO", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing bad tag length 0\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "x", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ torture_comment(tctx, "Testing bad tag length 1\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "x", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ torture_comment(tctx, "Testing bad tag length 2\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ torture_comment(tctx, "Testing bad tag length 3\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ torture_comment(tctx, "Testing tag length 4\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 5\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 6\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 7\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 8\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxxxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 16\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxxxxxxxxxxxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 17\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxxxxxxxxxxxxxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Testing tag length 34\n");
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create_blob_add(tctx, &io.in.blobs,
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smb2_deltree(tree, FNAME);
+
+ return true;
+}
+
+#define FAIL_UNLESS(__cond) \
+ do { \
+ if (__cond) {} else { \
+ torture_result(tctx, TORTURE_FAIL, "%s) condition violated: %s\n", \
+ __location__, #__cond); \
+ ret = false; goto done; \
+ } \
+ } while(0)
+
+/*
+ try creating with acls
+ */
+static bool test_create_acl_ext(struct torture_context *tctx, struct smb2_tree *tree, bool test_dir)
+{
+ bool ret = true;
+ struct smb2_create io;
+ NTSTATUS status;
+ struct security_ace ace;
+ struct security_descriptor *sd;
+ struct dom_sid *test_sid;
+ union smb_fileinfo q = {};
+ uint32_t attrib =
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM |
+ (test_dir ? FILE_ATTRIBUTE_DIRECTORY : 0);
+ NTSTATUS (*delete_func)(struct smb2_tree *, const char *) =
+ test_dir ? smb2_util_rmdir : smb2_util_unlink;
+
+ ZERO_STRUCT(ace);
+
+ smb2_deltree(tree, FNAME);
+
+ ZERO_STRUCT(io);
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE |
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = NTCREATEX_OPTIONS_ASYNC_ALERT | 0x00200000 |
+ (test_dir ? NTCREATEX_OPTIONS_DIRECTORY :
+ (NTCREATEX_OPTIONS_NON_DIRECTORY_FILE));
+
+ io.in.fname = FNAME;
+
+ torture_comment(tctx, "basic create\n");
+
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.handle = io.out.file.handle;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd = q.query_secdesc.out.sd;
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = delete_func(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "adding a new ACE\n");
+ test_sid = dom_sid_parse_talloc(tctx, SID_NT_AUTHENTICATED_USERS);
+
+ ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace.flags = 0;
+ ace.access_mask = SEC_STD_ALL;
+ ace.trustee = *test_sid;
+
+ status = security_descriptor_dacl_add(sd, &ace);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "creating a file with an initial ACL\n");
+
+ io.in.sec_desc = sd;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, io.out.file.handle, sd));
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = delete_func(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "creating with attributes\n");
+
+ io.in.sec_desc = NULL;
+ io.in.file_attributes = attrib;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ FAIL_UNLESS(smb2_util_verify_attrib(tctx, tree, io.out.file.handle, attrib));
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = delete_func(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "creating with attributes and ACL\n");
+
+ io.in.sec_desc = sd;
+ io.in.file_attributes = attrib;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, io.out.file.handle, sd));
+ FAIL_UNLESS(smb2_util_verify_attrib(tctx, tree, io.out.file.handle, attrib));
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = delete_func(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "creating with attributes, ACL and owner\n");
+ sd = security_descriptor_dacl_create(tctx,
+ 0, SID_WORLD, SID_BUILTIN_USERS,
+ SID_WORLD,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
+ 0,
+ NULL);
+
+ io.in.sec_desc = sd;
+ io.in.file_attributes = attrib;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, io.out.file.handle, sd));
+ FAIL_UNLESS(smb2_util_verify_attrib(tctx, tree, io.out.file.handle, attrib));
+
+ done:
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = delete_func(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ return ret;
+}
+
+/*
+ test SMB2 open
+*/
+static bool test_smb2_open(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = DNAME "\\torture_ntcreatex.txt";
+ const char *dname = DNAME "\\torture_ntcreatex.dir";
+ NTSTATUS status;
+ struct smb2_handle h = {{0}};
+ struct smb2_handle h1 = {{0}};
+ bool ret = true;
+ size_t i;
+ struct {
+ uint32_t create_disp;
+ bool with_file;
+ NTSTATUS correct_status;
+ } open_funcs[] = {
+ { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_OK },
+ { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_OK },
+ { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK },
+ { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION },
+ { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK },
+ { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK },
+ { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK },
+ { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_OK },
+ { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_OK },
+ { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_OK },
+ { 6, true, NT_STATUS_INVALID_PARAMETER },
+ { 6, false, NT_STATUS_INVALID_PARAMETER },
+ };
+
+ torture_comment(tctx, "Checking SMB2 Open\n");
+
+ smb2_util_unlink(tree, fname);
+ smb2_util_rmdir(tree, dname);
+
+ status = torture_smb2_testdir(tree, DNAME, &h);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(io.smb2);
+ /* reasonable default parameters */
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED;
+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io.smb2.in.alloc_size = 1024*1024;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.create_options = 0;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = fname;
+
+ /* test the create disposition */
+ for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ status= smb2_create(tree, tctx, &(io.smb2));
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx,
+ "Failed to create file %s status %s %zu\n",
+ fname, nt_errstr(status), i);
+
+ ret = false;
+ goto done;
+ }
+ smb2_util_close(tree, io.smb2.out.file.handle);
+ }
+ io.smb2.in.create_disposition = open_funcs[i].create_disp;
+ status = smb2_create(tree, tctx, &(io.smb2));
+ if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+ torture_comment(tctx,
+ "(%s) incorrect status %s should be %s (i=%zu "
+ "with_file=%d open_disp=%d)\n",
+ __location__, nt_errstr(status),
+ nt_errstr(open_funcs[i].correct_status),
+ i, (int)open_funcs[i].with_file,
+ (int)open_funcs[i].create_disp);
+
+ ret = false;
+ goto done;
+ }
+ if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) {
+ smb2_util_close(tree, io.smb2.out.file.handle);
+ smb2_util_unlink(tree, fname);
+ }
+ }
+
+ /* basic field testing */
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ h1 = io.smb2.out.file.handle;
+
+ CHECK_VAL(io.smb2.out.oplock_level, 0);
+ CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_NTTIME(io.smb2.out.create_time, create_time);
+ CHECK_NTTIME(io.smb2.out.access_time, access_time);
+ CHECK_NTTIME(io.smb2.out.write_time, write_time);
+ CHECK_NTTIME(io.smb2.out.change_time, change_time);
+ CHECK_ALL_INFO(io.smb2.out.file_attr, attrib);
+ CHECK_ALL_INFO(io.smb2.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.smb2.out.size, size);
+
+ /* check fields when the file already existed */
+ smb2_util_close(tree, h1);
+ smb2_util_unlink(tree, fname);
+
+ status = smb2_create_complex_file(tctx, tree, fname, &h1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smb2_util_close(tree, h1);
+
+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ h1 = io.smb2.out.file.handle;
+
+ CHECK_VAL(io.smb2.out.oplock_level, 0);
+ CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_NTTIME(io.smb2.out.create_time, create_time);
+ CHECK_NTTIME(io.smb2.out.access_time, access_time);
+ CHECK_NTTIME(io.smb2.out.write_time, write_time);
+ CHECK_NTTIME(io.smb2.out.change_time, change_time);
+ CHECK_ALL_INFO(io.smb2.out.file_attr, attrib);
+ CHECK_ALL_INFO(io.smb2.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.smb2.out.size, size);
+ smb2_util_close(tree, h1);
+ smb2_util_unlink(tree, fname);
+
+ /* create a directory */
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.smb2.in.create_options = 0;
+ io.smb2.in.fname = dname;
+ fname = dname;
+
+ smb2_util_rmdir(tree, fname);
+ smb2_util_unlink(tree, fname);
+
+ io.smb2.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ h1 = io.smb2.out.file.handle;
+
+ CHECK_VAL(io.smb2.out.oplock_level, 0);
+ CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_NTTIME(io.smb2.out.create_time, create_time);
+ CHECK_NTTIME(io.smb2.out.access_time, access_time);
+ CHECK_NTTIME(io.smb2.out.write_time, write_time);
+ CHECK_NTTIME(io.smb2.out.change_time, change_time);
+ CHECK_ALL_INFO(io.smb2.out.file_attr, attrib);
+ CHECK_VAL(io.smb2.out.file_attr & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_DIRECTORY);
+ CHECK_ALL_INFO(io.smb2.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.smb2.out.size, size);
+ CHECK_VAL(io.smb2.out.size, 0);
+ smb2_util_unlink(tree, fname);
+
+done:
+ smb2_util_close(tree, h1);
+ smb2_util_unlink(tree, fname);
+ smb2_deltree(tree, DNAME);
+ return ret;
+}
+
+/*
+ test with an already opened and byte range locked file
+*/
+
+static bool test_smb2_open_brlocked(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ union smb_open io, io1;
+ union smb_lock io2;
+ struct smb2_lock_element lock[1];
+ const char *fname = DNAME "\\torture_ntcreatex.txt";
+ NTSTATUS status;
+ bool ret = true;
+ struct smb2_handle h;
+ char b = 42;
+
+ torture_comment(tctx,
+ "Testing SMB2 open with a byte range locked file\n");
+
+ smb2_util_unlink(tree, fname);
+
+ status = torture_smb2_testdir(tree, DNAME, &h);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED;
+ io.smb2.in.desired_access = 0x2019f;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+ io.smb2.in.security_flags = SMB2_SECURITY_DYNAMIC_TRACKING;
+ io.smb2.in.fname = fname;
+
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_write(tree, io.smb2.out.file.handle, &b, 0, 1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(io2.smb2);
+ io2.smb2.level = RAW_LOCK_SMB2;
+ io2.smb2.in.file.handle = io.smb2.out.file.handle;
+ io2.smb2.in.lock_count = 1;
+
+ ZERO_STRUCT(lock);
+ lock[0].offset = 0;
+ lock[0].length = 1;
+ lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE |
+ SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+ io2.smb2.in.locks = &lock[0];
+ status = smb2_lock(tree, &(io2.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(io1.smb2);
+ io1.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED;
+ io1.smb2.in.desired_access = 0x20196;
+ io1.smb2.in.alloc_size = 0;
+ io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io1.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io1.smb2.in.create_options = 0;
+ io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+ io1.smb2.in.security_flags = SMB2_SECURITY_DYNAMIC_TRACKING;
+ io1.smb2.in.fname = fname;
+
+ status = smb2_create(tree, tctx, &(io1.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smb2_util_close(tree, io.smb2.out.file.handle);
+ smb2_util_close(tree, io1.smb2.out.file.handle);
+ smb2_util_unlink(tree, fname);
+ smb2_deltree(tree, DNAME);
+
+ return ret;
+}
+
+/* A little torture test to expose a race condition in Samba 3.0.20 ... :-) */
+
+static bool test_smb2_open_multi(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ const char *fname = "test_oplock.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ struct smb2_tree **trees;
+ struct smb2_request **requests;
+ union smb_open *ios;
+ int i, num_files = 3;
+ int num_ok = 0;
+ int num_collision = 0;
+
+ torture_comment(tctx,
+ "Testing SMB2 Open with multiple connections\n");
+ trees = talloc_array(tctx, struct smb2_tree *, num_files);
+ requests = talloc_array(tctx, struct smb2_request *, num_files);
+ ios = talloc_array(tctx, union smb_open, num_files);
+ if ((tctx->ev == NULL) || (trees == NULL) || (requests == NULL) ||
+ (ios == NULL)) {
+ torture_comment(tctx, ("talloc failed\n"));
+ ret = false;
+ goto done;
+ }
+
+ tree->session->transport->options.request_timeout = 60;
+
+ for (i=0; i<num_files; i++) {
+ if (!torture_smb2_connection(tctx, &(trees[i]))) {
+ torture_comment(tctx,
+ "Could not open %d'th connection\n", i);
+ ret = false;
+ goto done;
+ }
+ trees[i]->session->transport->options.request_timeout = 60;
+ }
+
+ /* cleanup */
+ smb2_util_unlink(tree, fname);
+
+ /*
+ base ntcreatex parms
+ */
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.create_options = 0;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = fname;
+ io.smb2.in.create_flags = 0;
+
+ for (i=0; i<num_files; i++) {
+ ios[i] = io;
+ requests[i] = smb2_create_send(trees[i], &(ios[i].smb2));
+ if (requests[i] == NULL) {
+ torture_comment(tctx,
+ "could not send %d'th request\n", i);
+ ret = false;
+ goto done;
+ }
+ }
+
+ torture_comment(tctx, "waiting for replies\n");
+ while (1) {
+ bool unreplied = false;
+ for (i=0; i<num_files; i++) {
+ if (requests[i] == NULL) {
+ continue;
+ }
+ if (requests[i]->state < SMB2_REQUEST_DONE) {
+ unreplied = true;
+ break;
+ }
+ status = smb2_create_recv(requests[i], tctx,
+ &(ios[i].smb2));
+
+ torture_comment(tctx,
+ "File %d returned status %s\n", i,
+ nt_errstr(status));
+
+ if (NT_STATUS_IS_OK(status)) {
+ num_ok += 1;
+ }
+
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_OBJECT_NAME_COLLISION)) {
+ num_collision += 1;
+ }
+
+ requests[i] = NULL;
+ }
+ if (!unreplied) {
+ break;
+ }
+
+ if (tevent_loop_once(tctx->ev) != 0) {
+ torture_comment(tctx, "tevent_loop_once failed\n");
+ ret = false;
+ goto done;
+ }
+ }
+
+ if ((num_ok != 1) || (num_ok + num_collision != num_files)) {
+ ret = false;
+ }
+done:
+ smb2_deltree(tree, fname);
+
+ return ret;
+}
+
+/*
+ test opening for delete on a read-only attribute file.
+*/
+
+static bool test_smb2_open_for_delete(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = DNAME "\\torture_open_for_delete.txt";
+ NTSTATUS status;
+ struct smb2_handle h, h1;
+ bool ret = true;
+
+ torture_comment(tctx,
+ "Checking SMB2_OPEN for delete on a readonly file.\n");
+ smb2_util_unlink(tree, fname);
+ smb2_deltree(tree, fname);
+
+ status = torture_smb2_testdir(tree, DNAME, &h);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* reasonable default parameters */
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_READONLY;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.create_options = 0;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = fname;
+
+ /* Create the readonly file. */
+
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ h1 = io.smb2.out.file.handle;
+
+ CHECK_VAL(io.smb2.out.oplock_level, 0);
+ io.smb2.in.create_options = 0;
+ CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_ALL_INFO(io.smb2.out.file_attr, attrib);
+ smb2_util_close(tree, h1);
+
+ /* Now try and open for delete only - should succeed. */
+ io.smb2.in.desired_access = SEC_STD_DELETE;
+ io.smb2.in.file_attributes = 0;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smb2_util_close(tree, io.smb2.out.file.handle);
+
+ /* Clear readonly flag to allow file deletion */
+ io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
+ SEC_FILE_WRITE_ATTRIBUTE;
+ status = smb2_create(tree, tctx, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_OK);
+ h1 = io.smb2.out.file.handle;
+ SET_ATTRIB(FILE_ATTRIBUTE_ARCHIVE);
+ smb2_util_close(tree, h1);
+
+ smb2_util_close(tree, h);
+ smb2_util_unlink(tree, fname);
+ smb2_deltree(tree, DNAME);
+
+ return ret;
+}
+
+/*
+ test SMB2 open with a leading slash on the path.
+ Trying to create a directory with a leading slash
+ should give NT_STATUS_INVALID_PARAMETER error
+*/
+static bool test_smb2_leading_slash(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ union smb_open io;
+ const char *dnameslash = "\\"DNAME;
+ NTSTATUS status;
+ bool ret = true;
+
+ torture_comment(tctx,
+ "Trying to create a directory with leading slash on path\n");
+ smb2_deltree(tree, dnameslash);
+
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.oplock_level = 0;
+ io.smb2.in.desired_access = SEC_RIGHTS_DIR_ALL;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.smb2.in.fname = dnameslash;
+
+ status = smb2_create(tree, tree, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ smb2_deltree(tree, dnameslash);
+ return ret;
+}
+
+/*
+ test SMB2 open with an invalid impersonation level.
+ Should give NT_STATUS_BAD_IMPERSONATION_LEVEL error
+*/
+static bool test_smb2_impersonation_level(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ union smb_open io;
+ const char *fname = DNAME "\\torture_invalid_impersonation_level.txt";
+ NTSTATUS status;
+ struct smb2_handle h;
+ bool ret = true;
+
+ torture_comment(tctx,
+ "Testing SMB2 open with an invalid impersonation level.\n");
+
+ smb2_util_unlink(tree, fname);
+ smb2_util_rmdir(tree, DNAME);
+
+ status = torture_smb2_testdir(tree, DNAME, &h);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.smb2.in.create_options = 0;
+ io.smb2.in.impersonation_level = 0x12345678;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = fname;
+ io.smb2.in.create_flags = 0;
+
+ status = smb2_create(tree, tree, &(io.smb2));
+ CHECK_STATUS(status, NT_STATUS_BAD_IMPERSONATION_LEVEL);
+
+ smb2_util_close(tree, h);
+ smb2_util_unlink(tree, fname);
+ smb2_deltree(tree, DNAME);
+ return ret;
+}
+
+static bool test_create_acl_file(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ torture_comment(tctx, "Testing nttrans create with sec_desc on files\n");
+
+ return test_create_acl_ext(tctx, tree, false);
+}
+
+static bool test_create_acl_dir(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ torture_comment(tctx, "Testing nttrans create with sec_desc on directories\n");
+
+ return test_create_acl_ext(tctx, tree, true);
+}
+
+#define CHECK_ACCESS_FLAGS(_fh, flags) do { \
+ union smb_fileinfo _q; \
+ _q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; \
+ _q.access_information.in.file.handle = (_fh); \
+ status = smb2_getinfo_file(tree, tctx, &_q); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ if (_q.access_information.out.access_flags != (flags)) { \
+ torture_result(tctx, TORTURE_FAIL, "(%s) Incorrect access_flags 0x%08x - should be 0x%08x\n", \
+ __location__, _q.access_information.out.access_flags, (flags)); \
+ ret = false; \
+ goto done; \
+ } \
+} while (0)
+
+/*
+ * Test creating a file with a NULL DACL.
+ */
+static bool test_create_null_dacl(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ NTSTATUS status;
+ struct smb2_create io;
+ const char *fname = "nulldacl.txt";
+ bool ret = true;
+ struct smb2_handle handle;
+ union smb_fileinfo q;
+ union smb_setfileinfo s;
+ struct security_descriptor *sd = security_descriptor_initialise(tctx);
+ struct security_acl dacl;
+
+ torture_comment(tctx, "TESTING SEC_DESC WITH A NULL DACL\n");
+
+ smb2_util_unlink(tree, fname);
+
+ ZERO_STRUCT(io);
+ io.level = RAW_OPEN_SMB2;
+ io.in.create_flags = 0;
+ io.in.desired_access = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC
+ | SEC_STD_WRITE_OWNER;
+ io.in.create_options = 0;
+ io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.alloc_size = 0;
+ io.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.in.security_flags = 0;
+ io.in.fname = fname;
+ io.in.sec_desc = sd;
+ /* XXX create_options ? */
+ io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+ NTCREATEX_OPTIONS_ASYNC_ALERT |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ 0x00200000;
+
+ torture_comment(tctx, "creating a file with a empty sd\n");
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ handle = io.out.file.handle;
+
+ torture_comment(tctx, "get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.handle = handle;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /*
+ * Testing the created DACL,
+ * the server should add the inherited DACL
+ * when SEC_DESC_DACL_PRESENT isn't specified
+ */
+ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n");
+ }
+ if (q.query_secdesc.out.sd->dacl == NULL) {
+ ret = false;
+ torture_fail_goto(tctx, done, "no DACL has been created on the server!\n");
+ }
+
+ torture_comment(tctx, "set NULL DACL\n");
+ sd->type |= SEC_DESC_DACL_PRESENT;
+
+ s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ s.set_secdesc.in.file.handle = handle;
+ s.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ s.set_secdesc.in.sd = sd;
+ status = smb2_setinfo_file(tree, &s);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "get the sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.handle = handle;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* Testing the modified DACL */
+ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n");
+ }
+ if (q.query_secdesc.out.sd->dacl != NULL) {
+ ret = false;
+ torture_fail_goto(tctx, done, "DACL has been created on the server!\n");
+ }
+
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+
+ torture_comment(tctx, "try open for read control\n");
+ io.in.desired_access = SEC_STD_READ_CONTROL;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.out.file.handle,
+ SEC_STD_READ_CONTROL);
+ smb2_util_close(tree, io.out.file.handle);
+
+ torture_comment(tctx, "try open for write\n");
+ io.in.desired_access = SEC_FILE_WRITE_DATA;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.out.file.handle,
+ SEC_FILE_WRITE_DATA);
+ smb2_util_close(tree, io.out.file.handle);
+
+ torture_comment(tctx, "try open for read\n");
+ io.in.desired_access = SEC_FILE_READ_DATA;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.out.file.handle,
+ SEC_FILE_READ_DATA);
+ smb2_util_close(tree, io.out.file.handle);
+
+ torture_comment(tctx, "try open for generic write\n");
+ io.in.desired_access = SEC_GENERIC_WRITE;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.out.file.handle,
+ SEC_RIGHTS_FILE_WRITE);
+ smb2_util_close(tree, io.out.file.handle);
+
+ torture_comment(tctx, "try open for generic read\n");
+ io.in.desired_access = SEC_GENERIC_READ;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.out.file.handle,
+ SEC_RIGHTS_FILE_READ);
+ smb2_util_close(tree, io.out.file.handle);
+
+ torture_comment(tctx, "set DACL with 0 aces\n");
+ ZERO_STRUCT(dacl);
+ dacl.revision = SECURITY_ACL_REVISION_NT4;
+ dacl.num_aces = 0;
+ sd->dacl = &dacl;
+
+ s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ s.set_secdesc.in.file.handle = handle;
+ s.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ s.set_secdesc.in.sd = sd;
+ status = smb2_setinfo_file(tree, &s);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "get the sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.handle = handle;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* Testing the modified DACL */
+ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n");
+ }
+ if (q.query_secdesc.out.sd->dacl == NULL) {
+ ret = false;
+ torture_fail_goto(tctx, done, "no DACL has been created on the server!\n");
+ }
+ if (q.query_secdesc.out.sd->dacl->num_aces != 0) {
+ torture_result(tctx, TORTURE_FAIL, "DACL has %u aces!\n",
+ q.query_secdesc.out.sd->dacl->num_aces);
+ ret = false;
+ goto done;
+ }
+
+ torture_comment(tctx, "try open for read control\n");
+ io.in.desired_access = SEC_STD_READ_CONTROL;
+ status = smb2_create(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.out.file.handle,
+ SEC_STD_READ_CONTROL);
+ smb2_util_close(tree, io.out.file.handle);
+
+ torture_comment(tctx, "try open for write => access_denied\n");
+ io.in.desired_access = SEC_FILE_WRITE_DATA;
+ status = smb2_create(tree, tctx, &io);
+ if (torture_setting_bool(tctx, "hide_on_access_denied", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+
+ torture_comment(tctx, "try open for read => access_denied\n");
+ io.in.desired_access = SEC_FILE_READ_DATA;
+ status = smb2_create(tree, tctx, &io);
+ if (torture_setting_bool(tctx, "hide_on_access_denied", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+
+ torture_comment(tctx, "try open for generic write => access_denied\n");
+ io.in.desired_access = SEC_GENERIC_WRITE;
+ status = smb2_create(tree, tctx, &io);
+ if (torture_setting_bool(tctx, "hide_on_access_denied", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+
+ torture_comment(tctx, "try open for generic read => access_denied\n");
+ io.in.desired_access = SEC_GENERIC_READ;
+ status = smb2_create(tree, tctx, &io);
+ if (torture_setting_bool(tctx, "hide_on_access_denied", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+
+ torture_comment(tctx, "set empty sd\n");
+ sd->type &= ~SEC_DESC_DACL_PRESENT;
+ sd->dacl = NULL;
+
+ s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ s.set_secdesc.in.file.handle = handle;
+ s.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ s.set_secdesc.in.sd = sd;
+ status = smb2_setinfo_file(tree, &s);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_comment(tctx, "get the sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.handle = handle;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* Testing the modified DACL */
+ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n");
+ }
+ if (q.query_secdesc.out.sd->dacl != NULL) {
+ ret = false;
+ torture_fail_goto(tctx, done, "DACL has been created on the server!\n");
+ }
+done:
+ smb2_util_close(tree, handle);
+ smb2_util_unlink(tree, fname);
+ smb2_tdis(tree);
+ smb2_logoff(tree->session);
+ return ret;
+}
+
+/*
+ test SMB2 mkdir with OPEN_IF on the same name twice.
+ Must use 2 connections to hit the race.
+*/
+
+static bool test_mkdir_dup(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ const char *fname = "mkdir_dup";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ struct smb2_tree **trees;
+ struct smb2_request **requests;
+ union smb_open *ios;
+ int i, num_files = 2;
+ int num_ok = 0;
+ int num_created = 0;
+ int num_existed = 0;
+
+ torture_comment(tctx,
+ "Testing SMB2 Create Directory with multiple connections\n");
+ trees = talloc_array(tctx, struct smb2_tree *, num_files);
+ requests = talloc_array(tctx, struct smb2_request *, num_files);
+ ios = talloc_array(tctx, union smb_open, num_files);
+ if ((tctx->ev == NULL) || (trees == NULL) || (requests == NULL) ||
+ (ios == NULL)) {
+ torture_fail(tctx, ("talloc failed\n"));
+ ret = false;
+ goto done;
+ }
+
+ tree->session->transport->options.request_timeout = 60;
+
+ for (i=0; i<num_files; i++) {
+ if (!torture_smb2_connection(tctx, &(trees[i]))) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "Could not open %d'th connection\n", i));
+ ret = false;
+ goto done;
+ }
+ trees[i]->session->transport->options.request_timeout = 60;
+ }
+
+ /* cleanup */
+ smb2_util_unlink(tree, fname);
+ smb2_util_rmdir(tree, fname);
+
+ /*
+ base ntcreatex parms
+ */
+ ZERO_STRUCT(io.smb2);
+ io.generic.level = RAW_OPEN_SMB2;
+ io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io.smb2.in.alloc_size = 0;
+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+ io.smb2.in.security_flags = 0;
+ io.smb2.in.fname = fname;
+ io.smb2.in.create_flags = 0;
+
+ for (i=0; i<num_files; i++) {
+ ios[i] = io;
+ requests[i] = smb2_create_send(trees[i], &(ios[i].smb2));
+ if (requests[i] == NULL) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "could not send %d'th request\n", i));
+ ret = false;
+ goto done;
+ }
+ }
+
+ torture_comment(tctx, "waiting for replies\n");
+ while (1) {
+ bool unreplied = false;
+ for (i=0; i<num_files; i++) {
+ if (requests[i] == NULL) {
+ continue;
+ }
+ if (requests[i]->state < SMB2_REQUEST_DONE) {
+ unreplied = true;
+ break;
+ }
+ status = smb2_create_recv(requests[i], tctx,
+ &(ios[i].smb2));
+
+ if (NT_STATUS_IS_OK(status)) {
+ num_ok += 1;
+
+ if (ios[i].smb2.out.create_action ==
+ NTCREATEX_ACTION_CREATED) {
+ num_created++;
+ }
+ if (ios[i].smb2.out.create_action ==
+ NTCREATEX_ACTION_EXISTED) {
+ num_existed++;
+ }
+ } else {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "File %d returned status %s\n", i,
+ nt_errstr(status)));
+ }
+
+
+ requests[i] = NULL;
+ }
+ if (!unreplied) {
+ break;
+ }
+
+ if (tevent_loop_once(tctx->ev) != 0) {
+ torture_fail(tctx, "tevent_loop_once failed\n");
+ ret = false;
+ goto done;
+ }
+ }
+
+ if (num_ok != 2) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "num_ok == %d\n", num_ok));
+ ret = false;
+ }
+ if (num_created != 1) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "num_created == %d\n", num_created));
+ ret = false;
+ }
+ if (num_existed != 1) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "num_existed == %d\n", num_existed));
+ ret = false;
+ }
+done:
+ smb2_deltree(tree, fname);
+
+ return ret;
+}
+
+/*
+ test directory creation with an initial allocation size > 0
+*/
+static bool test_dir_alloc_size(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ bool ret = true;
+ const char *dname = DNAME "\\torture_alloc_size.dir";
+ NTSTATUS status;
+ struct smb2_create c;
+ struct smb2_handle h1 = {{0}}, h2;
+
+ torture_comment(tctx, "Checking initial allocation size on directories\n");
+
+ smb2_deltree(tree, dname);
+
+ status = torture_smb2_testdir(tree, DNAME, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed");
+
+ ZERO_STRUCT(c);
+ c.in.create_disposition = NTCREATEX_DISP_CREATE;
+ c.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ c.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ c.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ c.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ c.in.fname = dname;
+ /*
+ * An insanely large value so we can check the value is
+ * ignored: Samba either returns 0 (current behaviour), or,
+ * once vfswrap_get_alloc_size() is fixed to allow retrieving
+ * the allocated size for directories, returns
+ * smb_roundup(..., stat.st_size) which would be 1 MB by
+ * default.
+ *
+ * Windows returns 0 for empty directories, once directories
+ * have a few entries it starts replying with values > 0.
+ */
+ c.in.alloc_size = 1024*1024*1024;
+
+ status = smb2_create(tree, tctx, &c);
+ h2 = c.out.file.handle;
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "dir create with initial alloc size failed");
+
+ smb2_util_close(tree, h2);
+
+ torture_comment(tctx, "Got directory alloc size: %ju\n", (uintmax_t)c.out.alloc_size);
+
+ /*
+ * See above for the rational for this test
+ */
+ if (c.out.alloc_size > 1024*1024) {
+ torture_fail_goto(tctx, done, talloc_asprintf(tctx, "bad alloc size: %ju",
+ (uintmax_t)c.out.alloc_size));
+ }
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ smb2_deltree(tree, DNAME);
+ return ret;
+}
+
+static bool test_twrp_write(struct torture_context *tctx, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ struct smb2_handle h1 = {{0}};
+ NTSTATUS status;
+ bool ret = true;
+ char *p = NULL;
+ struct tm tm;
+ time_t t;
+ uint64_t nttime;
+ const char *file = NULL;
+ const char *snapshot = NULL;
+ uint32_t expected_access;
+ union smb_fileinfo getinfo;
+ union smb_setfileinfo setinfo;
+ struct security_descriptor *sd = NULL, *sd_orig = NULL;
+ const char *owner_sid = NULL;
+ struct create_disps_tests {
+ const char *file;
+ uint32_t create_disposition;
+ uint32_t create_options;
+ NTSTATUS expected_status;
+ };
+ struct create_disps_tests *cd_test = NULL;
+
+ file = torture_setting_string(tctx, "twrp_file", NULL);
+ if (file == NULL) {
+ torture_skip(tctx, "missing 'twrp_file' option\n");
+ }
+
+ snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL);
+ if (snapshot == NULL) {
+ torture_skip(tctx, "missing 'twrp_snapshot' option\n");
+ }
+
+ torture_comment(tctx, "Testing timewarp (%s) (%s)\n", file, snapshot);
+
+ setenv("TZ", "GMT", 1);
+
+ /* strptime does not set tm.tm_isdst but mktime assumes DST is in
+ * effect if it is greater than 1. */
+ ZERO_STRUCT(tm);
+
+ p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm);
+ torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n");
+ torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n");
+
+ t = mktime(&tm);
+ unix_to_nt_time(&nttime, t);
+
+ io = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_DATA,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = file,
+ .in.query_maximal_access = true,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &io);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ smb2_util_close(tree, io.out.file.handle);
+
+ expected_access = SEC_RIGHTS_FILE_ALL &
+ ~(SEC_FILE_EXECUTE | SEC_DIR_DELETE_CHILD);
+
+ torture_assert_int_equal_goto(tctx,
+ io.out.maximal_access & expected_access,
+ expected_access,
+ ret, done, "Bad access\n");
+
+ {
+ /*
+ * Test create dispositions
+ */
+ struct create_disps_tests cd_tests[] = {
+ {
+ .file = file,
+ .create_disposition = NTCREATEX_DISP_OPEN,
+ .expected_status = NT_STATUS_OK,
+ },
+ {
+ .file = file,
+ .create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .expected_status = NT_STATUS_OK,
+ },
+ {
+ .file = file,
+ .create_disposition = NTCREATEX_DISP_OVERWRITE,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = file,
+ .create_disposition = NTCREATEX_DISP_OVERWRITE_IF,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = file,
+ .create_disposition = NTCREATEX_DISP_SUPERSEDE,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = "newfile",
+ .create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = "newfile",
+ .create_disposition = NTCREATEX_DISP_OVERWRITE_IF,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = "newfile",
+ .create_disposition = NTCREATEX_DISP_CREATE,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = "newfile",
+ .create_disposition = NTCREATEX_DISP_SUPERSEDE,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = "newdir",
+ .create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = "newdir",
+ .create_disposition = NTCREATEX_DISP_CREATE,
+ .create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .expected_status = NT_STATUS_MEDIA_WRITE_PROTECTED,
+ },
+ {
+ .file = NULL,
+ },
+ };
+
+ for (cd_test = &cd_tests[0]; cd_test->file != NULL; cd_test++) {
+ io = (struct smb2_create) {
+ .in.fname = cd_test->file,
+ .in.create_disposition = cd_test->create_disposition,
+ .in.create_options = cd_test->create_options,
+
+ .in.desired_access = SEC_FILE_READ_DATA,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &io);
+ torture_assert_ntstatus_equal_goto(
+ tctx, status, cd_test->expected_status, ret, done,
+ "Bad status\n");
+ }
+ }
+
+ io = (struct smb2_create) {
+ .in.desired_access = expected_access,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = file,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &io);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ h1 = io.out.file.handle;
+
+ status = smb2_util_write(tree, h1, "123", 0, 3);
+ torture_assert_ntstatus_equal_goto(tctx, status,
+ NT_STATUS_MEDIA_WRITE_PROTECTED,
+ ret, done, "smb2_create\n");
+
+ /*
+ * Verify access mask
+ */
+
+ ZERO_STRUCT(getinfo);
+ getinfo.generic.level = RAW_FILEINFO_ACCESS_INFORMATION;
+ getinfo.generic.in.file.handle = h1;
+
+ status = smb2_getinfo_file(tree, tree, &getinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file\n");
+
+ torture_assert_int_equal_goto(
+ tctx,
+ getinfo.access_information.out.access_flags,
+ expected_access,
+ ret, done,
+ "Bad access mask\n");
+
+ /*
+ * Check we can't set various things
+ */
+
+ ZERO_STRUCT(getinfo);
+ getinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ getinfo.query_secdesc.in.file.handle = h1;
+ getinfo.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+
+ status = smb2_getinfo_file(tree, tctx, &getinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file\n");
+
+ sd_orig = getinfo.query_secdesc.out.sd;
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA,
+ 0,
+ NULL);
+
+ /* Try to set ACL */
+
+ ZERO_STRUCT(setinfo);
+ setinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ setinfo.set_secdesc.in.file.handle = h1;
+ setinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ setinfo.set_secdesc.in.sd = sd;
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx,
+ status,
+ NT_STATUS_MEDIA_WRITE_PROTECTED,
+ ret, done,
+ "smb2_setinfo_file\n");
+
+ /* Try to delete */
+
+ ZERO_STRUCT(setinfo);
+ setinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
+ setinfo.disposition_info.in.delete_on_close = 1;
+ setinfo.generic.in.file.handle = h1;
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx,
+ status,
+ NT_STATUS_MEDIA_WRITE_PROTECTED,
+ ret, done,
+ "smb2_setinfo_file\n");
+
+ ZERO_STRUCT(setinfo);
+ setinfo.basic_info.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ setinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
+ setinfo.generic.in.file.handle = h1;
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx,
+ status,
+ NT_STATUS_MEDIA_WRITE_PROTECTED,
+ ret, done,
+ "smb2_setinfo_file\n");
+
+ /* Try to truncate */
+
+ ZERO_STRUCT(setinfo);
+ setinfo.generic.level = SMB_SFILEINFO_END_OF_FILE_INFORMATION;
+ setinfo.generic.in.file.handle = h1;
+ setinfo.end_of_file_info.in.size = 0x100000;
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx,
+ status,
+ NT_STATUS_MEDIA_WRITE_PROTECTED,
+ ret, done,
+ "smb2_setinfo_file\n");
+
+ /* Try to set a hardlink */
+
+ ZERO_STRUCT(setinfo);
+ setinfo.generic.level = RAW_SFILEINFO_LINK_INFORMATION;
+ setinfo.generic.in.file.handle = h1;
+ setinfo.link_information.in.new_name = "hardlink";
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx,
+ status,
+ NT_STATUS_NOT_SAME_DEVICE,
+ ret, done,
+ "smb2_setinfo_file\n");
+
+ /* Try to rename */
+
+ ZERO_STRUCT(setinfo);
+ setinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ setinfo.rename_information.in.file.handle = h1;
+ setinfo.rename_information.in.new_name = "renamed";
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_equal_goto(
+ tctx,
+ status,
+ NT_STATUS_NOT_SAME_DEVICE,
+ ret, done,
+ "smb2_setinfo_file\n");
+
+ smb2_util_close(tree, h1);
+ ZERO_STRUCT(h1);
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ return ret;
+}
+
+static bool test_twrp_stream(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ bool ret = true;
+ char *p = NULL;
+ struct tm tm;
+ time_t t;
+ uint64_t nttime;
+ const char *file = NULL;
+ const char *stream = NULL;
+ const char *snapshot = NULL;
+ int stream_size;
+ char *path = NULL;
+ uint8_t *buf = NULL;
+ struct smb2_handle h1 = {{0}};
+ struct smb2_read r;
+
+ file = torture_setting_string(tctx, "twrp_file", NULL);
+ if (file == NULL) {
+ torture_skip(tctx, "missing 'twrp_file' option\n");
+ }
+
+ stream = torture_setting_string(tctx, "twrp_stream", NULL);
+ if (stream == NULL) {
+ torture_skip(tctx, "missing 'twrp_stream' option\n");
+ }
+
+ snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL);
+ if (snapshot == NULL) {
+ torture_skip(tctx, "missing 'twrp_snapshot' option\n");
+ }
+
+ stream_size = torture_setting_int(tctx, "twrp_stream_size", 0);
+ if (stream_size == 0) {
+ torture_skip(tctx, "missing 'twrp_stream_size' option\n");
+ }
+
+ torture_comment(tctx, "Testing timewarp on stream (%s) (%s)\n",
+ file, snapshot);
+
+ path = talloc_asprintf(tree, "%s:%s", file, stream);
+ torture_assert_not_null_goto(tctx, path, ret, done, "path\n");
+
+ buf = talloc_zero_array(tree, uint8_t, stream_size);
+ torture_assert_not_null_goto(tctx, buf, ret, done, "buf\n");
+
+ setenv("TZ", "GMT", 1);
+
+ /* strptime does not set tm.tm_isdst but mktime assumes DST is in
+ * effect if it is greater than 1. */
+ ZERO_STRUCT(tm);
+
+ p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm);
+ torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n");
+ torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n");
+
+ t = mktime(&tm);
+ unix_to_nt_time(&nttime, t);
+
+ io = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_DATA,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = path,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &io);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ h1 = io.out.file.handle;
+
+ r = (struct smb2_read) {
+ .in.file.handle = h1,
+ .in.length = stream_size,
+ .in.offset = 0,
+ };
+
+ status = smb2_read(tree, tree, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+
+ smb2_util_close(tree, h1);
+
+done:
+ return ret;
+}
+
+static bool test_twrp_openroot(struct torture_context *tctx, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ bool ret = true;
+ char *p = NULL;
+ struct tm tm;
+ time_t t;
+ uint64_t nttime;
+ const char *snapshot = NULL;
+
+ snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL);
+ if (snapshot == NULL) {
+ torture_skip(tctx, "missing 'twrp_snapshot' option\n");
+ }
+
+ torture_comment(tctx, "Testing open of root of "
+ "share with timewarp (%s)\n",
+ snapshot);
+
+ setenv("TZ", "GMT", 1);
+
+ /* strptime does not set tm.tm_isdst but mktime assumes DST is in
+ * effect if it is greater than 1. */
+ ZERO_STRUCT(tm);
+
+ p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm);
+ torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n");
+ torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n");
+
+ t = mktime(&tm);
+ unix_to_nt_time(&nttime, t);
+
+ io = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_DATA,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = "",
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &io);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ smb2_util_close(tree, io.out.file.handle);
+
+done:
+ return ret;
+}
+
+static bool test_twrp_listdir(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create create;
+ struct smb2_handle h = {{0}};
+ struct smb2_find find;
+ unsigned int count;
+ union smb_search_data *d;
+ char *p = NULL;
+ struct tm tm;
+ time_t t;
+ uint64_t nttime;
+ const char *snapshot = NULL;
+ uint64_t normal_fileid;
+ uint64_t snapshot_fileid;
+ NTSTATUS status;
+ bool ret = true;
+
+ snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL);
+ if (snapshot == NULL) {
+ torture_fail(tctx, "missing 'twrp_snapshot' option\n");
+ }
+
+ torture_comment(tctx, "Testing File-Ids of directory listing "
+ "with timewarp (%s)\n",
+ snapshot);
+
+ setenv("TZ", "GMT", 1);
+
+ /* strptime does not set tm.tm_isdst but mktime assumes DST is in
+ * effect if it is greater than 1. */
+ ZERO_STRUCT(tm);
+
+ p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm);
+ torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n");
+ torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n");
+
+ t = mktime(&tm);
+ unix_to_nt_time(&nttime, t);
+
+ /*
+ * 1: Query the file's File-Id
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_DATA,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = "subdir/hardlink",
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ smb2_util_close(tree, create.out.file.handle);
+ normal_fileid = BVAL(&create.out.on_disk_id, 0);
+
+ /*
+ * 2: check directory listing of the file returns same File-Id
+ */
+
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_DIR_LIST,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = "subdir",
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ h = create.out.file.handle;
+
+ find = (struct smb2_find) {
+ .in.file.handle = h,
+ .in.pattern = "*",
+ .in.max_response_size = 0x1000,
+ .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+ };
+
+ status = smb2_find_level(tree, tree, &find, &count, &d);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_find_level failed\n");
+
+ smb2_util_close(tree, h);
+
+ torture_assert_int_equal_goto(tctx, count, 3, ret, done, "Bad count\n");
+ torture_assert_str_equal_goto(tctx,
+ d[2].id_both_directory_info.name.s,
+ "hardlink",
+ ret, done, "bad name");
+ torture_assert_u64_equal_goto(tctx,
+ d[2].id_both_directory_info.file_id,
+ normal_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * 3: Query File-Id of snapshot of the file and check the File-Id is
+ * different compared to the basefile
+ */
+
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_DATA,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = "subdir/hardlink",
+ .in.query_on_disk_id = true,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ smb2_util_close(tree, create.out.file.handle);
+
+ snapshot_fileid = BVAL(&create.out.on_disk_id, 0);
+
+ /*
+ * 4: List directory of the snapshot and check the File-Id returned here
+ * is the same as in 3.
+ */
+
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_DIR_LIST,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = "subdir",
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .in.timewarp = nttime,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ h = create.out.file.handle;
+
+ find = (struct smb2_find) {
+ .in.file.handle = h,
+ .in.pattern = "*",
+ .in.max_response_size = 0x1000,
+ .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+ };
+
+ status = smb2_find_level(tree, tree, &find, &count, &d);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_find_level failed\n");
+ smb2_util_close(tree, h);
+
+ torture_assert_int_equal_goto(tctx, count, 3, ret, done, "Bad count\n");
+ torture_assert_str_equal_goto(tctx,
+ d[2].id_both_directory_info.name.s,
+ "hardlink",
+ ret, done, "bad name");
+ torture_assert_u64_equal_goto(tctx,
+ snapshot_fileid,
+ d[2].id_both_directory_info.file_id,
+ ret, done, "bad fileid\n");
+
+done:
+ return ret;
+}
+
+static bool test_fileid(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ const char *fname = DNAME "\\foo";
+ const char *sname = DNAME "\\foo:bar";
+ struct smb2_handle testdirh;
+ struct smb2_handle h1;
+ struct smb2_create create;
+ union smb_fileinfo finfo;
+ union smb_setfileinfo sinfo;
+ struct smb2_find f;
+ unsigned int count;
+ union smb_search_data *d;
+ uint64_t expected_fileid;
+ uint64_t returned_fileid;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, DNAME);
+
+ status = torture_smb2_testdir(tree, DNAME, &testdirh);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir failed\n");
+
+ /*
+ * Initial create with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.fname = fname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ expected_fileid = BVAL(&create.out.on_disk_id, 0);
+
+ /*
+ * Getinfo the File-ID on the just opened handle
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Open existing with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = fname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the just opened handle
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Overwrite with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OVERWRITE,
+ .in.fname = fname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the open with overwrite handle
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Do some modifications on the basefile (IO, setinfo), verifying
+ * File-ID after each step.
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = fname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+
+ status = smb2_util_write(tree, h1, "foo", 0, strlen("foo"));
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_util_write failed\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ sinfo = (union smb_setfileinfo) {
+ .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
+ .basic_info.in.file.handle = h1,
+ };
+ unix_to_nt_time(&sinfo.basic_info.in.write_time, time(NULL));
+
+ status = smb2_setinfo_file(tree, &sinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_setinfo_file failed\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Create stream, check the stream's File-ID, should be the same as the
+ * base file (sic!, tested against Windows).
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_CREATE,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the created stream
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Open stream, check the stream's File-ID, should be the same as the
+ * base file (sic!, tested against Windows).
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the opened stream
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Overwrite stream, check the stream's File-ID, should be the same as
+ * the base file (sic!, tested against Windows).
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OVERWRITE,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the overwritten stream
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Do some modifications on the stream (IO, setinfo), verifying File-ID
+ * after each step.
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+
+ status = smb2_util_write(tree, h1, "foo", 0, strlen("foo"));
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_util_write failed\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ sinfo = (union smb_setfileinfo) {
+ .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
+ .basic_info.in.file.handle = h1,
+ };
+ unix_to_nt_time(&sinfo.basic_info.in.write_time, time(NULL));
+
+ status = smb2_setinfo_file(tree, &sinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_setinfo_file failed\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Final open of the basefile with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = fname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Final Getinfo checking File-ID
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Final list directory, verifying the operations on basefile and stream
+ * didn't modify the base file metadata.
+ */
+ f = (struct smb2_find) {
+ .in.file.handle = testdirh,
+ .in.pattern = "foo",
+ .in.max_response_size = 0x1000,
+ .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+ .in.continue_flags = SMB2_CONTINUE_FLAG_RESTART,
+ };
+
+ status = smb2_find_level(tree, tree, &f, &count, &d);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_find_level failed\n");
+ torture_assert_u64_equal_goto(tctx,
+ d->id_both_directory_info.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+done:
+ smb2_util_close(tree, testdirh);
+ smb2_deltree(tree, DNAME);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool test_fileid_dir(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ const char *dname = DNAME "\\foo";
+ const char *sname = DNAME "\\foo:bar";
+ struct smb2_handle testdirh;
+ struct smb2_handle h1;
+ struct smb2_create create;
+ union smb_fileinfo finfo;
+ union smb_setfileinfo sinfo;
+ struct smb2_find f;
+ unsigned int count;
+ union smb_search_data *d;
+ uint64_t expected_fileid;
+ uint64_t returned_fileid;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, DNAME);
+
+ status = torture_smb2_testdir(tree, DNAME, &testdirh);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir failed\n");
+
+ /*
+ * Initial directory create with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .in.fname = dname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ expected_fileid = BVAL(&create.out.on_disk_id, 0);
+
+ /*
+ * Getinfo the File-ID on the just opened handle
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Open existing directory with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .in.fname = dname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the just opened handle
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Create stream, check the stream's File-ID, should be the same as the
+ * base file (sic!, tested against Windows).
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_CREATE,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the created stream
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Open stream, check the stream's File-ID, should be the same as the
+ * base file (sic!, tested against Windows).
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the opened stream
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Overwrite stream, check the stream's File-ID, should be the same as
+ * the base file (sic!, tested against Windows).
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OVERWRITE,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Getinfo the File-ID on the overwritten stream
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Do some modifications on the stream (IO, setinfo), verifying File-ID
+ * after each step.
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = sname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+
+ status = smb2_util_write(tree, h1, "foo", 0, strlen("foo"));
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_util_write failed\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ sinfo = (union smb_setfileinfo) {
+ .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
+ .basic_info.in.file.handle = h1,
+ };
+ unix_to_nt_time(&sinfo.basic_info.in.write_time, time(NULL));
+
+ status = smb2_setinfo_file(tree, &sinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_setinfo_file failed\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_getinfo_file failed\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Final open of the directory with QFID
+ */
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_ALL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = dname,
+ .in.query_on_disk_id = true,
+ };
+
+ status = smb2_create(tree, tctx, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test file could not be created\n");
+ h1 = create.out.file.handle;
+ returned_fileid = BVAL(&create.out.on_disk_id, 0);
+ torture_assert_u64_equal_goto(tctx, returned_fileid, expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Final Getinfo checking File-ID
+ */
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "torture_smb2_testdir\n");
+ smb2_util_close(tree, h1);
+ torture_assert_u64_equal_goto(tctx, finfo.all_info2.out.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+ /*
+ * Final list directory, verifying the operations on basefile and stream
+ * didn't modify the base file metadata.
+ */
+ f = (struct smb2_find) {
+ .in.file.handle = testdirh,
+ .in.pattern = "foo",
+ .in.max_response_size = 0x1000,
+ .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+ .in.continue_flags = SMB2_CONTINUE_FLAG_RESTART,
+ };
+
+ status = smb2_find_level(tree, tree, &f, &count, &d);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_find_level failed\n");
+ torture_assert_u64_equal_goto(tctx,
+ d->id_both_directory_info.file_id,
+ expected_fileid,
+ ret, done, "bad fileid\n");
+
+done:
+ smb2_util_close(tree, testdirh);
+ smb2_deltree(tree, DNAME);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool test_fileid_unique_object(
+ struct torture_context *tctx,
+ struct smb2_tree *tree,
+ unsigned int num_objs,
+ bool create_dirs)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ char *fname = NULL;
+ struct smb2_handle testdirh;
+ struct smb2_handle h1;
+ struct smb2_create create;
+ unsigned int i;
+ uint64_t fileid_array[num_objs];
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, DNAME);
+
+ status = torture_smb2_testdir(tree, DNAME, &testdirh);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "test_fileid_unique failed\n");
+ smb2_util_close(tree, testdirh);
+
+ /* Create num_obj files as rapidly as we can. */
+ for (i = 0; i < num_objs; i++) {
+ fname = talloc_asprintf(mem_ctx,
+ "%s\\testfile.%u",
+ DNAME,
+ i);
+ torture_assert_goto(tctx,
+ fname != NULL,
+ ret,
+ done,
+ "talloc failed\n");
+
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_ATTRIBUTE,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_CREATE,
+ .in.fname = fname,
+ };
+
+ if (create_dirs) {
+ create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ create.in.create_options = FILE_DIRECTORY_FILE;
+ }
+
+ status = smb2_create(tree, tctx, &create);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "test file %s could not be created\n",
+ fname));
+ TALLOC_FREE(fname);
+ ret = false;
+ goto done;
+ }
+
+ h1 = create.out.file.handle;
+ smb2_util_close(tree, h1);
+ TALLOC_FREE(fname);
+ }
+
+ /*
+ * Get the file ids.
+ */
+ for (i = 0; i < num_objs; i++) {
+ union smb_fileinfo finfo;
+
+ fname = talloc_asprintf(mem_ctx,
+ "%s\\testfile.%u",
+ DNAME,
+ i);
+ torture_assert_goto(tctx,
+ fname != NULL,
+ ret,
+ done,
+ "talloc failed\n");
+
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_READ_ATTRIBUTE,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.fname = fname,
+ };
+
+ if (create_dirs) {
+ create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ create.in.create_options = FILE_DIRECTORY_FILE;
+ }
+
+ status = smb2_create(tree, tctx, &create);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "test file %s could not "
+ "be opened: %s\n",
+ fname,
+ nt_errstr(status)));
+ TALLOC_FREE(fname);
+ ret = false;
+ goto done;
+ }
+
+ h1 = create.out.file.handle;
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+
+ status = smb2_getinfo_file(tree, tctx, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "failed to get fileid for "
+ "test file %s: %s\n",
+ fname,
+ nt_errstr(status)));
+ TALLOC_FREE(fname);
+ ret = false;
+ goto done;
+ }
+ smb2_util_close(tree, h1);
+
+ fileid_array[i] = finfo.all_info2.out.file_id;
+ TALLOC_FREE(fname);
+ }
+
+ /* All returned fileids must be unique. 100 is small so brute force. */
+ for (i = 0; i < num_objs - 1; i++) {
+ unsigned int j;
+ for (j = i + 1; j < num_objs; j++) {
+ if (fileid_array[i] == fileid_array[j]) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "fileid %u == fileid %u (0x%"PRIu64")\n",
+ i,
+ j,
+ fileid_array[i]));
+ ret = false;
+ goto done;
+ }
+ }
+ }
+
+done:
+
+ smb2_util_close(tree, testdirh);
+ smb2_deltree(tree, DNAME);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool test_fileid_unique(
+ struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ return test_fileid_unique_object(tctx, tree, 100, false);
+}
+
+static bool test_fileid_unique_dir(
+ struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ return test_fileid_unique_object(tctx, tree, 100, true);
+}
+
+static bool test_dosattr_tmp_dir(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct smb2_create c;
+ struct smb2_handle h1 = {{0}};
+ const char *fname = DNAME;
+
+ smb2_deltree(tree, fname);
+ smb2_util_rmdir(tree, fname);
+
+ c = (struct smb2_create) {
+ .in.desired_access = SEC_RIGHTS_DIR_ALL,
+ .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE,
+ .in.create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .in.fname = DNAME,
+ };
+
+ status = smb2_create(tree, tctx, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create\n");
+ h1 = c.out.file.handle;
+
+ /* Try to set temporary attribute on directory */
+ SET_ATTRIB(FILE_ATTRIBUTE_TEMPORARY);
+
+ torture_assert_ntstatus_equal_goto(tctx, status,
+ NT_STATUS_INVALID_PARAMETER,
+ ret, done,
+ "Unexpected setinfo result\n");
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ smb2_util_unlink(tree, fname);
+ smb2_deltree(tree, fname);
+
+ return ret;
+}
+
+/*
+ test opening quota fakefile handle and returned attributes
+*/
+static bool test_smb2_open_quota_fake_file(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ const char *fname = "$Extend\\$Quota:$Q:$INDEX_ALLOCATION";
+ struct smb2_create create;
+ struct smb2_handle h = {{0}};
+ NTSTATUS status;
+ bool ret = true;
+
+ create = (struct smb2_create) {
+ .in.desired_access = SEC_RIGHTS_FILE_READ,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
+ .in.fname = fname,
+ };
+
+ status = smb2_create(tree, tree, &create);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ h = create.out.file.handle;
+
+ torture_assert_u64_equal_goto(tctx,
+ create.out.file_attr,
+ FILE_ATTRIBUTE_HIDDEN
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_DIRECTORY
+ | FILE_ATTRIBUTE_ARCHIVE,
+ ret,
+ done,
+ "Wrong attributes\n");
+
+ torture_assert_u64_equal_goto(tctx,
+ create.out.create_time, 0,
+ ret,
+ done,
+ "create_time is not 0\n");
+ torture_assert_u64_equal_goto(tctx,
+ create.out.access_time, 0,
+ ret,
+ done,
+ "access_time is not 0\n");
+ torture_assert_u64_equal_goto(tctx,
+ create.out.write_time, 0,
+ ret,
+ done,
+ "write_time is not 0\n");
+ torture_assert_u64_equal_goto(tctx,
+ create.out.change_time, 0,
+ ret,
+ done,
+ "change_time is not 0\n");
+
+done:
+ smb2_util_close(tree, h);
+ return ret;
+}
+
+/**
+ Find Maximum Path Length
+ */
+static bool generate_path(const size_t len,
+ char *buffer,
+ const size_t buf_len)
+{
+ size_t i;
+
+ if (len >= buf_len) {
+ return false;
+ }
+
+ for (i = 0; i < len ; i++) {
+ buffer[i] = (char)(i % 10) + 48;
+ }
+ buffer[i] = '\0';
+ return true;
+}
+
+static bool test_path_length_test(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ const size_t max_name = 2048;
+ char *name = talloc_array(tctx, char, max_name);
+ struct smb2_handle fh = {{0}};
+ size_t length = 128;
+ size_t max_file_name = 0;
+ size_t max_path_length = 0;
+ char *path_ok = NULL;
+ char *path_next = NULL;
+ char *topdir = NULL;
+ bool is_interactive = torture_setting_bool(tctx, "interactive", false);
+ NTSTATUS status;
+ bool ret = true;
+
+ if (!is_interactive) {
+ torture_result(tctx, TORTURE_SKIP,
+ "Interactive Test: Skipping... "
+ "(enable with --interactive)\n");
+ return ret;
+ }
+
+ torture_comment(tctx, "Testing filename and path lengths\n");
+
+ /* Find Longest File Name */
+ for (length = 128; length < max_name; length++) {
+ if (!generate_path(length, name, max_name)) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Failed to generate path.");
+ return false;
+ }
+
+ status = torture_smb2_testfile(tree, name, &fh);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ smb2_util_close(tree, fh);
+ smb2_util_unlink(tree, name);
+
+ max_file_name = length;
+ }
+
+ torture_assert_int_not_equal_goto(tctx, length, max_name, ret, done,
+ "Name too big\n");
+
+ torture_comment(tctx, "Max file name length: %zu\n", max_file_name);
+
+ /* Remove one char that caused the failure above */
+ name[max_file_name] = '\0';
+
+ path_ok = talloc_strdup(tree, name);
+ torture_assert_not_null_goto(tctx, path_ok, ret, done,
+ "talloc_strdup failed\n");
+
+ topdir = talloc_strdup(tree, name);
+ torture_assert_not_null_goto(tctx, topdir, ret, done,
+ "talloc_strdup failed\n");
+
+ status = smb2_util_mkdir(tree, path_ok);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "mkdir [%s] failed: %s\n",
+ path_ok, nt_errstr(status));
+ torture_result(tctx, TORTURE_FAIL, "Initial mkdir failed");
+ return false;
+ }
+
+ while (true) {
+ path_next = talloc_asprintf(tctx, "%s\\%s", path_ok, name);
+ torture_assert_not_null_goto(tctx, path_next, ret, done,
+ "talloc_asprintf failed\n");
+
+ status = smb2_util_mkdir(tree, path_next);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ path_ok = path_next;
+ }
+
+ for (length = 1; length < max_name; length++) {
+ if (!generate_path(length, name, max_name)) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Failed to generate path.");
+ return false;
+ }
+
+ path_next = talloc_asprintf(tctx, "%s\\%s", path_ok, name);
+ torture_assert_not_null_goto(tctx, path_next, ret, done,
+ "talloc_asprintf failed\n");
+
+ status = torture_smb2_testfile(tree, path_next, &fh);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ smb2_util_close(tree, fh);
+ path_ok = path_next;
+ }
+
+ max_path_length = talloc_array_length(path_ok);
+
+ torture_comment(tctx, "Max path name length: %zu\n", max_path_length);
+
+done:
+ return ret;
+}
+
+/*
+ basic testing of SMB2 read
+*/
+struct torture_suite *torture_smb2_create_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "create");
+
+ torture_suite_add_1smb2_test(suite, "gentest", test_create_gentest);
+ torture_suite_add_1smb2_test(suite, "blob", test_create_blob);
+ torture_suite_add_1smb2_test(suite, "open", test_smb2_open);
+ torture_suite_add_1smb2_test(suite, "brlocked", test_smb2_open_brlocked);
+ torture_suite_add_1smb2_test(suite, "multi", test_smb2_open_multi);
+ torture_suite_add_1smb2_test(suite, "delete", test_smb2_open_for_delete);
+ torture_suite_add_1smb2_test(suite, "leading-slash", test_smb2_leading_slash);
+ torture_suite_add_1smb2_test(suite, "impersonation", test_smb2_impersonation_level);
+ torture_suite_add_1smb2_test(suite, "aclfile", test_create_acl_file);
+ torture_suite_add_1smb2_test(suite, "acldir", test_create_acl_dir);
+ torture_suite_add_1smb2_test(suite, "nulldacl", test_create_null_dacl);
+ torture_suite_add_1smb2_test(suite, "mkdir-dup", test_mkdir_dup);
+ torture_suite_add_1smb2_test(suite, "dir-alloc-size", test_dir_alloc_size);
+ torture_suite_add_1smb2_test(suite, "dosattr_tmp_dir", test_dosattr_tmp_dir);
+ torture_suite_add_1smb2_test(suite, "quota-fake-file", test_smb2_open_quota_fake_file);
+ torture_suite_add_1smb2_test(suite, "path-length", test_path_length_test);
+ torture_suite_add_1smb2_test(suite, "bench-path-contention-shared", test_smb2_bench_path_contention_shared);
+
+ suite->description = talloc_strdup(suite, "SMB2-CREATE tests");
+
+ return suite;
+}
+
+struct torture_suite *torture_smb2_twrp_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "twrp");
+
+ torture_suite_add_1smb2_test(suite, "write", test_twrp_write);
+ torture_suite_add_1smb2_test(suite, "stream", test_twrp_stream);
+ torture_suite_add_1smb2_test(suite, "openroot", test_twrp_openroot);
+ torture_suite_add_1smb2_test(suite, "listdir", test_twrp_listdir);
+
+ suite->description = talloc_strdup(suite, "SMB2-TWRP tests");
+
+ return suite;
+}
+
+/*
+ basic testing of SMB2 File-IDs
+*/
+struct torture_suite *torture_smb2_fileid_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "fileid");
+
+ torture_suite_add_1smb2_test(suite, "fileid", test_fileid);
+ torture_suite_add_1smb2_test(suite, "fileid-dir", test_fileid_dir);
+ torture_suite_add_1smb2_test(suite, "unique", test_fileid_unique);
+ torture_suite_add_1smb2_test(suite, "unique-dir", test_fileid_unique_dir);
+
+ suite->description = talloc_strdup(suite, "SMB2-FILEID tests");
+
+ return suite;
+}
+
+static bool test_no_stream(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create c;
+ NTSTATUS status;
+ bool ret = true;
+ const char *names[] = {
+ "test_no_stream::$DATA",
+ "test_no_stream::foooooooooooo",
+ "test_no_stream:stream",
+ "test_no_stream:stream:$DATA",
+ NULL
+ };
+ int i;
+
+ for (i = 0; names[i] != NULL; i++) {
+ c = (struct smb2_create) {
+ .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+ .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = names[i],
+ };
+
+ status = smb2_create(tree, tctx, &c);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ torture_comment(
+ tctx, "Expected NT_STATUS_OBJECT_NAME_INVALID, "
+ "got %s, name: '%s'\n",
+ nt_errstr(status), names[i]);
+ torture_fail_goto(tctx, done, "Bad create result\n");
+ }
+ }
+done:
+ return ret;
+}
+
+struct torture_suite *torture_smb2_create_no_streams_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "create_no_streams");
+
+ torture_suite_add_1smb2_test(suite, "no_stream", test_no_stream);
+
+ suite->description = talloc_strdup(suite, "SMB2-CREATE stream test on share without streams support");
+
+ return suite;
+}