summaryrefslogtreecommitdiff
path: root/source4/torture/raw
diff options
context:
space:
mode:
Diffstat (limited to 'source4/torture/raw')
-rw-r--r--source4/torture/raw/acls.c1758
-rw-r--r--source4/torture/raw/chkpath.c313
-rw-r--r--source4/torture/raw/close.c177
-rw-r--r--source4/torture/raw/composite.c424
-rw-r--r--source4/torture/raw/context.c902
-rw-r--r--source4/torture/raw/eas.c490
-rw-r--r--source4/torture/raw/ioctl.c171
-rw-r--r--source4/torture/raw/lock.c1336
-rw-r--r--source4/torture/raw/lockbench.c414
-rw-r--r--source4/torture/raw/lookuprate.c320
-rw-r--r--source4/torture/raw/missing.txt160
-rw-r--r--source4/torture/raw/mkdir.c175
-rw-r--r--source4/torture/raw/mux.c361
-rw-r--r--source4/torture/raw/notify.c1312
-rw-r--r--source4/torture/raw/offline.c514
-rw-r--r--source4/torture/raw/open.c1575
-rw-r--r--source4/torture/raw/openbench.c484
-rw-r--r--source4/torture/raw/oplock.c3030
-rwxr-xr-xsource4/torture/raw/pingpong.c267
-rw-r--r--source4/torture/raw/qfileinfo.c865
-rw-r--r--source4/torture/raw/qfsinfo.c296
-rw-r--r--source4/torture/raw/raw.c86
-rw-r--r--source4/torture/raw/read.c954
-rw-r--r--source4/torture/raw/rename.c455
-rw-r--r--source4/torture/raw/samba3hide.c339
-rw-r--r--source4/torture/raw/samba3misc.c891
-rw-r--r--source4/torture/raw/search.c1391
-rw-r--r--source4/torture/raw/seek.c243
-rw-r--r--source4/torture/raw/setfileinfo.c703
-rw-r--r--source4/torture/raw/streams.c649
-rw-r--r--source4/torture/raw/tconrate.c201
-rw-r--r--source4/torture/raw/unlink.c450
-rw-r--r--source4/torture/raw/write.c724
33 files changed, 22430 insertions, 0 deletions
diff --git a/source4/torture/raw/acls.c b/source4/torture/raw/acls.c
new file mode 100644
index 0000000000..95e7282895
--- /dev/null
+++ b/source4/torture/raw/acls.c
@@ -0,0 +1,1758 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test security descriptor operations
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "libcli/util/clilsa.h"
+#include "libcli/security/security.h"
+#include "torture/util.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define BASEDIR "\\testsd"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+static bool test_sd(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\sd.txt";
+ bool ret = true;
+ int fnum = -1;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_ace ace;
+ struct security_descriptor *sd;
+ struct dom_sid *test_sid;
+
+ printf("TESTING SETFILEINFO EA_SET\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd = q.query_secdesc.out.sd;
+
+ printf("add a new ACE to the DACL\n");
+
+ test_sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+
+ 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);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = q.query_secdesc.in.secinfo_flags;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!security_acl_equal(q.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+ printf("remove it again\n");
+
+ status = security_descriptor_dacl_del(sd, test_sid);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!security_acl_equal(q.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+
+/*
+ test using nttrans create to create a file with an initial acl set
+*/
+static bool test_nttrans_create(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\acl2.txt";
+ bool ret = true;
+ int fnum = -1;
+ union smb_fileinfo q;
+ struct security_ace ace;
+ struct security_descriptor *sd;
+ struct dom_sid *test_sid;
+
+ printf("testing nttrans create with sec_desc\n");
+
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.sec_desc = NULL;
+ io.ntcreatex.in.ea_list = NULL;
+
+ printf("creating normal file\n");
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("querying ACL\n");
+
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd = q.query_secdesc.out.sd;
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ printf("adding a new ACE\n");
+ test_sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-54321");
+
+ 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);
+
+ printf("creating a file with an initial ACL\n");
+
+ io.ntcreatex.in.sec_desc = sd;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ q.query_secdesc.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!security_acl_equal(q.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+#define CHECK_ACCESS_FLAGS(_fnum, flags) do { \
+ union smb_fileinfo _q; \
+ _q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; \
+ _q.access_information.in.file.fnum = (_fnum); \
+ status = smb_raw_fileinfo(cli->tree, tctx, &_q); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ if (_q.access_information.out.access_flags != (flags)) { \
+ printf("(%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 the behaviour of the well known SID_CREATOR_OWNER sid, and some generic
+ mapping bits
+*/
+static bool test_creator_sid(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\creator.txt";
+ bool ret = true;
+ int fnum = -1;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_descriptor *sd, *sd_orig, *sd2;
+ const char *owner_sid;
+
+ printf("TESTING SID_CREATOR_OWNER\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC | SEC_STD_WRITE_OWNER;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ printf("set a sec desc allowing no write by CREATOR_OWNER\n");
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ SID_CREATOR_OWNER,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("try open for write\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for read\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for generic write\n");
+ io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for generic read\n");
+ io.ntcreatex.in.access_mask = SEC_GENERIC_READ;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("set a sec desc allowing no write by owner\n");
+ sd = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("check that sd has been mapped correctly\n");
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+ printf("try open for write\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for read\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ printf("try open for generic write\n");
+ io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for generic read\n");
+ io.ntcreatex.in.access_mask = SEC_GENERIC_READ;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ SEC_RIGHTS_FILE_READ);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ printf("set a sec desc allowing generic read by owner\n");
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_READ | SEC_STD_ALL,
+ 0,
+ NULL);
+
+ set.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("check that generic read has been mapped correctly\n");
+ sd2 = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
+ 0,
+ NULL);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+
+ printf("try open for write\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for read\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ SEC_FILE_READ_DATA |
+ SEC_FILE_READ_ATTRIBUTE);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ printf("try open for generic write\n");
+ io.ntcreatex.in.access_mask = SEC_GENERIC_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("try open for generic read\n");
+ io.ntcreatex.in.access_mask = SEC_GENERIC_READ;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, SEC_RIGHTS_FILE_READ);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+
+ printf("put back original sd\n");
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+
+/*
+ test the mapping of the SEC_GENERIC_xx bits to SEC_STD_xx and
+ SEC_FILE_xx bits
+*/
+static bool test_generic_bits(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\generic.txt";
+ bool ret = true;
+ int fnum = -1, i;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_descriptor *sd, *sd_orig, *sd2;
+ const char *owner_sid;
+ const struct {
+ uint32_t gen_bits;
+ uint32_t specific_bits;
+ } file_mappings[] = {
+ { 0, 0 },
+ { SEC_GENERIC_READ, SEC_RIGHTS_FILE_READ },
+ { SEC_GENERIC_WRITE, SEC_RIGHTS_FILE_WRITE },
+ { SEC_GENERIC_EXECUTE, SEC_RIGHTS_FILE_EXECUTE },
+ { SEC_GENERIC_ALL, SEC_RIGHTS_FILE_ALL },
+ { SEC_FILE_READ_DATA, SEC_FILE_READ_DATA },
+ { SEC_FILE_READ_ATTRIBUTE, SEC_FILE_READ_ATTRIBUTE }
+ };
+ const struct {
+ uint32_t gen_bits;
+ uint32_t specific_bits;
+ } dir_mappings[] = {
+ { 0, 0 },
+ { SEC_GENERIC_READ, SEC_RIGHTS_DIR_READ },
+ { SEC_GENERIC_WRITE, SEC_RIGHTS_DIR_WRITE },
+ { SEC_GENERIC_EXECUTE, SEC_RIGHTS_DIR_EXECUTE },
+ { SEC_GENERIC_ALL, SEC_RIGHTS_DIR_ALL }
+ };
+ bool has_restore_privilege;
+ bool has_take_ownership_privilege;
+
+ printf("TESTING FILE GENERIC BITS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask =
+ SEC_STD_READ_CONTROL |
+ SEC_STD_WRITE_DAC |
+ SEC_STD_WRITE_OWNER;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ status = smblsa_sid_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_RESTORE));
+ has_restore_privilege = NT_STATUS_IS_OK(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No");
+
+ status = smblsa_sid_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP));
+ has_take_ownership_privilege = NT_STATUS_IS_OK(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No");
+
+ for (i=0;i<ARRAY_SIZE(file_mappings);i++) {
+ uint32_t expected_mask =
+ SEC_STD_WRITE_DAC |
+ SEC_STD_READ_CONTROL |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_STD_DELETE;
+ uint32_t expected_mask_anon = SEC_FILE_READ_ATTRIBUTE;
+
+ if (has_restore_privilege) {
+ expected_mask_anon |= SEC_STD_DELETE;
+ }
+
+ printf("testing generic bits 0x%08x\n",
+ file_mappings[i].gen_bits);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ file_mappings[i].gen_bits,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sd2 = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ file_mappings[i].specific_bits,
+ 0,
+ NULL);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ expected_mask | file_mappings[i].specific_bits);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ if (!has_take_ownership_privilege) {
+ continue;
+ }
+
+ printf("testing generic bits 0x%08x (anonymous)\n",
+ file_mappings[i].gen_bits);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, SID_NT_ANONYMOUS, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ file_mappings[i].gen_bits,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sd2 = security_descriptor_dacl_create(tctx,
+ 0, SID_NT_ANONYMOUS, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ file_mappings[i].specific_bits,
+ 0,
+ NULL);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ expected_mask_anon | file_mappings[i].specific_bits);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ }
+
+ printf("put back original sd\n");
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+
+ printf("TESTING DIR GENERIC BITS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask =
+ SEC_STD_READ_CONTROL |
+ SEC_STD_WRITE_DAC |
+ SEC_STD_WRITE_OWNER;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ status = smblsa_sid_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_RESTORE));
+ has_restore_privilege = NT_STATUS_IS_OK(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No");
+
+ status = smblsa_sid_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP));
+ has_take_ownership_privilege = NT_STATUS_IS_OK(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No");
+
+ for (i=0;i<ARRAY_SIZE(dir_mappings);i++) {
+ uint32_t expected_mask =
+ SEC_STD_WRITE_DAC |
+ SEC_STD_READ_CONTROL |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_STD_DELETE;
+ uint32_t expected_mask_anon = SEC_FILE_READ_ATTRIBUTE;
+
+ if (has_restore_privilege) {
+ expected_mask_anon |= SEC_STD_DELETE;
+ }
+
+ printf("testing generic bits 0x%08x\n",
+ file_mappings[i].gen_bits);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ dir_mappings[i].gen_bits,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sd2 = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ dir_mappings[i].specific_bits,
+ 0,
+ NULL);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ expected_mask | dir_mappings[i].specific_bits);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ if (!has_take_ownership_privilege) {
+ continue;
+ }
+
+ printf("testing generic bits 0x%08x (anonymous)\n",
+ file_mappings[i].gen_bits);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, SID_NT_ANONYMOUS, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ file_mappings[i].gen_bits,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sd2 = security_descriptor_dacl_create(tctx,
+ 0, SID_NT_ANONYMOUS, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ file_mappings[i].specific_bits,
+ 0,
+ NULL);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum,
+ expected_mask_anon | dir_mappings[i].specific_bits);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ }
+
+ printf("put back original sd\n");
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+
+/*
+ see what access bits the owner of a file always gets
+*/
+static bool test_owner_bits(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\generic.txt";
+ bool ret = true;
+ int fnum = -1, i;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_descriptor *sd, *sd_orig;
+ const char *owner_sid;
+ bool has_restore_privilege;
+ bool has_take_ownership_privilege;
+ uint32_t expected_bits;
+
+ printf("TESTING FILE OWNER BITS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask =
+ SEC_STD_READ_CONTROL |
+ SEC_STD_WRITE_DAC |
+ SEC_STD_WRITE_OWNER;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ status = smblsa_sid_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_RESTORE));
+ has_restore_privilege = NT_STATUS_IS_OK(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_RESTORE - %s\n", has_restore_privilege?"Yes":"No");
+
+ status = smblsa_sid_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_TAKE_OWNERSHIP));
+ has_take_ownership_privilege = NT_STATUS_IS_OK(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No");
+
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA,
+ 0,
+ NULL);
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ expected_bits = SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE;
+
+ for (i=0;i<16;i++) {
+ uint32_t bit = (1<<i);
+ io.ntcreatex.in.access_mask = bit;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (expected_bits & bit) {
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed with access mask 0x%08x of expected 0x%08x\n",
+ bit, expected_bits);
+ }
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_ACCESS_FLAGS(io.ntcreatex.out.file.fnum, bit | SEC_FILE_READ_ATTRIBUTE);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+ }
+
+ printf("put back original sd\n");
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+
+
+/*
+ test the inheritance of ACL flags onto new files and directories
+*/
+static bool test_inheritance(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *dname = BASEDIR "\\inheritance";
+ const char *fname1 = BASEDIR "\\inheritance\\testfile";
+ const char *fname2 = BASEDIR "\\inheritance\\testdir";
+ bool ret = true;
+ int fnum=0, fnum2, i;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_descriptor *sd, *sd2, *sd_orig=NULL, *sd_def;
+ const char *owner_sid;
+ const struct dom_sid *creator_owner;
+ const struct {
+ uint32_t parent_flags;
+ uint32_t file_flags;
+ uint32_t dir_flags;
+ } test_flags[] = {
+ {
+ 0,
+ 0,
+ 0
+ },
+ {
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_INHERIT_ONLY,
+ },
+ {
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ 0,
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ },
+ {
+ SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ 0,
+ SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ },
+ {
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_INHERIT_ONLY,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ 0,
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ 0,
+ 0,
+ },
+ {
+ SEC_ACE_FLAG_INHERIT_ONLY |
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ 0,
+ 0,
+ }
+ };
+
+ smbcli_rmdir(cli->tree, dname);
+
+ printf("TESTING ACL INHERITANCE\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = dname;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ printf("owner_sid is %s\n", owner_sid);
+
+ sd_def = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_ALL,
+ 0,
+ SID_NT_SYSTEM,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_ALL,
+ 0,
+ NULL);
+
+ creator_owner = dom_sid_parse_talloc(tctx, SID_CREATOR_OWNER);
+
+ for (i=0;i<ARRAY_SIZE(test_flags);i++) {
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ SID_CREATOR_OWNER,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA,
+ test_flags[i].parent_flags,
+ SID_WORLD,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_ALL | SEC_STD_ALL,
+ 0,
+ NULL);
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.ntcreatex.in.fname = fname1;
+ io.ntcreatex.in.create_options = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ q.query_secdesc.in.file.fnum = fnum2;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum2);
+ smbcli_unlink(cli->tree, fname1);
+
+ if (!(test_flags[i].parent_flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd_def)) {
+ printf("Expected default sd at %d - got:\n", i);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ }
+ goto check_dir;
+ }
+
+ if (q.query_secdesc.out.sd->dacl == NULL ||
+ q.query_secdesc.out.sd->dacl->num_aces != 1 ||
+ q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA ||
+ !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee,
+ sd_orig->owner_sid)) {
+ printf("Bad sd in child file at %d\n", i);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ ret = false;
+ goto check_dir;
+ }
+
+ if (q.query_secdesc.out.sd->dacl->aces[0].flags !=
+ test_flags[i].file_flags) {
+ printf("incorrect file_flags 0x%x - expected 0x%x for parent 0x%x with (i=%d)\n",
+ q.query_secdesc.out.sd->dacl->aces[0].flags,
+ test_flags[i].file_flags,
+ test_flags[i].parent_flags,
+ i);
+ ret = false;
+ }
+
+ check_dir:
+ io.ntcreatex.in.fname = fname2;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ q.query_secdesc.in.file.fnum = fnum2;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum2);
+ smbcli_rmdir(cli->tree, fname2);
+
+ if (!(test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) &&
+ (!(test_flags[i].parent_flags & SEC_ACE_FLAG_OBJECT_INHERIT) ||
+ (test_flags[i].parent_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT))) {
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd_def)) {
+ printf("Expected default sd for dir at %d - got:\n", i);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ }
+ continue;
+ }
+
+ if ((test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) &&
+ (test_flags[i].parent_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ if (q.query_secdesc.out.sd->dacl == NULL ||
+ q.query_secdesc.out.sd->dacl->num_aces != 1 ||
+ q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA ||
+ !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee,
+ sd_orig->owner_sid) ||
+ q.query_secdesc.out.sd->dacl->aces[0].flags != test_flags[i].dir_flags) {
+ printf("Bad sd in child dir at %d (parent 0x%x)\n",
+ i, test_flags[i].parent_flags);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ ret = false;
+ continue;
+ }
+ } else if (test_flags[i].parent_flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ if (q.query_secdesc.out.sd->dacl == NULL ||
+ q.query_secdesc.out.sd->dacl->num_aces != 2 ||
+ q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA ||
+ !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee,
+ sd_orig->owner_sid) ||
+ q.query_secdesc.out.sd->dacl->aces[1].access_mask != SEC_FILE_WRITE_DATA ||
+ !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[1].trustee,
+ creator_owner) ||
+ q.query_secdesc.out.sd->dacl->aces[0].flags != 0 ||
+ q.query_secdesc.out.sd->dacl->aces[1].flags !=
+ (test_flags[i].dir_flags | SEC_ACE_FLAG_INHERIT_ONLY)) {
+ printf("Bad sd in child dir at %d (parent 0x%x)\n",
+ i, test_flags[i].parent_flags);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ ret = false;
+ continue;
+ }
+ } else {
+ if (q.query_secdesc.out.sd->dacl == NULL ||
+ q.query_secdesc.out.sd->dacl->num_aces != 1 ||
+ q.query_secdesc.out.sd->dacl->aces[0].access_mask != SEC_FILE_WRITE_DATA ||
+ !dom_sid_equal(&q.query_secdesc.out.sd->dacl->aces[0].trustee,
+ creator_owner) ||
+ q.query_secdesc.out.sd->dacl->aces[0].flags != test_flags[i].dir_flags) {
+ printf("Bad sd in child dir at %d (parent 0x%x)\n",
+ i, test_flags[i].parent_flags);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ ret = false;
+ continue;
+ }
+ }
+ }
+
+ printf("testing access checks on inherited create with %s\n", fname1);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ SID_WORLD,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_ALL | SEC_STD_ALL,
+ 0,
+ NULL);
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.ntcreatex.in.fname = fname1;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_ACCESS_FLAGS(fnum2, SEC_RIGHTS_FILE_ALL);
+
+ q.query_secdesc.in.file.fnum = fnum2;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, fnum2);
+
+ sd2 = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC,
+ 0,
+ NULL);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("failed: w2k3 ACL bug (allowed open when ACL should deny)\n");
+ ret = false;
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_ACCESS_FLAGS(fnum2, SEC_RIGHTS_FILE_ALL);
+ smbcli_close(cli->tree, fnum2);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+
+ printf("trying without execute\n");
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL & ~SEC_FILE_EXECUTE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("and with full permissions again\n");
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_ACCESS_FLAGS(fnum2, SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE);
+ smbcli_close(cli->tree, fnum2);
+
+ printf("put back original sd\n");
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_ACCESS_FLAGS(fnum2, SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE);
+ smbcli_close(cli->tree, fnum2);
+
+ smbcli_unlink(cli->tree, fname1);
+ smbcli_rmdir(cli->tree, dname);
+
+done:
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+
+/*
+ test dynamic acl inheritance
+*/
+static bool test_inheritance_dynamic(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *dname = BASEDIR "\\inheritance";
+ const char *fname1 = BASEDIR "\\inheritance\\testfile";
+ bool ret = true;
+ int fnum=0, fnum2;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_descriptor *sd, *sd_orig=NULL;
+ const char *owner_sid;
+
+ printf("TESTING DYNAMIC ACL INHERITANCE\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = dname;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("get the original sd\n");
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ printf("owner_sid is %s\n", owner_sid);
+
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA | SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+ sd->type |= SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ;
+
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("create a file with an inherited acl\n");
+ io.ntcreatex.in.fname = fname1;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ smbcli_close(cli->tree, fnum2);
+
+ printf("try and access file with base rights - should be OK\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ smbcli_close(cli->tree, fnum2);
+
+ printf("try and access file with extra rights - should be denied\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA | SEC_FILE_EXECUTE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("update parent sd\n");
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA | SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE | SEC_FILE_EXECUTE,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+ sd->type |= SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_DACL_AUTO_INHERIT_REQ;
+
+ set.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("try and access file with base rights - should be OK\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ smbcli_close(cli->tree, fnum2);
+
+
+ printf("try and access now - should be OK if dynamic inheritance works\n");
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA | SEC_FILE_EXECUTE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Server does not have dynamic inheritance\n");
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ printf("Server does have dynamic inheritance\n");
+ }
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ smbcli_unlink(cli->tree, fname1);
+
+done:
+ printf("put back original sd\n");
+ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ set.set_secdesc.in.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd_orig;
+ status = smb_raw_setfileinfo(cli->tree, &set);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_rmdir(cli->tree, dname);
+
+ return ret;
+}
+
+#define CHECK_STATUS_FOR_BIT_ACTION(status, bits, action) do { \
+ if (!(bits & desired_64)) {\
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); \
+ action; \
+ } else { \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ } \
+} while (0)
+
+#define CHECK_STATUS_FOR_BIT(status, bits, access) do { \
+ if (NT_STATUS_IS_OK(status)) { \
+ if (!(granted & access)) {\
+ printf("(%s) %s but flags 0x%08X are not granted! granted[0x%08X] desired[0x%08X]\n", \
+ __location__, nt_errstr(status), access, granted, desired); \
+ ret = false; \
+ goto done; \
+ } \
+ } else { \
+ if (granted & access) {\
+ printf("(%s) %s but flags 0x%08X are granted! granted[0x%08X] desired[0x%08X]\n", \
+ __location__, nt_errstr(status), access, granted, desired); \
+ ret = false; \
+ goto done; \
+ } \
+ } \
+ CHECK_STATUS_FOR_BIT_ACTION(status, bits, do {} while (0)); \
+} while (0)
+
+/* test what access mask is needed for getting and setting security_descriptors */
+static bool test_sd_get_set(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo fi;
+ union smb_setfileinfo si;
+ struct security_descriptor *sd;
+ struct security_descriptor *sd_owner = NULL;
+ struct security_descriptor *sd_group = NULL;
+ struct security_descriptor *sd_dacl = NULL;
+ struct security_descriptor *sd_sacl = NULL;
+ int fnum=0;
+ const char *fname = BASEDIR "\\sd_get_set.txt";
+ uint64_t desired_64;
+ uint32_t desired = 0, granted;
+ int i = 0;
+#define NO_BITS_HACK (((uint64_t)1)<<32)
+ uint64_t open_bits =
+ SEC_MASK_GENERIC |
+ SEC_FLAG_SYSTEM_SECURITY |
+ SEC_FLAG_MAXIMUM_ALLOWED |
+ SEC_STD_ALL |
+ SEC_FILE_ALL |
+ NO_BITS_HACK;
+ uint64_t get_owner_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL;
+ uint64_t set_owner_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_OWNER;
+ uint64_t get_group_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL;
+ uint64_t set_group_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_OWNER;
+ uint64_t get_dacl_bits = SEC_MASK_GENERIC | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_READ_CONTROL;
+ uint64_t set_dacl_bits = SEC_GENERIC_ALL | SEC_FLAG_MAXIMUM_ALLOWED | SEC_STD_WRITE_DAC;
+ uint64_t get_sacl_bits = SEC_FLAG_SYSTEM_SECURITY;
+ uint64_t set_sacl_bits = SEC_FLAG_SYSTEM_SECURITY;
+
+ printf("TESTING ACCESS MASKS FOR SD GET/SET\n");
+
+ /* first create a file with full access for everyone */
+ sd = security_descriptor_dacl_create(tctx,
+ 0, SID_NT_ANONYMOUS, SID_BUILTIN_USERS,
+ SID_WORLD,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ 0,
+ NULL);
+ sd->type |= SEC_DESC_SACL_PRESENT;
+ sd->sacl = NULL;
+ io.ntcreatex.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_GENERIC_ALL;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.sec_desc = sd;
+ io.ntcreatex.in.ea_list = NULL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ status = smbcli_close(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /*
+ * now try each access_mask bit and no bit at all in a loop
+ * and see what's allowed
+ * NOTE: if i == 32 it means access_mask = 0 (see NO_BITS_HACK above)
+ */
+ for (i=0; i <= 32; i++) {
+ desired_64 = ((uint64_t)1) << i;
+ desired = (uint32_t)desired_64;
+
+ /* first open the file with the desired access */
+ io.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.access_mask = desired;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS_FOR_BIT_ACTION(status, open_bits, goto next);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* then check what access was granted */
+ fi.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION;
+ fi.access_information.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &fi);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ granted = fi.access_information.out.access_flags;
+
+ /* test the owner */
+ ZERO_STRUCT(fi);
+ fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ fi.query_secdesc.in.file.fnum = fnum;
+ fi.query_secdesc.in.secinfo_flags = SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &fi);
+ CHECK_STATUS_FOR_BIT(status, get_owner_bits, SEC_STD_READ_CONTROL);
+ if (fi.query_secdesc.out.sd) {
+ sd_owner = fi.query_secdesc.out.sd;
+ } else if (!sd_owner) {
+ sd_owner = sd;
+ }
+ si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ si.set_secdesc.in.file.fnum = fnum;
+ si.set_secdesc.in.secinfo_flags = SECINFO_OWNER;
+ si.set_secdesc.in.sd = sd_owner;
+ status = smb_raw_setfileinfo(cli->tree, &si);
+ CHECK_STATUS_FOR_BIT(status, set_owner_bits, SEC_STD_WRITE_OWNER);
+
+ /* test the group */
+ ZERO_STRUCT(fi);
+ fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ fi.query_secdesc.in.file.fnum = fnum;
+ fi.query_secdesc.in.secinfo_flags = SECINFO_GROUP;
+ status = smb_raw_fileinfo(cli->tree, tctx, &fi);
+ CHECK_STATUS_FOR_BIT(status, get_group_bits, SEC_STD_READ_CONTROL);
+ if (fi.query_secdesc.out.sd) {
+ sd_group = fi.query_secdesc.out.sd;
+ } else if (!sd_group) {
+ sd_group = sd;
+ }
+ si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ si.set_secdesc.in.file.fnum = fnum;
+ si.set_secdesc.in.secinfo_flags = SECINFO_GROUP;
+ si.set_secdesc.in.sd = sd_group;
+ status = smb_raw_setfileinfo(cli->tree, &si);
+ CHECK_STATUS_FOR_BIT(status, set_group_bits, SEC_STD_WRITE_OWNER);
+
+ /* test the DACL */
+ ZERO_STRUCT(fi);
+ fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ fi.query_secdesc.in.file.fnum = fnum;
+ fi.query_secdesc.in.secinfo_flags = SECINFO_DACL;
+ status = smb_raw_fileinfo(cli->tree, tctx, &fi);
+ CHECK_STATUS_FOR_BIT(status, get_dacl_bits, SEC_STD_READ_CONTROL);
+ if (fi.query_secdesc.out.sd) {
+ sd_dacl = fi.query_secdesc.out.sd;
+ } else if (!sd_dacl) {
+ sd_dacl = sd;
+ }
+ si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ si.set_secdesc.in.file.fnum = fnum;
+ si.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ si.set_secdesc.in.sd = sd_dacl;
+ status = smb_raw_setfileinfo(cli->tree, &si);
+ CHECK_STATUS_FOR_BIT(status, set_dacl_bits, SEC_STD_WRITE_DAC);
+
+ /* test the SACL */
+ ZERO_STRUCT(fi);
+ fi.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ fi.query_secdesc.in.file.fnum = fnum;
+ fi.query_secdesc.in.secinfo_flags = SECINFO_SACL;
+ status = smb_raw_fileinfo(cli->tree, tctx, &fi);
+ CHECK_STATUS_FOR_BIT(status, get_sacl_bits, SEC_FLAG_SYSTEM_SECURITY);
+ if (fi.query_secdesc.out.sd) {
+ sd_sacl = fi.query_secdesc.out.sd;
+ } else if (!sd_sacl) {
+ sd_sacl = sd;
+ }
+ si.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ si.set_secdesc.in.file.fnum = fnum;
+ si.set_secdesc.in.secinfo_flags = SECINFO_SACL;
+ si.set_secdesc.in.sd = sd_sacl;
+ status = smb_raw_setfileinfo(cli->tree, &si);
+ CHECK_STATUS_FOR_BIT(status, set_sacl_bits, SEC_FLAG_SYSTEM_SECURITY);
+
+ /* close the handle */
+ status = smbcli_close(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+next:
+ continue;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ basic testing of security descriptor calls
+*/
+bool torture_raw_acls(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_sd(tctx, cli);
+ ret &= test_nttrans_create(tctx, cli);
+ ret &= test_creator_sid(tctx, cli);
+ ret &= test_generic_bits(tctx, cli);
+ ret &= test_owner_bits(tctx, cli);
+ ret &= test_inheritance(tctx, cli);
+ ret &= test_inheritance_dynamic(tctx, cli);
+ ret &= test_sd_get_set(tctx, cli);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/chkpath.c b/source4/torture/raw/chkpath.c
new file mode 100644
index 0000000000..2ed83d308c
--- /dev/null
+++ b/source4/torture/raw/chkpath.c
@@ -0,0 +1,313 @@
+/*
+ Unix SMB/CIFS implementation.
+ chkpath individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\rawchkpath"
+
+#define CHECK_STATUS(status, correct, dos_correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct) && !NT_STATUS_EQUAL(status, dos_correct)) { \
+ printf("(%d) Incorrect status %s - should be %s\n", \
+ __LINE__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+static NTSTATUS single_search(struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx, const char *pattern)
+{
+ union smb_search_first io;
+ NTSTATUS status;
+
+ io.t2ffirst.level = RAW_SEARCH_TRANS2;
+ io.t2ffirst.data_level = RAW_SEARCH_DATA_STANDARD;
+ io.t2ffirst.in.search_attrib = 0;
+ io.t2ffirst.in.max_count = 1;
+ io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
+ io.t2ffirst.in.storage_type = 0;
+ io.t2ffirst.in.pattern = pattern;
+
+ status = smb_raw_search_first(cli->tree, mem_ctx,
+ &io, NULL, NULL);
+
+ return status;
+}
+
+static bool test_path_ex(struct smbcli_state *cli, struct torture_context *tctx,
+ const char *path, const char *path_expected,
+ NTSTATUS expected, NTSTATUS dos_expected)
+{
+ union smb_chkpath io;
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+
+ io.chkpath.in.path = path;
+ status = smb_raw_chkpath(cli->tree, &io);
+ if (!NT_STATUS_EQUAL(status, expected) && !NT_STATUS_EQUAL(status, dos_expected)) {
+ printf("FAILED %-30s chkpath %s should be %s or %s\n",
+ path, nt_errstr(status), nt_errstr(expected), nt_errstr(dos_expected));
+ return false;
+ } else {
+ printf("%-30s chkpath correct (%s)\n", path, nt_errstr(status));
+ }
+
+ if (NT_STATUS_EQUAL(expected, NT_STATUS_NOT_A_DIRECTORY)) {
+ expected = NT_STATUS_OK;
+ }
+
+ ZERO_STRUCT(finfo);
+ finfo.generic.level = RAW_FILEINFO_NAME_INFO;
+ finfo.generic.in.file.path = path;
+ status = smb_raw_pathinfo(cli->tree, cli, &finfo);
+ if (!NT_STATUS_EQUAL(status, expected) && !NT_STATUS_EQUAL(status, dos_expected)) {
+ printf("FAILED: %-30s pathinfo %s should be %s or %s\n",
+ path, nt_errstr(status), nt_errstr(expected), nt_errstr(dos_expected));
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%-30s chkpath correct (%s)\n", path, nt_errstr(status));
+ return true;
+ }
+
+ if (path_expected &&
+ (!finfo.name_info.out.fname.s ||
+ strcmp(finfo.name_info.out.fname.s, path_expected) != 0)) {
+ if (tctx && torture_setting_bool(tctx, "samba4", false)) {
+ printf("IGNORE: %-30s => %-20s should be %s\n",
+ path, finfo.name_info.out.fname.s, path_expected);
+ return true;
+ }
+ printf("FAILED: %-30s => %-20s should be %s\n",
+ path, finfo.name_info.out.fname.s, path_expected);
+ return false;
+ }
+ printf("%-30s => %-20s correct\n",
+ path, finfo.name_info.out.fname.s);
+
+ return true;
+}
+
+static bool test_path(struct smbcli_state *cli, const char *path,
+ NTSTATUS expected, NTSTATUS dos_expected)
+{
+ return test_path_ex(cli, NULL, path, path, expected, dos_expected);
+}
+
+static bool test_chkpath(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_chkpath io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum = -1;
+ int fnum1 = -1;
+
+ io.chkpath.in.path = BASEDIR;
+
+ status = smb_raw_chkpath(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK, NT_STATUS_OK);
+
+ ret &= test_path(cli, BASEDIR "\\nodir", NT_STATUS_OBJECT_NAME_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ fnum = create_complex_file(cli, tctx, BASEDIR "\\test.txt..");
+ if (fnum == -1) {
+ printf("failed to open test.txt - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ ret &= test_path(cli, BASEDIR "\\test.txt..", NT_STATUS_NOT_A_DIRECTORY, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ if (!torture_set_file_attribute(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN)) {
+ printf("failed to set basedir hidden\n");
+ ret = false;
+ goto done;
+ }
+
+ ret &= test_path_ex(cli, tctx, BASEDIR, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, ((char *)BASEDIR) + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, ((char *)BASEDIR"\\\\") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, ((char *)BASEDIR"\\foo\\..") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, ((char *)BASEDIR"\\f\\o\\o\\..\\..\\..") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, ((char *)BASEDIR"\\foo\\\\..\\\\") + 1, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, BASEDIR"\\", BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, BASEDIR"\\\\..\\"BASEDIR, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, BASEDIR"\\\\\\", BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, "\\\\\\\\"BASEDIR"\\\\\\\\", BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, "\\\\\\\\"BASEDIR, BASEDIR, NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path_ex(cli, tctx, BASEDIR "\\foo\\..\\test.txt..", BASEDIR "\\test.txt..",
+ NT_STATUS_NOT_A_DIRECTORY, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path_ex(cli, tctx, "", "\\", NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path(cli, ".", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "\\\\\\.\\", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "." BASEDIR, NT_STATUS_OBJECT_PATH_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\.", NT_STATUS_OBJECT_NAME_INVALID, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\.\\test.txt..", NT_STATUS_OBJECT_PATH_NOT_FOUND, NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\.\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\.\\.aaaaa", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "\\.\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "\\.\\\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "\\.\\\\\\\\\\\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ /* Note that the two following paths are identical but
+ give different NT status returns for chkpth and findfirst. */
+
+ printf("testing findfirst on %s\n", "\\.\\\\\\\\\\\\.");
+ status = single_search(cli, tctx, "\\.\\\\\\\\\\\\.");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRinvalidname));
+
+ ret &= test_path(cli, "\\.\\\\\\\\\\\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ /* We expect this open to fail with the same error code as the chkpath below. */
+ printf("testing Open on %s\n", "\\.\\\\\\\\\\\\.");
+ /* findfirst seems to fail with a different error. */
+ fnum1 = smbcli_nt_create_full(cli->tree, "\\.\\\\\\\\\\\\.",
+ 0, SEC_RIGHTS_FILE_ALL,
+ FILE_ATTRIBUTE_NORMAL,
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE,
+ NTCREATEX_DISP_OVERWRITE_IF,
+ 0, 0);
+ status = smbcli_nt_error(cli->tree);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+
+ ret &= test_path(cli, "\\.\\\\xxx", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "..\\..\\..", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath));
+ ret &= test_path(cli, "\\..", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath));
+ ret &= test_path(cli, "\\.\\\\\\\\\\\\xxx", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR"\\.\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR"\\.\\\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR"\\.\\nt", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR"\\.\\.\\nt", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR"\\nt", NT_STATUS_OK, NT_STATUS_OK);
+ ret &= test_path(cli, BASEDIR".\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR"xx\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\.", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, ".\\.\\.\\.\\foo\\.\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR".\\.\\.\\.\\foo\\.\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR".\\.\\.\\.\\foo\\..\\.\\", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR".", NT_STATUS_OBJECT_NAME_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "\\", NT_STATUS_OK,NT_STATUS_OK);
+ ret &= test_path(cli, "\\.", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, "\\..\\", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath));
+ ret &= test_path(cli, "\\..", NT_STATUS_OBJECT_PATH_SYNTAX_BAD,NT_STATUS_DOS(ERRDOS,ERRinvalidpath));
+ ret &= test_path(cli, BASEDIR "\\.", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path_ex(cli, tctx, BASEDIR "\\..", "\\", NT_STATUS_OK,NT_STATUS_OK);
+ ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb600", NT_STATUS_OBJECT_NAME_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb6.exe", NT_STATUS_NOT_A_DIRECTORY,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ /* We expect this open to fail with the same error code as the chkpath below. */
+ printf("testing Open on %s\n", BASEDIR".\\.\\.\\.\\foo\\..\\.\\");
+ /* findfirst seems to fail with a different error. */
+ fnum1 = smbcli_nt_create_full(cli->tree, BASEDIR".\\.\\.\\.\\foo\\..\\.\\",
+ 0, SEC_RIGHTS_FILE_ALL,
+ FILE_ATTRIBUTE_NORMAL,
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE,
+ NTCREATEX_DISP_OVERWRITE_IF,
+ 0, 0);
+ status = smbcli_nt_error(cli->tree);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ printf("testing findfirst on %s\n", BASEDIR".\\.\\.\\.\\foo\\..\\.\\");
+ status = single_search(cli, tctx, BASEDIR".\\.\\.\\.\\foo\\..\\.\\");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ /* We expect this open to fail with the same error code as the chkpath below. */
+ /* findfirst seems to fail with a different error. */
+ printf("testing Open on %s\n", BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3");
+ fnum1 = smbcli_nt_create_full(cli->tree, BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3",
+ 0, SEC_RIGHTS_FILE_ALL,
+ FILE_ATTRIBUTE_NORMAL,
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE,
+ NTCREATEX_DISP_OVERWRITE_IF,
+ 0, 0);
+ status = smbcli_nt_error(cli->tree);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+ ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\nt\\V S\\VB98\\vb6.exe\\3\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\nt\\3\\foo", NT_STATUS_OBJECT_PATH_NOT_FOUND,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\nt\\V S\\*\\vb6.exe\\3", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+ ret &= test_path(cli, BASEDIR "\\nt\\V S\\*\\*\\vb6.exe\\3", NT_STATUS_OBJECT_NAME_INVALID,NT_STATUS_DOS(ERRDOS,ERRbadpath));
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ basic testing of chkpath calls
+*/
+bool torture_raw_chkpath(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ int fnum;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR "\\nt"))) {
+ printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR "\\nt\\V S"))) {
+ printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR "\\nt\\V S\\VB98"))) {
+ printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ fnum = create_complex_file(cli, torture, BASEDIR "\\nt\\V S\\VB98\\vb6.exe");
+ if (fnum == -1) {
+ printf("failed to open \\nt\\V S\\VB98\\vb6.exe - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ ret &= test_chkpath(cli, torture);
+
+ done:
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/close.c b/source4/torture/raw/close.c
new file mode 100644
index 0000000000..01175836df
--- /dev/null
+++ b/source4/torture/raw/close.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_CLOSE_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "system/time.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+/**
+ * basic testing of all RAW_CLOSE_* calls
+*/
+bool torture_raw_close(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ union smb_close io;
+ union smb_flush io_flush;
+ int fnum;
+ const char *fname = "\\torture_close.txt";
+ time_t basetime = (time(NULL) + 3*86400) & ~1;
+ union smb_fileinfo finfo, finfo2;
+ NTSTATUS status;
+
+#define REOPEN do { \
+ fnum = create_complex_file(cli, torture, fname); \
+ if (fnum == -1) { \
+ printf("(%d) Failed to create %s\n", __LINE__, fname); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%d) Incorrect status %s - should be %s\n", \
+ __LINE__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+ REOPEN;
+
+ io.close.level = RAW_CLOSE_CLOSE;
+ io.close.in.file.fnum = fnum;
+ io.close.in.write_time = basetime;
+ status = smb_raw_close(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_close(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("testing close.in.write_time\n");
+
+ /* the file should have the write time set */
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, torture, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (basetime != nt_time_to_unix(finfo.all_info.out.write_time)) {
+ printf("Incorrect write time on file - %s - %s\n",
+ timestring(torture, basetime),
+ nt_time_string(torture, finfo.all_info.out.write_time));
+ dump_all_info(torture, &finfo);
+ ret = false;
+ }
+
+ printf("testing other times\n");
+
+ /* none of the other times should be set to that time */
+ if (nt_time_equal(&finfo.all_info.out.write_time,
+ &finfo.all_info.out.access_time) ||
+ nt_time_equal(&finfo.all_info.out.write_time,
+ &finfo.all_info.out.create_time) ||
+ nt_time_equal(&finfo.all_info.out.write_time,
+ &finfo.all_info.out.change_time)) {
+ printf("Incorrect times after close - only write time should be set\n");
+ dump_all_info(torture, &finfo);
+
+ if (!torture_setting_bool(torture, "samba3", false)) {
+ /*
+ * In Samba3 as of 3.0.23d we don't yet support all
+ * file times, so don't mark this as a critical
+ * failure
+ */
+ ret = false;
+ }
+ }
+
+
+ smbcli_unlink(cli->tree, fname);
+ REOPEN;
+
+ finfo2.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo2.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, torture, &finfo2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.close.level = RAW_CLOSE_CLOSE;
+ io.close.in.file.fnum = fnum;
+ io.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* the file should have the write time set equal to access time */
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, torture, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!nt_time_equal(&finfo.all_info.out.write_time,
+ &finfo2.all_info.out.write_time)) {
+ printf("Incorrect write time on file - 0 time should be ignored\n");
+ dump_all_info(torture, &finfo);
+ ret = false;
+ }
+
+ printf("testing splclose\n");
+
+ /* check splclose on a file */
+ REOPEN;
+ io.splclose.level = RAW_CLOSE_SPLCLOSE;
+ io.splclose.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror));
+
+ printf("testing flush\n");
+ smbcli_close(cli->tree, fnum);
+
+ io_flush.flush.level = RAW_FLUSH_FLUSH;
+ io_flush.flush.in.file.fnum = fnum;
+ status = smb_raw_flush(cli->tree, &io_flush);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ io_flush.flush_all.level = RAW_FLUSH_ALL;
+ status = smb_raw_flush(cli->tree, &io_flush);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ REOPEN;
+
+ io_flush.flush.level = RAW_FLUSH_FLUSH;
+ io_flush.flush.in.file.fnum = fnum;
+ status = smb_raw_flush(cli->tree, &io_flush);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Testing SMBexit\n");
+ smb_raw_exit(cli->session);
+
+ io_flush.flush.level = RAW_FLUSH_FLUSH;
+ io_flush.flush.in.file.fnum = fnum;
+ status = smb_raw_flush(cli->tree, &io_flush);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
diff --git a/source4/torture/raw/composite.c b/source4/torture/raw/composite.c
new file mode 100644
index 0000000000..d73ac1327e
--- /dev/null
+++ b/source4/torture/raw/composite.c
@@ -0,0 +1,424 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ libcli composite function testing
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "torture/torture.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "libcli/security/security.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/util.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+#define BASEDIR "\\composite"
+
+static void loadfile_complete(struct composite_context *c)
+{
+ int *count = talloc_get_type(c->async.private_data, int);
+ (*count)++;
+}
+
+/*
+ test a simple savefile/loadfile combination
+*/
+static bool test_loadfile(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ const char *fname = BASEDIR "\\test.txt";
+ NTSTATUS status;
+ struct smb_composite_savefile io1;
+ struct smb_composite_loadfile io2;
+ struct composite_context **c;
+ uint8_t *data;
+ size_t len = random() % 100000;
+ const int num_ops = 50;
+ int i;
+ int *count = talloc_zero(tctx, int);
+
+ data = talloc_array(tctx, uint8_t, len);
+
+ generate_random_buffer(data, len);
+
+ io1.in.fname = fname;
+ io1.in.data = data;
+ io1.in.size = len;
+
+ printf("testing savefile\n");
+
+ status = smb_composite_savefile(cli->tree, &io1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) savefile failed: %s\n", __location__,nt_errstr(status));
+ return false;
+ }
+
+ io2.in.fname = fname;
+
+ printf("testing parallel loadfile with %d ops\n", num_ops);
+
+ c = talloc_array(tctx, struct composite_context *, num_ops);
+
+ for (i=0;i<num_ops;i++) {
+ c[i] = smb_composite_loadfile_send(cli->tree, &io2);
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ printf("waiting for completion\n");
+ while (*count != num_ops) {
+ event_loop_once(cli->transport->socket->event.ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0;i<num_ops;i++) {
+ status = smb_composite_loadfile_recv(c[i], tctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) loadfile[%d] failed - %s\n", __location__, i, nt_errstr(status));
+ return false;
+ }
+
+ if (io2.out.size != len) {
+ printf("(%s) wrong length in returned data - %d should be %d\n",__location__,
+ io2.out.size, (int)len);
+ return false;
+ }
+
+ if (memcmp(io2.out.data, data, len) != 0) {
+ printf("(%s) wrong data in loadfile!\n",__location__);
+ return false;
+ }
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+/*
+ test a simple savefile/loadfile combination
+*/
+static bool test_fetchfile(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ const char *fname = BASEDIR "\\test.txt";
+ NTSTATUS status;
+ struct smb_composite_savefile io1;
+ struct smb_composite_fetchfile io2;
+ struct composite_context **c;
+ uint8_t *data;
+ int i;
+ size_t len = random() % 10000;
+ extern int torture_numops;
+ struct event_context *event_ctx;
+ int *count = talloc_zero(tctx, int);
+ bool ret = true;
+
+ data = talloc_array(tctx, uint8_t, len);
+
+ generate_random_buffer(data, len);
+
+ io1.in.fname = fname;
+ io1.in.data = data;
+ io1.in.size = len;
+
+ printf("testing savefile\n");
+
+ status = smb_composite_savefile(cli->tree, &io1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) savefile failed: %s\n",__location__, nt_errstr(status));
+ return false;
+ }
+
+ io2.in.dest_host = torture_setting_string(tctx, "host", NULL);
+ io2.in.ports = lp_smb_ports(tctx->lp_ctx);
+ io2.in.called_name = torture_setting_string(tctx, "host", NULL);
+ io2.in.service = torture_setting_string(tctx, "share", NULL);
+ io2.in.service_type = "A:";
+
+ io2.in.credentials = cmdline_credentials;
+ io2.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ io2.in.filename = fname;
+ io2.in.resolve_ctx = lp_resolve_context(tctx->lp_ctx);
+ lp_smbcli_options(tctx->lp_ctx, &io2.in.options);
+
+ printf("testing parallel fetchfile with %d ops\n", torture_numops);
+
+ event_ctx = cli->transport->socket->event.ctx;
+ c = talloc_array(tctx, struct composite_context *, torture_numops);
+
+ for (i=0; i<torture_numops; i++) {
+ c[i] = smb_composite_fetchfile_send(&io2, event_ctx);
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ printf("waiting for completion\n");
+
+ while (*count != torture_numops) {
+ event_loop_once(event_ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0;i<torture_numops;i++) {
+ status = smb_composite_fetchfile_recv(c[i], tctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) loadfile[%d] failed - %s\n", __location__, i,
+ nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ if (io2.out.size != len) {
+ printf("(%s) wrong length in returned data - %d "
+ "should be %d\n", __location__,
+ io2.out.size, (int)len);
+ ret = false;
+ continue;
+ }
+
+ if (memcmp(io2.out.data, data, len) != 0) {
+ printf("(%s) wrong data in loadfile!\n", __location__);
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ test setfileacl
+*/
+static bool test_appendacl(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ struct smb_composite_appendacl **io;
+ struct smb_composite_appendacl **io_orig;
+ struct composite_context **c;
+ struct event_context *event_ctx;
+
+ struct security_descriptor *test_sd;
+ struct security_ace *ace;
+ struct dom_sid *test_sid;
+
+ const int num_ops = 50;
+ int *count = talloc_zero(tctx, int);
+ struct smb_composite_savefile io1;
+
+ NTSTATUS status;
+ int i;
+
+ io_orig = talloc_array(tctx, struct smb_composite_appendacl *, num_ops);
+
+ printf ("creating %d empty files and getting their acls with appendacl\n", num_ops);
+
+ for (i = 0; i < num_ops; i++) {
+ io1.in.fname = talloc_asprintf(io_orig, BASEDIR "\\test%d.txt", i);
+ io1.in.data = NULL;
+ io1.in.size = 0;
+
+ status = smb_composite_savefile(cli->tree, &io1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) savefile failed: %s\n", __location__, nt_errstr(status));
+ return false;
+ }
+
+ io_orig[i] = talloc (io_orig, struct smb_composite_appendacl);
+ io_orig[i]->in.fname = talloc_steal(io_orig[i], io1.in.fname);
+ io_orig[i]->in.sd = security_descriptor_initialise(io_orig[i]);
+ status = smb_composite_appendacl(cli->tree, io_orig[i], io_orig[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) appendacl failed: %s\n", __location__, nt_errstr(status));
+ return false;
+ }
+ }
+
+
+ /* fill Security Descriptor with aces to be added */
+
+ test_sd = security_descriptor_initialise(tctx);
+ test_sid = dom_sid_parse_talloc (tctx, "S-1-5-32-1234-5432");
+
+ ace = talloc_zero(tctx, struct security_ace);
+
+ 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(test_sd, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) appendacl failed: %s\n", __location__, nt_errstr(status));
+ return false;
+ }
+
+ /* set parameters for appendacl async call */
+
+ printf("testing parallel appendacl with %d ops\n", num_ops);
+
+ c = talloc_array(tctx, struct composite_context *, num_ops);
+ io = talloc_array(tctx, struct smb_composite_appendacl *, num_ops);
+
+ for (i=0; i < num_ops; i++) {
+ io[i] = talloc (io, struct smb_composite_appendacl);
+ io[i]->in.sd = test_sd;
+ io[i]->in.fname = talloc_asprintf(io[i], BASEDIR "\\test%d.txt", i);
+
+ c[i] = smb_composite_appendacl_send(cli->tree, io[i]);
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ event_ctx = tctx->ev;
+ printf("waiting for completion\n");
+ while (*count != num_ops) {
+ event_loop_once(event_ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0; i < num_ops; i++) {
+ status = smb_composite_appendacl_recv(c[i], io[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) appendacl[%d] failed - %s\n", __location__, i, nt_errstr(status));
+ return false;
+ }
+
+ security_descriptor_dacl_add(io_orig[i]->out.sd, ace);
+ if (!security_acl_equal(io_orig[i]->out.sd->dacl, io[i]->out.sd->dacl)) {
+ printf("(%s) appendacl[%d] failed - needed acl isn't set\n", __location__, i);
+ return false;
+ }
+ }
+
+
+ talloc_free (ace);
+ talloc_free (test_sid);
+ talloc_free (test_sd);
+
+ return true;
+}
+
+/* test a query FS info by asking for share's GUID */
+static bool test_fsinfo(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ char *guid = NULL;
+ NTSTATUS status;
+ struct smb_composite_fsinfo io1;
+ struct composite_context **c;
+
+ int i;
+ extern int torture_numops;
+ struct event_context *event_ctx;
+ int *count = talloc_zero(tctx, int);
+ bool ret = true;
+
+ io1.in.dest_host = torture_setting_string(tctx, "host", NULL);
+ io1.in.dest_ports = lp_smb_ports(tctx->lp_ctx);
+ io1.in.called_name = torture_setting_string(tctx, "host", NULL);
+ io1.in.service = torture_setting_string(tctx, "share", NULL);
+ io1.in.service_type = "A:";
+ io1.in.credentials = cmdline_credentials;
+ io1.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ io1.in.level = RAW_QFS_OBJECTID_INFORMATION;
+
+ printf("testing parallel queryfsinfo [Object ID] with %d ops\n", torture_numops);
+
+ event_ctx = tctx->ev;
+ c = talloc_array(tctx, struct composite_context *, torture_numops);
+
+ for (i=0; i<torture_numops; i++) {
+ c[i] = smb_composite_fsinfo_send(cli->tree,&io1);
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ printf("waiting for completion\n");
+
+ while (*count < torture_numops) {
+ event_loop_once(event_ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0;i<torture_numops;i++) {
+ status = smb_composite_fsinfo_recv(c[i], tctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) fsinfo[%d] failed - %s\n", __location__, i, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ if (io1.out.fsinfo->generic.level != RAW_QFS_OBJECTID_INFORMATION) {
+ printf("(%s) wrong level in returned info - %d "
+ "should be %d\n", __location__,
+ io1.out.fsinfo->generic.level, RAW_QFS_OBJECTID_INFORMATION);
+ ret = false;
+ continue;
+ }
+
+ guid=GUID_string(tctx, &io1.out.fsinfo->objectid_information.out.guid);
+ printf("[%d] GUID: %s\n", i, guid);
+
+
+ }
+
+ return ret;
+}
+
+
+/*
+ basic testing of libcli composite calls
+*/
+bool torture_raw_composite(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_fetchfile(cli, tctx);
+ ret &= test_loadfile(cli, tctx);
+ ret &= test_appendacl(cli, tctx);
+ ret &= test_fsinfo(cli, tctx);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/context.c b/source4/torture/raw/context.c
new file mode 100644
index 0000000000..15e736fff5
--- /dev/null
+++ b/source4/torture/raw/context.c
@@ -0,0 +1,902 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for session setup operations
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+#define BASEDIR "\\rawcontext"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_NOT_VALUE(v, correct) do { \
+ if ((v) == (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should not be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+/*
+ test session ops
+*/
+static bool test_session(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct smbcli_session *session;
+ struct smbcli_session *session2;
+ struct smbcli_session *session3;
+ struct smbcli_session *session4;
+ struct cli_credentials *anon_creds;
+ struct smbcli_session *sessions[15];
+ struct composite_context *composite_contexts[15];
+ struct smbcli_tree *tree;
+ struct smb_composite_sesssetup setup;
+ struct smb_composite_sesssetup setups[15];
+ union smb_open io;
+ union smb_write wr;
+ union smb_close cl;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+ uint8_t c = 1;
+ int i;
+
+ printf("TESTING SESSION HANDLING\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("create a second security context on the same transport\n");
+ session = smbcli_session_init(cli->transport, tctx, false);
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+
+ status = smb_composite_sesssetup(session, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ session->vuid = setup.out.vuid;
+
+ printf("create a third security context on the same transport, with vuid set\n");
+ session2 = smbcli_session_init(cli->transport, tctx, false);
+
+ session2->vuid = session->vuid;
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+
+ status = smb_composite_sesssetup(session2, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ session2->vuid = setup.out.vuid;
+ printf("vuid1=%d vuid2=%d vuid3=%d\n", cli->session->vuid, session->vuid, session2->vuid);
+
+ if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) {
+ /* Samba4 currently fails this - we need to determine if this insane behaviour is important */
+ if (session2->vuid == session->vuid) {
+ printf("server allows the user to re-use an existing vuid in session setup \n");
+ }
+ } else {
+ CHECK_NOT_VALUE(session2->vuid, session->vuid);
+ }
+ talloc_free(session2);
+
+ if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) {
+ printf("create a fourth security context on the same transport, without extended security\n");
+ session3 = smbcli_session_init(cli->transport, tctx, false);
+
+ session3->vuid = session->vuid;
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; /* force a non extended security login (should fail) */
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+
+
+ status = smb_composite_sesssetup(session3, &setup);
+ CHECK_STATUS(status, NT_STATUS_LOGON_FAILURE);
+
+ printf("create a fouth anonymous security context on the same transport, without extended security\n");
+ session4 = smbcli_session_init(cli->transport, tctx, false);
+
+ session4->vuid = session->vuid;
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; /* force a non extended security login (should fail) */
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ anon_creds = cli_credentials_init(tctx);
+ cli_credentials_set_conf(anon_creds, tctx->lp_ctx);
+ cli_credentials_set_anonymous(anon_creds);
+
+ setup.in.credentials = anon_creds;
+
+ status = smb_composite_sesssetup(session3, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ talloc_free(session4);
+ }
+
+ printf("use the same tree as the existing connection\n");
+ tree = smbcli_tree_init(session, tctx, false);
+ tree->tid = cli->tree->tid;
+
+ printf("create a file using the new vuid\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("write using the old vuid\n");
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("write with the new vuid\n");
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("logoff the new vuid\n");
+ status = smb_raw_ulogoff(session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the new vuid should not now be accessible\n");
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("second logoff for the new vuid should fail\n");
+ status = smb_raw_ulogoff(session);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRbaduid));
+ talloc_free(session);
+
+ printf("the fnum should have been auto-closed\n");
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("create %d secondary security contexts on the same transport\n",
+ (int)ARRAY_SIZE(sessions));
+ for (i=0; i <ARRAY_SIZE(sessions); i++) {
+ setups[i].in.sesskey = cli->transport->negotiate.sesskey;
+ setups[i].in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */
+ setups[i].in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ setups[i].in.credentials = cmdline_credentials;
+
+ sessions[i] = smbcli_session_init(cli->transport, tctx, false);
+ composite_contexts[i] = smb_composite_sesssetup_send(sessions[i], &setups[i]);
+
+ }
+
+
+ printf("finishing %d secondary security contexts on the same transport\n",
+ (int)ARRAY_SIZE(sessions));
+ for (i=0; i< ARRAY_SIZE(sessions); i++) {
+ status = smb_composite_sesssetup_recv(composite_contexts[i]);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sessions[i]->vuid = setups[i].out.vuid;
+ printf("VUID: %d\n", sessions[i]->vuid);
+ status = smb_raw_ulogoff(sessions[i]);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+
+ talloc_free(tree);
+
+done:
+ return ret;
+}
+
+
+/*
+ test tree ops
+*/
+static bool test_tree(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ const char *share, *host;
+ struct smbcli_tree *tree;
+ union smb_tcon tcon;
+ union smb_open io;
+ union smb_write wr;
+ union smb_close cl;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+ uint8_t c = 1;
+
+ printf("TESTING TREE HANDLING\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+
+ printf("create a second tree context on the same session\n");
+ tree = smbcli_tree_init(cli->session, tctx, false);
+
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
+ tcon.tconx.in.device = "A:";
+ status = smb_raw_tcon(tree, tctx, &tcon);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+
+ tree->tid = tcon.tconx.out.tid;
+ printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
+
+ printf("try a tconx with a bad device type\n");
+ tcon.tconx.in.device = "FOO";
+ status = smb_raw_tcon(tree, tctx, &tcon);
+ CHECK_STATUS(status, NT_STATUS_BAD_DEVICE_TYPE);
+
+
+ printf("create a file using the new tid\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("write using the old tid\n");
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("write with the new tid\n");
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("disconnect the new tid\n");
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the new tid should not now be accessible\n");
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("the fnum should have been auto-closed\n");
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ /* close down the new tree */
+ talloc_free(tree);
+
+done:
+ return ret;
+}
+
+/*
+ test tree with ulogoff
+ this demonstrates that a tcon isn't autoclosed by a ulogoff
+ the tcon can be reused using any other valid session later
+*/
+static bool test_tree_ulogoff(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ const char *share, *host;
+ struct smbcli_session *session1;
+ struct smbcli_session *session2;
+ struct smb_composite_sesssetup setup;
+ struct smbcli_tree *tree;
+ union smb_tcon tcon;
+ union smb_open io;
+ union smb_write wr;
+ int fnum1, fnum2;
+ const char *fname1 = BASEDIR "\\test1.txt";
+ const char *fname2 = BASEDIR "\\test2.txt";
+ uint8_t c = 1;
+
+ printf("TESTING TREE with ulogoff\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+
+ printf("create the first new sessions\n");
+ session1 = smbcli_session_init(cli->transport, tctx, false);
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ status = smb_composite_sesssetup(session1, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session1->vuid = setup.out.vuid;
+ printf("vuid1=%d\n", session1->vuid);
+
+ printf("create a tree context on the with vuid1\n");
+ tree = smbcli_tree_init(session1, tctx, false);
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
+ tcon.tconx.in.device = "A:";
+ status = smb_raw_tcon(tree, tctx, &tcon);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ tree->tid = tcon.tconx.out.tid;
+ printf("tid=%d\n", tree->tid);
+
+ printf("create a file using vuid1\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+ status = smb_raw_open(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ printf("write using vuid1\n");
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum1;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("ulogoff the vuid1\n");
+ status = smb_raw_ulogoff(session1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("create the second new sessions\n");
+ session2 = smbcli_session_init(cli->transport, tctx, false);
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ status = smb_composite_sesssetup(session2, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session2->vuid = setup.out.vuid;
+ printf("vuid2=%d\n", session2->vuid);
+
+ printf("use the existing tree with vuid2\n");
+ tree->session = session2;
+
+ printf("create a file using vuid2\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname2;
+ status = smb_raw_open(tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ printf("write using vuid2\n");
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum2;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("ulogoff the vuid2\n");
+ status = smb_raw_ulogoff(session2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* this also demonstrates that SMBtdis doesn't need a valid vuid */
+ printf("disconnect the existing tree connection\n");
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("disconnect the existing tree connection\n");
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV,ERRinvnid));
+
+ /* close down the new tree */
+ talloc_free(tree);
+
+done:
+ return ret;
+}
+
+/*
+ test pid ops
+ this test demonstrates that exit() only sees the PID
+ used for the open() calls
+*/
+static bool test_pid_exit_only_sees_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_write wr;
+ union smb_close cl;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+ uint8_t c = 1;
+ uint16_t pid1, pid2;
+
+ printf("TESTING PID HANDLING exit() only cares about open() PID\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ pid1 = cli->session->pid;
+ pid2 = pid1 + 1;
+
+ printf("pid1=%d pid2=%d\n", pid1, pid2);
+
+ printf("create a file using pid1\n");
+ cli->session->pid = pid1;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("write using pid2\n");
+ cli->session->pid = pid2;
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("exit pid2\n");
+ cli->session->pid = pid2;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum should still be accessible via pid2\n");
+ cli->session->pid = pid2;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("exit pid2\n");
+ cli->session->pid = pid2;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum should still be accessible via pid1 and pid2\n");
+ cli->session->pid = pid1;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+ cli->session->pid = pid2;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("exit pid1\n");
+ cli->session->pid = pid1;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum should not now be accessible via pid1 or pid2\n");
+ cli->session->pid = pid1;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+ cli->session->pid = pid2;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("the fnum should have been auto-closed\n");
+ cli->session->pid = pid1;
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+done:
+ return ret;
+}
+
+/*
+ test pid ops with 2 sessions
+*/
+static bool test_pid_2sess(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct smbcli_session *session;
+ struct smb_composite_sesssetup setup;
+ union smb_open io;
+ union smb_write wr;
+ union smb_close cl;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+ uint8_t c = 1;
+ uint16_t vuid1, vuid2;
+
+ printf("TESTING PID HANDLING WITH 2 SESSIONS\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("create a second security context on the same transport\n");
+ session = smbcli_session_init(cli->transport, tctx, false);
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities; /* ignored in secondary session setup, except by our libs, which care about the extended security bit */
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+
+ status = smb_composite_sesssetup(session, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session->vuid = setup.out.vuid;
+
+ vuid1 = cli->session->vuid;
+ vuid2 = session->vuid;
+
+ printf("vuid1=%d vuid2=%d\n", vuid1, vuid2);
+
+ printf("create a file using the vuid1\n");
+ cli->session->vuid = vuid1;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("write using the vuid1 (fnum=%d)\n", fnum);
+ cli->session->vuid = vuid1;
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("exit the pid with vuid2\n");
+ cli->session->vuid = vuid2;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum should still be accessible\n");
+ cli->session->vuid = vuid1;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("exit the pid with vuid1\n");
+ cli->session->vuid = vuid1;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum should not now be accessible\n");
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("the fnum should have been auto-closed\n");
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+done:
+ return ret;
+}
+
+/*
+ test pid ops with 2 tcons
+*/
+static bool test_pid_2tcon(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ const char *share, *host;
+ struct smbcli_tree *tree;
+ union smb_tcon tcon;
+ union smb_open io;
+ union smb_write wr;
+ union smb_close cl;
+ int fnum1, fnum2;
+ const char *fname1 = BASEDIR "\\test1.txt";
+ const char *fname2 = BASEDIR "\\test2.txt";
+ uint8_t c = 1;
+ uint16_t tid1, tid2;
+
+ printf("TESTING PID HANDLING WITH 2 TCONS\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+
+ printf("create a second tree context on the same session\n");
+ tree = smbcli_tree_init(cli->session, tctx, false);
+
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
+ tcon.tconx.in.device = "A:";
+ status = smb_raw_tcon(tree, tctx, &tcon);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ tree->tid = tcon.tconx.out.tid;
+
+ tid1 = cli->tree->tid;
+ tid2 = tree->tid;
+ printf("tid1=%d tid2=%d\n", tid1, tid2);
+
+ printf("create a file using the tid1\n");
+ cli->tree->tid = tid1;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ printf("write using the tid1\n");
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum1;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("create a file using the tid2\n");
+ cli->tree->tid = tid2;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname2;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ printf("write using the tid2\n");
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum2;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = 1;
+ wr.writex.in.data = &c;
+
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+ printf("exit the pid\n");
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum1 on tid1 should not be accessible\n");
+ cli->tree->tid = tid1;
+ wr.writex.in.file.fnum = fnum1;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("the fnum1 on tid1 should have been auto-closed\n");
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum1;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("the fnum2 on tid2 should not be accessible\n");
+ cli->tree->tid = tid2;
+ wr.writex.in.file.fnum = fnum2;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("the fnum2 on tid2 should have been auto-closed\n");
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum2;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+done:
+ return ret;
+}
+
+
+/*
+ basic testing of session/tree context calls
+*/
+static bool torture_raw_context_int(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ ret &= test_session(cli, tctx);
+ ret &= test_tree(cli, tctx);
+ ret &= test_tree_ulogoff(cli, tctx);
+ ret &= test_pid_exit_only_sees_open(cli, tctx);
+ ret &= test_pid_2sess(cli, tctx);
+ ret &= test_pid_2tcon(cli, tctx);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+/*
+ basic testing of session/tree context calls
+*/
+bool torture_raw_context(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ if (lp_use_spnego(torture->lp_ctx)) {
+ ret &= torture_raw_context_int(torture, cli);
+ lp_set_cmdline(torture->lp_ctx, "use spnego", "False");
+ }
+
+ ret &= torture_raw_context_int(torture, cli);
+
+ return ret;
+}
diff --git a/source4/torture/raw/eas.c b/source4/torture/raw/eas.c
new file mode 100644
index 0000000000..bc4473b17a
--- /dev/null
+++ b/source4/torture/raw/eas.c
@@ -0,0 +1,490 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test DOS extended attributes
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Guenter Kukkukk 2005
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+#define BASEDIR "\\testeas"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+static bool maxeadebug; /* need that here, to allow no file delete in debug case */
+
+static bool check_ea(struct smbcli_state *cli,
+ const char *fname, const char *eaname, const char *value)
+{
+ NTSTATUS status = torture_check_ea(cli, fname, eaname, value);
+ return NT_STATUS_IS_OK(status);
+}
+
+static bool test_eas(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_setfileinfo setfile;
+ union smb_open io;
+ const char *fname = BASEDIR "\\ea.txt";
+ bool ret = true;
+ int fnum = -1;
+
+ printf("TESTING SETFILEINFO EA_SET\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ ret &= check_ea(cli, fname, "EAONE", NULL);
+
+ printf("Adding first two EAs\n");
+ setfile.generic.level = RAW_SFILEINFO_EA_SET;
+ setfile.generic.in.file.fnum = fnum;
+ setfile.ea_set.in.num_eas = 2;
+ setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
+ setfile.ea_set.in.eas[0].flags = 0;
+ setfile.ea_set.in.eas[0].name.s = "EAONE";
+ setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE1");
+ setfile.ea_set.in.eas[1].flags = 0;
+ setfile.ea_set.in.eas[1].name.s = "SECONDEA";
+ setfile.ea_set.in.eas[1].value = data_blob_string_const("ValueTwo");
+
+ status = smb_raw_setfileinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= check_ea(cli, fname, "EAONE", "VALUE1");
+ ret &= check_ea(cli, fname, "SECONDEA", "ValueTwo");
+
+ printf("Modifying 2nd EA\n");
+ setfile.ea_set.in.num_eas = 1;
+ setfile.ea_set.in.eas[0].name.s = "SECONDEA";
+ setfile.ea_set.in.eas[0].value = data_blob_string_const(" Changed Value");
+ status = smb_raw_setfileinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= check_ea(cli, fname, "EAONE", "VALUE1");
+ ret &= check_ea(cli, fname, "SECONDEA", " Changed Value");
+
+ printf("Setting a NULL EA\n");
+ setfile.ea_set.in.eas[0].value = data_blob(NULL, 0);
+ setfile.ea_set.in.eas[0].name.s = "NULLEA";
+ status = smb_raw_setfileinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= check_ea(cli, fname, "EAONE", "VALUE1");
+ ret &= check_ea(cli, fname, "SECONDEA", " Changed Value");
+ ret &= check_ea(cli, fname, "NULLEA", NULL);
+
+ printf("Deleting first EA\n");
+ setfile.ea_set.in.eas[0].flags = 0;
+ setfile.ea_set.in.eas[0].name.s = "EAONE";
+ setfile.ea_set.in.eas[0].value = data_blob(NULL, 0);
+ status = smb_raw_setfileinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= check_ea(cli, fname, "EAONE", NULL);
+ ret &= check_ea(cli, fname, "SECONDEA", " Changed Value");
+
+ printf("Deleting second EA\n");
+ setfile.ea_set.in.eas[0].flags = 0;
+ setfile.ea_set.in.eas[0].name.s = "SECONDEA";
+ setfile.ea_set.in.eas[0].value = data_blob(NULL, 0);
+ status = smb_raw_setfileinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= check_ea(cli, fname, "EAONE", NULL);
+ ret &= check_ea(cli, fname, "SECONDEA", NULL);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+
+/*
+ * Helper function to retrieve the max. ea size for one ea name
+ */
+static int test_one_eamax(struct smbcli_state *cli, const int fnum,
+ const char *eaname, DATA_BLOB eablob,
+ const int eastart, const int eadebug)
+{
+ NTSTATUS status;
+ struct ea_struct eastruct;
+ union smb_setfileinfo setfile;
+ int i, high, low, maxeasize;
+
+ setfile.generic.level = RAW_SFILEINFO_EA_SET;
+ setfile.generic.in.file.fnum = fnum;
+ setfile.ea_set.in.num_eas = 1;
+ setfile.ea_set.in.eas = &eastruct;
+ setfile.ea_set.in.eas->flags = 0;
+ setfile.ea_set.in.eas->name.s = eaname;
+ setfile.ea_set.in.eas->value = eablob;
+
+ maxeasize = eablob.length;
+ i = eastart;
+ low = 0;
+ high = maxeasize;
+
+ do {
+ if (eadebug) {
+ printf ("Testing EA size: %d\n", i);
+ }
+ setfile.ea_set.in.eas->value.length = i;
+
+ status = smb_raw_setfileinfo(cli->tree, &setfile);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ if (eadebug) {
+ printf ("[%s] EA size %d succeeded! "
+ "(high=%d low=%d)\n",
+ eaname, i, high, low);
+ }
+ low = i;
+ if (low == maxeasize) {
+ printf ("Max. EA size for \"%s\"=%d "
+ "[but could be possibly larger]\n",
+ eaname, low);
+ break;
+ }
+ if (high - low == 1 && high != maxeasize) {
+ printf ("Max. EA size for \"%s\"=%d\n",
+ eaname, low);
+ break;
+ }
+ i += (high - low + 1) / 2;
+ } else {
+ if (eadebug) {
+ printf ("[%s] EA size %d failed! "
+ "(high=%d low=%d) [%s]\n",
+ eaname, i, high, low,
+ nt_errstr(status));
+ }
+ high = i;
+ if (high - low <= 1) {
+ printf ("Max. EA size for \"%s\"=%d\n",
+ eaname, low);
+ break;
+ }
+ i -= (high - low + 1) / 2;
+ }
+ } while (true);
+
+ return low;
+}
+
+/*
+ * Test for maximum ea size - more than one ea name is checked.
+ *
+ * Additional parameters can be passed, to allow further testing:
+ *
+ * default
+ * maxeasize 65536 limit the max. size for a single EA name
+ * maxeanames 101 limit of the number of tested names
+ * maxeastart 1 this EA size is used to test for the 1st EA (atm)
+ * maxeadebug 0 if set true, further debug output is done - in addition
+ * the testfile is not deleted for further inspection!
+ *
+ * Set some/all of these options on the cmdline with:
+ * --option torture:maxeasize=1024 --option torture:maxeadebug=1 ...
+ *
+ */
+static bool test_max_eas(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\ea_max.txt";
+ int fnum = -1;
+ bool ret = true;
+ bool err = false;
+
+ int i, j, k, last, total;
+ DATA_BLOB eablob;
+ char *eaname = NULL;
+ int maxeasize;
+ int maxeanames;
+ int maxeastart;
+
+ printf("TESTING SETFILEINFO MAX. EA_SET\n");
+
+ maxeasize = torture_setting_int(tctx, "maxeasize", 65536);
+ maxeanames = torture_setting_int(tctx, "maxeanames", 101);
+ maxeastart = torture_setting_int(tctx, "maxeastart", 1);
+ maxeadebug = torture_setting_int(tctx, "maxeadebug", 0);
+
+ /* Do some sanity check on possibly passed parms */
+ if (maxeasize <= 0) {
+ printf("Invalid parameter 'maxeasize=%d'",maxeasize);
+ err = true;
+ }
+ if (maxeanames <= 0) {
+ printf("Invalid parameter 'maxeanames=%d'",maxeanames);
+ err = true;
+ }
+ if (maxeastart <= 0) {
+ printf("Invalid parameter 'maxeastart=%d'",maxeastart);
+ err = true;
+ }
+ if (maxeadebug < 0) {
+ printf("Invalid parameter 'maxeadebug=%d'",maxeadebug);
+ err = true;
+ }
+ if (err) {
+ printf("\n\n");
+ goto done;
+ }
+ if (maxeastart > maxeasize) {
+ maxeastart = maxeasize;
+ printf ("'maxeastart' outside range - corrected to %d\n",
+ maxeastart);
+ }
+ printf("MAXEA parms: maxeasize=%d maxeanames=%d maxeastart=%d"
+ " maxeadebug=%d\n", maxeasize, maxeanames, maxeastart,
+ maxeadebug);
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ eablob = data_blob_talloc(tctx, NULL, maxeasize);
+ if (eablob.data == NULL) {
+ goto done;
+ }
+ /*
+ * Fill in some EA data - the offset could be easily checked
+ * during a hexdump.
+ */
+ for (i = 0, k = 0; i < eablob.length / 4; i++, k+=4) {
+ eablob.data[k] = k & 0xff;
+ eablob.data[k+1] = (k >> 8) & 0xff;
+ eablob.data[k+2] = (k >> 16) & 0xff;
+ eablob.data[k+3] = (k >> 24) & 0xff;
+ }
+
+ i = eablob.length % 4;
+ if (i-- > 0) {
+ eablob.data[k] = k & 0xff;
+ if (i-- > 0) {
+ eablob.data[k+1] = (k >> 8) & 0xff;
+ if (i-- > 0) {
+ eablob.data[k+2] = (k >> 16) & 0xff;
+ }
+ }
+ }
+ /*
+ * Filesystems might allow max. EAs data for different EA names.
+ * So more than one EA name should be checked.
+ */
+ total = 0;
+ last = maxeastart;
+
+ for (i = 0; i < maxeanames; i++) {
+ if (eaname != NULL) {
+ talloc_free(eaname);
+ }
+ eaname = talloc_asprintf(tctx, "MAX%d", i);
+ if(eaname == NULL) {
+ goto done;
+ }
+ j = test_one_eamax(cli, fnum, eaname, eablob, last, maxeadebug);
+ if (j <= 0) {
+ break;
+ }
+ total += j;
+ last = j;
+ }
+
+ printf("Total EA size:%d\n", total);
+ if (i == maxeanames) {
+ printf ("NOTE: More EAs could be available!\n");
+ }
+ if (total == 0) {
+ ret = false;
+ }
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ test using NTTRANS CREATE to create a file with an initial EA set
+*/
+static bool test_nttrans_create(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\ea2.txt";
+ bool ret = true;
+ int fnum = -1;
+ struct ea_struct eas[3];
+ struct smb_ea_list ea_list;
+
+ printf("TESTING NTTRANS CREATE WITH EAS\n");
+
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ ea_list.num_eas = 3;
+ ea_list.eas = eas;
+
+ eas[0].flags = 0;
+ eas[0].name.s = "1st EA";
+ eas[0].value = data_blob_string_const("Value One");
+
+ eas[1].flags = 0;
+ eas[1].name.s = "2nd EA";
+ eas[1].value = data_blob_string_const("Second Value");
+
+ eas[2].flags = 0;
+ eas[2].name.s = "and 3rd";
+ eas[2].value = data_blob_string_const("final value");
+
+ io.ntcreatex.in.ea_list = &ea_list;
+ io.ntcreatex.in.sec_desc = NULL;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ ret &= check_ea(cli, fname, "EAONE", NULL);
+ ret &= check_ea(cli, fname, "1st EA", "Value One");
+ ret &= check_ea(cli, fname, "2nd EA", "Second Value");
+ ret &= check_ea(cli, fname, "and 3rd", "final value");
+
+ smbcli_close(cli->tree, fnum);
+
+ printf("Trying to add EAs on non-create\n");
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.fname = fname;
+
+ ea_list.num_eas = 1;
+ eas[0].flags = 0;
+ eas[0].name.s = "Fourth EA";
+ eas[0].value = data_blob_string_const("Value Four");
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ ret &= check_ea(cli, fname, "1st EA", "Value One");
+ ret &= check_ea(cli, fname, "2nd EA", "Second Value");
+ ret &= check_ea(cli, fname, "and 3rd", "final value");
+ ret &= check_ea(cli, fname, "Fourth EA", NULL);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ basic testing of EA calls
+*/
+bool torture_raw_eas(struct torture_context *torture, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_eas(cli, torture);
+ ret &= test_nttrans_create(cli, torture);
+
+ smb_raw_exit(cli->session);
+
+ return ret;
+}
+
+/*
+ test max EA size
+*/
+bool torture_max_eas(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ bool ret = true;
+
+ if (!torture_open_connection(&cli, torture, 0)) {
+ return false;
+ }
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_max_eas(cli, torture);
+
+ smb_raw_exit(cli->session);
+ if (!maxeadebug) {
+ /* in no ea debug case, all files are gone now */
+ smbcli_deltree(cli->tree, BASEDIR);
+ }
+
+ torture_close_connection(cli);
+ return ret;
+}
diff --git a/source4/torture/raw/ioctl.c b/source4/torture/raw/ioctl.c
new file mode 100644
index 0000000000..4cb366f807
--- /dev/null
+++ b/source4/torture/raw/ioctl.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ ioctl individual test suite
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ 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 "torture/torture.h"
+#include "libcli/raw/ioctl.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\rawioctl"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%d) Incorrect status %s - should be %s\n", \
+ __LINE__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+/* test some ioctls */
+static bool test_ioctl(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ union smb_ioctl ctl;
+ int fnum;
+ NTSTATUS status;
+ bool ret = true;
+ const char *fname = BASEDIR "\\test.dat";
+
+ printf("TESTING IOCTL FUNCTIONS\n");
+
+ fnum = create_complex_file(cli, mem_ctx, fname);
+ if (fnum == -1) {
+ printf("Failed to create test.dat - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying 0xFFFF\n");
+ ctl.ioctl.level = RAW_IOCTL_IOCTL;
+ ctl.ioctl.in.file.fnum = fnum;
+ ctl.ioctl.in.request = 0xFFFF;
+
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror));
+
+ printf("Trying QUERY_JOB_INFO\n");
+ ctl.ioctl.level = RAW_IOCTL_IOCTL;
+ ctl.ioctl.in.file.fnum = fnum;
+ ctl.ioctl.in.request = IOCTL_QUERY_JOB_INFO;
+
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror));
+
+ printf("Trying bad handle\n");
+ ctl.ioctl.in.file.fnum = fnum+1;
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRerror));
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/* test some filesystem control functions */
+static bool test_fsctl(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ int fnum;
+ NTSTATUS status;
+ bool ret = true;
+ const char *fname = BASEDIR "\\test.dat";
+ union smb_ioctl nt;
+
+ printf("\nTESTING FSCTL FUNCTIONS\n");
+
+ fnum = create_complex_file(cli, mem_ctx, fname);
+ if (fnum == -1) {
+ printf("Failed to create test.dat - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("trying sparse file\n");
+ nt.ioctl.level = RAW_IOCTL_NTIOCTL;
+ nt.ntioctl.in.function = FSCTL_SET_SPARSE;
+ nt.ntioctl.in.file.fnum = fnum;
+ nt.ntioctl.in.fsctl = true;
+ nt.ntioctl.in.filter = 0;
+ nt.ntioctl.in.max_data = 0;
+ nt.ntioctl.in.blob = data_blob(NULL, 0);
+
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &nt);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("trying batch oplock\n");
+ nt.ioctl.level = RAW_IOCTL_NTIOCTL;
+ nt.ntioctl.in.function = FSCTL_REQUEST_BATCH_OPLOCK;
+ nt.ntioctl.in.file.fnum = fnum;
+ nt.ntioctl.in.fsctl = true;
+ nt.ntioctl.in.filter = 0;
+ nt.ntioctl.in.max_data = 0;
+ nt.ntioctl.in.blob = data_blob(NULL, 0);
+
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &nt);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("Server supports batch oplock upgrades on open files\n");
+ } else {
+ printf("Server does not support batch oplock upgrades on open files\n");
+ }
+
+ printf("Trying bad handle\n");
+ nt.ntioctl.in.file.fnum = fnum+1;
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &nt);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+#if 0
+ nt.ntioctl.in.file.fnum = fnum;
+ for (i=0;i<100;i++) {
+ nt.ntioctl.in.function = FSCTL_FILESYSTEM + (i<<2);
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &nt);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("filesystem fsctl 0x%x - %s\n",
+ i, nt_errstr(status));
+ }
+ }
+#endif
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ basic testing of some ioctl calls
+*/
+bool torture_raw_ioctl(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_ioctl(cli, torture);
+ ret &= test_fsctl(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c
new file mode 100644
index 0000000000..cd8d606795
--- /dev/null
+++ b/source4/torture/raw/lock.c
@@ -0,0 +1,1336 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for various lock operations
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define BASEDIR "\\testlock"
+
+
+/*
+ test SMBlock and SMBunlock ops
+*/
+static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ union smb_lock io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_LOCK_LOCK\n");
+ io.generic.level = RAW_LOCK_LOCK;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying 0/0 lock\n");
+ io.lock.level = RAW_LOCK_LOCK;
+ io.lock.in.file.fnum = fnum;
+ io.lock.in.count = 0;
+ io.lock.in.offset = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid--;
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying 0/1 lock\n");
+ io.lock.level = RAW_LOCK_LOCK;
+ io.lock.in.file.fnum = fnum;
+ io.lock.in.count = 1;
+ io.lock.in.offset = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ cli->session->pid--;
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying 0xEEFFFFFF lock\n");
+ io.lock.level = RAW_LOCK_LOCK;
+ io.lock.in.file.fnum = fnum;
+ io.lock.in.count = 4000;
+ io.lock.in.offset = 0xEEFFFFFF;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ cli->session->pid--;
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying 0xEF000000 lock\n");
+ io.lock.level = RAW_LOCK_LOCK;
+ io.lock.in.file.fnum = fnum;
+ io.lock.in.count = 4000;
+ io.lock.in.offset = 0xEEFFFFFF;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+ cli->session->pid--;
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying max lock\n");
+ io.lock.level = RAW_LOCK_LOCK;
+ io.lock.in.file.fnum = fnum;
+ io.lock.in.count = 4000;
+ io.lock.in.offset = 0xEF000000;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+ cli->session->pid--;
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying wrong pid unlock\n");
+ io.lock.level = RAW_LOCK_LOCK;
+ io.lock.in.file.fnum = fnum;
+ io.lock.in.count = 4002;
+ io.lock.in.offset = 10001;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->session->pid++;
+ io.lock.level = RAW_LOCK_UNLOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+ cli->session->pid--;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test locking&X ops
+*/
+static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ union smb_lock io;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_LOCK_LOCKX\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 10;
+ lock[0].count = 1;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+
+ printf("Trying 0xEEFFFFFF lock\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 4000;
+ lock[0].offset = 0xEEFFFFFF;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying 0xEF000000 lock\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 4000;
+ lock[0].offset = 0xEF000000;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying zero lock\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 0;
+ lock[0].offset = ~0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying max lock\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 0;
+ lock[0].offset = ~0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying 2^63\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 1;
+ lock[0].offset = 1;
+ lock[0].offset <<= 63;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying 2^63 - 1\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 1;
+ lock[0].offset = 1;
+ lock[0].offset <<= 63;
+ lock[0].offset--;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ printf("Trying max lock 2\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].count = 1;
+ lock[0].offset = ~0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid++;
+ lock[0].count = 2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ lock[0].pid--;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ lock[0].count = 1;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test high pid
+*/
+static bool test_pidhigh(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_lock io;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+ uint8_t c = 1;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing high pid\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ cli->session->pid = 1;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ if (smbcli_write(cli->tree, fnum, 0, &c, 0, 1) != 1) {
+ printf("Failed to write 1 byte - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 0;
+ lock[0].count = 0xFFFFFFFF;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) {
+ printf("Failed to read 1 byte - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ cli->session->pid |= 0x10000;
+
+ cli->session->pid = 2;
+
+ if (smbcli_read(cli->tree, fnum, &c, 0, 1) == 1) {
+ printf("pid is incorrect handled for read with lock!\n");
+ ret = false;
+ goto done;
+ }
+
+ cli->session->pid = 0x10001;
+
+ if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) {
+ printf("High pid is used on this server!\n");
+ ret = false;
+ } else {
+ printf("High pid is not used on this server (correct)\n");
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test locking&X async operation
+*/
+static bool test_async(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ struct smbcli_session *session;
+ struct smb_composite_sesssetup setup;
+ struct smbcli_tree *tree;
+ union smb_tcon tcon;
+ const char *host, *share;
+ union smb_lock io;
+ struct smb_lock_entry lock[2];
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ const char *fname = BASEDIR "\\test.txt";
+ time_t t;
+ struct smbcli_request *req;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing LOCKING_ANDX_CANCEL_LOCK\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ t = time(NULL);
+
+ printf("testing cancel by CANCEL_LOCK\n");
+
+ /* setup a timed lock */
+ io.lockx.in.timeout = 10000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ /* cancel the wrong range */
+ lock[0].offset = 0;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation));
+
+ /* cancel with the wrong bits set */
+ lock[0].offset = 100;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation));
+
+ /* cancel the right range */
+ lock[0].offset = 100;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* receive the failed lock request */
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("testing cancel by unlock\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.timeout = 5000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ t = time(NULL);
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by unlock was not immediate (%s) - took %d secs\n",
+ __location__, (int)(time(NULL)-t));
+ ret = false;
+ goto done;
+ }
+
+ printf("testing cancel by close\n");
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ t = time(NULL);
+ io.lockx.in.timeout = 10000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smbcli_close(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by close was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("create a new sessions\n");
+ session = smbcli_session_init(cli->transport, tctx, false);
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ status = smb_composite_sesssetup(session, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session->vuid = setup.out.vuid;
+
+ printf("create new tree context\n");
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+ tree = smbcli_tree_init(session, tctx, false);
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
+ tcon.tconx.in.device = "A:";
+ status = smb_raw_tcon(tree, tctx, &tcon);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ tree->tid = tcon.tconx.out.tid;
+
+ printf("testing cancel by exit\n");
+ fname = BASEDIR "\\test_exit.txt";
+ fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(tree));
+ ret = false;
+ goto done;
+ }
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ io.lockx.in.timeout = 10000;
+ t = time(NULL);
+ req = smb_raw_lock_send(tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_raw_exit(session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by exit was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("testing cancel by ulogoff\n");
+ fname = BASEDIR "\\test_ulogoff.txt";
+ fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(tree));
+ ret = false;
+ goto done;
+ }
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ io.lockx.in.timeout = 10000;
+ t = time(NULL);
+ req = smb_raw_lock_send(tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_raw_ulogoff(session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ if (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) {
+ printf("lock not canceled by ulogoff - %s (ignored because of vfs_vifs fails it)\n",
+ nt_errstr(status));
+ smb_tree_disconnect(tree);
+ smb_raw_exit(session);
+ goto done;
+ }
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by ulogoff was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("testing cancel by tdis\n");
+ tree->session = cli->session;
+
+ fname = BASEDIR "\\test_tdis.txt";
+ fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(tree));
+ ret = false;
+ goto done;
+ }
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_lock(tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ io.lockx.in.timeout = 10000;
+ t = time(NULL);
+ req = smb_raw_lock_send(tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by tdis was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+/*
+ test NT_STATUS_LOCK_NOT_GRANTED vs. NT_STATUS_FILE_LOCK_CONFLICT
+*/
+static bool test_errorcode(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_lock io;
+ union smb_open op;
+ struct smb_lock_entry lock[2];
+ NTSTATUS status;
+ bool ret = true;
+ int fnum, fnum2;
+ const char *fname;
+ struct smbcli_request *req;
+ time_t start;
+ int t;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing LOCK_NOT_GRANTED vs. FILE_LOCK_CONFLICT\n");
+
+ printf("testing with timeout = 0\n");
+ fname = BASEDIR "\\test0.txt";
+ t = 0;
+
+ /*
+ * the first run is with t = 0,
+ * the second with t > 0 (=1)
+ */
+next_run:
+ /*
+ * use the DENY_DOS mode, that creates two fnum's of one low-level file handle,
+ * this demonstrates that the cache is per fnum
+ */
+ op.openx.level = RAW_OPEN_OPENX;
+ op.openx.in.fname = fname;
+ op.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ op.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_DOS;
+ op.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+ op.openx.in.search_attrs = 0;
+ op.openx.in.file_attrs = 0;
+ op.openx.in.write_time = 0;
+ op.openx.in.size = 0;
+ op.openx.in.timeout = 0;
+
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.openx.out.file.fnum;
+
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = op.openx.out.file.fnum;
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = t;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /*
+ * demonstrate that the first conflicting lock on each handle give LOCK_NOT_GRANTED
+ * this also demonstrates that the error code cache is per file handle
+ * (LOCK_NOT_GRANTED is only be used when timeout is 0!)
+ */
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+
+ /* demonstrate that each following conflict gives FILE_LOCK_CONFLICT */
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /* demonstrate that the smbpid doesn't matter */
+ lock[0].pid++;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+ lock[0].pid--;
+
+ /*
+ * demonstrate the a successful lock with count = 0 and the same offset,
+ * doesn't reset the error cache
+ */
+ lock[0].offset = 100;
+ lock[0].count = 0;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /*
+ * demonstrate the a successful lock with count = 0 and outside the locked range,
+ * doesn't reset the error cache
+ */
+ lock[0].offset = 110;
+ lock[0].count = 0;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 99;
+ lock[0].count = 0;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /* demonstrate that a changing count doesn't reset the error cache */
+ lock[0].offset = 100;
+ lock[0].count = 5;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 100;
+ lock[0].count = 15;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /*
+ * demonstrate the a lock with count = 0 and inside the locked range,
+ * fails and resets the error cache
+ */
+ lock[0].offset = 101;
+ lock[0].count = 0;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /* demonstrate the a changing offset, resets the error cache */
+ lock[0].offset = 105;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 95;
+ lock[0].count = 9;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /*
+ * demonstrate the a successful lock in a different range,
+ * doesn't reset the cache, the failing lock on the 2nd handle
+ * resets the resets the cache
+ */
+ lock[0].offset = 120;
+ lock[0].count = 15;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.file.fnum = fnum;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.file.fnum = fnum2;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ /* end of the loop */
+ if (t == 0) {
+ smb_raw_exit(cli->session);
+ printf("testing with timeout > 0 (=1)\n");
+ fname = BASEDIR "\\test1.txt";
+ t = 1;
+ goto next_run;
+ }
+
+ /*
+ * the following 3 test sections demonstrate that
+ * the cache is only set when the error is reported
+ * to the client (after the timeout went by)
+ */
+ smb_raw_exit(cli->session);
+ printf("testing a conflict while a lock is pending\n");
+ fname = BASEDIR "\\test2.txt";
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ start = time(NULL);
+ io.lockx.in.timeout = 1000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.in.timeout = 0;
+ lock[0].offset = 105;
+ lock[0].count = 10;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ if (time(NULL) < start+1) {
+ printf("lock comes back to early (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ smbcli_close(cli->tree, fnum);
+ fname = BASEDIR "\\test3.txt";
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ start = time(NULL);
+ io.lockx.in.timeout = 1000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.in.timeout = 0;
+ lock[0].offset = 105;
+ lock[0].count = 10;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (time(NULL) < start+1) {
+ printf("lock comes back to early (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ smbcli_close(cli->tree, fnum);
+ fname = BASEDIR "\\test4.txt";
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ start = time(NULL);
+ io.lockx.in.timeout = 1000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (time(NULL) < start+1) {
+ printf("lock comes back to early (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test LOCKING_ANDX_CHANGE_LOCKTYPE
+*/
+static bool test_changetype(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_lock io;
+ struct smb_lock_entry lock[2];
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t c = 0;
+ const char *fname = BASEDIR "\\test.txt";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing LOCKING_ANDX_CHANGE_LOCKTYPE\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ io.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
+ printf("allowed write on read locked region (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ /* windows server don't seem to support this */
+ io.lockx.in.mode = LOCKING_ANDX_CHANGE_LOCKTYPE;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks));
+
+ if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
+ printf("allowed write after lock change (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ basic testing of lock calls
+*/
+struct torture_suite *torture_raw_lock(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "LOCK");
+
+ torture_suite_add_1smb_test(suite, "lockx", test_lockx);
+ torture_suite_add_1smb_test(suite, "lock", test_lock);
+ torture_suite_add_1smb_test(suite, "pidhigh", test_pidhigh);
+ torture_suite_add_1smb_test(suite, "async", test_async);
+ torture_suite_add_1smb_test(suite, "errorcode", test_errorcode);
+ torture_suite_add_1smb_test(suite, "changetype", test_changetype);
+
+ return suite;
+}
diff --git a/source4/torture/raw/lockbench.c b/source4/torture/raw/lockbench.c
new file mode 100644
index 0000000000..21541d003b
--- /dev/null
+++ b/source4/torture/raw/lockbench.c
@@ -0,0 +1,414 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ locking benchmark
+
+ Copyright (C) Andrew Tridgell 2006
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+#define BASEDIR "\\benchlock"
+#define FNAME BASEDIR "\\lock.dat"
+
+static int nprocs;
+static int lock_failed;
+static int num_connected;
+
+enum lock_stage {LOCK_INITIAL, LOCK_LOCK, LOCK_UNLOCK};
+
+struct benchlock_state {
+ struct torture_context *tctx;
+ struct event_context *ev;
+ struct smbcli_tree *tree;
+ TALLOC_CTX *mem_ctx;
+ int client_num;
+ int fnum;
+ enum lock_stage stage;
+ int lock_offset;
+ int unlock_offset;
+ int count;
+ int lastcount;
+ struct smbcli_request *req;
+ struct smb_composite_connect reconnect;
+ struct timed_event *te;
+
+ /* these are used for reconnections */
+ const char **dest_ports;
+ const char *dest_host;
+ const char *called_name;
+ const char *service_type;
+};
+
+static void lock_completion(struct smbcli_request *);
+
+/*
+ send the next lock request
+*/
+static void lock_send(struct benchlock_state *state)
+{
+ union smb_lock io;
+ struct smb_lock_entry lock;
+
+ switch (state->stage) {
+ case LOCK_INITIAL:
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ state->lock_offset = 0;
+ state->unlock_offset = 0;
+ lock.offset = state->lock_offset;
+ break;
+ case LOCK_LOCK:
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ state->lock_offset = (state->lock_offset+1)%(nprocs+1);
+ lock.offset = state->lock_offset;
+ break;
+ case LOCK_UNLOCK:
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ lock.offset = state->unlock_offset;
+ state->unlock_offset = (state->unlock_offset+1)%(nprocs+1);
+ break;
+ }
+
+ lock.count = 1;
+ lock.pid = state->tree->session->pid;
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 100000;
+ io.lockx.in.locks = &lock;
+ io.lockx.in.file.fnum = state->fnum;
+
+ state->req = smb_raw_lock_send(state->tree, &io);
+ if (state->req == NULL) {
+ DEBUG(0,("Failed to setup lock\n"));
+ lock_failed++;
+ }
+ state->req->async.private = state;
+ state->req->async.fn = lock_completion;
+}
+
+static void reopen_connection(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data);
+
+
+static void reopen_file(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct benchlock_state *state = (struct benchlock_state *)private_data;
+
+ /* reestablish our open file */
+ state->fnum = smbcli_open(state->tree, FNAME, O_RDWR|O_CREAT, DENY_NONE);
+ if (state->fnum == -1) {
+ printf("Failed to open %s on connection %d\n", FNAME, state->client_num);
+ exit(1);
+ }
+
+ num_connected++;
+
+ DEBUG(0,("reconnect to %s finished (%u connected)\n", state->dest_host,
+ num_connected));
+
+ state->stage = LOCK_INITIAL;
+ lock_send(state);
+}
+
+/*
+ complete an async reconnect
+ */
+static void reopen_connection_complete(struct composite_context *ctx)
+{
+ struct benchlock_state *state = (struct benchlock_state *)ctx->async.private_data;
+ NTSTATUS status;
+ struct smb_composite_connect *io = &state->reconnect;
+
+ status = smb_composite_connect_recv(ctx, state->mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ return;
+ }
+
+ talloc_free(state->tree);
+ state->tree = io->out.tree;
+
+ /* do the reopen as a separate event */
+ event_add_timed(state->ev, state->mem_ctx, timeval_zero(), reopen_file, state);
+}
+
+
+
+/*
+ reopen a connection
+ */
+static void reopen_connection(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct benchlock_state *state = (struct benchlock_state *)private_data;
+ struct composite_context *ctx;
+ struct smb_composite_connect *io = &state->reconnect;
+ char *host, *share;
+
+ state->te = NULL;
+
+ if (!torture_get_conn_index(state->client_num, state->mem_ctx, state->tctx, &host, &share)) {
+ DEBUG(0,("Can't find host/share for reconnect?!\n"));
+ exit(1);
+ }
+
+ io->in.dest_host = state->dest_host;
+ io->in.dest_ports = state->dest_ports;
+ io->in.called_name = state->called_name;
+ io->in.service = share;
+ io->in.service_type = state->service_type;
+ io->in.credentials = cmdline_credentials;
+ io->in.fallback_to_anonymous = false;
+ io->in.workgroup = lp_workgroup(state->tctx->lp_ctx);
+ lp_smbcli_options(state->tctx->lp_ctx, &io->in.options);
+
+ /* kill off the remnants of the old connection */
+ talloc_free(state->tree);
+ state->tree = NULL;
+
+ ctx = smb_composite_connect_send(io, state->mem_ctx,
+ lp_resolve_context(state->tctx->lp_ctx),
+ state->ev);
+ if (ctx == NULL) {
+ DEBUG(0,("Failed to setup async reconnect\n"));
+ exit(1);
+ }
+
+ ctx->async.fn = reopen_connection_complete;
+ ctx->async.private_data = state;
+}
+
+
+/*
+ called when a lock completes
+*/
+static void lock_completion(struct smbcli_request *req)
+{
+ struct benchlock_state *state = (struct benchlock_state *)req->async.private;
+ NTSTATUS status = smbcli_request_simple_recv(req);
+ state->req = NULL;
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
+ talloc_free(state->tree);
+ state->tree = NULL;
+ num_connected--;
+ DEBUG(0,("reopening connection to %s\n", state->dest_host));
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ } else {
+ DEBUG(0,("Lock failed - %s\n", nt_errstr(status)));
+ lock_failed++;
+ }
+ return;
+ }
+
+ switch (state->stage) {
+ case LOCK_INITIAL:
+ state->stage = LOCK_LOCK;
+ break;
+ case LOCK_LOCK:
+ state->stage = LOCK_UNLOCK;
+ break;
+ case LOCK_UNLOCK:
+ state->stage = LOCK_LOCK;
+ break;
+ }
+
+ state->count++;
+ lock_send(state);
+}
+
+
+static void echo_completion(struct smbcli_request *req)
+{
+ struct benchlock_state *state = (struct benchlock_state *)req->async.private;
+ NTSTATUS status = smbcli_request_simple_recv(req);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
+ talloc_free(state->tree);
+ state->tree = NULL;
+ num_connected--;
+ DEBUG(0,("reopening connection to %s\n", state->dest_host));
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ }
+}
+
+static void report_rate(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct benchlock_state *state = talloc_get_type(private_data,
+ struct benchlock_state);
+ int i;
+ for (i=0;i<nprocs;i++) {
+ printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
+ state[i].lastcount = state[i].count;
+ }
+ printf("\r");
+ fflush(stdout);
+ event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
+
+ /* send an echo on each interface to ensure it stays alive - this helps
+ with IP takeover */
+ for (i=0;i<nprocs;i++) {
+ struct smb_echo p;
+ struct smbcli_request *req;
+
+ if (!state[i].tree) {
+ continue;
+ }
+
+ p.in.repeat_count = 1;
+ p.in.size = 0;
+ p.in.data = NULL;
+ req = smb_raw_echo_send(state[i].tree->session->transport, &p);
+ req->async.private = &state[i];
+ req->async.fn = echo_completion;
+ }
+}
+
+/*
+ benchmark locking calls
+*/
+bool torture_bench_lock(struct torture_context *torture)
+{
+ bool ret = true;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ int i;
+ int timelimit = torture_setting_int(torture, "timelimit", 10);
+ struct timeval tv;
+ struct benchlock_state *state;
+ int total = 0, minops=0;
+ struct smbcli_state *cli;
+ bool progress;
+
+ progress = torture_setting_bool(torture, "progress", true);
+
+ nprocs = torture_setting_int(torture, "nprocs", 4);
+
+ state = talloc_zero_array(mem_ctx, struct benchlock_state, nprocs);
+
+ printf("Opening %d connections\n", nprocs);
+ for (i=0;i<nprocs;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].client_num = i;
+ state[i].ev = torture->ev;
+ if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) {
+ return false;
+ }
+ talloc_steal(mem_ctx, state);
+ state[i].tree = cli->tree;
+ state[i].dest_host = talloc_strdup(state[i].mem_ctx,
+ cli->tree->session->transport->socket->hostname);
+ state[i].dest_ports = talloc_array(state[i].mem_ctx,
+ const char *, 2);
+ state[i].dest_ports[0] = talloc_asprintf(state[i].dest_ports,
+ "%u",
+ cli->tree->session->transport->socket->port);
+ state[i].dest_ports[1] = NULL;
+ state[i].called_name = talloc_strdup(state[i].mem_ctx,
+ cli->tree->session->transport->called.name);
+ state[i].service_type = talloc_strdup(state[i].mem_ctx,
+ cli->tree->device);
+ }
+
+ num_connected = i;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ goto failed;
+ }
+
+ for (i=0;i<nprocs;i++) {
+ state[i].fnum = smbcli_open(state[i].tree,
+ FNAME,
+ O_RDWR|O_CREAT, DENY_NONE);
+ if (state[i].fnum == -1) {
+ printf("Failed to open %s on connection %d\n", FNAME, i);
+ goto failed;
+ }
+
+ state[i].stage = LOCK_INITIAL;
+ lock_send(&state[i]);
+ }
+
+ tv = timeval_current();
+
+ if (progress) {
+ event_add_timed(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
+ }
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ event_loop_once(torture->ev);
+
+ if (lock_failed) {
+ DEBUG(0,("locking failed\n"));
+ goto failed;
+ }
+ }
+
+ printf("%.2f ops/second\n", total/timeval_elapsed(&tv));
+ minops = state[0].count;
+ for (i=0;i<nprocs;i++) {
+ printf("[%d] %u ops\n", i, state[i].count);
+ if (state[i].count < minops) minops = state[i].count;
+ }
+ if (minops < 0.5*total/nprocs) {
+ printf("Failed: unbalanced locking\n");
+ goto failed;
+ }
+
+ for (i=0;i<nprocs;i++) {
+ talloc_free(state[i].req);
+ smb_raw_exit(state[i].tree->session);
+ }
+
+ smbcli_deltree(state[0].tree, BASEDIR);
+ talloc_free(mem_ctx);
+ printf("\n");
+ return ret;
+
+failed:
+ talloc_free(mem_ctx);
+ return false;
+}
diff --git a/source4/torture/raw/lookuprate.c b/source4/torture/raw/lookuprate.c
new file mode 100644
index 0000000000..782cb1b31b
--- /dev/null
+++ b/source4/torture/raw/lookuprate.c
@@ -0,0 +1,320 @@
+/*
+ File lookup rate test.
+
+ Copyright (C) James Peach 2006
+
+ 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 "param/param.h"
+#include "system/filesys.h"
+#include "torture/smbtorture.h"
+#include "torture/basic/proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+
+#define BASEDIR "\\lookuprate"
+#define MISSINGNAME BASEDIR "\\foo"
+
+#define FUZZ_PERCENT 10
+
+#define usec_to_sec(s) ((s) / 1000000)
+#define sec_to_usec(s) ((s) * 1000000)
+
+struct rate_record
+{
+ unsigned dirent_count;
+ unsigned querypath_persec;
+ unsigned findfirst_persec;
+};
+
+static struct rate_record records[] =
+{
+ { 0, 0, 0 }, /* Base (optimal) lookup rate. */
+ { 100, 0, 0},
+ { 1000, 0, 0},
+ { 10000, 0, 0},
+ { 100000, 0, 0}
+};
+
+typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path);
+
+/* Test whether rhs is within fuzz% of lhs. */
+static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent)
+{
+ double fuzz = (double)lhs * (double)percent/100.0;
+
+ if (((double)rhs >= ((double)lhs - fuzz)) &&
+ ((double)rhs <= ((double)lhs + fuzz))) {
+ return true;
+ }
+
+ return false;
+
+}
+
+static NTSTATUS fill_directory(struct smbcli_tree *tree,
+ const char * path, unsigned count)
+{
+ NTSTATUS status;
+ char *fname = NULL;
+ unsigned i;
+ unsigned current;
+
+ struct timeval start;
+ struct timeval now;
+
+ status = smbcli_mkdir(tree, path);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ printf("filling directory %s with %u files... ", path, count);
+ fflush(stdout);
+
+ current = random();
+ start = timeval_current();
+
+ for (i = 0; i < count; ++i) {
+ int fnum;
+
+ ++current;
+ fname = talloc_asprintf(NULL, "%s\\fill%u",
+ path, current);
+
+ fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
+ OPENX_MODE_DENY_NONE);
+ if (fnum < 0) {
+ talloc_free(fname);
+ return smbcli_nt_error(tree);
+ }
+
+ smbcli_close(tree, fnum);
+ talloc_free(fname);
+ }
+
+ if (count) {
+ double rate;
+ now = timeval_current();
+ rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
+ printf("%u/sec\n", (unsigned)rate);
+ } else {
+ printf("done\n");
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS squash_lookup_error(NTSTATUS status)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ /* We don't care if the file isn't there. */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
+ return NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+/* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
+static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
+{
+ NTSTATUS status;
+ time_t ftimes[3];
+ size_t fsize;
+ uint16_t fmode;
+
+ status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
+ &fsize, &fmode);
+
+ return squash_lookup_error(status);
+}
+
+/* Look up a pathname using TRANS2_FIND_FIRST2. */
+static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbcli_list(tree, path, 0, NULL, NULL) < 0) {
+ status = smbcli_nt_error(tree);
+ }
+
+ return squash_lookup_error(status);
+}
+
+static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree,
+ lookup_function lookup, const char * path, unsigned * rate)
+{
+ NTSTATUS status;
+
+ struct timeval start;
+ struct timeval now;
+ unsigned count = 0;
+ int64_t elapsed = 0;
+
+#define LOOKUP_PERIOD_SEC (2)
+
+ start = timeval_current();
+ while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) {
+
+ status = lookup(tree, path);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ++count;
+ now = timeval_current();
+ elapsed = usec_time_diff(&now, &start);
+ }
+
+#undef LOOKUP_PERIOD_SEC
+
+ *rate = (unsigned)((double)count / (double)usec_to_sec(elapsed));
+ return NT_STATUS_OK;
+}
+
+static bool remove_working_directory(struct smbcli_tree *tree,
+ const char * path)
+{
+ int tries;
+
+ /* Using smbcli_deltree to delete a very large number of files
+ * doesn't work against all servers. Work around this by
+ * retrying.
+ */
+ for (tries = 0; tries < 5; ) {
+ int ret;
+
+ ret = smbcli_deltree(tree, BASEDIR);
+ if (ret == -1) {
+ tries++;
+ printf("(%s) failed to deltree %s: %s\n",
+ __location__, BASEDIR,
+ smbcli_errstr(tree));
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+
+}
+
+/* Verify that looking up a file name takes constant time.
+ *
+ * This test samples the lookup rate for a non-existant filename in a
+ * directory, while varying the number of files in the directory. The
+ * lookup rate should continue to approximate the lookup rate for the
+ * empty directory case.
+ */
+bool torture_bench_lookup(struct torture_context *torture)
+{
+ NTSTATUS status;
+ bool result = false;
+
+ int i, tries;
+ struct smbcli_state *cli = NULL;
+
+ if (!torture_open_connection(&cli, torture, 0)) {
+ goto done;
+ }
+
+ remove_working_directory(cli->tree, BASEDIR);
+
+ for (i = 0; i < ARRAY_SIZE(records); ++i) {
+ printf("testing lookup rate with %u directory entries\n",
+ records[i].dirent_count);
+
+ status = fill_directory(cli->tree, BASEDIR,
+ records[i].dirent_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to fill directory: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ status = lookup_rate_convert(cli->tree, querypath_lookup,
+ MISSINGNAME, &records[i].querypath_persec);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("querypathinfo of %s failed: %s\n",
+ MISSINGNAME, nt_errstr(status));
+ goto done;
+ }
+
+ status = lookup_rate_convert(cli->tree, findfirst_lookup,
+ MISSINGNAME, &records[i].findfirst_persec);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("findfirst of %s failed: %s\n",
+ MISSINGNAME, nt_errstr(status));
+ goto done;
+ }
+
+ printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n",
+ records[i].dirent_count,
+ records[i].querypath_persec,
+ records[i].findfirst_persec);
+
+ if (!remove_working_directory(cli->tree, BASEDIR)) {
+ goto done;
+ }
+ }
+
+ /* Ok. We have run all our tests. Walk through the records we
+ * accumulated and figure out whether the lookups took constant
+ * time of not.
+ */
+ for (i = 0; i < ARRAY_SIZE(records); ++i) {
+ if (!fuzzily_equal(records[0].querypath_persec,
+ records[i].querypath_persec,
+ FUZZ_PERCENT)) {
+ printf("querypath rate for %d entries differed by "
+ "more than %d%% from base rate\n",
+ records[i].dirent_count, FUZZ_PERCENT);
+ result = false;
+ }
+
+ if (!fuzzily_equal(records[0].findfirst_persec,
+ records[i].findfirst_persec,
+ FUZZ_PERCENT)) {
+ printf("findfirst rate for %d entries differed by "
+ "more than %d%% from base rate\n",
+ records[i].dirent_count, FUZZ_PERCENT);
+ result = false;
+ }
+ }
+
+done:
+ if (cli) {
+ remove_working_directory(cli->tree, BASEDIR);
+ talloc_free(cli);
+ }
+
+ return result;
+}
+
+/* vim: set sts=8 sw=8 : */
diff --git a/source4/torture/raw/missing.txt b/source4/torture/raw/missing.txt
new file mode 100644
index 0000000000..0f4104b596
--- /dev/null
+++ b/source4/torture/raw/missing.txt
@@ -0,0 +1,160 @@
+- RAW-CONTEXT passes on nt4 but TCON doesn't !!??
+
+- all messaging commands
+
+- writebraw
+
+- writebmpx
+
+- acl ops
+
+- readbmpx
+
+- rap commands
+
+- rpc commands
+
+- SMBcopy
+
+- SMBtcon
+
+- SMBecho
+
+- SMBfunique
+
+- SMBsearch vs SMBffirst?
+
+- SMBfclose
+
+- SMBkeepalive
+
+- secondary trans2 and nttrans
+
+- trans2 ioctl
+
+- trans2 session setup
+
+- trans2 DFS ops
+
+- unix ops
+
+--------------
+done:
+
+mkdir
+rmdir
+open
+create
+close
+flush
+unlink
+mv
+getatr
+setatr
+read
+write
+lock
+unlock
+ctemp
+mknew
+chkpath
+exit
+lseek
+tconX
+tdis
+negprot
+dskattr
+search
+lockread
+writeunlock
+readbraw
+setattrE
+getattrE
+lockingX
+ioctl
+openX
+readX
+writeX
+sesssetupX
+trans2
+findclose
+ulogoffX
+nttrans
+ntcreateX
+ntcancel
+trans2_open
+trans2_findfirst
+trans2_findnext?
+trans2_qfsinfo
+trans2_setfsinfo
+trans2_qpathinfo
+trans2_setpathinfo
+trans2_qfileinfo
+trans2_setfileinfo
+trans2_fsctl
+trans2_mkdir
+trans2_findnext
+NTrename
+SMB_QFS_ALLOCATION
+SMB_QFS_VOLUME
+SMB_QFS_VOLUME_INFO
+SMB_QFS_SIZE_INFO
+SMB_QFS_DEVICE_INFO
+SMB_QFS_ATTRIBUTE_INFO
+SMB_QFS_VOLUME_INFORMATION
+SMB_QFS_SIZE_INFORMATION
+SMB_QFS_DEVICE_INFORMATION
+SMB_QFS_ATTRIBUTE_INFORMATION
+SMB_QFS_QUOTA_INFORMATION
+SMB_QFS_FULL_SIZE_INFORMATION
+SMB_QFS_OBJECTID_INFORMATION
+SMB_QFILEINFO_STANDARD
+SMB_QFILEINFO_EA_SIZE
+SMB_QFILEINFO_ALL_EAS
+SMB_QFILEINFO_IS_NAME_VALID
+SMB_QFILEINFO_BASIC_INFO
+SMB_QFILEINFO_STANDARD_INFO
+SMB_QFILEINFO_EA_INFO
+SMB_QFILEINFO_NAME_INFO
+SMB_QFILEINFO_ALL_INFO
+SMB_QFILEINFO_ALT_NAME_INFO
+SMB_QFILEINFO_STREAM_INFO
+SMB_QFILEINFO_COMPRESSION_INFO
+SMB_QFILEINFO_BASIC_INFORMATION
+SMB_QFILEINFO_STANDARD_INFORMATION
+SMB_QFILEINFO_INTERNAL_INFORMATION
+SMB_QFILEINFO_EA_INFORMATION
+SMB_QFILEINFO_ACCESS_INFORMATION
+SMB_QFILEINFO_NAME_INFORMATION
+SMB_QFILEINFO_POSITION_INFORMATION
+SMB_QFILEINFO_MODE_INFORMATION
+SMB_QFILEINFO_ALIGNMENT_INFORMATION
+SMB_QFILEINFO_ALL_INFORMATION
+SMB_QFILEINFO_ALT_NAME_INFORMATION
+SMB_QFILEINFO_STREAM_INFORMATION
+SMB_QFILEINFO_COMPRESSION_INFORMATION
+SMB_QFILEINFO_NETWORK_OPEN_INFORMATION
+SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION
+SMB_SFILEINFO_STANDARD
+SMB_SFILEINFO_EA_SET
+SMB_SFILEINFO_BASIC_INFO
+SMB_SFILEINFO_DISPOSITION_INFO
+SMB_SFILEINFO_ALLOCATION_INFO
+SMB_SFILEINFO_END_OF_FILE_INFO
+SMB_SFILEINFO_UNIX_BASIC
+SMB_SFILEINFO_UNIX_LINK
+SMB_SFILEINFO_BASIC_INFORMATION
+SMB_SFILEINFO_RENAME_INFORMATION
+SMB_SFILEINFO_DISPOSITION_INFORMATION
+SMB_SFILEINFO_POSITION_INFORMATION
+SMB_SFILEINFO_MODE_INFORMATION
+SMB_SFILEINFO_ALLOCATION_INFORMATION
+SMB_SFILEINFO_END_OF_FILE_INFORMATION
+SMB_FIND_STANDARD
+SMB_FIND_EA_SIZE
+SMB_FIND_DIRECTORY_INFO
+SMB_FIND_FULL_DIRECTORY_INFO
+SMB_FIND_NAME_INFO
+SMB_FIND_BOTH_DIRECTORY_INFO
+SMB_FIND_261
+SMB_FIND_262
diff --git a/source4/torture/raw/mkdir.c b/source4/torture/raw/mkdir.c
new file mode 100644
index 0000000000..cea959347d
--- /dev/null
+++ b/source4/torture/raw/mkdir.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_MKDIR_* and RAW_RMDIR_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+#define BASEDIR "\\mkdirtest"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+/*
+ test mkdir ops
+*/
+static bool test_mkdir(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_mkdir md;
+ struct smb_rmdir rd;
+ const char *path = BASEDIR "\\mkdir.dir";
+ NTSTATUS status;
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ /*
+ basic mkdir
+ */
+ md.mkdir.level = RAW_MKDIR_MKDIR;
+ md.mkdir.in.path = path;
+
+ status = smb_raw_mkdir(cli->tree, &md);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing mkdir collision\n");
+
+ /* 2nd create */
+ status = smb_raw_mkdir(cli->tree, &md);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+
+ /* basic rmdir */
+ rd.in.path = path;
+ status = smb_raw_rmdir(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_rmdir(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ printf("testing mkdir collision with file\n");
+
+ /* name collision with a file */
+ smbcli_close(cli->tree, create_complex_file(cli, tctx, path));
+ status = smb_raw_mkdir(cli->tree, &md);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+
+ printf("testing rmdir with file\n");
+
+ /* delete a file with rmdir */
+ status = smb_raw_rmdir(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
+
+ smbcli_unlink(cli->tree, path);
+
+ printf("testing invalid dir\n");
+
+ /* create an invalid dir */
+ md.mkdir.in.path = "..\\..\\..";
+ status = smb_raw_mkdir(cli->tree, &md);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+ printf("testing t2mkdir\n");
+
+ /* try a t2mkdir - need to work out why this fails! */
+ md.t2mkdir.level = RAW_MKDIR_T2MKDIR;
+ md.t2mkdir.in.path = path;
+ md.t2mkdir.in.num_eas = 0;
+ status = smb_raw_mkdir(cli->tree, &md);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_rmdir(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing t2mkdir bad path\n");
+ md.t2mkdir.in.path = talloc_asprintf(tctx, "%s\\bad_path\\bad_path",
+ BASEDIR);
+ status = smb_raw_mkdir(cli->tree, &md);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND);
+
+ printf("testing t2mkdir with EAs\n");
+
+ /* with EAs */
+ md.t2mkdir.level = RAW_MKDIR_T2MKDIR;
+ md.t2mkdir.in.path = path;
+ md.t2mkdir.in.num_eas = 3;
+ md.t2mkdir.in.eas = talloc_array(tctx, struct ea_struct, md.t2mkdir.in.num_eas);
+ md.t2mkdir.in.eas[0].flags = 0;
+ md.t2mkdir.in.eas[0].name.s = "EAONE";
+ md.t2mkdir.in.eas[0].value = data_blob_talloc(tctx, "blah", 4);
+ md.t2mkdir.in.eas[1].flags = 0;
+ md.t2mkdir.in.eas[1].name.s = "EA TWO";
+ md.t2mkdir.in.eas[1].value = data_blob_talloc(tctx, "foo bar", 7);
+ md.t2mkdir.in.eas[2].flags = 0;
+ md.t2mkdir.in.eas[2].name.s = "EATHREE";
+ md.t2mkdir.in.eas[2].value = data_blob_talloc(tctx, "xx1", 3);
+ status = smb_raw_mkdir(cli->tree, &md);
+
+ if (torture_setting_bool(tctx, "samba3", false)
+ && NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED)) {
+ d_printf("EAS not supported -- not treating as fatal\n");
+ }
+ else {
+ /*
+ * In Samba3, don't see this error as fatal
+ */
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = torture_check_ea(cli, path, "EAONE", "blah");
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = torture_check_ea(cli, path, "EA TWO", "foo bar");
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = torture_check_ea(cli, path, "EATHREE", "xx1");
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_rmdir(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ basic testing of all RAW_MKDIR_* calls
+*/
+bool torture_raw_mkdir(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!test_mkdir(cli, torture)) {
+ ret = false;
+ }
+
+ return ret;
+}
diff --git a/source4/torture/raw/mux.c b/source4/torture/raw/mux.c
new file mode 100644
index 0000000000..5b5db3a557
--- /dev/null
+++ b/source4/torture/raw/mux.c
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+ basic raw test suite for multiplexing
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\test_mux"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+/*
+ test the delayed reply to a open that leads to a sharing violation
+*/
+static bool test_mux_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ union smb_open io;
+ NTSTATUS status;
+ int fnum1, fnum2;
+ bool ret = true;
+ struct smbcli_request *req1, *req2;
+ struct timeval tv;
+ double d;
+
+ printf("testing multiplexed open/open/close\n");
+
+ printf("send first open\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR "\\open.dat";
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ printf("send 2nd open, non-conflicting\n");
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ tv = timeval_current();
+
+ printf("send 3rd open, conflicting\n");
+ io.ntcreatex.in.share_access = 0;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ d = timeval_elapsed(&tv);
+ if (d < 0.5 || d > 1.5) {
+ printf("bad timeout for conflict - %.2f should be 1.0\n", d);
+ } else {
+ printf("open delay %.2f\n", d);
+ }
+
+ printf("send async open, conflicting\n");
+ tv = timeval_current();
+ req1 = smb_raw_open_send(cli->tree, &io);
+
+ printf("send 2nd async open, conflicting\n");
+ tv = timeval_current();
+ req2 = smb_raw_open_send(cli->tree, &io);
+
+ printf("close first sync open\n");
+ smbcli_close(cli->tree, fnum1);
+
+ printf("cancel 2nd async open (should be ignored)\n");
+ smb_raw_ntcancel(req2);
+
+ d = timeval_elapsed(&tv);
+ if (d > 0.25) {
+ printf("bad timeout after cancel - %.2f should be <0.25\n", d);
+ ret = false;
+ }
+
+ printf("close the 2nd sync open\n");
+ smbcli_close(cli->tree, fnum2);
+
+ printf("see if the 1st async open now succeeded\n");
+ status = smb_raw_open_recv(req1, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ d = timeval_elapsed(&tv);
+ if (d > 0.25) {
+ printf("bad timeout for async conflict - %.2f should be <0.25\n", d);
+ ret = false;
+ } else {
+ printf("async open delay %.2f\n", d);
+ }
+
+ printf("2nd async open should have timed out\n");
+ status = smb_raw_open_recv(req2, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+ d = timeval_elapsed(&tv);
+ if (d < 0.8) {
+ printf("bad timeout for async conflict - %.2f should be 1.0\n", d);
+ }
+
+ printf("close the 1st async open\n");
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+done:
+ return ret;
+}
+
+
+/*
+ test a write that hits a byte range lock and send the close after the write
+*/
+static bool test_mux_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ union smb_write io;
+ NTSTATUS status;
+ int fnum;
+ bool ret = true;
+ struct smbcli_request *req;
+
+ printf("testing multiplexed lock/write/close\n");
+
+ fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ cli->session->pid = 1;
+
+ /* lock a range */
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 0, 4, 0, WRITE_LOCK))) {
+ printf("lock failed in mux_write - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ cli->session->pid = 2;
+
+ /* send an async write */
+ io.generic.level = RAW_WRITE_WRITEX;
+ io.writex.in.file.fnum = fnum;
+ io.writex.in.offset = 0;
+ io.writex.in.wmode = 0;
+ io.writex.in.remaining = 0;
+ io.writex.in.count = 4;
+ io.writex.in.data = (const uint8_t *)&fnum;
+ req = smb_raw_write_send(cli->tree, &io);
+
+ /* unlock the range */
+ cli->session->pid = 1;
+ smbcli_unlock(cli->tree, fnum, 0, 4);
+
+ /* and recv the async write reply */
+ status = smb_raw_write_recv(req, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ smbcli_close(cli->tree, fnum);
+
+done:
+ return ret;
+}
+
+
+/*
+ test a lock that conflicts with an existing lock
+*/
+static bool test_mux_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ union smb_lock io;
+ NTSTATUS status;
+ int fnum;
+ bool ret = true;
+ struct smbcli_request *req;
+ struct smb_lock_entry lock[1];
+ struct timeval t;
+
+ printf("TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\n");
+
+ fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("establishing a lock\n");
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = 0;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.ulock_cnt = 0;
+ lock[0].pid = 1;
+ lock[0].offset = 0;
+ lock[0].count = 4;
+ io.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the second lock will conflict with the first\n");
+ lock[0].pid = 2;
+ io.lockx.in.timeout = 1000;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ printf("this will too, but we'll unlock while waiting\n");
+ t = timeval_current();
+ req = smb_raw_lock_send(cli->tree, &io);
+
+ printf("unlock the first range\n");
+ lock[0].pid = 1;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("recv the async reply\n");
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("async lock took %.2f msec\n", timeval_elapsed(&t) * 1000);
+ if (timeval_elapsed(&t) > 0.1) {
+ printf("failed to trigger early lock retry\n");
+ return false;
+ }
+
+ printf("reopening with an exit\n");
+ smb_raw_exit(cli->session);
+ fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+
+ printf("Now trying with a cancel\n");
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.file.fnum = fnum;
+ io.lockx.in.mode = 0;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.ulock_cnt = 0;
+ lock[0].pid = 1;
+ lock[0].offset = 0;
+ lock[0].count = 4;
+ io.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ lock[0].pid = 2;
+ io.lockx.in.timeout = 1000;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ req = smb_raw_lock_send(cli->tree, &io);
+
+ /* cancel the blocking lock */
+ smb_raw_ntcancel(req);
+
+ printf("sending 2nd cancel\n");
+ /* the 2nd cancel is totally harmless, but tests the server trying to
+ cancel an already cancelled request */
+ smb_raw_ntcancel(req);
+
+ printf("sent 2nd cancel\n");
+
+ lock[0].pid = 1;
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+ io.lockx.in.timeout = 0;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ printf("cancel a lock using exit to close file\n");
+ lock[0].pid = 1;
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+ io.lockx.in.timeout = 1000;
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ t = timeval_current();
+ lock[0].pid = 2;
+ req = smb_raw_lock_send(cli->tree, &io);
+
+ smb_raw_exit(cli->session);
+ smb_raw_exit(cli->session);
+ smb_raw_exit(cli->session);
+ smb_raw_exit(cli->session);
+
+ printf("recv the async reply\n");
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+ printf("async lock exit took %.2f msec\n", timeval_elapsed(&t) * 1000);
+ if (timeval_elapsed(&t) > 0.1) {
+ printf("failed to trigger early lock failure\n");
+ return false;
+ }
+
+done:
+ return ret;
+}
+
+
+
+/*
+ basic testing of multiplexing notify
+*/
+bool torture_raw_mux(struct torture_context *torture, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_mux_open(cli, torture);
+ ret &= test_mux_write(cli, torture);
+ ret &= test_mux_lock(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
diff --git a/source4/torture/raw/notify.c b/source4/torture/raw/notify.c
new file mode 100644
index 0000000000..25c91eb88b
--- /dev/null
+++ b/source4/torture/raw/notify.c
@@ -0,0 +1,1312 @@
+/*
+ Unix SMB/CIFS implementation.
+ basic raw test suite for change notify
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "system/filesys.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+#define BASEDIR "\\test_notify"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%d) Incorrect status %s - should be %s\n", \
+ __LINE__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+#define CHECK_VAL(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%d) wrong value for %s 0x%x should be 0x%x\n", \
+ __LINE__, #v, (int)v, (int)correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_WSTR(field, value, flags) do { \
+ if (!field.s || strcmp(field.s, value) || wire_bad_flags(&field, flags, cli->transport)) { \
+ printf("(%d) %s [%s] != %s\n", __LINE__, #field, field.s, value); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+/*
+ basic testing of change notify on directories
+*/
+static bool test_notify_dir(struct smbcli_state *cli, struct smbcli_state *cli2,
+ TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ union smb_close cl;
+ int i, count, fnum, fnum2;
+ struct smbcli_request *req, *req2;
+ extern int torture_numops;
+
+ printf("TESTING CHANGE NOTIFY ON DIRECTRIES\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.recursive = true;
+
+ printf("testing notify cancel\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smb_raw_ntcancel(req);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ printf("testing notify mkdir\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify rmdir\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify mkdir - rmdir - mkdir - rmdir\n");
+
+ smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
+ smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
+ smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
+ smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
+ msleep(200);
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 4);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[2].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[3].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[3].name, "subdir-name", STR_UNICODE);
+
+ count = torture_numops;
+ printf("testing buffered notify on create of %d files\n", count);
+ for (i=0;i<count;i++) {
+ char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
+ int fnum3 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ if (fnum3 == -1) {
+ printf("Failed to create %s - %s\n",
+ fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ talloc_free(fname);
+ smbcli_close(cli->tree, fnum3);
+ }
+
+ /* (1st notify) setup a new notify on a different directory handle.
+ This new notify won't see the events above. */
+ notify.nttrans.in.file.fnum = fnum2;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* (2nd notify) whereas this notify will see the above buffered events,
+ and it directly returns the buffered events */
+ notify.nttrans.in.file.fnum = fnum;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistant.txt");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ /* (1st unlink) as the 2nd notify directly returns,
+ this unlink is only seen by the 1st notify and
+ the 3rd notify (later) */
+ printf("testing notify on unlink for the first file\n");
+ status = smbcli_unlink(cli2->tree, BASEDIR "\\test0.txt");
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* receive the reply from the 2nd notify */
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, count);
+ for (i=1;i<count;i++) {
+ CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_ADDED);
+ }
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
+
+ printf("and now from the 1st notify\n");
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
+
+ printf("(3rd notify) this notify will only see the 1st unlink\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistant.txt");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ printf("testing notify on wildcard unlink for %d files\n", count-1);
+ /* (2nd unlink) do a wildcard unlink */
+ status = smbcli_unlink(cli2->tree, BASEDIR "\\test*.txt");
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* receive the 3rd notify */
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
+
+ /* and we now see the rest of the unlink calls on both directory handles */
+ notify.nttrans.in.file.fnum = fnum;
+ sleep(3);
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, count-1);
+ for (i=0;i<notify.nttrans.out.num_changes;i++) {
+ CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_REMOVED);
+ }
+ notify.nttrans.in.file.fnum = fnum2;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, count-1);
+ for (i=0;i<notify.nttrans.out.num_changes;i++) {
+ CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_REMOVED);
+ }
+
+ printf("testing if a close() on the dir handle triggers the notify reply\n");
+
+ notify.nttrans.in.file.fnum = fnum;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ * Check notify reply for a rename action. Not sure if this is a valid thing
+ * to do, but depending on timing between inotify and messaging we get the
+ * add/remove/modify in any order. This routines tries to find the action/name
+ * pair in any of the three following notify_changes.
+ */
+
+static bool check_rename_reply(struct smbcli_state *cli,
+ int line,
+ struct notify_changes *actions,
+ uint32_t action, const char *name)
+{
+ int i;
+
+ for (i=0; i<3; i++) {
+ if (actions[i].action == action) {
+ if ((actions[i].name.s == NULL)
+ || (strcmp(actions[i].name.s, name) != 0)
+ || (wire_bad_flags(&actions[i].name, STR_UNICODE,
+ cli->transport))) {
+ printf("(%d) name [%s] != %s\n", line,
+ actions[i].name.s, name);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ printf("(%d) expected action %d, not found\n", line, action);
+ return false;
+}
+
+/*
+ testing of recursive change notify
+*/
+static bool test_notify_recursive(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req1, *req2;
+
+ printf("TESTING CHANGE NOTIFY WITH RECURSION\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify, on file or directory name
+ changes. Setup both with and without recursion */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
+ notify.nttrans.in.file.fnum = fnum;
+
+ notify.nttrans.in.recursive = true;
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* cancel initial requests so the buffer is setup */
+ smb_raw_ntcancel(req1);
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ smb_raw_ntcancel(req2);
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
+ smbcli_close(cli->tree,
+ smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
+
+ notify.nttrans.in.completion_filter = 0;
+ notify.nttrans.in.recursive = true;
+ msleep(200);
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 11);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name\\subname1", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[2].name, "subdir-name\\subname2", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[3].action, NOTIFY_ACTION_OLD_NAME);
+ CHECK_WSTR(notify.nttrans.out.changes[3].name, "subdir-name\\subname1", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[4].action, NOTIFY_ACTION_NEW_NAME);
+ CHECK_WSTR(notify.nttrans.out.changes[4].name, "subdir-name\\subname1-r", STR_UNICODE);
+
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[5],
+ NOTIFY_ACTION_ADDED, "subname2-r");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[5],
+ NOTIFY_ACTION_REMOVED, "subdir-name\\subname2");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[5],
+ NOTIFY_ACTION_MODIFIED, "subname2-r");
+
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[8],
+ NOTIFY_ACTION_OLD_NAME, "subname2-r");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[8],
+ NOTIFY_ACTION_NEW_NAME, "subname3-r");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[8],
+ NOTIFY_ACTION_MODIFIED, "subname3-r");
+
+ if (!ret) {
+ goto done;
+ }
+
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 3);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name\\subname1-r", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[2].name, "subname3-r", STR_UNICODE);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ testing of change notify mask change
+*/
+static bool test_notify_mask_change(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req1, *req2;
+
+ printf("TESTING CHANGE NOTIFY WITH MASK CHANGE\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify, on file or directory name
+ changes. Setup both with and without recursion */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ notify.nttrans.in.file.fnum = fnum;
+
+ notify.nttrans.in.recursive = true;
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* cancel initial requests so the buffer is setup */
+ smb_raw_ntcancel(req1);
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ smb_raw_ntcancel(req2);
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ notify.nttrans.in.recursive = true;
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* Set to hidden then back again. */
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "tname1", STR_UNICODE);
+
+ /* Now try and change the mask to include other events.
+ * This should not work - once the mask is set on a directory
+ * fnum it seems to be fixed until the fnum is closed. */
+
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
+ notify.nttrans.in.recursive = true;
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
+ smbcli_close(cli->tree,
+ smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
+
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subname2-r", STR_UNICODE);
+
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subname3-r", STR_UNICODE);
+
+ if (!ret) {
+ goto done;
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+
+/*
+ testing of mask bits for change notify
+*/
+static bool test_notify_mask(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum, fnum2;
+ uint32_t mask;
+ int i;
+ char c = 1;
+ struct timeval tv;
+ NTTIME t;
+
+ printf("TESTING CHANGE NOTIFY COMPLETION FILTERS\n");
+
+ tv = timeval_current_ofs(1000, 0);
+ t = timeval_to_nttime(&tv);
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.recursive = true;
+
+#define NOTIFY_MASK_TEST(setup, op, cleanup, Action, expected, nchanges) \
+ do { for (mask=i=0;i<32;i++) { \
+ struct smbcli_request *req; \
+ status = smb_raw_open(cli->tree, tctx, &io); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ fnum = io.ntcreatex.out.file.fnum; \
+ setup \
+ notify.nttrans.in.file.fnum = fnum; \
+ notify.nttrans.in.completion_filter = (1<<i); \
+ req = smb_raw_changenotify_send(cli->tree, &notify); \
+ op \
+ msleep(200); smb_raw_ntcancel(req); \
+ status = smb_raw_changenotify_recv(req, tctx, &notify); \
+ cleanup \
+ smbcli_close(cli->tree, fnum); \
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ /* special case to cope with file rename behaviour */ \
+ if (nchanges == 2 && notify.nttrans.out.num_changes == 1 && \
+ notify.nttrans.out.changes[0].action == NOTIFY_ACTION_MODIFIED && \
+ ((expected) & FILE_NOTIFY_CHANGE_ATTRIBUTES) && \
+ Action == NOTIFY_ACTION_OLD_NAME) { \
+ printf("(rename file special handling OK)\n"); \
+ } else if (nchanges != notify.nttrans.out.num_changes) { \
+ printf("ERROR: nchanges=%d expected=%d action=%d filter=0x%08x\n", \
+ notify.nttrans.out.num_changes, \
+ nchanges, \
+ notify.nttrans.out.changes[0].action, \
+ notify.nttrans.in.completion_filter); \
+ ret = false; \
+ } else if (notify.nttrans.out.changes[0].action != Action) { \
+ printf("ERROR: nchanges=%d action=%d expectedAction=%d filter=0x%08x\n", \
+ notify.nttrans.out.num_changes, \
+ notify.nttrans.out.changes[0].action, \
+ Action, \
+ notify.nttrans.in.completion_filter); \
+ ret = false; \
+ } else if (strcmp(notify.nttrans.out.changes[0].name.s, "tname1") != 0) { \
+ printf("ERROR: nchanges=%d action=%d filter=0x%08x name=%s\n", \
+ notify.nttrans.out.num_changes, \
+ notify.nttrans.out.changes[0].action, \
+ notify.nttrans.in.completion_filter, \
+ notify.nttrans.out.changes[0].name.s); \
+ ret = false; \
+ } \
+ mask |= (1<<i); \
+ } \
+ if ((expected) != mask) { \
+ if (((expected) & ~mask) != 0) { \
+ printf("ERROR: trigger on too few bits. mask=0x%08x expected=0x%08x\n", \
+ mask, expected); \
+ ret = false; \
+ } else { \
+ printf("WARNING: trigger on too many bits. mask=0x%08x expected=0x%08x\n", \
+ mask, expected); \
+ } \
+ } \
+ } while (0)
+
+ printf("testing mkdir\n");
+ NOTIFY_MASK_TEST(;,
+ smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
+ smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME, 1);
+
+ printf("testing create file\n");
+ NOTIFY_MASK_TEST(;,
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_FILE_NAME, 1);
+
+ printf("testing unlink\n");
+ NOTIFY_MASK_TEST(
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ ;,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME, 1);
+
+ printf("testing rmdir\n");
+ NOTIFY_MASK_TEST(
+ smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
+ smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
+ ;,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_DIR_NAME, 1);
+
+ printf("testing rename file\n");
+ NOTIFY_MASK_TEST(
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname2");,
+ NOTIFY_ACTION_OLD_NAME,
+ FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION, 2);
+
+ printf("testing rename dir\n");
+ NOTIFY_MASK_TEST(
+ smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
+ smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
+ smbcli_rmdir(cli->tree, BASEDIR "\\tname2");,
+ NOTIFY_ACTION_OLD_NAME,
+ FILE_NOTIFY_CHANGE_DIR_NAME, 2);
+
+ printf("testing set path attribute\n");
+ NOTIFY_MASK_TEST(
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
+
+ printf("testing set path write time\n");
+ NOTIFY_MASK_TEST(
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_NORMAL, 1000);,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
+
+ printf("testing set file attribute\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, FILE_ATTRIBUTE_HIDDEN, 0, 0, 0, 0);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("Samba3 does not yet support create times "
+ "everywhere\n");
+ }
+ else {
+ printf("testing set file create time\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx,
+ BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, t, 0, 0, 0);,
+ (smbcli_close(cli->tree, fnum2),
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_CREATION, 1);
+ }
+
+ printf("testing set file access time\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, 0, t, 0, 0);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_ACCESS, 1);
+
+ printf("testing set file write time\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, t, 0);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
+
+ printf("testing set file change time\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, 0, t);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ 0, 1);
+
+
+ printf("testing write\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_write(cli->tree, fnum2, 1, &c, 10000, 1);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ 0, 1);
+
+ printf("testing truncate\n");
+ NOTIFY_MASK_TEST(
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_ftruncate(cli->tree, fnum2, 10000);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ basic testing of change notify on files
+*/
+static bool test_notify_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_close cl;
+ union smb_notify notify;
+ struct smbcli_request *req;
+ int fnum;
+ const char *fname = BASEDIR "\\file.txt";
+
+ printf("TESTING CHANGE NOTIFY ON FILES\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
+ notify.nttrans.in.recursive = false;
+
+ printf("testing if notifies on file handles are invalid (should be)\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ cl.close.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ cl.close.in.write_time = 0;
+ status = smb_raw_close(cli->tree, &cl);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ basic testing of change notifies followed by a tdis
+*/
+static bool test_notify_tdis(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.recursive = true;
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smbcli_tdis(cli);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->tree = NULL;
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+/*
+ basic testing of change notifies followed by a exit
+*/
+static bool test_notify_exit(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.recursive = true;
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+/*
+ basic testing of change notifies followed by a ulogoff
+*/
+static bool test_notify_ulogoff(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.recursive = true;
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smb_raw_ulogoff(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+static void tcp_dis_handler(struct smbcli_transport *t, void *p)
+{
+ struct smbcli_state *cli = (struct smbcli_state *)p;
+ smbcli_transport_dead(cli->transport, NT_STATUS_LOCAL_DISCONNECT);
+ cli->transport = NULL;
+ cli->tree = NULL;
+}
+/*
+ basic testing of change notifies followed by tcp disconnect
+*/
+static bool test_notify_tcp_dis(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.recursive = true;
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_transport_idle_handler(cli->transport, tcp_dis_handler, 250, cli);
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+/*
+ test setting up two change notify requests on one handle
+*/
+static bool test_notify_double(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req1, *req2;
+
+ printf("TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* ask for a change notify,
+ on file or directory name changes */
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
+ notify.nttrans.in.file.fnum = fnum;
+ notify.nttrans.in.recursive = true;
+
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name2");
+
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name2", STR_UNICODE);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+
+/*
+ test multiple change notifies at different depths and with/without recursion
+*/
+static bool test_notify_tree(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ union smb_notify notify;
+ union smb_open io;
+ struct smbcli_request *req;
+ struct timeval tv;
+ struct {
+ const char *path;
+ bool recursive;
+ uint32_t filter;
+ int expected;
+ int fnum;
+ int counted;
+ } dirs[] = {
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 30 },
+ {BASEDIR "\\zqy", true, FILE_NOTIFY_CHANGE_NAME, 8 },
+ {BASEDIR "\\atsy", true, FILE_NOTIFY_CHANGE_NAME, 4 },
+ {BASEDIR "\\abc\\foo", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\blah", true, FILE_NOTIFY_CHANGE_NAME, 13 },
+ {BASEDIR "\\abc\\blah", false, FILE_NOTIFY_CHANGE_NAME, 7 },
+ {BASEDIR "\\abc\\blah\\a", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\blah\\b", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\blah\\c", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\fooblah", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\zqy\\xx", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\zqy\\yyy", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\zqy\\..", true, FILE_NOTIFY_CHANGE_NAME, 40 },
+ {BASEDIR, true, FILE_NOTIFY_CHANGE_NAME, 40 },
+ {BASEDIR, false,FILE_NOTIFY_CHANGE_NAME, 6 },
+ {BASEDIR "\\atsy", false,FILE_NOTIFY_CHANGE_NAME, 4 },
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 24 },
+ {BASEDIR "\\abc", false,FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 24 },
+ };
+ int i;
+ NTSTATUS status;
+ bool all_done = false;
+
+ printf("TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_ALL;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 20000;
+
+ /*
+ setup the directory tree, and the notify buffer on each directory
+ */
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ io.ntcreatex.in.fname = dirs[i].path;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ dirs[i].fnum = io.ntcreatex.out.file.fnum;
+
+ notify.nttrans.in.completion_filter = dirs[i].filter;
+ notify.nttrans.in.file.fnum = dirs[i].fnum;
+ notify.nttrans.in.recursive = dirs[i].recursive;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smb_raw_ntcancel(req);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+ }
+
+ /* trigger 2 events in each dir */
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ char *path = talloc_asprintf(mem_ctx, "%s\\test.dir", dirs[i].path);
+ smbcli_mkdir(cli->tree, path);
+ smbcli_rmdir(cli->tree, path);
+ talloc_free(path);
+ }
+
+ /* give a bit of time for the events to propogate */
+ tv = timeval_current();
+
+ do {
+ /* count events that have happened in each dir */
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ notify.nttrans.in.file.fnum = dirs[i].fnum;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smb_raw_ntcancel(req);
+ notify.nttrans.out.num_changes = 0;
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ dirs[i].counted += notify.nttrans.out.num_changes;
+ }
+
+ all_done = true;
+
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ if (dirs[i].counted != dirs[i].expected) {
+ all_done = false;
+ }
+ }
+ } while (!all_done && timeval_elapsed(&tv) < 20);
+
+ printf("took %.4f seconds to propogate all events\n", timeval_elapsed(&tv));
+
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ if (dirs[i].counted != dirs[i].expected) {
+ printf("ERROR: i=%d expected %d got %d for '%s'\n",
+ i, dirs[i].expected, dirs[i].counted, dirs[i].path);
+ ret = false;
+ }
+ }
+
+ /*
+ run from the back, closing and deleting
+ */
+ for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
+ smbcli_close(cli->tree, dirs[i].fnum);
+ smbcli_rmdir(cli->tree, dirs[i].path);
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ basic testing of change notify
+*/
+bool torture_raw_notify(struct torture_context *torture,
+ struct smbcli_state *cli,
+ struct smbcli_state *cli2)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_notify_dir(cli, cli2, torture);
+ ret &= test_notify_mask(cli, torture);
+ ret &= test_notify_recursive(cli, torture);
+ ret &= test_notify_mask_change(cli, torture);
+ ret &= test_notify_file(cli, torture);
+ ret &= test_notify_tdis(torture);
+ ret &= test_notify_exit(torture);
+ ret &= test_notify_ulogoff(torture);
+ ret &= test_notify_tcp_dis(torture);
+ ret &= test_notify_double(cli, torture);
+ ret &= test_notify_tree(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
diff --git a/source4/torture/raw/offline.c b/source4/torture/raw/offline.c
new file mode 100644
index 0000000000..f2f0bf5d27
--- /dev/null
+++ b/source4/torture/raw/offline.c
@@ -0,0 +1,514 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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/>.
+*/
+
+/*
+ test offline files
+ */
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+#define BASEDIR "\\testoffline"
+
+static int nconnections;
+static int numstates;
+static int num_connected;
+static int test_failed;
+extern int torture_numops;
+extern int torture_entries;
+static bool test_finished;
+
+enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_SETOFFLINE, OP_GETOFFLINE, OP_ENDOFLIST};
+
+static double latencies[OP_ENDOFLIST];
+static double worst_latencies[OP_ENDOFLIST];
+
+#define FILE_SIZE 8192
+
+
+struct offline_state {
+ struct torture_context *tctx;
+ struct event_context *ev;
+ struct smbcli_tree *tree;
+ TALLOC_CTX *mem_ctx;
+ int client;
+ int fnum;
+ uint32_t count;
+ uint32_t lastcount;
+ uint32_t fnumber;
+ uint32_t offline_count;
+ uint32_t online_count;
+ char *fname;
+ struct smb_composite_loadfile *loadfile;
+ struct smb_composite_savefile *savefile;
+ struct smbcli_request *req;
+ enum offline_op op;
+ struct timeval tv_start;
+};
+
+static void test_offline(struct offline_state *state);
+
+
+static char *filename(TALLOC_CTX *ctx, int i)
+{
+ char *s = talloc_asprintf(ctx, BASEDIR "\\file%u.dat", i);
+ return s;
+}
+
+
+/*
+ called when a loadfile completes
+ */
+static void loadfile_callback(struct composite_context *ctx)
+{
+ struct offline_state *state = ctx->async.private_data;
+ NTSTATUS status;
+ int i;
+
+ status = smb_composite_loadfile_recv(ctx, state->mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to read file '%s' - %s\n",
+ state->loadfile->in.fname, nt_errstr(status));
+ test_failed++;
+ }
+
+ /* check the data is correct */
+ if (state->loadfile->out.size != FILE_SIZE) {
+ printf("Wrong file size %u - expected %u\n",
+ state->loadfile->out.size, FILE_SIZE);
+ test_failed++;
+ return;
+ }
+
+ for (i=0;i<FILE_SIZE;i++) {
+ if (state->loadfile->out.data[i] != 1+(state->fnumber % 255)) {
+ printf("Bad data in file %u (got %u expected %u)\n",
+ state->fnumber,
+ state->loadfile->out.data[i],
+ 1+(state->fnumber % 255));
+ test_failed++;
+ return;
+ }
+ }
+
+ talloc_steal(state->loadfile, state->loadfile->out.data);
+
+ state->count++;
+ talloc_free(state->loadfile);
+ state->loadfile = NULL;
+
+ if (!test_finished) {
+ test_offline(state);
+ }
+}
+
+
+/*
+ called when a savefile completes
+ */
+static void savefile_callback(struct composite_context *ctx)
+{
+ struct offline_state *state = ctx->async.private_data;
+ NTSTATUS status;
+
+ status = smb_composite_savefile_recv(ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to save file '%s' - %s\n",
+ state->savefile->in.fname, nt_errstr(status));
+ test_failed++;
+ }
+
+ state->count++;
+ talloc_free(state->savefile);
+ state->savefile = NULL;
+
+ if (!test_finished) {
+ test_offline(state);
+ }
+}
+
+
+/*
+ called when a setoffline completes
+ */
+static void setoffline_callback(struct smbcli_request *req)
+{
+ struct offline_state *state = req->async.private;
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to set offline file '%s' - %s\n",
+ state->fname, nt_errstr(status));
+ test_failed++;
+ }
+
+ state->req = NULL;
+ state->count++;
+
+ if (!test_finished) {
+ test_offline(state);
+ }
+}
+
+
+/*
+ called when a getoffline completes
+ */
+static void getoffline_callback(struct smbcli_request *req)
+{
+ struct offline_state *state = req->async.private;
+ NTSTATUS status;
+ union smb_fileinfo io;
+
+ io.getattr.level = RAW_FILEINFO_GETATTR;
+
+ status = smb_raw_pathinfo_recv(req, state->mem_ctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to get offline file '%s' - %s\n",
+ state->fname, nt_errstr(status));
+ test_failed++;
+ }
+
+ if (io.getattr.out.attrib & FILE_ATTRIBUTE_OFFLINE) {
+ state->offline_count++;
+ } else {
+ state->online_count++;
+ }
+
+ state->req = NULL;
+ state->count++;
+
+ if (!test_finished) {
+ test_offline(state);
+ }
+}
+
+
+/*
+ send the next offline file fetch request
+*/
+static void test_offline(struct offline_state *state)
+{
+ struct composite_context *ctx;
+ double lat;
+
+ lat = timeval_elapsed(&state->tv_start);
+ if (latencies[state->op] < lat) {
+ latencies[state->op] = lat;
+ }
+
+ state->op = (enum offline_op) (random() % OP_ENDOFLIST);
+
+ state->fnumber = random() % torture_numops;
+ talloc_free(state->fname);
+ state->fname = filename(state->mem_ctx, state->fnumber);
+
+ state->tv_start = timeval_current();
+
+ switch (state->op) {
+ case OP_LOADFILE:
+ state->loadfile = talloc_zero(state->mem_ctx, struct smb_composite_loadfile);
+ state->loadfile->in.fname = state->fname;
+
+ ctx = smb_composite_loadfile_send(state->tree, state->loadfile);
+ if (ctx == NULL) {
+ printf("Failed to setup loadfile for %s\n", state->fname);
+ test_failed = true;
+ }
+
+ talloc_steal(state->loadfile, ctx);
+
+ ctx->async.fn = loadfile_callback;
+ ctx->async.private_data = state;
+ break;
+
+ case OP_SAVEFILE:
+ state->savefile = talloc_zero(state->mem_ctx, struct smb_composite_savefile);
+
+ state->savefile->in.fname = state->fname;
+ state->savefile->in.data = talloc_size(state->savefile, FILE_SIZE);
+ state->savefile->in.size = FILE_SIZE;
+ memset(state->savefile->in.data, 1+(state->fnumber%255), FILE_SIZE);
+
+ ctx = smb_composite_savefile_send(state->tree, state->savefile);
+ if (ctx == NULL) {
+ printf("Failed to setup savefile for %s\n", state->fname);
+ test_failed = true;
+ }
+
+ talloc_steal(state->savefile, ctx);
+
+ ctx->async.fn = savefile_callback;
+ ctx->async.private_data = state;
+ break;
+
+ case OP_SETOFFLINE: {
+ union smb_setfileinfo io;
+ ZERO_STRUCT(io);
+ io.setattr.level = RAW_SFILEINFO_SETATTR;
+ io.setattr.in.attrib = FILE_ATTRIBUTE_OFFLINE;
+ io.setattr.in.file.path = state->fname;
+ /* make the file 1 hour old, to get past mininum age restrictions
+ for HSM systems */
+ io.setattr.in.write_time = time(NULL) - 60*60;
+
+ state->req = smb_raw_setpathinfo_send(state->tree, &io);
+ if (state->req == NULL) {
+ printf("Failed to setup setoffline for %s\n", state->fname);
+ test_failed = true;
+ }
+
+ state->req->async.fn = setoffline_callback;
+ state->req->async.private = state;
+ break;
+ }
+
+ case OP_GETOFFLINE: {
+ union smb_fileinfo io;
+ ZERO_STRUCT(io);
+ io.getattr.level = RAW_FILEINFO_GETATTR;
+ io.getattr.in.file.path = state->fname;
+
+ state->req = smb_raw_pathinfo_send(state->tree, &io);
+ if (state->req == NULL) {
+ printf("Failed to setup getoffline for %s\n", state->fname);
+ test_failed = true;
+ }
+
+ state->req->async.fn = getoffline_callback;
+ state->req->async.private = state;
+ break;
+ }
+
+ default:
+ printf("bad operation??\n");
+ break;
+ }
+}
+
+
+
+
+static void echo_completion(struct smbcli_request *req)
+{
+ struct offline_state *state = (struct offline_state *)req->async.private;
+ NTSTATUS status = smbcli_request_simple_recv(req);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
+ talloc_free(state->tree);
+ state->tree = NULL;
+ num_connected--;
+ DEBUG(0,("lost connection\n"));
+ test_failed++;
+ }
+}
+
+static void report_rate(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct offline_state *state = talloc_get_type(private_data,
+ struct offline_state);
+ int i;
+ uint32_t total=0, total_offline=0, total_online=0;
+ for (i=0;i<numstates;i++) {
+ total += state[i].count - state[i].lastcount;
+ if (timeval_elapsed(&state[i].tv_start) > latencies[state[i].op]) {
+ latencies[state[i].op] = timeval_elapsed(&state[i].tv_start);
+ }
+ state[i].lastcount = state[i].count;
+ total_online += state[i].online_count;
+ total_offline += state[i].offline_count;
+ }
+ printf("ops/s=%4u offline=%5u online=%4u set_lat=%.1f/%.1f get_lat=%.1f/%.1f save_lat=%.1f/%.1f load_lat=%.1f/%.1f\n",
+ total, total_offline, total_online,
+ latencies[OP_SETOFFLINE],
+ worst_latencies[OP_SETOFFLINE],
+ latencies[OP_GETOFFLINE],
+ worst_latencies[OP_GETOFFLINE],
+ latencies[OP_SAVEFILE],
+ worst_latencies[OP_SAVEFILE],
+ latencies[OP_LOADFILE],
+ worst_latencies[OP_LOADFILE]);
+ fflush(stdout);
+ event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
+
+ for (i=0;i<OP_ENDOFLIST;i++) {
+ if (latencies[i] > worst_latencies[i]) {
+ worst_latencies[i] = latencies[i];
+ }
+ latencies[i] = 0;
+ }
+
+ /* send an echo on each interface to ensure it stays alive - this helps
+ with IP takeover */
+ for (i=0;i<numstates;i++) {
+ struct smb_echo p;
+ struct smbcli_request *req;
+
+ if (!state[i].tree) {
+ continue;
+ }
+
+ p.in.repeat_count = 1;
+ p.in.size = 0;
+ p.in.data = NULL;
+ req = smb_raw_echo_send(state[i].tree->session->transport, &p);
+ req->async.private = &state[i];
+ req->async.fn = echo_completion;
+ }
+}
+
+/*
+ test offline file handling
+*/
+bool torture_test_offline(struct torture_context *torture)
+{
+ bool ret = true;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ int i;
+ int timelimit = torture_setting_int(torture, "timelimit", 10);
+ struct timeval tv;
+ struct offline_state *state;
+ struct smbcli_state *cli;
+ bool progress;
+ progress = torture_setting_bool(torture, "progress", true);
+
+ nconnections = torture_setting_int(torture, "nprocs", 4);
+ numstates = nconnections * torture_entries;
+
+ state = talloc_zero_array(mem_ctx, struct offline_state, numstates);
+
+ printf("Opening %d connections with %d simultaneous operations and %u files\n", nconnections, numstates, torture_numops);
+ for (i=0;i<nconnections;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].ev = torture->ev;
+ if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) {
+ return false;
+ }
+ state[i].tree = cli->tree;
+ state[i].client = i;
+ /* allow more time for offline files */
+ state[i].tree->session->transport->options.request_timeout = 200;
+ }
+
+ /* the others are repeats on the earlier connections */
+ for (i=nconnections;i<numstates;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].ev = torture->ev;
+ state[i].tree = state[i % nconnections].tree;
+ state[i].client = i;
+ }
+
+ num_connected = i;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ goto failed;
+ }
+
+ /* pre-create files */
+ printf("Pre-creating %u files ....\n", torture_numops);
+ for (i=0;i<torture_numops;i++) {
+ int fnum;
+ char *fname = filename(mem_ctx, i);
+ char buf[FILE_SIZE];
+ NTSTATUS status;
+
+ memset(buf, 1+(i % 255), sizeof(buf));
+
+ fnum = smbcli_open(state[0].tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to open %s on connection %d\n", fname, i);
+ goto failed;
+ }
+
+ if (smbcli_write(state[0].tree, fnum, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to write file of size %u\n", FILE_SIZE);
+ goto failed;
+ }
+
+ status = smbcli_close(state[0].tree, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ talloc_free(fname);
+ }
+
+ /* start the async ops */
+ for (i=0;i<numstates;i++) {
+ state[i].tv_start = timeval_current();
+ test_offline(&state[i]);
+ }
+
+ tv = timeval_current();
+
+ if (progress) {
+ event_add_timed(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
+ }
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ event_loop_once(torture->ev);
+
+ if (test_failed) {
+ DEBUG(0,("test failed\n"));
+ goto failed;
+ }
+ }
+
+ printf("\nWaiting for completion\n");
+ test_finished = true;
+ for (i=0;i<numstates;i++) {
+ while (state[i].loadfile ||
+ state[i].savefile ||
+ state[i].req) {
+ event_loop_once(torture->ev);
+ }
+ }
+
+ printf("worst latencies: set_lat=%.1f get_lat=%.1f save_lat=%.1f load_lat=%.1f\n",
+ worst_latencies[OP_SETOFFLINE],
+ worst_latencies[OP_GETOFFLINE],
+ worst_latencies[OP_SAVEFILE],
+ worst_latencies[OP_LOADFILE]);
+
+ smbcli_deltree(state[0].tree, BASEDIR);
+ talloc_free(mem_ctx);
+ printf("\n");
+ return ret;
+
+failed:
+ talloc_free(mem_ctx);
+ return false;
+}
diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c
new file mode 100644
index 0000000000..f3494ea3d0
--- /dev/null
+++ b/source4/torture/raw/open.c
@@ -0,0 +1,1575 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_OPEN_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "librpc/gen_ndr/security.h"
+#include "lib/events/events.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+/* enum for whether reads/writes are possible on a file */
+enum rdwr_mode {RDWR_NONE, RDWR_RDONLY, RDWR_WRONLY, RDWR_RDWR};
+
+#define BASEDIR "\\rawopen"
+
+/*
+ check if a open file can be read/written
+*/
+static enum rdwr_mode check_rdwr(struct smbcli_tree *tree, int fnum)
+{
+ uint8_t c = 1;
+ bool can_read = (smbcli_read(tree, fnum, &c, 0, 1) == 1);
+ bool can_write = (smbcli_write(tree, fnum, 0, &c, 0, 1) == 1);
+ if ( can_read && can_write) return RDWR_RDWR;
+ if ( can_read && !can_write) return RDWR_RDONLY;
+ if (!can_read && can_write) return RDWR_WRONLY;
+ return RDWR_NONE;
+}
+
+/*
+ describe a RDWR mode as a string
+*/
+static const char *rdwr_string(enum rdwr_mode m)
+{
+ switch (m) {
+ case RDWR_NONE: return "NONE";
+ case RDWR_RDONLY: return "RDONLY";
+ case RDWR_WRONLY: return "WRONLY";
+ case RDWR_RDWR: return "RDWR";
+ }
+ return "-";
+}
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CREATE_FILE do { \
+ fnum = create_complex_file(cli, tctx, fname); \
+ if (fnum == -1) { \
+ printf("(%s) Failed to create %s - %s\n", __location__, fname, smbcli_errstr(cli->tree)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_RDWR(fnum, correct) do { \
+ enum rdwr_mode m = check_rdwr(cli->tree, fnum); \
+ if (m != correct) { \
+ printf("(%s) Incorrect readwrite mode %s - expected %s\n", \
+ __location__, rdwr_string(m), rdwr_string(correct)); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_TIME(t, field) do { \
+ time_t t1, t2; \
+ finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \
+ finfo.all_info.in.file.path = fname; \
+ status = smb_raw_pathinfo(cli->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) { \
+ printf("(%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_INFO; \
+ finfo.all_info.in.file.path = fname; \
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ t2 = finfo.all_info.out.field; \
+ if (t != t2) { \
+ printf("(%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_INFO; \
+ finfo.all_info.in.file.path = fname; \
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ if ((v) != (finfo.all_info.out.field)) { \
+ printf("(%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)) { \
+ printf("(%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.path = fname; \
+ sfinfo.basic_info.in.attrib = sattrib; \
+ status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ printf("(%s) Failed to set attrib 0x%x on %s\n", \
+ __location__, sattrib, fname); \
+ }} while (0)
+
+/*
+ test RAW_OPEN_OPEN
+*/
+static bool test_open(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = BASEDIR "\\torture_open.txt";
+ NTSTATUS status;
+ int fnum = -1, fnum2;
+ bool ret = true;
+
+ printf("Checking RAW_OPEN_OPEN\n");
+
+ io.openold.level = RAW_OPEN_OPEN;
+ io.openold.in.fname = fname;
+ io.openold.in.open_mode = OPEN_FLAGS_FCB;
+ io.openold.in.search_attrs = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ fnum = io.openold.out.file.fnum;
+
+ smbcli_unlink(cli->tree, fname);
+ CREATE_FILE;
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum, RDWR_RDWR);
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum2, RDWR_RDWR);
+ smbcli_close(cli->tree, fnum2);
+ smbcli_close(cli->tree, fnum);
+
+ /* check the read/write modes */
+ io.openold.level = RAW_OPEN_OPEN;
+ io.openold.in.fname = fname;
+ io.openold.in.search_attrs = 0;
+
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_READ;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum, RDWR_RDONLY);
+ smbcli_close(cli->tree, fnum);
+
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum, RDWR_WRONLY);
+ smbcli_close(cli->tree, fnum);
+
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_RDWR;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum, RDWR_RDWR);
+ smbcli_close(cli->tree, fnum);
+
+ /* check the share modes roughly - not a complete matrix */
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum, RDWR_RDWR);
+
+ if (io.openold.in.open_mode != io.openold.out.rmode) {
+ printf("(%s) rmode should equal open_mode - 0x%x 0x%x\n",
+ __location__, io.openold.out.rmode, io.openold.in.open_mode);
+ }
+
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_NONE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_READ | OPEN_FLAGS_DENY_NONE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.openold.out.file.fnum;
+ CHECK_RDWR(fnum2, RDWR_RDONLY);
+ smbcli_close(cli->tree, fnum);
+ smbcli_close(cli->tree, fnum2);
+
+
+ /* check the returned write time */
+ io.openold.level = RAW_OPEN_OPEN;
+ io.openold.in.fname = fname;
+ io.openold.in.search_attrs = 0;
+ io.openold.in.open_mode = OPEN_FLAGS_OPEN_READ;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openold.out.file.fnum;
+
+ /* check other reply fields */
+ CHECK_TIME(io.openold.out.write_time, write_time);
+ CHECK_ALL_INFO(io.openold.out.size, size);
+ CHECK_ALL_INFO(io.openold.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_OPENX
+*/
+static bool test_openx(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = BASEDIR "\\torture_openx.txt";
+ const char *fname_exe = BASEDIR "\\torture_openx.exe";
+ NTSTATUS status;
+ int fnum = -1, fnum2;
+ bool ret = true;
+ int i;
+ struct timeval tv;
+ struct {
+ uint16_t open_func;
+ bool with_file;
+ NTSTATUS correct_status;
+ } open_funcs[] = {
+ { OPENX_OPEN_FUNC_OPEN, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_FAIL, true, NT_STATUS_DOS(ERRDOS, ERRbadaccess) },
+ { OPENX_OPEN_FUNC_FAIL, false, NT_STATUS_DOS(ERRDOS, ERRbadaccess) },
+ { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION },
+ { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_TRUNC, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_TRUNC, false, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK },
+ };
+
+ printf("Checking RAW_OPEN_OPENX\n");
+ smbcli_unlink(cli->tree, fname);
+
+ io.openx.level = RAW_OPEN_OPENX;
+ io.openx.in.fname = fname;
+ io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+ io.openx.in.search_attrs = 0;
+ io.openx.in.file_attrs = 0;
+ io.openx.in.write_time = 0;
+ io.openx.in.size = 1024*1024;
+ io.openx.in.timeout = 0;
+
+ /* check all combinations of open_func */
+ for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ fnum = create_complex_file(cli, tctx, fname);
+ if (fnum == -1) {
+ d_printf("Failed to create file %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+ }
+ io.openx.in.open_func = open_funcs[i].open_func;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+ printf("(%s) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n",
+ __location__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status),
+ i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_func);
+ ret = false;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+ }
+ if (open_funcs[i].with_file) {
+ smbcli_unlink(cli->tree, fname);
+ }
+ }
+
+ smbcli_unlink(cli->tree, fname);
+
+ /* check the basic return fields */
+ io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openx.out.file.fnum;
+
+ CHECK_ALL_INFO(io.openx.out.size, size);
+ CHECK_TIME(io.openx.out.write_time, write_time);
+ CHECK_ALL_INFO(io.openx.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED);
+ CHECK_VAL(io.openx.out.access, OPENX_MODE_ACCESS_RDWR);
+ CHECK_VAL(io.openx.out.ftype, 0);
+ CHECK_VAL(io.openx.out.devstate, 0);
+ CHECK_VAL(io.openx.out.action, OPENX_ACTION_CREATED);
+ CHECK_VAL(io.openx.out.size, 1024*1024);
+ CHECK_ALL_INFO(io.openx.in.size, size);
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* check the fields when the file already existed */
+ fnum2 = create_complex_file(cli, tctx, fname);
+ if (fnum2 == -1) {
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum2);
+
+ io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openx.out.file.fnum;
+
+ CHECK_ALL_INFO(io.openx.out.size, size);
+ CHECK_TIME(io.openx.out.write_time, write_time);
+ CHECK_VAL(io.openx.out.action, OPENX_ACTION_EXISTED);
+ CHECK_VAL(io.openx.out.unknown, 0);
+ CHECK_ALL_INFO(io.openx.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED);
+ smbcli_close(cli->tree, fnum);
+
+ /* now check the search attrib for hidden files - win2003 ignores this? */
+ SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN);
+ CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib);
+
+ io.openx.in.search_attrs = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+
+ io.openx.in.search_attrs = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+
+ SET_ATTRIB(FILE_ATTRIBUTE_NORMAL);
+ smbcli_unlink(cli->tree, fname);
+
+ /* and check attrib on create */
+ io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE;
+ io.openx.in.search_attrs = 0;
+ io.openx.in.file_attrs = FILE_ATTRIBUTE_SYSTEM;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE,
+ attrib & ~(FILE_ATTRIBUTE_NONINDEXED|
+ FILE_ATTRIBUTE_SPARSE));
+ }
+ else {
+ CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE,
+ attrib & ~(FILE_ATTRIBUTE_NONINDEXED));
+ }
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* check timeout on create - win2003 ignores the timeout! */
+ io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+ io.openx.in.file_attrs = 0;
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openx.out.file.fnum;
+
+ io.openx.in.timeout = 20000;
+ tv = timeval_current();
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+ if (timeval_elapsed(&tv) > 3.0) {
+ printf("(%s) Incorrect timing in openx with timeout - waited %.2f seconds\n",
+ __location__, timeval_elapsed(&tv));
+ ret = false;
+ }
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* now this is a really weird one - open for execute implies create?! */
+ io.openx.in.fname = fname;
+ io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_EXEC | OPENX_MODE_DENY_NONE;
+ io.openx.in.search_attrs = 0;
+ io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL;
+ io.openx.in.file_attrs = 0;
+ io.openx.in.write_time = 0;
+ io.openx.in.size = 0;
+ io.openx.in.timeout = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+
+ /* check the extended return flag */
+ io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | OPENX_FLAGS_EXTENDED_RETURN;
+ io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(io.openx.out.access_mask, SEC_STD_ALL);
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+
+ io.openx.in.fname = "\\A.+,;=[].B";
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ /* Check the mapping for open exec. */
+
+ /* First create an .exe file. */
+ smbcli_unlink(cli->tree, fname_exe);
+ fnum = create_complex_file(cli, tctx, fname_exe);
+ smbcli_close(cli->tree, fnum);
+
+ io.openx.level = RAW_OPEN_OPENX;
+ io.openx.in.fname = fname_exe;
+ io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_EXEC | OPENX_MODE_DENY_NONE;
+ io.openx.in.search_attrs = 0;
+ io.openx.in.file_attrs = 0;
+ io.openx.in.write_time = 0;
+ io.openx.in.size = 0;
+ io.openx.in.timeout = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* Can we read and write ? */
+ CHECK_RDWR(io.openx.out.file.fnum, RDWR_RDONLY);
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+ smbcli_unlink(cli->tree, fname);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname_exe);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_T2OPEN
+
+ many thanks to kukks for a sniff showing how this works with os2->w2k
+*/
+static bool test_t2open(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname1 = BASEDIR "\\torture_t2open_yes.txt";
+ const char *fname2 = BASEDIR "\\torture_t2open_no.txt";
+ const char *fname = BASEDIR "\\torture_t2open_3.txt";
+ NTSTATUS status;
+ int fnum;
+ bool ret = true;
+ int i;
+ struct {
+ uint16_t open_func;
+ bool with_file;
+ NTSTATUS correct_status;
+ } open_funcs[] = {
+ { OPENX_OPEN_FUNC_OPEN, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_FAIL, true, NT_STATUS_OBJECT_NAME_COLLISION },
+ { OPENX_OPEN_FUNC_FAIL, false, NT_STATUS_OBJECT_NAME_COLLISION },
+ { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION },
+ { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OBJECT_NAME_COLLISION },
+ { OPENX_OPEN_FUNC_TRUNC, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_TRUNC, false, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, true, NT_STATUS_OK },
+ { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, false, NT_STATUS_OK },
+ };
+
+ fnum = create_complex_file(cli, tctx, fname1);
+ if (fnum == -1) {
+ d_printf("Failed to create file %s - %s\n", fname1, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+
+ printf("Checking RAW_OPEN_T2OPEN\n");
+
+ io.t2open.level = RAW_OPEN_T2OPEN;
+ io.t2open.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ io.t2open.in.open_mode = OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR;
+ io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+ io.t2open.in.search_attrs = 0;
+ io.t2open.in.file_attrs = 0;
+ io.t2open.in.write_time = 0;
+ io.t2open.in.size = 0;
+ io.t2open.in.timeout = 0;
+
+ io.t2open.in.num_eas = 3;
+ io.t2open.in.eas = talloc_array(tctx, struct ea_struct, io.t2open.in.num_eas);
+ io.t2open.in.eas[0].flags = 0;
+ io.t2open.in.eas[0].name.s = ".CLASSINFO";
+ io.t2open.in.eas[0].value = data_blob_talloc(tctx, "first value", 11);
+ io.t2open.in.eas[1].flags = 0;
+ io.t2open.in.eas[1].name.s = "EA TWO";
+ io.t2open.in.eas[1].value = data_blob_talloc(tctx, "foo", 3);
+ io.t2open.in.eas[2].flags = 0;
+ io.t2open.in.eas[2].name.s = "X THIRD";
+ io.t2open.in.eas[2].value = data_blob_talloc(tctx, "xy", 2);
+
+ /* check all combinations of open_func */
+ for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+ again:
+ if (open_funcs[i].with_file) {
+ io.t2open.in.fname = fname1;
+ } else {
+ io.t2open.in.fname = fname2;
+ }
+ io.t2open.in.open_func = open_funcs[i].open_func;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if ((io.t2open.in.num_eas != 0)
+ && NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED)
+ && torture_setting_bool(tctx, "samba3", false)) {
+ printf("(%s) EAs not supported, not treating as fatal "
+ "in Samba3 test\n", __location__);
+ io.t2open.in.num_eas = 0;
+ goto again;
+ }
+
+ if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+ printf("(%s) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n",
+ __location__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status),
+ i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_func);
+ ret = false;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ smbcli_close(cli->tree, io.t2open.out.file.fnum);
+ }
+ }
+
+ smbcli_unlink(cli->tree, fname1);
+ smbcli_unlink(cli->tree, fname2);
+
+ /* check the basic return fields */
+ io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+ io.t2open.in.write_time = 0;
+ io.t2open.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.t2open.out.file.fnum;
+
+ CHECK_ALL_INFO(io.t2open.out.size, size);
+#if 0
+ /* windows appears to leak uninitialised memory here */
+ CHECK_VAL(io.t2open.out.write_time, 0);
+#endif
+ CHECK_ALL_INFO(io.t2open.out.attrib, attrib & ~FILE_ATTRIBUTE_NONINDEXED);
+ CHECK_VAL(io.t2open.out.access, OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR);
+ CHECK_VAL(io.t2open.out.ftype, 0);
+ CHECK_VAL(io.t2open.out.devstate, 0);
+ CHECK_VAL(io.t2open.out.action, OPENX_ACTION_CREATED);
+ smbcli_close(cli->tree, fnum);
+
+ status = torture_check_ea(cli, fname, ".CLASSINFO", "first value");
+ CHECK_STATUS(status, io.t2open.in.num_eas
+ ? NT_STATUS_OK : NT_STATUS_EAS_NOT_SUPPORTED);
+ status = torture_check_ea(cli, fname, "EA TWO", "foo");
+ CHECK_STATUS(status, io.t2open.in.num_eas
+ ? NT_STATUS_OK : NT_STATUS_EAS_NOT_SUPPORTED);
+ status = torture_check_ea(cli, fname, "X THIRD", "xy");
+ CHECK_STATUS(status, io.t2open.in.num_eas
+ ? NT_STATUS_OK : NT_STATUS_EAS_NOT_SUPPORTED);
+
+ /* now check the search attrib for hidden files - win2003 ignores this? */
+ SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN);
+ CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib);
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.t2open.out.file.fnum);
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.t2open.out.file.fnum);
+
+ SET_ATTRIB(FILE_ATTRIBUTE_NORMAL);
+ smbcli_unlink(cli->tree, fname);
+
+ /* and check attrib on create */
+ io.t2open.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE;
+ io.t2open.in.file_attrs = FILE_ATTRIBUTE_SYSTEM;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* check timeout on create - win2003 ignores the timeout! */
+ io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+ io.t2open.in.file_attrs = 0;
+ io.t2open.in.timeout = 20000;
+ io.t2open.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_NTCREATEX
+*/
+static bool test_ntcreatex(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = BASEDIR "\\torture_ntcreatex.txt";
+ const char *dname = BASEDIR "\\torture_ntcreatex.dir";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ int i;
+ struct {
+ uint32_t open_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 },
+ };
+
+ printf("Checking RAW_OPEN_NTCREATEX\n");
+
+ /* reasonable default parameters */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 1024*1024;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /* test the open disposition */
+ for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Failed to create file %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+ }
+ io.ntcreatex.in.open_disposition = open_funcs[i].open_disp;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+ printf("(%s) incorrect status %s should be %s (i=%d 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].open_disp);
+ ret = false;
+ }
+ if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) {
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ smbcli_unlink(cli->tree, fname);
+ }
+ }
+
+ /* basic field testing */
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+
+ /* check fields when the file already existed */
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+ fnum = create_complex_file(cli, tctx, fname);
+ if (fnum == -1) {
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+
+ /* create a directory */
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.fname = dname;
+ fname = dname;
+
+ smbcli_rmdir(cli->tree, fname);
+ smbcli_unlink(cli->tree, fname);
+
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_VAL(io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_DIRECTORY);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.is_directory, 1);
+ CHECK_VAL(io.ntcreatex.out.size, 0);
+ CHECK_VAL(io.ntcreatex.out.alloc_size, 0);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_unlink(cli->tree, fname);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_NTTRANS_CREATE
+*/
+static bool test_nttrans_create(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = BASEDIR "\\torture_ntcreatex.txt";
+ const char *dname = BASEDIR "\\torture_ntcreatex.dir";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ int i;
+ uint32_t ok_mask, not_supported_mask, invalid_parameter_mask;
+ uint32_t not_a_directory_mask, unexpected_mask;
+ struct {
+ uint32_t open_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 },
+ };
+
+ printf("Checking RAW_OPEN_NTTRANS_CREATE\n");
+
+ /* reasonable default parameters */
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 1024*1024;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.sec_desc = NULL;
+ io.ntcreatex.in.ea_list = NULL;
+
+ /* test the open disposition */
+ for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Failed to create file %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+ }
+ io.ntcreatex.in.open_disposition = open_funcs[i].open_disp;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+ printf("(%s) incorrect status %s should be %s (i=%d 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].open_disp);
+ ret = false;
+ }
+ if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) {
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ smbcli_unlink(cli->tree, fname);
+ }
+ }
+
+ /* basic field testing */
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+
+ /* check fields when the file already existed */
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+ fnum = create_complex_file(cli, tctx, fname);
+ if (fnum == -1) {
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_close(cli->tree, fnum);
+
+ /* check no-recall - don't pull a file from tape on a HSM */
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NO_RECALL;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_close(cli->tree, fnum);
+
+ /* Check some create options (these all should be ignored) */
+ for (i=0; i < 32; i++) {
+ uint32_t create_option = (1 << i) & NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
+ if (create_option == 0) {
+ continue;
+ }
+ io.ntcreatex.in.create_options = create_option;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ntcreatex create option 0x%08x gave %s - should give NT_STATUS_OK\n",
+ create_option, nt_errstr(status));
+ }
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_close(cli->tree, fnum);
+ }
+
+ io.ntcreatex.in.file_attr = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ /* Check for options that should return NOT_SUPPORTED, OK or INVALID_PARAMETER */
+ ok_mask = 0;
+ not_supported_mask = 0;
+ invalid_parameter_mask = 0;
+ not_a_directory_mask = 0;
+ unexpected_mask = 0;
+ for (i=0; i < 32; i++) {
+ uint32_t create_option = 1<<i;
+ if (create_option & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+ continue;
+ }
+ io.ntcreatex.in.create_options = create_option;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ not_supported_mask |= create_option;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ ok_mask |= create_option;
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ invalid_parameter_mask |= create_option;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ not_a_directory_mask |= 1<<i;
+ } else {
+ unexpected_mask |= 1<<i;
+ printf("create option 0x%08x returned %s\n", create_option, nt_errstr(status));
+ }
+ }
+
+ CHECK_VAL(ok_mask, 0x00efcfce);
+ CHECK_VAL(not_a_directory_mask, 0x00000001);
+ CHECK_VAL(not_supported_mask, 0x00002000);
+ CHECK_VAL(invalid_parameter_mask, 0xff100030);
+ CHECK_VAL(unexpected_mask, 0x00000000);
+
+ smbcli_unlink(cli->tree, fname);
+
+
+ /* create a directory */
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.fname = dname;
+ fname = dname;
+
+ smbcli_rmdir(cli->tree, fname);
+ smbcli_unlink(cli->tree, fname);
+
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+ CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+ CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+ CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+ CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+ CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+ CHECK_VAL(io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_DIRECTORY);
+ CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+ CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+ CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+ CHECK_VAL(io.ntcreatex.out.is_directory, 1);
+ CHECK_VAL(io.ntcreatex.out.size, 0);
+ CHECK_VAL(io.ntcreatex.out.alloc_size, 0);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_unlink(cli->tree, fname);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+/*
+ test RAW_OPEN_NTCREATEX with an already opened and byte range locked file
+
+ I've got an application that does a similar sequence of ntcreate&x,
+ locking&x and another ntcreate&x with
+ open_disposition==NTCREATEX_DISP_OVERWRITE_IF. Windows 2003 allows the
+ second open.
+*/
+static bool test_ntcreatex_brlocked(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io, io1;
+ union smb_lock io2;
+ struct smb_lock_entry lock[1];
+ const char *fname = BASEDIR "\\torture_ntcreatex.txt";
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing ntcreatex with a byte range locked file\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = 0x2019f;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION;
+ io.ntcreatex.in.security_flags = NTCREATEX_SECURITY_DYNAMIC |
+ NTCREATEX_SECURITY_ALL;
+ io.ntcreatex.in.fname = fname;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io2.lockx.level = RAW_LOCK_LOCKX;
+ io2.lockx.in.file.fnum = io.ntcreatex.out.file.fnum;
+ io2.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io2.lockx.in.timeout = 0;
+ io2.lockx.in.ulock_cnt = 0;
+ io2.lockx.in.lock_cnt = 1;
+ lock[0].pid = cli->session->pid;
+ lock[0].offset = 0;
+ lock[0].count = 0x1;
+ io2.lockx.in.locks = &lock[0];
+ status = smb_raw_lock(cli->tree, &io2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io1.generic.level = RAW_OPEN_NTCREATEX;
+ io1.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io1.ntcreatex.in.root_fid = 0;
+ io1.ntcreatex.in.access_mask = 0x20196;
+ io1.ntcreatex.in.alloc_size = 0;
+ io1.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io1.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io1.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io1.ntcreatex.in.create_options = 0;
+ io1.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION;
+ io1.ntcreatex.in.security_flags = NTCREATEX_SECURITY_DYNAMIC |
+ NTCREATEX_SECURITY_ALL;
+ io1.ntcreatex.in.fname = fname;
+
+ status = smb_raw_open(cli->tree, tctx, &io1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ done:
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ smbcli_close(cli->tree, io1.ntcreatex.out.file.fnum);
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+/*
+ test RAW_OPEN_MKNEW
+*/
+static bool test_mknew(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ const char *fname = BASEDIR "\\torture_mknew.txt";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ time_t basetime = (time(NULL) + 3600*24*3) & ~1;
+ union smb_fileinfo finfo;
+
+ printf("Checking RAW_OPEN_MKNEW\n");
+
+ io.mknew.level = RAW_OPEN_MKNEW;
+ io.mknew.in.attrib = 0;
+ io.mknew.in.write_time = 0;
+ io.mknew.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.mknew.out.file.fnum;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* make sure write_time works */
+ io.mknew.in.write_time = basetime;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.mknew.out.file.fnum;
+ CHECK_TIME(basetime, write_time);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* make sure file_attrs works */
+ io.mknew.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.mknew.out.file.fnum;
+ CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE,
+ attrib & ~FILE_ATTRIBUTE_NONINDEXED);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_CREATE
+*/
+static bool test_create(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ const char *fname = BASEDIR "\\torture_create.txt";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ time_t basetime = (time(NULL) + 3600*24*3) & ~1;
+ union smb_fileinfo finfo;
+
+ printf("Checking RAW_OPEN_CREATE\n");
+
+ io.create.level = RAW_OPEN_CREATE;
+ io.create.in.attrib = 0;
+ io.create.in.write_time = 0;
+ io.create.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.create.out.file.fnum;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, io.create.out.file.fnum);
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* make sure write_time works */
+ io.create.in.write_time = basetime;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.create.out.file.fnum;
+ CHECK_TIME(basetime, write_time);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ /* make sure file_attrs works */
+ io.create.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.create.out.file.fnum;
+ CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE,
+ attrib & ~FILE_ATTRIBUTE_NONINDEXED);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_CTEMP
+*/
+static bool test_ctemp(struct smbcli_state *cli, TALLOC_CTX *tctx)
+{
+ union smb_open io;
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ time_t basetime = (time(NULL) + 3600*24*3) & ~1;
+ union smb_fileinfo finfo;
+ const char *name, *fname = NULL;
+
+ printf("Checking RAW_OPEN_CTEMP\n");
+
+ io.ctemp.level = RAW_OPEN_CTEMP;
+ io.ctemp.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ io.ctemp.in.write_time = basetime;
+ io.ctemp.in.directory = BASEDIR;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ctemp.out.file.fnum;
+
+ name = io.ctemp.out.name;
+
+ finfo.generic.level = RAW_FILEINFO_NAME_INFO;
+ finfo.generic.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ fname = finfo.name_info.out.fname.s;
+ d_printf("ctemp name=%s real name=%s\n", name, fname);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ if (fname) {
+ smbcli_unlink(cli->tree, fname);
+ }
+
+ return ret;
+}
+
+
+/*
+ test chained RAW_OPEN_OPENX_READX
+*/
+static bool test_chained(struct smbcli_state *cli, TALLOC_CTX *tctx)
+{
+ union smb_open io;
+ const char *fname = BASEDIR "\\torture_chained.txt";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ const char *buf = "test";
+ char buf2[4];
+
+ printf("Checking RAW_OPEN_OPENX chained with READX\n");
+ smbcli_unlink(cli->tree, fname);
+
+ fnum = create_complex_file(cli, tctx, fname);
+
+ smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
+
+ smbcli_close(cli->tree, fnum);
+
+ io.openxreadx.level = RAW_OPEN_OPENX_READX;
+ io.openxreadx.in.fname = fname;
+ io.openxreadx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ io.openxreadx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+ io.openxreadx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+ io.openxreadx.in.search_attrs = 0;
+ io.openxreadx.in.file_attrs = 0;
+ io.openxreadx.in.write_time = 0;
+ io.openxreadx.in.size = 1024*1024;
+ io.openxreadx.in.timeout = 0;
+
+ io.openxreadx.in.offset = 0;
+ io.openxreadx.in.mincnt = sizeof(buf);
+ io.openxreadx.in.maxcnt = sizeof(buf);
+ io.openxreadx.in.remaining = 0;
+ io.openxreadx.out.data = (uint8_t *)buf2;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openxreadx.out.file.fnum;
+
+ if (memcmp(buf, buf2, sizeof(buf)) != 0) {
+ d_printf("wrong data in reply buffer\n");
+ ret = false;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+/*
+ test RAW_OPEN_OPENX without a leading slash on the path.
+ NetApp filers are known to fail on this.
+
+*/
+static bool test_no_leading_slash(struct smbcli_state *cli, TALLOC_CTX *tctx)
+{
+ union smb_open io;
+ const char *fname = BASEDIR "\\torture_no_leading_slash.txt";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+ const char *buf = "test";
+
+ printf("Checking RAW_OPEN_OPENX without leading slash on path\n");
+ smbcli_unlink(cli->tree, fname);
+
+ /* Create the file */
+ fnum = create_complex_file(cli, tctx, fname);
+ smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
+ smbcli_close(cli->tree, fnum);
+
+ /* Prepare to open the file using path without leading slash */
+ io.openx.level = RAW_OPEN_OPENX;
+ io.openx.in.fname = fname + 1;
+ io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+ io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+ io.openx.in.search_attrs = 0;
+ io.openx.in.file_attrs = 0;
+ io.openx.in.write_time = 0;
+ io.openx.in.size = 1024*1024;
+ io.openx.in.timeout = 0;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.openx.out.file.fnum;
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+/* A little torture test to expose a race condition in Samba 3.0.20 ... :-) */
+
+static bool test_raw_open_multi(struct torture_context *tctx)
+{
+ struct smbcli_state *cli;
+ TALLOC_CTX *mem_ctx = talloc_init("torture_test_oplock_multi");
+ const char *fname = "\\test_oplock.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ struct smbcli_state **clients;
+ struct smbcli_request **requests;
+ union smb_open *ios;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ const char *share = torture_setting_string(tctx, "share", NULL);
+ int i, num_files = 3;
+ int num_ok = 0;
+ int num_collision = 0;
+
+ clients = talloc_array(mem_ctx, struct smbcli_state *, num_files);
+ requests = talloc_array(mem_ctx, struct smbcli_request *, num_files);
+ ios = talloc_array(mem_ctx, union smb_open, num_files);
+ if ((tctx->ev == NULL) || (clients == NULL) || (requests == NULL) ||
+ (ios == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ return false;
+ }
+
+ if (!torture_open_connection_share(mem_ctx, &cli, tctx, host, share, tctx->ev)) {
+ return false;
+ }
+
+ cli->tree->session->transport->options.request_timeout = 60;
+
+ for (i=0; i<num_files; i++) {
+ if (!torture_open_connection_share(mem_ctx, &(clients[i]),
+ tctx, host, share, tctx->ev)) {
+ DEBUG(0, ("Could not open %d'th connection\n", i));
+ return false;
+ }
+ clients[i]->tree->session->transport->options.request_timeout = 60;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli->tree, fname);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.flags = 0;
+
+ for (i=0; i<num_files; i++) {
+ ios[i] = io;
+ requests[i] = smb_raw_open_send(clients[i]->tree, &ios[i]);
+ if (requests[i] == NULL) {
+ DEBUG(0, ("could not send %d'th request\n", i));
+ return false;
+ }
+ }
+
+ DEBUG(10, ("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 < SMBCLI_REQUEST_DONE) {
+ unreplied = true;
+ break;
+ }
+ status = smb_raw_open_recv(requests[i], mem_ctx,
+ &ios[i]);
+
+ DEBUG(0, ("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 (event_loop_once(tctx->ev) != 0) {
+ DEBUG(0, ("event_loop_once failed\n"));
+ return false;
+ }
+ }
+
+ if ((num_ok != 1) || (num_ok + num_collision != num_files)) {
+ ret = false;
+ }
+
+ for (i=0; i<num_files; i++) {
+ torture_close_connection(clients[i]);
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* basic testing of all RAW_OPEN_* calls
+*/
+bool torture_raw_open(struct torture_context *torture, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_ntcreatex_brlocked(cli, torture);
+ ret &= test_open(cli, torture);
+ ret &= test_raw_open_multi(torture);
+ ret &= test_openx(cli, torture);
+ ret &= test_ntcreatex(cli, torture);
+ ret &= test_nttrans_create(cli, torture);
+ ret &= test_t2open(cli, torture);
+ ret &= test_mknew(cli, torture);
+ ret &= test_create(cli, torture);
+ ret &= test_ctemp(cli, torture);
+ ret &= test_chained(cli, torture);
+ ret &= test_no_leading_slash(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/openbench.c b/source4/torture/raw/openbench.c
new file mode 100644
index 0000000000..26b862c33f
--- /dev/null
+++ b/source4/torture/raw/openbench.c
@@ -0,0 +1,484 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ open benchmark
+
+ Copyright (C) Andrew Tridgell 2007
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+#define BASEDIR "\\benchopen"
+
+static int nprocs;
+static int open_failed;
+static int close_failed;
+static char **fnames;
+static int num_connected;
+static struct timed_event *report_te;
+
+struct benchopen_state {
+ struct torture_context *tctx;
+ TALLOC_CTX *mem_ctx;
+ struct event_context *ev;
+ struct smbcli_state *cli;
+ struct smbcli_tree *tree;
+ int client_num;
+ int close_fnum;
+ int open_fnum;
+ int close_file_num;
+ int open_file_num;
+ int pending_file_num;
+ int next_file_num;
+ int count;
+ int lastcount;
+ union smb_open open_parms;
+ int open_retries;
+ union smb_close close_parms;
+ struct smbcli_request *req_open;
+ struct smbcli_request *req_close;
+ struct smb_composite_connect reconnect;
+ struct timed_event *te;
+
+ /* these are used for reconnections */
+ const char **dest_ports;
+ const char *dest_host;
+ const char *called_name;
+ const char *service_type;
+};
+
+static void next_open(struct benchopen_state *state);
+static void reopen_connection(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data);
+
+
+/*
+ complete an async reconnect
+ */
+static void reopen_connection_complete(struct composite_context *ctx)
+{
+ struct benchopen_state *state = (struct benchopen_state *)ctx->async.private_data;
+ NTSTATUS status;
+ struct smb_composite_connect *io = &state->reconnect;
+
+ status = smb_composite_connect_recv(ctx, state->mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ return;
+ }
+
+ state->tree = io->out.tree;
+
+ num_connected++;
+
+ DEBUG(0,("[%u] reconnect to %s finished (%u connected)\n",
+ state->client_num, state->dest_host, num_connected));
+
+ state->open_fnum = -1;
+ state->close_fnum = -1;
+ next_open(state);
+}
+
+
+
+/*
+ reopen a connection
+ */
+static void reopen_connection(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct benchopen_state *state = (struct benchopen_state *)private_data;
+ struct composite_context *ctx;
+ struct smb_composite_connect *io = &state->reconnect;
+ char *host, *share;
+
+ state->te = NULL;
+
+ if (!torture_get_conn_index(state->client_num, state->mem_ctx, state->tctx, &host, &share)) {
+ DEBUG(0,("Can't find host/share for reconnect?!\n"));
+ exit(1);
+ }
+
+ io->in.dest_host = state->dest_host;
+ io->in.dest_ports = state->dest_ports;
+ io->in.called_name = state->called_name;
+ io->in.service = share;
+ io->in.service_type = state->service_type;
+ io->in.credentials = cmdline_credentials;
+ io->in.fallback_to_anonymous = false;
+ io->in.workgroup = lp_workgroup(state->tctx->lp_ctx);
+ lp_smbcli_options(state->tctx->lp_ctx, &io->in.options);
+
+ /* kill off the remnants of the old connection */
+ talloc_free(state->tree);
+ state->tree = NULL;
+ state->open_fnum = -1;
+ state->close_fnum = -1;
+
+ ctx = smb_composite_connect_send(io, state->mem_ctx,
+ lp_resolve_context(state->tctx->lp_ctx),
+ state->ev);
+ if (ctx == NULL) {
+ DEBUG(0,("Failed to setup async reconnect\n"));
+ exit(1);
+ }
+
+ ctx->async.fn = reopen_connection_complete;
+ ctx->async.private_data = state;
+}
+
+static void open_completed(struct smbcli_request *req);
+static void close_completed(struct smbcli_request *req);
+
+
+static void next_open(struct benchopen_state *state)
+{
+ state->count++;
+
+ state->pending_file_num = state->next_file_num;
+ state->next_file_num = (state->next_file_num+1) % (3*nprocs);
+
+ DEBUG(2,("[%d] opening %u\n", state->client_num, state->pending_file_num));
+ state->open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ state->open_parms.ntcreatex.in.flags = 0;
+ state->open_parms.ntcreatex.in.root_fid = 0;
+ state->open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ state->open_parms.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ state->open_parms.ntcreatex.in.alloc_size = 0;
+ state->open_parms.ntcreatex.in.share_access = 0;
+ state->open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ state->open_parms.ntcreatex.in.create_options = 0;
+ state->open_parms.ntcreatex.in.impersonation = 0;
+ state->open_parms.ntcreatex.in.security_flags = 0;
+ state->open_parms.ntcreatex.in.fname = fnames[state->pending_file_num];
+
+ state->req_open = smb_raw_open_send(state->tree, &state->open_parms);
+ state->req_open->async.fn = open_completed;
+ state->req_open->async.private = state;
+}
+
+
+static void next_close(struct benchopen_state *state)
+{
+ if (state->close_fnum == -1) {
+ return;
+ }
+ DEBUG(2,("[%d] closing %d (fnum[%d])\n",
+ state->client_num, state->close_file_num, state->close_fnum));
+ state->close_parms.close.level = RAW_CLOSE_CLOSE;
+ state->close_parms.close.in.file.fnum = state->close_fnum;
+ state->close_parms.close.in.write_time = 0;
+
+ state->req_close = smb_raw_close_send(state->tree, &state->close_parms);
+ state->req_close->async.fn = close_completed;
+ state->req_close->async.private = state;
+}
+
+/*
+ called when a open completes
+*/
+static void open_completed(struct smbcli_request *req)
+{
+ struct benchopen_state *state = (struct benchopen_state *)req->async.private;
+ TALLOC_CTX *tmp_ctx = talloc_new(state->mem_ctx);
+ NTSTATUS status;
+
+ status = smb_raw_open_recv(req, tmp_ctx, &state->open_parms);
+
+ talloc_free(tmp_ctx);
+
+ state->req_open = NULL;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
+ talloc_free(state->tree);
+ talloc_free(state->cli);
+ state->tree = NULL;
+ state->cli = NULL;
+ num_connected--;
+ DEBUG(0,("[%u] reopening connection to %s\n",
+ state->client_num, state->dest_host));
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ DEBUG(2,("[%d] retrying open %d\n",
+ state->client_num, state->pending_file_num));
+ state->open_retries++;
+ state->req_open = smb_raw_open_send(state->tree, &state->open_parms);
+ state->req_open->async.fn = open_completed;
+ state->req_open->async.private = state;
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ open_failed++;
+ DEBUG(0,("[%u] open failed %d - %s\n",
+ state->client_num, state->pending_file_num,
+ nt_errstr(status)));
+ return;
+ }
+
+ state->close_file_num = state->open_file_num;
+ state->close_fnum = state->open_fnum;
+ state->open_file_num = state->pending_file_num;
+ state->open_fnum = state->open_parms.ntcreatex.out.file.fnum;
+
+ DEBUG(2,("[%d] open completed %d (fnum[%d])\n",
+ state->client_num, state->open_file_num, state->open_fnum));
+
+ if (state->close_fnum != -1) {
+ next_close(state);
+ }
+
+ next_open(state);
+}
+
+/*
+ called when a close completes
+*/
+static void close_completed(struct smbcli_request *req)
+{
+ struct benchopen_state *state = (struct benchopen_state *)req->async.private;
+ NTSTATUS status = smbcli_request_simple_recv(req);
+
+ state->req_close = NULL;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
+ talloc_free(state->tree);
+ talloc_free(state->cli);
+ state->tree = NULL;
+ state->cli = NULL;
+ num_connected--;
+ DEBUG(0,("[%u] reopening connection to %s\n",
+ state->client_num, state->dest_host));
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ close_failed++;
+ DEBUG(0,("[%u] close failed %d (fnum[%d]) - %s\n",
+ state->client_num, state->close_file_num,
+ state->close_fnum,
+ nt_errstr(status)));
+ return;
+ }
+
+ DEBUG(2,("[%d] close completed %d (fnum[%d])\n",
+ state->client_num, state->close_file_num,
+ state->close_fnum));
+}
+
+static void echo_completion(struct smbcli_request *req)
+{
+ struct benchopen_state *state = (struct benchopen_state *)req->async.private;
+ NTSTATUS status = smbcli_request_simple_recv(req);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
+ talloc_free(state->tree);
+ state->tree = NULL;
+ num_connected--;
+ DEBUG(0,("[%u] reopening connection to %s\n",
+ state->client_num, state->dest_host));
+ talloc_free(state->te);
+ state->te = event_add_timed(state->ev, state->mem_ctx,
+ timeval_current_ofs(1,0),
+ reopen_connection, state);
+ }
+}
+
+static void report_rate(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct benchopen_state *state = talloc_get_type(private_data,
+ struct benchopen_state);
+ int i;
+ for (i=0;i<nprocs;i++) {
+ printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
+ state[i].lastcount = state[i].count;
+ }
+ printf("\r");
+ fflush(stdout);
+ report_te = event_add_timed(ev, state, timeval_current_ofs(1, 0),
+ report_rate, state);
+
+ /* send an echo on each interface to ensure it stays alive - this helps
+ with IP takeover */
+ for (i=0;i<nprocs;i++) {
+ struct smb_echo p;
+ struct smbcli_request *req;
+
+ if (!state[i].tree) {
+ continue;
+ }
+
+ p.in.repeat_count = 1;
+ p.in.size = 0;
+ p.in.data = NULL;
+ req = smb_raw_echo_send(state[i].tree->session->transport, &p);
+ req->async.private = &state[i];
+ req->async.fn = echo_completion;
+ }
+}
+
+/*
+ benchmark open calls
+*/
+bool torture_bench_open(struct torture_context *torture)
+{
+ bool ret = true;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ int i;
+ int timelimit = torture_setting_int(torture, "timelimit", 10);
+ struct timeval tv;
+ struct benchopen_state *state;
+ int total = 0;
+ int total_retries = 0;
+ int minops = 0;
+ bool progress=false;
+
+ progress = torture_setting_bool(torture, "progress", true);
+
+ nprocs = torture_setting_int(torture, "nprocs", 4);
+
+ state = talloc_zero_array(mem_ctx, struct benchopen_state, nprocs);
+
+ printf("Opening %d connections\n", nprocs);
+ for (i=0;i<nprocs;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].client_num = i;
+ state[i].ev = torture->ev;
+ if (!torture_open_connection_ev(&state[i].cli, i, torture, torture->ev)) {
+ return false;
+ }
+ talloc_steal(mem_ctx, state);
+ state[i].tree = state[i].cli->tree;
+ state[i].dest_host = talloc_strdup(state[i].mem_ctx,
+ state[i].cli->tree->session->transport->socket->hostname);
+ state[i].dest_ports = talloc_array(state[i].mem_ctx,
+ const char *, 2);
+ state[i].dest_ports[0] = talloc_asprintf(state[i].dest_ports,
+ "%u", state[i].cli->tree->session->transport->socket->port);
+ state[i].dest_ports[1] = NULL;
+ state[i].called_name = talloc_strdup(state[i].mem_ctx,
+ state[i].cli->tree->session->transport->called.name);
+ state[i].service_type = talloc_strdup(state[i].mem_ctx,
+ state[i].cli->tree->device);
+ }
+
+ num_connected = i;
+
+ if (!torture_setup_dir(state[0].cli, BASEDIR)) {
+ goto failed;
+ }
+
+ fnames = talloc_array(mem_ctx, char *, 3*nprocs);
+ for (i=0;i<3*nprocs;i++) {
+ fnames[i] = talloc_asprintf(fnames, "%s\\file%d.dat", BASEDIR, i);
+ }
+
+ for (i=0;i<nprocs;i++) {
+ /* all connections start with the same file */
+ state[i].next_file_num = 0;
+ state[i].open_fnum = -1;
+ state[i].close_fnum = -1;
+ next_open(&state[i]);
+ }
+
+ tv = timeval_current();
+
+ if (progress) {
+ report_te = event_add_timed(torture->ev, state, timeval_current_ofs(1, 0),
+ report_rate, state);
+ }
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ event_loop_once(torture->ev);
+
+ if (open_failed) {
+ DEBUG(0,("open failed\n"));
+ goto failed;
+ }
+ if (close_failed) {
+ DEBUG(0,("open failed\n"));
+ goto failed;
+ }
+ }
+
+ talloc_free(report_te);
+ if (progress) {
+ for (i=0;i<nprocs;i++) {
+ printf(" ");
+ }
+ printf("\r");
+ }
+
+ minops = state[0].count;
+ for (i=0;i<nprocs;i++) {
+ total += state[i].count;
+ total_retries += state[i].open_retries;
+ printf("[%d] %u ops (%u retries)\n",
+ i, state[i].count, state[i].open_retries);
+ if (state[i].count < minops) minops = state[i].count;
+ }
+ printf("%.2f ops/second (%d retries)\n",
+ total/timeval_elapsed(&tv), total_retries);
+ if (minops < 0.5*total/nprocs) {
+ printf("Failed: unbalanced open\n");
+ goto failed;
+ }
+
+ for (i=0;i<nprocs;i++) {
+ talloc_free(state[i].req_open);
+ talloc_free(state[i].req_close);
+ smb_raw_exit(state[i].tree->session);
+ }
+
+ smbcli_deltree(state[0].tree, BASEDIR);
+ talloc_free(mem_ctx);
+ return ret;
+
+failed:
+ talloc_free(mem_ctx);
+ return false;
+}
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
new file mode 100644
index 0000000000..fd8d292980
--- /dev/null
+++ b/source4/torture/raw/oplock.c
@@ -0,0 +1,3030 @@
+/*
+ Unix SMB/CIFS implementation.
+ basic raw test suite for oplocks
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/resolve/resolve.h"
+
+#define CHECK_VAL(v, correct) do { \
+ if ((v) != (correct)) { \
+ torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
+ __location__, #v, (int)v, (int)correct); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_RANGE(v, min, max) do { \
+ if ((v) < (min) || (v) > (max)) { \
+ torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got %d - should be between %d and %d\n", \
+ __location__, #v, (int)v, (int)min, (int)max); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_STRMATCH(v, correct) do { \
+ if (!v || strstr((v),(correct)) == NULL) { \
+ torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got '%s' - should be '%s'\n", \
+ __location__, #v, v?v:"NULL", correct); \
+ ret = false; \
+ } \
+} while (0)
+
+#define CHECK_STATUS(tctx, status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
+ nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+static struct {
+ int fnum;
+ uint8_t level;
+ int count;
+ int failures;
+} break_info;
+
+#define BASEDIR "\\test_oplock"
+
+/*
+ a handler function for oplock break requests. Ack it as a break to level II if possible
+*/
+static bool oplock_handler_ack_to_given(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum,
+ uint8_t level, void *private)
+{
+ struct smbcli_tree *tree = (struct smbcli_tree *)private;
+ const char *name;
+
+ break_info.fnum = fnum;
+ break_info.level = level;
+ break_info.count++;
+
+ switch (level) {
+ case OPLOCK_BREAK_TO_LEVEL_II:
+ name = "level II";
+ break;
+ case OPLOCK_BREAK_TO_NONE:
+ name = "none";
+ break;
+ default:
+ name = "unknown";
+ break_info.failures++;
+ }
+ printf("Acking to %s [0x%02X] in oplock handler\n",
+ name, level);
+
+ return smbcli_oplock_ack(tree, fnum, level);
+}
+
+/*
+ a handler function for oplock break requests. Ack it as a break to none
+*/
+static bool oplock_handler_ack_to_none(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum,
+ uint8_t level, void *private)
+{
+ struct smbcli_tree *tree = (struct smbcli_tree *)private;
+ break_info.fnum = fnum;
+ break_info.level = level;
+ break_info.count++;
+
+ printf("Acking to none in oplock handler\n");
+
+ return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE);
+}
+
+/*
+ a handler function for oplock break requests. Let it timeout
+*/
+static bool oplock_handler_timeout(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum,
+ uint8_t level, void *private)
+{
+ break_info.fnum = fnum;
+ break_info.level = level;
+ break_info.count++;
+
+ printf("Let oplock break timeout\n");
+ return true;
+}
+
+static void oplock_handler_close_recv(struct smbcli_request *req)
+{
+ NTSTATUS status;
+ status = smbcli_request_simple_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed in oplock_handler_close\n");
+ break_info.failures++;
+ }
+}
+
+/*
+ a handler function for oplock break requests - close the file
+*/
+static bool oplock_handler_close(struct smbcli_transport *transport, uint16_t tid,
+ uint16_t fnum, uint8_t level, void *private)
+{
+ union smb_close io;
+ struct smbcli_tree *tree = (struct smbcli_tree *)private;
+ struct smbcli_request *req;
+
+ break_info.fnum = fnum;
+ break_info.level = level;
+ break_info.count++;
+
+ io.close.level = RAW_CLOSE_CLOSE;
+ io.close.in.file.fnum = fnum;
+ io.close.in.write_time = 0;
+ req = smb_raw_close_send(tree, &io);
+ if (req == NULL) {
+ printf("failed to send close in oplock_handler_close\n");
+ return false;
+ }
+
+ req->async.fn = oplock_handler_close_recv;
+ req->async.private = NULL;
+
+ return true;
+}
+
+static bool open_connection_no_level2_oplocks(struct torture_context *tctx,
+ struct smbcli_state **c)
+{
+ NTSTATUS status;
+
+ struct smbcli_options options;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+
+ options.use_level2_oplocks = false;
+
+ status = smbcli_full_connection(tctx, c,
+ torture_setting_string(tctx, "host", NULL),
+ lp_smb_ports(tctx->lp_ctx),
+ torture_setting_string(tctx, "share", NULL),
+ NULL, cmdline_credentials,
+ lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open connection - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_raw_oplock_exclusive1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_exclusive1.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_unlink unl;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "EXCLUSIVE1: open a file with an exclusive oplock (share mode: none)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "a 2nd open should not cause a break\n");
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ torture_comment(tctx, "unlink it - should also be no break\n");
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_exclusive2(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_exclusive2.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_unlink unl;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "EXCLUSIVE2: open a file with an exclusive oplock (share mode: all)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "a 2nd open should cause a break to level 2\n");
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.failures, 0);
+ ZERO_STRUCT(break_info);
+
+ /* now we have 2 level II oplocks... */
+ torture_comment(tctx, "try to unlink it - should not cause a break, but a sharing violation\n");
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ torture_comment(tctx, "close 1st handle\n");
+ smbcli_close(cli1->tree, fnum);
+
+ torture_comment(tctx, "try to unlink it - should not cause a break, but a sharing violation\n");
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ torture_comment(tctx, "close 1st handle\n");
+ smbcli_close(cli2->tree, fnum2);
+
+ torture_comment(tctx, "unlink it\n");
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_exclusive3(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_exclusive3.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "EXCLUSIVE3: open a file with an exclusive oplock (share mode: none)\n");
+
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setpathinfo EOF should trigger a break to none\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+ sfi.generic.in.file.path = fname;
+ sfi.end_of_file_info.in.size = 100;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_exclusive4(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_exclusive4.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "EXCLUSIVE4: open with exclusive oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+ torture_comment(tctx, "second open with attributes only shouldn't cause oplock break\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_exclusive5(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_exclusive5.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "EXCLUSIVE5: open with exclusive oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_OVERWRITE_IF dispostion causes oplock break\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_exclusive6(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_exclusive6_1.dat";
+ const char *fname2 = BASEDIR "\\test_exclusive6_2.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_rename rn;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "EXCLUSIVE6: open a file with an exclusive oplock (share mode: none)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "rename should not generate a break but get a sharing violation\n");
+ ZERO_STRUCT(rn);
+ rn.generic.level = RAW_RENAME_RENAME;
+ rn.rename.in.pattern1 = fname1;
+ rn.rename.in.pattern2 = fname2;
+ rn.rename.in.attrib = 0;
+
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli2->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch1.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_unlink unl;
+ uint16_t fnum=0;
+ char c = 0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /*
+ with a batch oplock we get a break
+ */
+ torture_comment(tctx, "BATCH1: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "unlink should generate a break\n");
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.failures, 0);
+
+ torture_comment(tctx, "2nd unlink should not generate a break\n");
+ ZERO_STRUCT(break_info);
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+
+ CHECK_VAL(break_info.count, 0);
+
+ torture_comment(tctx, "writing should generate a self break to none\n");
+ smbcli_write(cli1->tree, fnum, 0, &c, 0, 1);
+ msleep(100);
+ smbcli_write(cli1->tree, fnum, 0, &c, 1, 1);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch2(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch2.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_unlink unl;
+ uint16_t fnum=0;
+ char c = 0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH2: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "unlink should generate a break, which we ack as break to none\n");
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_none, cli1->tree);
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.failures, 0);
+
+ torture_comment(tctx, "2nd unlink should not generate a break\n");
+ ZERO_STRUCT(break_info);
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+
+ CHECK_VAL(break_info.count, 0);
+
+ torture_comment(tctx, "writing should not generate a break\n");
+ smbcli_write(cli1->tree, fnum, 0, &c, 0, 1);
+ msleep(100);
+ smbcli_write(cli1->tree, fnum, 0, &c, 1, 1);
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch3(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch3.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_unlink unl;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH3: if we close on break then the unlink can succeed\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_close, cli1->tree);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ unl.unlink.in.pattern = fname;
+ unl.unlink.in.attrib = 0;
+ ZERO_STRUCT(break_info);
+ status = smb_raw_unlink(cli2->tree, &unl);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch4(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch4.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_read rd;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH4: a self read should not cause a break\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ rd.read.level = RAW_READ_READ;
+ rd.read.in.file.fnum = fnum;
+ rd.read.in.count = 1;
+ rd.read.in.offset = 0;
+ rd.read.in.remaining = 0;
+ status = smb_raw_read(cli1->tree, &rd);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch5(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch5.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH5: a 2nd open should give a break\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch6(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch6.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+ char c = 0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH6: a 2nd open should give a break to level II if the first open allowed shared read\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree);
+
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, 1);
+ CHECK_VAL(break_info.failures, 0);
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "write should trigger a break to none on both\n");
+ smbcli_write(cli1->tree, fnum, 0, &c, 0, 1);
+ msleep(100);
+ smbcli_write(cli1->tree, fnum, 0, &c, 1, 1);
+
+ CHECK_VAL(break_info.count, 2);
+ CHECK_VAL(break_info.level, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch7(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch7.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH7: a 2nd open should get an oplock when we close instead of ack\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_close, cli1->tree);
+
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum2);
+ CHECK_VAL(break_info.level, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli2->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch8(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch8.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH8: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+ torture_comment(tctx, "second open with attributes only shouldn't cause oplock break\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch9(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch9.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+ char c = 0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH9: open with attributes only can create file\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "Subsequent normal open should break oplock on attribute only open to level II\n");
+
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ smbcli_close(cli2->tree, fnum2);
+
+ torture_comment(tctx, "third oplocked open should grant level2 without break\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "write should trigger a break to none on both\n");
+ smbcli_write(cli2->tree, fnum2, 0, &c, 0, 1);
+
+ /* Now the oplock break request comes in. But right now we can't
+ * answer it. Do another write */
+
+ msleep(100);
+ smbcli_write(cli2->tree, fnum2, 0, &c, 1, 1);
+
+ CHECK_VAL(break_info.count, 2);
+ CHECK_VAL(break_info.level, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch10(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch10.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH10: Open with oplock after a non-oplock open should grant level2\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+
+ torture_comment(tctx, "write should trigger a break to none\n");
+ {
+ union smb_write wr;
+ wr.write.level = RAW_WRITE_WRITE;
+ wr.write.in.file.fnum = fnum;
+ wr.write.in.count = 1;
+ wr.write.in.offset = 0;
+ wr.write.in.remaining = 0;
+ wr.write.in.data = (const uint8_t *)"x";
+ status = smb_raw_write(cli1->tree, &wr);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ }
+
+ /* Now the oplock break request comes in. But right now we can't
+ * answer it. Do another write */
+
+ msleep(100);
+
+ {
+ union smb_write wr;
+ wr.write.level = RAW_WRITE_WRITE;
+ wr.write.in.file.fnum = fnum;
+ wr.write.in.count = 1;
+ wr.write.in.offset = 0;
+ wr.write.in.remaining = 0;
+ wr.write.in.data = (const uint8_t *)"x";
+ status = smb_raw_write(cli1->tree, &wr);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ }
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum2);
+ CHECK_VAL(break_info.level, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch11(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch11.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /* Test if a set-eof on pathname breaks an exclusive oplock. */
+ torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF breaks oplocks.\n");
+
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+ sfi.generic.in.file.path = fname;
+ sfi.end_of_file_info.in.size = 100;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch12(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch12.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /* Test if a set-allocation size on pathname breaks an exclusive oplock. */
+ torture_comment(tctx, "BATCH12: Test if setpathinfo allocation size breaks oplocks.\n");
+
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = SMB_SFILEINFO_ALLOCATION_INFORMATION;
+ sfi.generic.in.file.path = fname;
+ sfi.allocation_info.in.alloc_size = 65536 * 8;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch13(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch13.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH13: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_OVERWRITE dispostion causes oplock break\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch14(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch14.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH14: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_SUPERSEDE dispostion causes oplock break\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch15(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch15.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo qfi;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /* Test if a qpathinfo all info on pathname breaks a batch oplock. */
+ torture_comment(tctx, "BATCH15: Test if qpathinfo all info breaks a batch oplock (should not).\n");
+
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.path = fname;
+
+ status = smb_raw_pathinfo(cli2->tree, tctx, &qfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch16(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch16.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH16: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "second open with attributes only and NTCREATEX_DISP_OVERWRITE_IF dispostion causes oplock break\n");
+
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch17(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_batch17_1.dat";
+ const char *fname2 = BASEDIR "\\test_batch17_2.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_rename rn;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "BATCH17: open a file with an batch oplock (share mode: none)\n");
+
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "rename should trigger a break\n");
+ ZERO_STRUCT(rn);
+ rn.generic.level = RAW_RENAME_RENAME;
+ rn.rename.in.pattern1 = fname1;
+ rn.rename.in.pattern2 = fname2;
+ rn.rename.in.attrib = 0;
+
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli2->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch18(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_batch18_1.dat";
+ const char *fname2 = BASEDIR "\\test_batch18_2.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_rename rn;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "BATCH18: open a file with an batch oplock (share mode: none)\n");
+
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "ntrename should trigger a break\n");
+ ZERO_STRUCT(rn);
+ rn.generic.level = RAW_RENAME_NTRENAME;
+ rn.ntrename.in.attrib = 0;
+ rn.ntrename.in.flags = RENAME_FLAG_RENAME;
+ rn.ntrename.in.old_name = fname1;
+ rn.ntrename.in.new_name = fname2;
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli2->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_batch19_1.dat";
+ const char *fname2 = BASEDIR "\\test_batch19_2.dat";
+ const char *fname3 = BASEDIR "\\test_batch19_3.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo qfi;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+ smbcli_unlink(cli1->tree, fname3);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "BATCH19: open a file with an batch oplock (share mode: none)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setpathinfo rename info should not trigger a break nor a violation\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.path = fname1;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2);
+
+ torture_comment(tctx, "setfileinfo rename info should not trigger a break nor a violation\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.fnum = fnum;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1;
+
+ status = smb_raw_setfileinfo(cli1->tree, &sfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+/****************************************************
+ Called from raw-rename - we need oplock handling for
+ this test so this is why it's in oplock.c, not rename.c
+****************************************************/
+
+bool test_trans2rename(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_trans2rename_1.dat";
+ const char *fname2 = BASEDIR "\\test_trans2rename_2.dat";
+ const char *fname3 = BASEDIR "\\test_trans2rename_3.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo qfi;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+ smbcli_unlink(cli1->tree, fname3);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "open a file with an exclusive oplock (share mode: none)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setpathinfo rename info should not trigger a break nor a violation\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.path = fname1;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2);
+
+ torture_comment(tctx, "setfileinfo rename info should not trigger a break nor a violation\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.fnum = fnum;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1;
+
+ status = smb_raw_setfileinfo(cli1->tree, &sfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+/****************************************************
+ Called from raw-rename - we need oplock handling for
+ this test so this is why it's in oplock.c, not rename.c
+****************************************************/
+
+bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1)
+{
+ const char *fname1 = BASEDIR "\\test_nttransrename_1.dat";
+ const char *fname2 = BASEDIR "\\test_nttransrename_2.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo qfi, qpi;
+ union smb_rename rn;
+ uint16_t fnum=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = 0;/* ask for no access at all */;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "nttrans_rename: open a file with an exclusive oplock (share mode: none)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "nttrans_rename: should not trigger a break nor a share mode violation\n");
+ ZERO_STRUCT(rn);
+ rn.generic.level = RAW_RENAME_NTTRANS;
+ rn.nttrans.in.file.fnum = fnum;
+ rn.nttrans.in.flags = 0;
+ rn.nttrans.in.new_name = fname2+strlen(BASEDIR)+1;
+
+ status = smb_raw_rename(cli1->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ /* w2k3 does nothing, it doesn't rename the file */
+ torture_comment(tctx, "nttrans_rename: the server should have done nothing\n");
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname1);
+
+ ZERO_STRUCT(qpi);
+ qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qpi.generic.in.file.path = fname1;
+
+ status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1);
+
+ ZERO_STRUCT(qpi);
+ qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qpi.generic.in.file.path = fname2;
+
+ status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ torture_comment(tctx, "nttrans_rename: after closing the file the file is still not renamed\n");
+ status = smbcli_close(cli1->tree, fnum);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ ZERO_STRUCT(qpi);
+ qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qpi.generic.in.file.path = fname1;
+
+ status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1);
+
+ ZERO_STRUCT(qpi);
+ qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qpi.generic.in.file.path = fname2;
+
+ status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ torture_comment(tctx, "nttrans_rename: rename with an invalid handle gives NT_STATUS_INVALID_HANDLE\n");
+ ZERO_STRUCT(rn);
+ rn.generic.level = RAW_RENAME_NTTRANS;
+ rn.nttrans.in.file.fnum = fnum+1;
+ rn.nttrans.in.flags = 0;
+ rn.nttrans.in.new_name = fname2+strlen(BASEDIR)+1;
+
+ status = smb_raw_rename(cli1->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_INVALID_HANDLE);
+
+done:
+ smb_raw_exit(cli1->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+
+static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_batch20_1.dat";
+ const char *fname2 = BASEDIR "\\test_batch20_2.dat";
+ const char *fname3 = BASEDIR "\\test_batch20_3.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo qfi;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0,fnum2=0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+ smbcli_unlink(cli1->tree, fname3);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "BATCH20: open a file with an batch oplock (share mode: all)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setpathinfo rename info should not trigger a break nor a violation\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.path = fname1;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 0);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2);
+
+ torture_comment(tctx, "open a file with the new name an batch oplock (share mode: all)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.fname = fname2;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+
+ torture_comment(tctx, "setfileinfo rename info should not trigger a break nor a violation\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.fnum = fnum;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1;
+
+ status = smb_raw_setfileinfo(cli1->tree, &sfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum2;
+
+ status = smb_raw_fileinfo(cli2->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch21(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch21.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ struct smb_echo e;
+ uint16_t fnum=0;
+ char c = 0;
+ ssize_t wr;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /*
+ with a batch oplock we get a break
+ */
+ torture_comment(tctx, "BATCH21: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "writing should not generate a break\n");
+ wr = smbcli_write(cli1->tree, fnum, 0, &c, 0, 1);
+ CHECK_VAL(wr, 1);
+ CHECK_STATUS(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_OK);
+
+ ZERO_STRUCT(e);
+ e.in.repeat_count = 1;
+ status = smb_raw_echo(cli1->transport, &e);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch22(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch22.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0;
+ struct timeval tv;
+ int timeout = torture_setting_int(tctx, "oplocktimeout", 30);
+ int te;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "BATCH22 disabled against samba3\n");
+ }
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /*
+ with a batch oplock we get a break
+ */
+ torture_comment(tctx, "BATCH22: open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "a 2nd open shoud not succeed after the oplock break timeout\n");
+ tv = timeval_current();
+ smbcli_oplock_handler(cli1->transport, oplock_handler_timeout, cli1->tree);
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ te = (int)timeval_elapsed(&tv);
+ CHECK_RANGE(te, timeout - 1, timeout + 15);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.failures, 0);
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "a 2nd open shoud succeed after the oplock release without break\n");
+ tv = timeval_current();
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ te = (int)timeval_elapsed(&tv);
+ /* it should come in without delay */
+ CHECK_RANGE(te+1, 0, timeout);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli1->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch23(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch23.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum=0, fnum2=0,fnum3=0;
+ struct smbcli_state *cli3 = NULL;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "BATCH23 disabled against samba3\n");
+ }
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ ret = open_connection_no_level2_oplocks(tctx, &cli3);
+ CHECK_VAL(ret, true);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH23: a open and ask for a batch oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree);
+ smbcli_oplock_handler(cli3->transport, oplock_handler_ack_to_given, cli3->tree);
+
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "a 2nd open without level2 oplock support should generate a break to level2\n");
+ status = smb_raw_open(cli3->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum3 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.failures, 0);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "a 3rd open with level2 oplock support should not generate a break\n");
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+ smbcli_close(cli3->tree, fnum3);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smb_raw_exit(cli3->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch24(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch24.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ uint16_t fnum2=0,fnum3=0;
+ struct smbcli_state *cli3 = NULL;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ ret = open_connection_no_level2_oplocks(tctx, &cli3);
+ CHECK_VAL(ret, true);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ torture_comment(tctx, "BATCH24: a open without level support and ask for a batch oplock\n");
+ ZERO_STRUCT(break_info);
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+ smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree);
+ smbcli_oplock_handler(cli3->transport, oplock_handler_ack_to_given, cli3->tree);
+
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli3->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum3 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "a 2nd open with level2 oplock support should generate a break to none\n");
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum3);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
+ CHECK_VAL(break_info.failures, 0);
+
+ smbcli_close(cli3->tree, fnum3);
+ smbcli_close(cli2->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smb_raw_exit(cli3->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+/*
+ basic testing of oplocks
+*/
+struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "OPLOCK");
+
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE1", test_raw_oplock_exclusive1);
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE2", test_raw_oplock_exclusive2);
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE3", test_raw_oplock_exclusive3);
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE4", test_raw_oplock_exclusive4);
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE5", test_raw_oplock_exclusive5);
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE6", test_raw_oplock_exclusive6);
+ torture_suite_add_2smb_test(suite, "BATCH1", test_raw_oplock_batch1);
+ torture_suite_add_2smb_test(suite, "BATCH2", test_raw_oplock_batch2);
+ torture_suite_add_2smb_test(suite, "BATCH3", test_raw_oplock_batch3);
+ torture_suite_add_2smb_test(suite, "BATCH4", test_raw_oplock_batch4);
+ torture_suite_add_2smb_test(suite, "BATCH5", test_raw_oplock_batch5);
+ torture_suite_add_2smb_test(suite, "BATCH6", test_raw_oplock_batch6);
+ torture_suite_add_2smb_test(suite, "BATCH7", test_raw_oplock_batch7);
+ torture_suite_add_2smb_test(suite, "BATCH8", test_raw_oplock_batch8);
+ torture_suite_add_2smb_test(suite, "BATCH9", test_raw_oplock_batch9);
+ torture_suite_add_2smb_test(suite, "BATCH10", test_raw_oplock_batch10);
+ torture_suite_add_2smb_test(suite, "BATCH11", test_raw_oplock_batch11);
+ torture_suite_add_2smb_test(suite, "BATCH12", test_raw_oplock_batch12);
+ torture_suite_add_2smb_test(suite, "BATCH13", test_raw_oplock_batch13);
+ torture_suite_add_2smb_test(suite, "BATCH14", test_raw_oplock_batch14);
+ torture_suite_add_2smb_test(suite, "BATCH15", test_raw_oplock_batch15);
+ torture_suite_add_2smb_test(suite, "BATCH16", test_raw_oplock_batch16);
+ torture_suite_add_2smb_test(suite, "BATCH17", test_raw_oplock_batch17);
+ torture_suite_add_2smb_test(suite, "BATCH18", test_raw_oplock_batch18);
+ torture_suite_add_2smb_test(suite, "BATCH19", test_raw_oplock_batch19);
+ torture_suite_add_2smb_test(suite, "BATCH20", test_raw_oplock_batch20);
+ torture_suite_add_2smb_test(suite, "BATCH21", test_raw_oplock_batch21);
+ torture_suite_add_2smb_test(suite, "BATCH22", test_raw_oplock_batch22);
+ torture_suite_add_2smb_test(suite, "BATCH23", test_raw_oplock_batch23);
+ torture_suite_add_2smb_test(suite, "BATCH24", test_raw_oplock_batch24);
+
+ return suite;
+}
+
+/*
+ stress testing of oplocks
+*/
+bool torture_bench_oplock(struct torture_context *torture)
+{
+ struct smbcli_state **cli;
+ bool ret = true;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ int torture_nprocs = torture_setting_int(torture, "nprocs", 4);
+ int i, count=0;
+ int timelimit = torture_setting_int(torture, "timelimit", 10);
+ union smb_open io;
+ struct timeval tv;
+
+ cli = talloc_array(mem_ctx, struct smbcli_state *, torture_nprocs);
+
+ torture_comment(torture, "Opening %d connections\n", torture_nprocs);
+ for (i=0;i<torture_nprocs;i++) {
+ if (!torture_open_connection_ev(&cli[i], i, torture, torture->ev)) {
+ return false;
+ }
+ talloc_steal(mem_ctx, cli[i]);
+ smbcli_oplock_handler(cli[i]->transport, oplock_handler_close,
+ cli[i]->tree);
+ }
+
+ if (!torture_setup_dir(cli[0], BASEDIR)) {
+ ret = false;
+ goto done;
+ }
+
+ io.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = BASEDIR "\\test.dat";
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+
+ tv = timeval_current();
+
+ /*
+ we open the same file with SHARE_ACCESS_NONE from all the
+ connections in a round robin fashion. Each open causes an
+ oplock break on the previous connection, which is answered
+ by the oplock_handler_close() to close the file.
+
+ This measures how fast we can pass on oplocks, and stresses
+ the oplock handling code
+ */
+ torture_comment(torture, "Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ for (i=0;i<torture_nprocs;i++) {
+ NTSTATUS status;
+
+ status = smb_raw_open(cli[i]->tree, mem_ctx, &io);
+ CHECK_STATUS(torture, status, NT_STATUS_OK);
+ count++;
+ }
+
+ if (torture_setting_bool(torture, "progress", true)) {
+ torture_comment(torture, "%.2f ops/second\r", count/timeval_elapsed(&tv));
+ }
+ }
+
+ torture_comment(torture, "%.2f ops/second\n", count/timeval_elapsed(&tv));
+
+ smb_raw_exit(cli[torture_nprocs-1]->session);
+
+done:
+ smb_raw_exit(cli[0]->session);
+ smbcli_deltree(cli[0]->tree, BASEDIR);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+static struct hold_oplock_info {
+ const char *fname;
+ bool close_on_break;
+ uint32_t share_access;
+ uint16_t fnum;
+} hold_info[] = {
+ { BASEDIR "\\notshared_close", true,
+ NTCREATEX_SHARE_ACCESS_NONE, },
+ { BASEDIR "\\notshared_noclose", false,
+ NTCREATEX_SHARE_ACCESS_NONE, },
+ { BASEDIR "\\shared_close", true,
+ NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, },
+ { BASEDIR "\\shared_noclose", false,
+ NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, },
+};
+
+static bool oplock_handler_hold(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum, uint8_t level,
+ void *private)
+{
+ struct smbcli_tree *tree = (struct smbcli_tree *)private;
+ struct hold_oplock_info *info;
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(hold_info);i++) {
+ if (hold_info[i].fnum == fnum) break;
+ }
+
+ if (i == ARRAY_SIZE(hold_info)) {
+ printf("oplock break for unknown fnum %u\n", fnum);
+ return false;
+ }
+
+ info = &hold_info[i];
+
+ if (info->close_on_break) {
+ printf("oplock break on %s - closing\n",
+ info->fname);
+ oplock_handler_close(transport, tid, fnum, level, private);
+ return true;
+ }
+
+ printf("oplock break on %s - acking break\n", info->fname);
+
+ return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE);
+}
+
+
+/*
+ used for manual testing of oplocks - especially interaction with
+ other filesystems (such as NFS and local access)
+*/
+bool torture_hold_oplock(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ struct event_context *ev =
+ (struct event_context *)cli->transport->socket->event.ctx;
+ int i;
+
+ printf("Setting up open files with oplocks in %s\n", BASEDIR);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ smbcli_oplock_handler(cli->transport, oplock_handler_hold, cli->tree);
+
+ /* setup the files */
+ for (i=0;i<ARRAY_SIZE(hold_info);i++) {
+ union smb_open io;
+ NTSTATUS status;
+ char c = 1;
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = hold_info[i].share_access;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = hold_info[i].fname;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ printf("opening %s\n", hold_info[i].fname);
+
+ status = smb_raw_open(cli->tree, cli, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open %s - %s\n",
+ hold_info[i].fname, nt_errstr(status));
+ return false;
+ }
+
+ if (io.ntcreatex.out.oplock_level != BATCH_OPLOCK_RETURN) {
+ printf("Oplock not granted for %s - expected %d but got %d\n",
+ hold_info[i].fname, BATCH_OPLOCK_RETURN,
+ io.ntcreatex.out.oplock_level);
+ return false;
+ }
+ hold_info[i].fnum = io.ntcreatex.out.file.fnum;
+
+ /* make the file non-zero size */
+ if (smbcli_write(cli->tree, hold_info[i].fnum, 0, &c, 0, 1) != 1) {
+ printf("Failed to write to file\n");
+ return false;
+ }
+ }
+
+ printf("Waiting for oplock events\n");
+ event_loop_wait(ev);
+
+ return true;
+}
diff --git a/source4/torture/raw/pingpong.c b/source4/torture/raw/pingpong.c
new file mode 100755
index 0000000000..ca56f190a9
--- /dev/null
+++ b/source4/torture/raw/pingpong.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ ping pong test
+
+ Copyright (C) Ronnie Sahlberg 2007
+
+ Significantly based on and borrowed from lockbench.c by
+ Copyright (C) Andrew Tridgell 2006
+
+ 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/>.
+*/
+
+/*
+ filename is specified by
+ --option=torture:filename=...
+
+ number of locks is specified by
+ --option=torture:num_locks=...
+
+ locktimeout is specified in ms by
+ --option=torture:locktimeout=...
+
+ default is 100 seconds
+ if set to 0 pingpong will instead loop trying the lock operation
+ over and over until it completes.
+
+ reading from the file can be enabled with
+ --option=torture:read=true
+
+ writing to the file can be enabled with
+ --option=torture:write=true
+
+*/
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+static void lock_byte(struct smbcli_state *cli, int fd, int offset, int lock_timeout)
+{
+ union smb_lock io;
+ struct smb_lock_entry lock;
+ NTSTATUS status;
+
+try_again:
+ ZERO_STRUCT(lock);
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+
+ lock.count = 1;
+ lock.offset = offset;
+ lock.pid = cli->tree->session->pid;
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = lock_timeout;
+ io.lockx.in.locks = &lock;
+ io.lockx.in.file.fnum = fd;
+
+ status = smb_raw_lock(cli->tree, &io);
+
+ /* If we dont use timeouts and we got file lock conflict
+ just try the lock again.
+ */
+ if (lock_timeout==0) {
+ if ( (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status))
+ ||(NT_STATUS_EQUAL(NT_STATUS_LOCK_NOT_GRANTED, status)) ) {
+ goto try_again;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Lock failed\n"));
+ exit(1);
+ }
+}
+
+static void unlock_byte(struct smbcli_state *cli, int fd, int offset)
+{
+ union smb_lock io;
+ struct smb_lock_entry lock;
+ NTSTATUS status;
+
+ ZERO_STRUCT(lock);
+ io.lockx.in.ulock_cnt = 1;
+ io.lockx.in.lock_cnt = 0;
+
+ lock.count = 1;
+ lock.offset = offset;
+ lock.pid = cli->tree->session->pid;
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 100000;
+ io.lockx.in.locks = &lock;
+ io.lockx.in.file.fnum = fd;
+
+ status = smb_raw_lock(cli->tree, &io);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unlock failed\n"));
+ exit(1);
+ }
+}
+
+static void write_byte(struct smbcli_state *cli, int fd, uint8_t c, int offset)
+{
+ union smb_write io;
+ NTSTATUS status;
+
+ io.generic.level = RAW_WRITE_WRITEX;
+ io.writex.in.file.fnum = fd;
+ io.writex.in.offset = offset;
+ io.writex.in.wmode = 0;
+ io.writex.in.remaining = 0;
+ io.writex.in.count = 1;
+ io.writex.in.data = &c;
+
+ status = smb_raw_write(cli->tree, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed\n");
+ exit(1);
+ }
+}
+
+static void read_byte(struct smbcli_state *cli, int fd, uint8_t *c, int offset)
+{
+ union smb_read io;
+ NTSTATUS status;
+
+ io.generic.level = RAW_READ_READX;
+ io.readx.in.file.fnum = fd;
+ io.readx.in.mincnt = 1;
+ io.readx.in.maxcnt = 1;
+ io.readx.in.offset = offset;
+ io.readx.in.remaining = 0;
+ io.readx.in.read_for_execute = false;
+ io.readx.out.data = c;
+
+ status = smb_raw_read(cli->tree, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("read failed\n");
+ exit(1);
+ }
+}
+
+
+static struct timeval tp1, tp2;
+
+static void start_timer(void)
+{
+ gettimeofday(&tp1, NULL);
+}
+
+static double end_timer(void)
+{
+ gettimeofday(&tp2, NULL);
+ return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
+ (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
+}
+
+/*
+ ping pong
+*/
+bool torture_ping_pong(struct torture_context *torture)
+{
+ const char *fn;
+ int num_locks;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ static bool do_reads;
+ static bool do_writes;
+ int lock_timeout;
+ int fd;
+ struct smbcli_state *cli;
+ int i;
+ uint8_t incr=0, last_incr=0;
+ uint8_t *val;
+ int count, loops;
+
+ fn = torture_setting_string(torture, "filename", NULL);
+ if (fn == NULL) {
+ DEBUG(0,("You must specify the filename using --option=torture:filename=...\n"));
+ return false;
+ }
+
+ num_locks = torture_setting_int(torture, "num_locks", -1);
+ if (num_locks == -1) {
+ DEBUG(0,("You must specify num_locks using --option=torture:num_locks=...\n"));
+ return false;
+ }
+
+ do_reads = torture_setting_bool(torture, "read", false);
+ do_writes = torture_setting_bool(torture, "write", false);
+ lock_timeout = torture_setting_int(torture, "lock_timeout", 100000);
+
+ if (!torture_open_connection(&cli, torture, 0)) {
+ DEBUG(0,("Could not open connection\n"));
+ return false;
+ }
+
+ fd = smbcli_open(cli->tree, fn, O_RDWR|O_CREAT, DENY_NONE);
+ if (fd == -1) {
+ printf("Failed to open %s\n", fn);
+ exit(1);
+ }
+
+ write_byte(cli, fd, 0, num_locks);
+ lock_byte(cli, fd, 0, lock_timeout);
+
+
+ start_timer();
+ val = talloc_zero_array(mem_ctx, uint8_t, num_locks);
+ i = 0;
+ count = 0;
+ loops = 0;
+ while (1) {
+ lock_byte(cli, fd, (i+1)%num_locks, lock_timeout);
+
+ if (do_reads) {
+ uint8_t c;
+ read_byte(cli, fd, &c, i);
+ incr = c-val[i];
+ val[i] = c;
+ }
+
+ if (do_writes) {
+ uint8_t c = val[i] + 1;
+ write_byte(cli, fd, c, i);
+ }
+
+ unlock_byte(cli, fd, i);
+
+ i = (i+1)%num_locks;
+ count++;
+ if (loops>num_locks && incr!=last_incr) {
+ last_incr = incr;
+ printf("data increment = %u\n", incr);
+ fflush(stdout);
+ }
+ if (end_timer() > 1.0) {
+ printf("%8u locks/sec\r",
+ (unsigned)(2*count/end_timer()));
+ fflush(stdout);
+ start_timer();
+ count=0;
+ }
+ loops++;
+ }
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c
new file mode 100644
index 0000000000..c7673be526
--- /dev/null
+++ b/source4/torture/raw/qfileinfo.c
@@ -0,0 +1,865 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_FILEINFO_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "torture/raw/proto.h"
+#include "param/param.h"
+
+static struct {
+ const char *name;
+ enum smb_fileinfo_level level;
+ uint_t only_paths:1;
+ uint_t only_handles:1;
+ uint32_t capability_mask;
+ uint_t expected_ipc_access_denied:1;
+ NTSTATUS expected_ipc_fnum_status;
+ NTSTATUS fnum_status, fname_status;
+ union smb_fileinfo fnum_finfo, fname_finfo;
+} levels[] = {
+ { .name = "GETATTR",
+ .level = RAW_FILEINFO_GETATTR,
+ .only_paths = 1,
+ .only_handles = 0,
+ .expected_ipc_access_denied = 1},
+ { .name ="GETATTRE",
+ .level = RAW_FILEINFO_GETATTRE,
+ .only_paths = 0,
+ .only_handles = 1 },
+ { .name ="STANDARD",
+ .level = RAW_FILEINFO_STANDARD, },
+ { .name ="EA_SIZE",
+ .level = RAW_FILEINFO_EA_SIZE },
+ { .name ="ALL_EAS",
+ .level = RAW_FILEINFO_ALL_EAS,
+ .expected_ipc_fnum_status = NT_STATUS_ACCESS_DENIED,
+ },
+ { .name ="IS_NAME_VALID",
+ .level = RAW_FILEINFO_IS_NAME_VALID,
+ .only_paths = 1,
+ .only_handles = 0 },
+ { .name ="BASIC_INFO",
+ .level = RAW_FILEINFO_BASIC_INFO },
+ { .name ="STANDARD_INFO",
+ .level = RAW_FILEINFO_STANDARD_INFO },
+ { .name ="EA_INFO",
+ .level = RAW_FILEINFO_EA_INFO },
+ { .name ="NAME_INFO",
+ .level = RAW_FILEINFO_NAME_INFO },
+ { .name ="ALL_INFO",
+ .level = RAW_FILEINFO_ALL_INFO },
+ { .name ="ALT_NAME_INFO",
+ .level = RAW_FILEINFO_ALT_NAME_INFO,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="STREAM_INFO",
+ .level = RAW_FILEINFO_STREAM_INFO,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="COMPRESSION_INFO",
+ .level = RAW_FILEINFO_COMPRESSION_INFO,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="UNIX_BASIC_INFO",
+ .level = RAW_FILEINFO_UNIX_BASIC,
+ .only_paths = 0,
+ .only_handles = 0,
+ .capability_mask = CAP_UNIX},
+ { .name ="UNIX_LINK_INFO",
+ .level = RAW_FILEINFO_UNIX_LINK,
+ .only_paths = 0,
+ .only_handles = 0,
+ .capability_mask = CAP_UNIX},
+ { .name ="BASIC_INFORMATION",
+ .level = RAW_FILEINFO_BASIC_INFORMATION },
+ { .name ="STANDARD_INFORMATION",
+ .level = RAW_FILEINFO_STANDARD_INFORMATION },
+ { .name ="INTERNAL_INFORMATION",
+ .level = RAW_FILEINFO_INTERNAL_INFORMATION },
+ { .name ="EA_INFORMATION",
+ .level = RAW_FILEINFO_EA_INFORMATION },
+ { .name = "ACCESS_INFORMATION",
+ .level = RAW_FILEINFO_ACCESS_INFORMATION },
+ { .name = "NAME_INFORMATION",
+ .level = RAW_FILEINFO_NAME_INFORMATION },
+ { .name ="POSITION_INFORMATION",
+ .level = RAW_FILEINFO_POSITION_INFORMATION },
+ { .name ="MODE_INFORMATION",
+ .level = RAW_FILEINFO_MODE_INFORMATION },
+ { .name ="ALIGNMENT_INFORMATION",
+ .level = RAW_FILEINFO_ALIGNMENT_INFORMATION },
+ { .name ="ALL_INFORMATION",
+ .level = RAW_FILEINFO_ALL_INFORMATION },
+ { .name ="ALT_NAME_INFORMATION",
+ .level = RAW_FILEINFO_ALT_NAME_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="STREAM_INFORMATION",
+ .level = RAW_FILEINFO_STREAM_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name = "COMPRESSION_INFORMATION",
+ .level = RAW_FILEINFO_COMPRESSION_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="NETWORK_OPEN_INFORMATION",
+ .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name = "ATTRIBUTE_TAG_INFORMATION",
+ .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { NULL }
+};
+
+/*
+ compare a dos time (2 second resolution) to a nt time
+*/
+static int dos_nt_time_cmp(time_t t, NTTIME nt)
+{
+ time_t t2 = nt_time_to_unix(nt);
+ if (abs(t2 - t) <= 2) return 0;
+ return t2 - t;
+}
+
+
+/*
+ find a level in the levels[] table
+*/
+static union smb_fileinfo *fnum_find(const char *name)
+{
+ int i;
+ for (i=0; levels[i].name; i++) {
+ if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
+ strcmp(name, levels[i].name) == 0 &&
+ !levels[i].only_paths) {
+ return &levels[i].fnum_finfo;
+ }
+ }
+ return NULL;
+}
+
+/*
+ find a level in the levels[] table
+*/
+static union smb_fileinfo *fname_find(bool is_ipc, const char *name)
+{
+ int i;
+ if (is_ipc) {
+ return NULL;
+ }
+ for (i=0; levels[i].name; i++) {
+ if (NT_STATUS_IS_OK(levels[i].fname_status) &&
+ strcmp(name, levels[i].name) == 0 &&
+ !levels[i].only_handles) {
+ return &levels[i].fname_finfo;
+ }
+ }
+ return NULL;
+}
+
+/* local macros to make the code below more readable */
+#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
+ printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+ #n1, #v1, (uint_t)s1->n1.out.v1, \
+ #n2, #v2, (uint_t)s2->n2.out.v2, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
+ s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
+ printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
+ #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
+ #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
+ printf("%s/%s != %s/%s at %s(%d)\n", \
+ #n1, #v1, \
+ #n2, #v2, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+/* used to find hints on unknown values - and to make sure
+ we zero-fill */
+#if 0 /* unused */
+#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
+ printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
+ #n1, #v1, \
+ (uint_t)s1->n1.out.v1, \
+ (uint_t)s1->n1.out.v1, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+#endif
+
+/* basic testing of all RAW_FILEINFO_* calls
+ for each call we test that it succeeds, and where possible test
+ for consistency between the calls.
+*/
+static bool torture_raw_qfileinfo_internals(struct torture_context *torture,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_tree *tree,
+ int fnum, const char *fname,
+ bool is_ipc)
+{
+ int i;
+ bool ret = true;
+ int count;
+ union smb_fileinfo *s1, *s2;
+ NTTIME correct_time;
+ uint64_t correct_size;
+ uint32_t correct_attrib;
+ const char *correct_name;
+ bool skip_streams = false;
+
+ /* scan all the fileinfo and pathinfo levels */
+ for (i=0; levels[i].name; i++) {
+ if (!levels[i].only_paths) {
+ levels[i].fnum_finfo.generic.level = levels[i].level;
+ levels[i].fnum_finfo.generic.in.file.fnum = fnum;
+ levels[i].fnum_status = smb_raw_fileinfo(tree, mem_ctx,
+ &levels[i].fnum_finfo);
+ }
+
+ if (!levels[i].only_handles) {
+ levels[i].fname_finfo.generic.level = levels[i].level;
+ levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname);
+ levels[i].fname_status = smb_raw_pathinfo(tree, mem_ctx,
+ &levels[i].fname_finfo);
+ }
+ }
+
+ /* check for completely broken levels */
+ for (count=i=0; levels[i].name; i++) {
+ uint32_t cap = tree->session->transport->negotiate.capabilities;
+ /* see if this server claims to support this level */
+ if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
+ continue;
+ }
+
+ if (is_ipc) {
+ if (levels[i].expected_ipc_access_denied && NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, levels[i].fname_status)) {
+ } else if (!levels[i].only_handles && !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST, levels[i].fname_status)) {
+ printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n",
+ levels[i].name, nt_errstr(levels[i].fname_status));
+ count++;
+ }
+ if (!levels[i].only_paths && !NT_STATUS_EQUAL(levels[i].expected_ipc_fnum_status, levels[i].fnum_status)) {
+ printf("ERROR: fnum level %s failed, expected %s - %s\n",
+ levels[i].name, nt_errstr(levels[i].expected_ipc_fnum_status),
+ nt_errstr(levels[i].fnum_status));
+ count++;
+ }
+ } else {
+ if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
+ printf("ERROR: fnum level %s failed - %s\n",
+ levels[i].name, nt_errstr(levels[i].fnum_status));
+ count++;
+ }
+ if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
+ printf("ERROR: fname level %s failed - %s\n",
+ levels[i].name, nt_errstr(levels[i].fname_status));
+ count++;
+ }
+ }
+
+ }
+
+ if (count != 0) {
+ ret = false;
+ printf("%d levels failed\n", count);
+ if (count > 35) {
+ torture_fail(torture, "too many level failures - giving up");
+ }
+ }
+
+ /* see if we can do streams */
+ s1 = fnum_find("STREAM_INFO");
+ if (!s1 || s1->stream_info.out.num_streams == 0) {
+ if (!is_ipc) {
+ printf("STREAM_INFO broken (%d) - skipping streams checks\n",
+ s1 ? s1->stream_info.out.num_streams : -1);
+ }
+ skip_streams = true;
+ }
+
+
+ /* this code is incredibly repititive but doesn't lend itself to loops, so
+ we use lots of macros to make it less painful */
+
+ /* first off we check the levels that are supposed to be aliases. It will be quite rare for
+ this code to fail, but we need to check it for completeness */
+
+
+
+#define ALIAS_CHECK(sname1, sname2) \
+ do { \
+ s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
+ if (s1 && s2) { INFO_CHECK } \
+ s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
+ if (s1 && s2) { INFO_CHECK } \
+ s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
+ if (s1 && s2) { INFO_CHECK } \
+ } while (0)
+
+#define INFO_CHECK \
+ STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \
+ STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \
+ STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \
+ STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \
+ VAL_EQUAL (basic_info, attrib, basic_info, attrib);
+
+ ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+ VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \
+ VAL_EQUAL(standard_info, size, standard_info, size); \
+ VAL_EQUAL(standard_info, nlink, standard_info, nlink); \
+ VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \
+ VAL_EQUAL(standard_info, directory, standard_info, directory);
+
+ ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+ VAL_EQUAL(ea_info, ea_size, ea_info, ea_size);
+
+ ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+ STR_EQUAL(name_info, fname, name_info, fname);
+
+ ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+ STRUCT_EQUAL(all_info, create_time, all_info, create_time); \
+ STRUCT_EQUAL(all_info, access_time, all_info, access_time); \
+ STRUCT_EQUAL(all_info, write_time, all_info, write_time); \
+ STRUCT_EQUAL(all_info, change_time, all_info, change_time); \
+ VAL_EQUAL(all_info, attrib, all_info, attrib); \
+ VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \
+ VAL_EQUAL(all_info, size, all_info, size); \
+ VAL_EQUAL(all_info, nlink, all_info, nlink); \
+ VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \
+ VAL_EQUAL(all_info, directory, all_info, directory); \
+ VAL_EQUAL(all_info, ea_size, all_info, ea_size); \
+ STR_EQUAL(all_info, fname, all_info, fname);
+
+ ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+ VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
+ VAL_EQUAL(compression_info, format, compression_info, format); \
+ VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \
+ VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \
+ VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift);
+
+ ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
+
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+ STR_EQUAL(alt_name_info, fname, alt_name_info, fname);
+
+ ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
+
+
+#define TIME_CHECK_NT(sname, stype, tfield) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
+ printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
+ nt_time_string(mem_ctx, s1->stype.out.tfield), \
+ nt_time_string(mem_ctx, correct_time)); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
+ printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
+ nt_time_string(mem_ctx, s1->stype.out.tfield), \
+ nt_time_string(mem_ctx, correct_time)); \
+ ret = false; \
+ }} while (0)
+
+#define TIME_CHECK_DOS(sname, stype, tfield) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
+ printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
+ timestring(mem_ctx, s1->stype.out.tfield), \
+ nt_time_string(mem_ctx, correct_time)); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
+ printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
+ timestring(mem_ctx, s1->stype.out.tfield), \
+ nt_time_string(mem_ctx, correct_time)); \
+ ret = false; \
+ }} while (0)
+
+#if 0 /* unused */
+#define TIME_CHECK_UNX(sname, stype, tfield) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
+ printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
+ timestring(mem_ctx, s1->stype.out.tfield), \
+ nt_time_string(mem_ctx, correct_time)); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
+ printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
+ timestring(mem_ctx, s1->stype.out.tfield), \
+ nt_time_string(mem_ctx, correct_time)); \
+ ret = false; \
+ }} while (0)
+#endif
+
+ /* now check that all the times that are supposed to be equal are correct */
+ s1 = fnum_find("BASIC_INFO");
+ correct_time = s1->basic_info.out.create_time;
+ torture_comment(torture, "create_time: %s\n", nt_time_string(mem_ctx, correct_time));
+
+ TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time);
+ TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time);
+ TIME_CHECK_DOS("GETATTRE", getattre, create_time);
+ TIME_CHECK_DOS("STANDARD", standard, create_time);
+ TIME_CHECK_DOS("EA_SIZE", ea_size, create_time);
+ TIME_CHECK_NT ("ALL_INFO", all_info, create_time);
+ TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
+
+ s1 = fnum_find("BASIC_INFO");
+ correct_time = s1->basic_info.out.access_time;
+ torture_comment(torture, "access_time: %s\n", nt_time_string(mem_ctx, correct_time));
+
+ TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time);
+ TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time);
+ TIME_CHECK_DOS("GETATTRE", getattre, access_time);
+ TIME_CHECK_DOS("STANDARD", standard, access_time);
+ TIME_CHECK_DOS("EA_SIZE", ea_size, access_time);
+ TIME_CHECK_NT ("ALL_INFO", all_info, access_time);
+ TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
+
+ s1 = fnum_find("BASIC_INFO");
+ correct_time = s1->basic_info.out.write_time;
+ torture_comment(torture, "write_time : %s\n", nt_time_string(mem_ctx, correct_time));
+
+ TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time);
+ TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time);
+ TIME_CHECK_DOS("GETATTR", getattr, write_time);
+ TIME_CHECK_DOS("GETATTRE", getattre, write_time);
+ TIME_CHECK_DOS("STANDARD", standard, write_time);
+ TIME_CHECK_DOS("EA_SIZE", ea_size, write_time);
+ TIME_CHECK_NT ("ALL_INFO", all_info, write_time);
+ TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
+
+ s1 = fnum_find("BASIC_INFO");
+ correct_time = s1->basic_info.out.change_time;
+ torture_comment(torture, "change_time: %s\n", nt_time_string(mem_ctx, correct_time));
+
+ TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time);
+ TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time);
+ TIME_CHECK_NT ("ALL_INFO", all_info, change_time);
+ TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
+
+
+#define SIZE_CHECK(sname, stype, tfield) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && s1->stype.out.tfield != correct_size) { \
+ printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)correct_size); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && s1->stype.out.tfield != correct_size) { \
+ printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)correct_size); \
+ ret = false; \
+ }} while (0)
+
+ s1 = fnum_find("STANDARD_INFO");
+ correct_size = s1->standard_info.out.size;
+ torture_comment(torture, "size: %u\n", (uint_t)correct_size);
+
+ SIZE_CHECK("GETATTR", getattr, size);
+ SIZE_CHECK("GETATTRE", getattre, size);
+ SIZE_CHECK("STANDARD", standard, size);
+ SIZE_CHECK("EA_SIZE", ea_size, size);
+ SIZE_CHECK("STANDARD_INFO", standard_info, size);
+ SIZE_CHECK("STANDARD_INFORMATION", standard_info, size);
+ SIZE_CHECK("ALL_INFO", all_info, size);
+ SIZE_CHECK("ALL_INFORMATION", all_info, size);
+ SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size);
+ SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size);
+ SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
+ if (!skip_streams) {
+ SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size);
+ SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size);
+ }
+
+
+ s1 = fnum_find("STANDARD_INFO");
+ correct_size = s1->standard_info.out.alloc_size;
+ torture_comment(torture, "alloc_size: %u\n", (uint_t)correct_size);
+
+ SIZE_CHECK("GETATTRE", getattre, alloc_size);
+ SIZE_CHECK("STANDARD", standard, alloc_size);
+ SIZE_CHECK("EA_SIZE", ea_size, alloc_size);
+ SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size);
+ SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size);
+ SIZE_CHECK("ALL_INFO", all_info, alloc_size);
+ SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size);
+ SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
+ if (!skip_streams) {
+ SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size);
+ SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size);
+ }
+
+#define ATTRIB_CHECK(sname, stype, tfield) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && s1->stype.out.tfield != correct_attrib) { \
+ printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)correct_attrib); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && s1->stype.out.tfield != correct_attrib) { \
+ printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)correct_attrib); \
+ ret = false; \
+ }} while (0)
+
+ s1 = fnum_find("BASIC_INFO");
+ correct_attrib = s1->basic_info.out.attrib;
+ torture_comment(torture, "attrib: 0x%x\n", (uint_t)correct_attrib);
+
+ ATTRIB_CHECK("GETATTR", getattr, attrib);
+ if (!is_ipc) {
+ ATTRIB_CHECK("GETATTRE", getattre, attrib);
+ ATTRIB_CHECK("STANDARD", standard, attrib);
+ ATTRIB_CHECK("EA_SIZE", ea_size, attrib);
+ }
+ ATTRIB_CHECK("BASIC_INFO", basic_info, attrib);
+ ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib);
+ ATTRIB_CHECK("ALL_INFO", all_info, attrib);
+ ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib);
+ ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib);
+ ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
+
+ correct_name = fname;
+ torture_comment(torture, "name: %s\n", correct_name);
+
+#define NAME_CHECK(sname, stype, tfield, flags) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
+ wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
+ printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
+ s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
+ wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
+ printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
+ s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
+ ret = false; \
+ }} while (0)
+
+ NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE);
+ NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
+
+ /* the ALL_INFO file name is the full path on the filesystem */
+ s1 = fnum_find("ALL_INFO");
+ if (s1 && !s1->all_info.out.fname.s) {
+ torture_fail(torture, "ALL_INFO didn't give a filename");
+ }
+ if (s1 && s1->all_info.out.fname.s) {
+ char *p = strrchr(s1->all_info.out.fname.s, '\\');
+ if (!p) {
+ printf("Not a full path in all_info/fname? - '%s'\n",
+ s1->all_info.out.fname.s);
+ ret = false;
+ } else {
+ if (strcmp_safe(correct_name, p) != 0) {
+ printf("incorrect basename in all_info/fname - '%s'\n",
+ s1->all_info.out.fname.s);
+ ret = false;
+ }
+ }
+ if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, tree->session->transport)) {
+ printf("Should not null terminate all_info/fname\n");
+ ret = false;
+ }
+ }
+
+ s1 = fnum_find("ALT_NAME_INFO");
+ if (s1) {
+ correct_name = s1->alt_name_info.out.fname.s;
+ torture_comment(torture, "alt_name: %s\n", correct_name);
+
+ NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE);
+ NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
+
+ /* and make sure we can open by alternate name */
+ smbcli_close(tree, fnum);
+ fnum = smbcli_nt_create_full(tree, correct_name, 0,
+ SEC_RIGHTS_FILE_ALL,
+ FILE_ATTRIBUTE_NORMAL,
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE,
+ NTCREATEX_DISP_OVERWRITE_IF,
+ 0, 0);
+ if (fnum == -1) {
+ printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree));
+ ret = false;
+ }
+
+ if (!skip_streams) {
+ correct_name = "::$DATA";
+ torture_comment(torture, "stream_name: %s\n", correct_name);
+
+ NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE);
+ NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
+ }
+ }
+
+ /* make sure the EAs look right */
+ s1 = fnum_find("ALL_EAS");
+ s2 = fnum_find("ALL_INFO");
+ if (s1) {
+ for (i=0;i<s1->all_eas.out.num_eas;i++) {
+ printf(" flags=%d %s=%*.*s\n",
+ s1->all_eas.out.eas[i].flags,
+ s1->all_eas.out.eas[i].name.s,
+ (int)s1->all_eas.out.eas[i].value.length,
+ (int)s1->all_eas.out.eas[i].value.length,
+ s1->all_eas.out.eas[i].value.data);
+ }
+ }
+ if (s1 && s2) {
+ if (s1->all_eas.out.num_eas == 0) {
+ if (s2->all_info.out.ea_size != 0) {
+ printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
+ s2->all_info.out.ea_size);
+ }
+ } else {
+ if (s2->all_info.out.ea_size !=
+ ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
+ printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
+ (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
+ (int)s2->all_info.out.ea_size);
+ }
+ }
+ }
+ s2 = fname_find(is_ipc, "ALL_EAS");
+ if (s2) {
+ VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
+ for (i=0;i<s1->all_eas.out.num_eas;i++) {
+ VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
+ STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
+ VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
+ }
+ }
+
+#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
+ s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
+ if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+ printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+ #stype1, #tfield1, #stype2, #tfield2, \
+ s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
+ if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+ printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+ #stype1, #tfield1, #stype2, #tfield2, \
+ s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+ ret = false; \
+ } \
+ s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
+ if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+ printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+ #stype1, #tfield1, #stype2, #tfield2, \
+ s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+ ret = false; \
+ } \
+ s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \
+ if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+ printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+ #stype1, #tfield1, #stype2, #tfield2, \
+ s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+ ret = false; \
+ }} while (0)
+
+ VAL_CHECK("STANDARD_INFO", standard_info, delete_pending,
+ "ALL_INFO", all_info, delete_pending);
+ VAL_CHECK("STANDARD_INFO", standard_info, directory,
+ "ALL_INFO", all_info, directory);
+ VAL_CHECK("STANDARD_INFO", standard_info, nlink,
+ "ALL_INFO", all_info, nlink);
+ s1 = fnum_find("BASIC_INFO");
+ if (s1 && is_ipc) {
+ if (s1->basic_info.out.attrib != FILE_ATTRIBUTE_NORMAL) {
+ printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__, s1->basic_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
+ ret = false;
+ }
+ }
+ s1 = fnum_find("STANDARD_INFO");
+ if (s1 && is_ipc) {
+ if (s1->standard_info.out.nlink != 1) {
+ printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.nlink);
+ ret = false;
+ }
+ if (s1->standard_info.out.delete_pending != 1) {
+ printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.delete_pending);
+ ret = false;
+ }
+ }
+ VAL_CHECK("EA_INFO", ea_info, ea_size,
+ "ALL_INFO", all_info, ea_size);
+ if (!is_ipc) {
+ VAL_CHECK("EA_SIZE", ea_size, ea_size,
+ "ALL_INFO", all_info, ea_size);
+ }
+
+#define NAME_PATH_CHECK(sname, stype, field) do { \
+ s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \
+ if (s1 && s2) { \
+ VAL_EQUAL(stype, field, stype, field); \
+ } \
+} while (0)
+
+
+ s1 = fnum_find("INTERNAL_INFORMATION");
+ if (s1) {
+ torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id);
+ }
+
+ NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id);
+ NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
+ if (s1 && s2) {
+ printf("fnum pos = %.0f, fname pos = %.0f\n",
+ (double)s2->position_information.out.position,
+ (double)s1->position_information.out.position );
+ }
+ NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
+ NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
+ NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
+ NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
+
+#if 0
+ /* these are expected to differ */
+ NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
+#endif
+
+#if 0 /* unused */
+#define UNKNOWN_CHECK(sname, stype, tfield) do { \
+ s1 = fnum_find(sname); \
+ if (s1 && s1->stype.out.tfield != 0) { \
+ printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
+ #stype, #tfield, \
+ (uint_t)s1->stype.out.tfield); \
+ } \
+ s1 = fname_find(is_ipc, sname); \
+ if (s1 && s1->stype.out.tfield != 0) { \
+ printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
+ #stype, #tfield, \
+ (uint_t)s1->stype.out.tfield); \
+ }} while (0)
+#endif
+ /* now get a bit fancier .... */
+
+ /* when we set the delete disposition then the link count should drop
+ to 0 and delete_pending should be 1 */
+
+ return ret;
+}
+
+/* basic testing of all RAW_FILEINFO_* calls
+ for each call we test that it succeeds, and where possible test
+ for consistency between the calls.
+*/
+bool torture_raw_qfileinfo(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ int fnum;
+ bool ret;
+ const char *fname = "\\torture_qfileinfo.txt";
+
+ fnum = create_complex_file(cli, torture, fname);
+ if (fnum == -1) {
+ printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */);
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+bool torture_raw_qfileinfo_pipe(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ int fnum;
+ const char *fname = "\\lsass";
+ struct dcerpc_pipe *p;
+ struct smbcli_tree *ipc_tree;
+ NTSTATUS status;
+
+ if (!(p = dcerpc_pipe_init(torture, cli->tree->session->transport->socket->event.ctx,
+ lp_iconv_convenience(torture->lp_ctx)))) {
+ return false;
+ }
+
+ status = dcerpc_pipe_open_smb(p, cli->tree, fname);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_pipe_open_smb failed");
+
+ ipc_tree = dcerpc_smb_tree(p->conn);
+ fnum = dcerpc_smb_fnum(p->conn);
+
+ ret = torture_raw_qfileinfo_internals(torture, torture, ipc_tree, fnum, fname, true /* is_ipc */);
+
+ talloc_free(p);
+ return ret;
+}
diff --git a/source4/torture/raw/qfsinfo.c b/source4/torture/raw/qfsinfo.c
new file mode 100644
index 0000000000..c1c77edba9
--- /dev/null
+++ b/source4/torture/raw/qfsinfo.c
@@ -0,0 +1,296 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_QFS_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+
+static struct {
+ const char *name;
+ enum smb_fsinfo_level level;
+ uint32_t capability_mask;
+ NTSTATUS status;
+ union smb_fsinfo fsinfo;
+} levels[] = {
+ {"DSKATTR", RAW_QFS_DSKATTR, },
+ {"ALLOCATION", RAW_QFS_ALLOCATION, },
+ {"VOLUME", RAW_QFS_VOLUME, },
+ {"VOLUME_INFO", RAW_QFS_VOLUME_INFO, },
+ {"SIZE_INFO", RAW_QFS_SIZE_INFO, },
+ {"DEVICE_INFO", RAW_QFS_DEVICE_INFO, },
+ {"ATTRIBUTE_INFO", RAW_QFS_ATTRIBUTE_INFO, },
+ {"UNIX_INFO", RAW_QFS_UNIX_INFO, CAP_UNIX},
+ {"VOLUME_INFORMATION", RAW_QFS_VOLUME_INFORMATION, },
+ {"SIZE_INFORMATION", RAW_QFS_SIZE_INFORMATION, },
+ {"DEVICE_INFORMATION", RAW_QFS_DEVICE_INFORMATION, },
+ {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, },
+ {"QUOTA_INFORMATION", RAW_QFS_QUOTA_INFORMATION, },
+ {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, },
+#if 0
+ /* w2k3 seems to no longer support this */
+ {"OBJECTID_INFORMATION", RAW_QFS_OBJECTID_INFORMATION, },
+#endif
+ { NULL, }
+};
+
+
+/*
+ find a level in the levels[] table
+*/
+static union smb_fsinfo *find(const char *name)
+{
+ int i;
+ for (i=0; levels[i].name; i++) {
+ if (strcmp(name, levels[i].name) == 0 &&
+ NT_STATUS_IS_OK(levels[i].status)) {
+ return &levels[i].fsinfo;
+ }
+ }
+ return NULL;
+}
+
+/* local macros to make the code below more readable */
+#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
+ printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+ #n1, #v1, (uint_t)s1->n1.out.v1, \
+ #n2, #v2, (uint_t)s2->n2.out.v2, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+#define VAL_APPROX_EQUAL(n1, v1, n2, v2) do {if (abs((int)(s1->n1.out.v1) - (int)(s2->n2.out.v2)) > 0.1*s1->n1.out.v1) { \
+ printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+ #n1, #v1, (uint_t)s1->n1.out.v1, \
+ #n2, #v2, (uint_t)s2->n2.out.v2, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+#define STR_EQUAL(n1, v1, n2, v2) do { \
+ if (strcmp_safe(s1->n1.out.v1, s2->n2.out.v2)) { \
+ printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \
+ #n1, #v1, s1->n1.out.v1, \
+ #n2, #v2, s2->n2.out.v2, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
+ printf("%s/%s != %s/%s at %s(%d)\n", \
+ #n1, #v1, \
+ #n2, #v2, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+/* used to find hints on unknown values - and to make sure
+ we zero-fill */
+#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
+ printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
+ #n1, #v1, \
+ (uint_t)s1->n1.out.v1, \
+ (uint_t)s1->n1.out.v1, \
+ __FILE__, __LINE__); \
+ ret = false; \
+}} while(0)
+
+/* basic testing of all RAW_QFS_* calls
+ for each call we test that it succeeds, and where possible test
+ for consistency between the calls.
+
+ Some of the consistency tests assume that the target filesystem is
+ quiescent, which is sometimes hard to achieve
+*/
+bool torture_raw_qfsinfo(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ int i;
+ bool ret = true;
+ int count;
+ union smb_fsinfo *s1, *s2;
+
+ /* scan all the levels, pulling the results */
+ for (i=0; levels[i].name; i++) {
+ torture_comment(torture, "Running level %s\n", levels[i].name);
+ levels[i].fsinfo.generic.level = levels[i].level;
+ levels[i].status = smb_raw_fsinfo(cli->tree, torture, &levels[i].fsinfo);
+ }
+
+ /* check for completely broken levels */
+ for (count=i=0; levels[i].name; i++) {
+ uint32_t cap = cli->transport->negotiate.capabilities;
+ /* see if this server claims to support this level */
+ if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(levels[i].status)) {
+ printf("ERROR: level %s failed - %s\n",
+ levels[i].name, nt_errstr(levels[i].status));
+ count++;
+ }
+ }
+
+ if (count != 0) {
+ torture_comment(torture, "%d levels failed\n", count);
+ torture_assert(torture, count > 13, "too many level failures - giving up");
+ }
+
+ torture_comment(torture, "check for correct aliases\n");
+ s1 = find("SIZE_INFO");
+ s2 = find("SIZE_INFORMATION");
+ if (s1 && s2) {
+ VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units);
+ VAL_APPROX_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units);
+ VAL_EQUAL(size_info, sectors_per_unit, size_info, sectors_per_unit);
+ VAL_EQUAL(size_info, bytes_per_sector, size_info, bytes_per_sector);
+ }
+
+ s1 = find("DEVICE_INFO");
+ s2 = find("DEVICE_INFORMATION");
+ if (s1 && s2) {
+ VAL_EQUAL(device_info, device_type, device_info, device_type);
+ VAL_EQUAL(device_info, characteristics, device_info, characteristics);
+ }
+
+ s1 = find("VOLUME_INFO");
+ s2 = find("VOLUME_INFORMATION");
+ if (s1 && s2) {
+ STRUCT_EQUAL(volume_info, create_time, volume_info, create_time);
+ VAL_EQUAL (volume_info, serial_number, volume_info, serial_number);
+ STR_EQUAL (volume_info, volume_name.s, volume_info, volume_name.s);
+ torture_comment(torture, "volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s);
+ }
+
+ s1 = find("ATTRIBUTE_INFO");
+ s2 = find("ATTRIBUTE_INFORMATION");
+ if (s1 && s2) {
+ VAL_EQUAL(attribute_info, fs_attr,
+ attribute_info, fs_attr);
+ VAL_EQUAL(attribute_info, max_file_component_length,
+ attribute_info, max_file_component_length);
+ STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s);
+ torture_comment(torture, "attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s);
+ }
+
+ torture_comment(torture, "check for consistent disk sizes\n");
+ s1 = find("DSKATTR");
+ s2 = find("ALLOCATION");
+ if (s1 && s2) {
+ double size1, size2;
+ double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
+ size1 = 1.0 *
+ s1->dskattr.out.units_total *
+ s1->dskattr.out.blocks_per_unit *
+ s1->dskattr.out.block_size / scale;
+ size2 = 1.0 *
+ s2->allocation.out.sectors_per_unit *
+ s2->allocation.out.total_alloc_units *
+ s2->allocation.out.bytes_per_sector / scale;
+ if (abs(size1 - size2) > 1) {
+ printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n",
+ size1, size2);
+ ret = false;
+ }
+ torture_comment(torture, "total disk = %.0f MB\n", size1*scale/1.0e6);
+ }
+
+ torture_comment(torture, "check consistent free disk space\n");
+ s1 = find("DSKATTR");
+ s2 = find("ALLOCATION");
+ if (s1 && s2) {
+ double size1, size2;
+ double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
+ size1 = 1.0 *
+ s1->dskattr.out.units_free *
+ s1->dskattr.out.blocks_per_unit *
+ s1->dskattr.out.block_size / scale;
+ size2 = 1.0 *
+ s2->allocation.out.sectors_per_unit *
+ s2->allocation.out.avail_alloc_units *
+ s2->allocation.out.bytes_per_sector / scale;
+ if (abs(size1 - size2) > 1) {
+ printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n",
+ size1, size2);
+ ret = false;
+ }
+ torture_comment(torture, "free disk = %.0f MB\n", size1*scale/1.0e6);
+ }
+
+ torture_comment(torture, "volume info consistency\n");
+ s1 = find("VOLUME");
+ s2 = find("VOLUME_INFO");
+ if (s1 && s2) {
+ VAL_EQUAL(volume, serial_number, volume_info, serial_number);
+ STR_EQUAL(volume, volume_name.s, volume_info, volume_name.s);
+ }
+
+ /* disk size consistency - notice that 'avail_alloc_units' maps to the caller
+ available allocation units, not the total */
+ s1 = find("SIZE_INFO");
+ s2 = find("FULL_SIZE_INFORMATION");
+ if (s1 && s2) {
+ VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units);
+ VAL_APPROX_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units);
+ VAL_EQUAL(size_info, sectors_per_unit, full_size_information, sectors_per_unit);
+ VAL_EQUAL(size_info, bytes_per_sector, full_size_information, bytes_per_sector);
+ }
+
+ printf("check for non-zero unknown fields\n");
+ s1 = find("QUOTA_INFORMATION");
+ if (s1) {
+ VAL_UNKNOWN(quota_information, unknown[0]);
+ VAL_UNKNOWN(quota_information, unknown[1]);
+ VAL_UNKNOWN(quota_information, unknown[2]);
+ }
+
+ s1 = find("OBJECTID_INFORMATION");
+ if (s1) {
+ VAL_UNKNOWN(objectid_information, unknown[0]);
+ VAL_UNKNOWN(objectid_information, unknown[1]);
+ VAL_UNKNOWN(objectid_information, unknown[2]);
+ VAL_UNKNOWN(objectid_information, unknown[3]);
+ VAL_UNKNOWN(objectid_information, unknown[4]);
+ VAL_UNKNOWN(objectid_information, unknown[5]);
+ }
+
+
+#define STR_CHECK(sname, stype, field, flags) do { \
+ s1 = find(sname); \
+ if (s1) { \
+ if (s1->stype.out.field.s && wire_bad_flags(&s1->stype.out.field, flags, cli->transport)) { \
+ printf("(%d) incorrect string termination in %s/%s\n", \
+ __LINE__, #stype, #field); \
+ ret = false; \
+ } \
+ }} while (0)
+
+ torture_comment(torture, "check for correct termination\n");
+
+ STR_CHECK("VOLUME", volume, volume_name, 0);
+ STR_CHECK("VOLUME_INFO", volume_info, volume_name, STR_UNICODE);
+ STR_CHECK("VOLUME_INFORMATION", volume_info, volume_name, STR_UNICODE);
+ STR_CHECK("ATTRIBUTE_INFO", attribute_info, fs_type, STR_UNICODE);
+ STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE);
+
+ return ret;
+}
diff --git a/source4/torture/raw/raw.c b/source4/torture/raw/raw.c
new file mode 100644
index 0000000000..0a7fc3ebfd
--- /dev/null
+++ b/source4/torture/raw/raw.c
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Jelmer Vernooij 2006
+
+ 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/raw/libcliraw.h"
+#include "torture/util.h"
+#include "torture/smbtorture.h"
+#include "torture/raw/proto.h"
+
+NTSTATUS torture_raw_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(
+ talloc_autofree_context(),
+ "RAW");
+ /* RAW smb tests */
+ torture_suite_add_simple_test(suite, "BENCH-OPLOCK", torture_bench_oplock);
+ torture_suite_add_simple_test(suite, "PING-PONG", torture_ping_pong);
+ torture_suite_add_simple_test(suite, "BENCH-LOCK", torture_bench_lock);
+ torture_suite_add_simple_test(suite, "BENCH-OPEN", torture_bench_open);
+ torture_suite_add_simple_test(suite, "BENCH-LOOKUP",
+ torture_bench_lookup);
+ torture_suite_add_simple_test(suite, "BENCH-TCON",
+ torture_bench_treeconnect);
+ torture_suite_add_simple_test(suite, "OFFLINE", torture_test_offline);
+ torture_suite_add_1smb_test(suite, "QFSINFO", torture_raw_qfsinfo);
+ torture_suite_add_1smb_test(suite, "QFILEINFO", torture_raw_qfileinfo);
+ torture_suite_add_1smb_test(suite, "QFILEINFO-IPC", torture_raw_qfileinfo_pipe);
+ torture_suite_add_1smb_test(suite, "SFILEINFO", torture_raw_sfileinfo);
+ torture_suite_add_1smb_test(suite, "SFILEINFO-BUG", torture_raw_sfileinfo_bug);
+ torture_suite_add_1smb_test(suite, "SFILEINFO-RENAME",
+ torture_raw_sfileinfo_rename);
+ torture_suite_add_suite(suite, torture_raw_search(suite));
+ torture_suite_add_1smb_test(suite, "CLOSE", torture_raw_close);
+ torture_suite_add_1smb_test(suite, "OPEN", torture_raw_open);
+ torture_suite_add_1smb_test(suite, "MKDIR", torture_raw_mkdir);
+ torture_suite_add_suite(suite, torture_raw_oplock(suite));
+ torture_suite_add_1smb_test(suite, "HOLD-OPLOCK", torture_hold_oplock);
+ torture_suite_add_2smb_test(suite, "NOTIFY", torture_raw_notify);
+ torture_suite_add_1smb_test(suite, "MUX", torture_raw_mux);
+ torture_suite_add_1smb_test(suite, "IOCTL", torture_raw_ioctl);
+ torture_suite_add_1smb_test(suite, "CHKPATH", torture_raw_chkpath);
+ torture_suite_add_suite(suite, torture_raw_unlink(suite));
+ torture_suite_add_suite(suite, torture_raw_read(suite));
+ torture_suite_add_suite(suite, torture_raw_write(suite));
+ torture_suite_add_suite(suite, torture_raw_lock(suite));
+ torture_suite_add_1smb_test(suite, "CONTEXT", torture_raw_context);
+ torture_suite_add_suite(suite, torture_raw_rename(suite));
+ torture_suite_add_1smb_test(suite, "SEEK", torture_raw_seek);
+ torture_suite_add_1smb_test(suite, "EAS", torture_raw_eas);
+ torture_suite_add_1smb_test(suite, "STREAMS", torture_raw_streams);
+ torture_suite_add_1smb_test(suite, "ACLS", torture_raw_acls);
+ torture_suite_add_1smb_test(suite, "COMPOSITE", torture_raw_composite);
+ torture_suite_add_simple_test(suite, "SAMBA3HIDE", torture_samba3_hide);
+ torture_suite_add_simple_test(suite, "SAMBA3CLOSEERR", torture_samba3_closeerr);
+ torture_suite_add_simple_test(suite, "SAMBA3ROOTDIRFID",
+ torture_samba3_rootdirfid);
+ torture_suite_add_simple_test(suite, "SAMBA3CHECKFSP", torture_samba3_checkfsp);
+ torture_suite_add_simple_test(suite, "SAMBA3BADPATH", torture_samba3_badpath);
+ torture_suite_add_simple_test(suite, "SAMBA3CASEINSENSITIVE",
+ torture_samba3_caseinsensitive);
+ torture_suite_add_simple_test(suite, "SAMBA3POSIXTIMEDLOCK",
+ torture_samba3_posixtimedlock);
+ torture_suite_add_simple_test(suite, "SCAN-EAMAX", torture_max_eas);
+
+ suite->description = talloc_strdup(suite, "Tests for the raw SMB interface");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c
new file mode 100644
index 0000000000..ada9b1f432
--- /dev/null
+++ b/source4/torture/raw/read.c
@@ -0,0 +1,954 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for various read operations
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%ld - should be %ld\n", \
+ __location__, #v, (long)v, (long)correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_BUFFER(buf, seed, len) do { \
+ if (!check_buffer(buf, seed, len, __LINE__)) { \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define BASEDIR "\\testread"
+
+
+/*
+ setup a random buffer based on a seed
+*/
+static void setup_buffer(uint8_t *buf, uint_t seed, int len)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) buf[i] = random();
+}
+
+/*
+ check a random buffer based on a seed
+*/
+static bool check_buffer(uint8_t *buf, uint_t seed, int len, int line)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) {
+ uint8_t v = random();
+ if (buf[i] != v) {
+ printf("Buffer incorrect at line %d! ofs=%d v1=0x%x v2=0x%x\n",
+ line, i, buf[i], v);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ test read ops
+*/
+static bool test_read(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ union smb_read io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ const char *test_data = "TEST DATA";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_READ\n");
+ io.generic.level = RAW_READ_READ;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying empty file read\n");
+ io.read.in.file.fnum = fnum;
+ io.read.in.count = 1;
+ io.read.in.offset = 0;
+ io.read.in.remaining = 0;
+ io.read.out.data = buf;
+ status = smb_raw_read(cli->tree, &io);
+
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.read.out.nread, 0);
+
+ printf("Trying zero file read\n");
+ io.read.in.count = 0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.read.out.nread, 0);
+
+ printf("Trying bad fnum\n");
+ io.read.in.file.fnum = fnum+1;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+ io.read.in.file.fnum = fnum;
+
+ smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data));
+
+ printf("Trying small read\n");
+ io.read.in.file.fnum = fnum;
+ io.read.in.offset = 0;
+ io.read.in.remaining = 0;
+ io.read.in.count = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.read.out.nread, strlen(test_data));
+ if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+ goto done;
+ }
+
+ printf("Trying short read\n");
+ io.read.in.offset = 1;
+ io.read.in.count = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.read.out.nread, strlen(test_data)-1);
+ if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+ goto done;
+ }
+
+ if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ printf("Trying max offset\n");
+ io.read.in.offset = ~0;
+ io.read.in.count = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.read.out.nread, 0);
+ }
+
+ setup_buffer(buf, seed, maxsize);
+ smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize);
+ memset(buf, 0, maxsize);
+
+ printf("Trying large read\n");
+ io.read.in.offset = 0;
+ io.read.in.count = ~0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_BUFFER(buf, seed, io.read.out.nread);
+
+
+ printf("Trying locked region\n");
+ cli->session->pid++;
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %d\n", __LINE__);
+ ret = false;
+ goto done;
+ }
+ cli->session->pid--;
+ memset(buf, 0, maxsize);
+ io.read.in.offset = 0;
+ io.read.in.count = ~0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test lockread ops
+*/
+static bool test_lockread(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_read io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ const char *test_data = "TEST DATA";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_LOCKREAD\n");
+ io.generic.level = RAW_READ_LOCKREAD;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying empty file read\n");
+ io.lockread.in.file.fnum = fnum;
+ io.lockread.in.count = 1;
+ io.lockread.in.offset = 1;
+ io.lockread.in.remaining = 0;
+ io.lockread.out.data = buf;
+ status = smb_raw_read(cli->tree, &io);
+
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lockread.out.nread, 0);
+
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ printf("Trying zero file read\n");
+ io.lockread.in.count = 0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_unlock(cli->tree, fnum, 1, 1);
+
+ printf("Trying bad fnum\n");
+ io.lockread.in.file.fnum = fnum+1;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+ io.lockread.in.file.fnum = fnum;
+
+ smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data));
+
+ printf("Trying small read\n");
+ io.lockread.in.file.fnum = fnum;
+ io.lockread.in.offset = 0;
+ io.lockread.in.remaining = 0;
+ io.lockread.in.count = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ smbcli_unlock(cli->tree, fnum, 1, 0);
+
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lockread.out.nread, strlen(test_data));
+ if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+ goto done;
+ }
+
+ printf("Trying short read\n");
+ io.lockread.in.offset = 1;
+ io.lockread.in.count = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ smbcli_unlock(cli->tree, fnum, 0, strlen(test_data));
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VALUE(io.lockread.out.nread, strlen(test_data)-1);
+ if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+ goto done;
+ }
+
+ if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ printf("Trying max offset\n");
+ io.lockread.in.offset = ~0;
+ io.lockread.in.count = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lockread.out.nread, 0);
+ }
+
+ setup_buffer(buf, seed, maxsize);
+ smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize);
+ memset(buf, 0, maxsize);
+
+ printf("Trying large read\n");
+ io.lockread.in.offset = 0;
+ io.lockread.in.count = ~0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ smbcli_unlock(cli->tree, fnum, 1, strlen(test_data));
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_BUFFER(buf, seed, io.lockread.out.nread);
+ smbcli_unlock(cli->tree, fnum, 0, 0xFFFF);
+
+
+ printf("Trying locked region\n");
+ cli->session->pid++;
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %d\n", __LINE__);
+ ret = false;
+ goto done;
+ }
+ cli->session->pid--;
+ memset(buf, 0, maxsize);
+ io.lockread.in.offset = 0;
+ io.lockread.in.count = ~0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test readx ops
+*/
+static bool test_readx(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ union smb_read io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ const char *test_data = "TEST DATA";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_READX\n");
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying empty file read\n");
+ io.generic.level = RAW_READ_READX;
+ io.readx.in.file.fnum = fnum;
+ io.readx.in.mincnt = 1;
+ io.readx.in.maxcnt = 1;
+ io.readx.in.offset = 0;
+ io.readx.in.remaining = 0;
+ io.readx.in.read_for_execute = false;
+ io.readx.out.data = buf;
+ status = smb_raw_read(cli->tree, &io);
+
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, 0);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+
+ printf("Trying zero file read\n");
+ io.readx.in.mincnt = 0;
+ io.readx.in.maxcnt = 0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, 0);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+
+ printf("Trying bad fnum\n");
+ io.readx.in.file.fnum = fnum+1;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+ io.readx.in.file.fnum = fnum;
+
+ smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data));
+
+ printf("Trying small read\n");
+ io.readx.in.file.fnum = fnum;
+ io.readx.in.offset = 0;
+ io.readx.in.remaining = 0;
+ io.readx.in.read_for_execute = false;
+ io.readx.in.mincnt = strlen(test_data);
+ io.readx.in.maxcnt = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, strlen(test_data));
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+ goto done;
+ }
+
+ printf("Trying short read\n");
+ io.readx.in.offset = 1;
+ io.readx.in.mincnt = strlen(test_data);
+ io.readx.in.maxcnt = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, strlen(test_data)-1);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+ goto done;
+ }
+
+ if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ printf("Trying max offset\n");
+ io.readx.in.offset = 0xffffffff;
+ io.readx.in.mincnt = strlen(test_data);
+ io.readx.in.maxcnt = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, 0);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ }
+
+ printf("Trying mincnt past EOF\n");
+ memset(buf, 0, maxsize);
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 100;
+ io.readx.in.maxcnt = 110;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ CHECK_VALUE(io.readx.out.nread, strlen(test_data));
+ if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+ goto done;
+ }
+
+
+ setup_buffer(buf, seed, maxsize);
+ smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize);
+ memset(buf, 0, maxsize);
+
+ printf("Trying large read\n");
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 0xFFFF;
+ io.readx.in.maxcnt = 0xFFFF;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt);
+ CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+ printf("Trying extra large read\n");
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 100;
+ io.readx.in.maxcnt = 80000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("SAMBA3: large read extension\n");
+ CHECK_VALUE(io.readx.out.nread, 80000);
+ } else {
+ CHECK_VALUE(io.readx.out.nread, 0);
+ }
+ CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+ printf("Trying mincnt > maxcnt\n");
+ memset(buf, 0, maxsize);
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 30000;
+ io.readx.in.maxcnt = 20000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt);
+ CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+ printf("Trying mincnt < maxcnt\n");
+ memset(buf, 0, maxsize);
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 20000;
+ io.readx.in.maxcnt = 30000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(io.readx.out.compaction_mode, 0);
+ CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt);
+ CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+ if (cli->transport->negotiate.capabilities & CAP_LARGE_READX) {
+ printf("Trying large readx\n");
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 0;
+ io.readx.in.maxcnt = 0x10000 - 1;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, 0xFFFF);
+
+ io.readx.in.maxcnt = 0x10000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("SAMBA3: large read extension\n");
+ CHECK_VALUE(io.readx.out.nread, 0x10000);
+ } else {
+ CHECK_VALUE(io.readx.out.nread, 0);
+ }
+
+ io.readx.in.maxcnt = 0x10001;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("SAMBA3: large read extension\n");
+ CHECK_VALUE(io.readx.out.nread, 0x10001);
+ } else {
+ CHECK_VALUE(io.readx.out.nread, 0);
+ }
+ }
+
+ printf("Trying locked region\n");
+ cli->session->pid++;
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %d\n", __LINE__);
+ ret = false;
+ goto done;
+ }
+ cli->session->pid--;
+ memset(buf, 0, maxsize);
+ io.readx.in.offset = 0;
+ io.readx.in.mincnt = 100;
+ io.readx.in.maxcnt = 200;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("Trying large offset read\n");
+ io.readx.in.offset = ((uint64_t)0x2) << 32;
+ io.readx.in.mincnt = 10;
+ io.readx.in.maxcnt = 10;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, 0);
+
+ if (NT_STATUS_IS_ERR(smbcli_lock64(cli->tree, fnum, io.readx.in.offset, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %d\n", __LINE__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readx.out.nread, 0);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test readbraw ops
+*/
+static bool test_readbraw(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_read io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ const char *test_data = "TEST DATA";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_READBRAW\n");
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying empty file read\n");
+ io.generic.level = RAW_READ_READBRAW;
+ io.readbraw.in.file.fnum = fnum;
+ io.readbraw.in.mincnt = 1;
+ io.readbraw.in.maxcnt = 1;
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.timeout = 0;
+ io.readbraw.out.data = buf;
+ status = smb_raw_read(cli->tree, &io);
+
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+
+ printf("Trying zero file read\n");
+ io.readbraw.in.mincnt = 0;
+ io.readbraw.in.maxcnt = 0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+
+ printf("Trying bad fnum\n");
+ io.readbraw.in.file.fnum = fnum+1;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+ io.readbraw.in.file.fnum = fnum;
+
+ smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data));
+
+ printf("Trying small read\n");
+ io.readbraw.in.file.fnum = fnum;
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.mincnt = strlen(test_data);
+ io.readbraw.in.maxcnt = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, strlen(test_data));
+ if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+ goto done;
+ }
+
+ printf("Trying short read\n");
+ io.readbraw.in.offset = 1;
+ io.readbraw.in.mincnt = strlen(test_data);
+ io.readbraw.in.maxcnt = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, strlen(test_data)-1);
+ if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+ ret = false;
+ printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+ goto done;
+ }
+
+ if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ printf("Trying max offset\n");
+ io.readbraw.in.offset = ~0;
+ io.readbraw.in.mincnt = strlen(test_data);
+ io.readbraw.in.maxcnt = strlen(test_data);
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+ }
+
+ setup_buffer(buf, seed, maxsize);
+ smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize);
+ memset(buf, 0, maxsize);
+
+ printf("Trying large read\n");
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.mincnt = ~0;
+ io.readbraw.in.maxcnt = ~0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0xFFFF);
+ CHECK_BUFFER(buf, seed, io.readbraw.out.nread);
+
+ printf("Trying mincnt > maxcnt\n");
+ memset(buf, 0, maxsize);
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.mincnt = 30000;
+ io.readbraw.in.maxcnt = 20000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt);
+ CHECK_BUFFER(buf, seed, io.readbraw.out.nread);
+
+ printf("Trying mincnt < maxcnt\n");
+ memset(buf, 0, maxsize);
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.mincnt = 20000;
+ io.readbraw.in.maxcnt = 30000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt);
+ CHECK_BUFFER(buf, seed, io.readbraw.out.nread);
+
+ printf("Trying locked region\n");
+ cli->session->pid++;
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %d\n", __LINE__);
+ ret = false;
+ goto done;
+ }
+ cli->session->pid--;
+ memset(buf, 0, maxsize);
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.mincnt = 100;
+ io.readbraw.in.maxcnt = 200;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+
+ printf("Trying locked region with timeout\n");
+ memset(buf, 0, maxsize);
+ io.readbraw.in.offset = 0;
+ io.readbraw.in.mincnt = 100;
+ io.readbraw.in.maxcnt = 200;
+ io.readbraw.in.timeout = 10000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+
+ if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ printf("Trying large offset read\n");
+ io.readbraw.in.offset = ((uint64_t)0x2) << 32;
+ io.readbraw.in.mincnt = 10;
+ io.readbraw.in.maxcnt = 10;
+ io.readbraw.in.timeout = 0;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.readbraw.out.nread, 0);
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+/*
+ test read for execute
+*/
+static bool test_read_for_execute(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_open op;
+ union smb_write wr;
+ union smb_read rd;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum=0;
+ uint8_t *buf;
+ const int maxsize = 900;
+ const char *fname = BASEDIR "\\test.txt";
+ const uint8_t data[] = "TEST DATA";
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_READX with read_for_execute\n");
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ op.ntcreatex.in.create_options = 0;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ wr.generic.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = ARRAY_SIZE(data);
+ wr.writex.in.data = data;
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(wr.writex.out.nwritten, ARRAY_SIZE(data));
+
+ status = smbcli_close(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("open file with SEC_FILE_EXECUTE\n");
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_FILE_EXECUTE;
+ op.ntcreatex.in.create_options = 0;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ printf("read with FLAGS2_READ_PERMIT_EXECUTE\n");
+ rd.generic.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = fnum;
+ rd.readx.in.mincnt = 0;
+ rd.readx.in.maxcnt = maxsize;
+ rd.readx.in.offset = 0;
+ rd.readx.in.remaining = 0;
+ rd.readx.in.read_for_execute = true;
+ rd.readx.out.data = buf;
+ status = smb_raw_read(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(rd.readx.out.nread, ARRAY_SIZE(data));
+ CHECK_VALUE(rd.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(rd.readx.out.compaction_mode, 0);
+
+ printf("read without FLAGS2_READ_PERMIT_EXECUTE (should fail)\n");
+ rd.generic.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = fnum;
+ rd.readx.in.mincnt = 0;
+ rd.readx.in.maxcnt = maxsize;
+ rd.readx.in.offset = 0;
+ rd.readx.in.remaining = 0;
+ rd.readx.in.read_for_execute = false;
+ rd.readx.out.data = buf;
+ status = smb_raw_read(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ status = smbcli_close(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("open file with SEC_FILE_READ_DATA\n");
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ op.ntcreatex.in.create_options = 0;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ printf("read with FLAGS2_READ_PERMIT_EXECUTE\n");
+ rd.generic.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = fnum;
+ rd.readx.in.mincnt = 0;
+ rd.readx.in.maxcnt = maxsize;
+ rd.readx.in.offset = 0;
+ rd.readx.in.remaining = 0;
+ rd.readx.in.read_for_execute = true;
+ rd.readx.out.data = buf;
+ status = smb_raw_read(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(rd.readx.out.nread, ARRAY_SIZE(data));
+ CHECK_VALUE(rd.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(rd.readx.out.compaction_mode, 0);
+
+ printf("read without FLAGS2_READ_PERMIT_EXECUTE\n");
+ rd.generic.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = fnum;
+ rd.readx.in.mincnt = 0;
+ rd.readx.in.maxcnt = maxsize;
+ rd.readx.in.offset = 0;
+ rd.readx.in.remaining = 0;
+ rd.readx.in.read_for_execute = false;
+ rd.readx.out.data = buf;
+ status = smb_raw_read(cli->tree, &rd);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(rd.readx.out.nread, ARRAY_SIZE(data));
+ CHECK_VALUE(rd.readx.out.remaining, 0xFFFF);
+ CHECK_VALUE(rd.readx.out.compaction_mode, 0);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ basic testing of read calls
+*/
+struct torture_suite *torture_raw_read(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "READ");
+
+ torture_suite_add_1smb_test(suite, "read", test_read);
+ torture_suite_add_1smb_test(suite, "readx", test_readx);
+ torture_suite_add_1smb_test(suite, "lockread", test_lockread);
+ torture_suite_add_1smb_test(suite, "readbraw", test_readbraw);
+ torture_suite_add_1smb_test(suite, "read for execute",
+ test_read_for_execute);
+
+ return suite;
+}
diff --git a/source4/torture/raw/rename.c b/source4/torture/raw/rename.c
new file mode 100644
index 0000000000..9765c04101
--- /dev/null
+++ b/source4/torture/raw/rename.c
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+ rename test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect %s %d - should be %d\n", \
+ __location__, #v, (int)v, (int)correct); \
+ ret = false; \
+ }} while (0)
+
+#define BASEDIR "\\testrename"
+
+/*
+ test SMBmv ops
+*/
+static bool test_mv(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_rename io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum = -1;
+ const char *fname1 = BASEDIR "\\test1.txt";
+ const char *fname2 = BASEDIR "\\test2.txt";
+ const char *Fname1 = BASEDIR "\\Test1.txt";
+ union smb_fileinfo finfo;
+ union smb_open op;
+
+ printf("Testing SMBmv\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Trying simple rename\n");
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ op.ntcreatex.in.create_options = 0;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = fname1;
+
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ io.generic.level = RAW_RENAME_RENAME;
+ io.rename.in.pattern1 = fname1;
+ io.rename.in.pattern2 = fname2;
+ io.rename.in.attrib = 0;
+
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ smbcli_close(cli->tree, fnum);
+
+ op.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ op.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE |
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ printf("trying rename while first file open with SHARE_ACCESS_DELETE\n");
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.rename.in.pattern1 = fname2;
+ io.rename.in.pattern2 = fname1;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying case-changing rename\n");
+ io.rename.in.pattern1 = fname1;
+ io.rename.in.pattern2 = Fname1;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.all_info.in.file.path = fname1;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (strcmp(finfo.all_info.out.fname.s, Fname1) != 0) {
+ printf("(%s) Incorrect filename [%s] after case-changing "
+ "rename, should be [%s]\n", __location__,
+ finfo.all_info.out.fname.s, Fname1);
+ }
+
+ io.rename.in.pattern1 = fname1;
+ io.rename.in.pattern2 = fname2;
+
+ printf("trying rename while not open\n");
+ smb_raw_exit(cli->session);
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying self rename\n");
+ io.rename.in.pattern1 = fname2;
+ io.rename.in.pattern2 = fname2;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.rename.in.pattern1 = fname1;
+ io.rename.in.pattern2 = fname1;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+
+ printf("trying wildcard rename\n");
+ io.rename.in.pattern1 = BASEDIR "\\*.txt";
+ io.rename.in.pattern2 = fname1;
+
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("and again\n");
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying extension change\n");
+ io.rename.in.pattern1 = BASEDIR "\\*.txt";
+ io.rename.in.pattern2 = BASEDIR "\\*.bak";
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ printf("Checking attrib handling\n");
+ torture_set_file_attribute(cli->tree, BASEDIR "\\test1.bak", FILE_ATTRIBUTE_HIDDEN);
+ io.rename.in.pattern1 = BASEDIR "\\test1.bak";
+ io.rename.in.pattern2 = BASEDIR "\\*.txt";
+ io.rename.in.attrib = 0;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.rename.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+
+/*
+ test SMBntrename ops
+*/
+static bool test_ntrename(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_rename io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum, i;
+ const char *fname1 = BASEDIR "\\test1.txt";
+ const char *fname2 = BASEDIR "\\test2.txt";
+ union smb_fileinfo finfo;
+
+ printf("Testing SMBntrename\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Trying simple rename\n");
+
+ fnum = create_complex_file(cli, tctx, fname1);
+
+ io.generic.level = RAW_RENAME_NTRENAME;
+ io.ntrename.in.old_name = fname1;
+ io.ntrename.in.new_name = fname2;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.cluster_size = 0;
+ io.ntrename.in.flags = RENAME_FLAG_RENAME;
+
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ smb_raw_exit(cli->session);
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying self rename\n");
+ io.ntrename.in.old_name = fname2;
+ io.ntrename.in.new_name = fname2;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.ntrename.in.old_name = fname1;
+ io.ntrename.in.new_name = fname1;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ printf("trying wildcard rename\n");
+ io.ntrename.in.old_name = BASEDIR "\\*.txt";
+ io.ntrename.in.new_name = fname1;
+
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+ printf("Checking attrib handling\n");
+ torture_set_file_attribute(cli->tree, fname2, FILE_ATTRIBUTE_HIDDEN);
+ io.ntrename.in.old_name = fname2;
+ io.ntrename.in.new_name = fname1;
+ io.ntrename.in.attrib = 0;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.ntrename.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_NORMAL);
+
+ printf("Checking hard link\n");
+ io.ntrename.in.old_name = fname1;
+ io.ntrename.in.new_name = fname2;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.flags = RENAME_FLAG_HARD_LINK;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_SYSTEM);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname2;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 2);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_SYSTEM);
+
+ finfo.generic.in.file.path = fname1;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 2);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_SYSTEM);
+
+ torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_NORMAL);
+
+ smbcli_unlink(cli->tree, fname2);
+
+ finfo.generic.in.file.path = fname1;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 1);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
+
+ printf("Checking copy\n");
+ io.ntrename.in.old_name = fname1;
+ io.ntrename.in.new_name = fname2;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.flags = RENAME_FLAG_COPY;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname1;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 1);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname2;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 1);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
+
+ torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_SYSTEM);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname2;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 1);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
+
+ finfo.generic.in.file.path = fname1;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 1);
+ CHECK_VALUE(finfo.all_info.out.attrib, FILE_ATTRIBUTE_SYSTEM);
+
+ torture_set_file_attribute(cli->tree, fname1, FILE_ATTRIBUTE_NORMAL);
+
+ smbcli_unlink(cli->tree, fname2);
+
+ finfo.generic.in.file.path = fname1;
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.nlink, 1);
+
+ printf("Checking invalid flags\n");
+ io.ntrename.in.old_name = fname1;
+ io.ntrename.in.new_name = fname2;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.flags = 0;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.ntrename.in.flags = 300;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.ntrename.in.flags = 0x106;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("Checking unknown field\n");
+ io.ntrename.in.old_name = fname1;
+ io.ntrename.in.new_name = fname2;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.flags = RENAME_FLAG_RENAME;
+ io.ntrename.in.cluster_size = 0xff;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying RENAME_FLAG_MOVE_CLUSTER_INFORMATION\n");
+
+ io.ntrename.in.old_name = fname2;
+ io.ntrename.in.new_name = fname1;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.flags = RENAME_FLAG_MOVE_CLUSTER_INFORMATION;
+ io.ntrename.in.cluster_size = 1;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.ntrename.in.flags = RENAME_FLAG_COPY;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+#if 0
+ {
+ char buf[16384];
+ fnum = smbcli_open(cli->tree, fname1, O_RDWR, DENY_NONE);
+ memset(buf, 1, sizeof(buf));
+ smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
+ smbcli_close(cli->tree, fnum);
+
+ fnum = smbcli_open(cli->tree, fname2, O_RDWR, DENY_NONE);
+ memset(buf, 1, sizeof(buf));
+ smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)-1);
+ smbcli_close(cli->tree, fnum);
+
+ torture_all_info(cli->tree, fname1);
+ torture_all_info(cli->tree, fname2);
+ }
+
+
+ io.ntrename.in.flags = RENAME_FLAG_MOVE_CLUSTER_INFORMATION;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ for (i=0;i<20000;i++) {
+ io.ntrename.in.cluster_size = i;
+ status = smb_raw_rename(cli->tree, &io);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("i=%d status=%s\n", i, nt_errstr(status));
+ }
+ }
+#endif
+
+ printf("Checking other flags\n");
+
+ for (i=0;i<0xFFF;i++) {
+ if (i == RENAME_FLAG_RENAME ||
+ i == RENAME_FLAG_HARD_LINK ||
+ i == RENAME_FLAG_COPY) {
+ continue;
+ }
+
+ io.ntrename.in.old_name = fname2;
+ io.ntrename.in.new_name = fname1;
+ io.ntrename.in.flags = i;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.cluster_size = 0;
+ status = smb_raw_rename(cli->tree, &io);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("flags=0x%x status=%s\n", i, nt_errstr(status));
+ }
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+extern bool test_trans2rename(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2);
+extern bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1);
+
+/*
+ basic testing of rename calls
+*/
+struct torture_suite *torture_raw_rename(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "RENAME");
+
+ torture_suite_add_1smb_test(suite, "mv", test_mv);
+ /* test_trans2rename and test_nttransrename are actually in torture/raw/oplock.c to
+ use the handlers and macros there. */
+ torture_suite_add_2smb_test(suite, "trans2rename", test_trans2rename);
+ torture_suite_add_1smb_test(suite, "nttransrename", test_nttransrename);
+ torture_suite_add_1smb_test(suite, "ntrename", test_ntrename);
+
+ return suite;
+}
diff --git a/source4/torture/raw/samba3hide.c b/source4/torture/raw/samba3hide.c
new file mode 100644
index 0000000000..1f1501045c
--- /dev/null
+++ b/source4/torture/raw/samba3hide.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test samba3 hide unreadable/unwriteable
+ Copyright (C) Volker Lendecke 2006
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+static void init_unixinfo_nochange(union smb_setfileinfo *info)
+{
+ ZERO_STRUCTP(info);
+ info->unix_basic.level = RAW_SFILEINFO_UNIX_BASIC;
+ info->unix_basic.in.mode = SMB_MODE_NO_CHANGE;
+
+ info->unix_basic.in.end_of_file = SMB_SIZE_NO_CHANGE_HI;
+ info->unix_basic.in.end_of_file <<= 32;
+ info->unix_basic.in.end_of_file |= SMB_SIZE_NO_CHANGE_LO;
+
+ info->unix_basic.in.num_bytes = SMB_SIZE_NO_CHANGE_HI;
+ info->unix_basic.in.num_bytes <<= 32;
+ info->unix_basic.in.num_bytes |= SMB_SIZE_NO_CHANGE_LO;
+
+ info->unix_basic.in.status_change_time = SMB_TIME_NO_CHANGE_HI;
+ info->unix_basic.in.status_change_time <<= 32;
+ info->unix_basic.in.status_change_time |= SMB_TIME_NO_CHANGE_LO;
+
+ info->unix_basic.in.access_time = SMB_TIME_NO_CHANGE_HI;
+ info->unix_basic.in.access_time <<= 32;
+ info->unix_basic.in.access_time |= SMB_TIME_NO_CHANGE_LO;
+
+ info->unix_basic.in.change_time = SMB_TIME_NO_CHANGE_HI;
+ info->unix_basic.in.change_time <<= 32;
+ info->unix_basic.in.change_time |= SMB_TIME_NO_CHANGE_LO;
+
+ info->unix_basic.in.uid = SMB_UID_NO_CHANGE;
+ info->unix_basic.in.gid = SMB_GID_NO_CHANGE;
+}
+
+struct list_state {
+ const char *fname;
+ bool visible;
+};
+
+static void set_visible(struct clilist_file_info *i, const char *mask,
+ void *priv)
+{
+ struct list_state *state = (struct list_state *)priv;
+
+ if (strcasecmp_m(state->fname, i->name) == 0)
+ state->visible = true;
+}
+
+static bool is_visible(struct smbcli_tree *tree, const char *fname)
+{
+ struct list_state state;
+
+ state.visible = false;
+ state.fname = fname;
+
+ if (smbcli_list(tree, "*.*", 0, set_visible, &state) < 0) {
+ return false;
+ }
+ return state.visible;
+}
+
+static bool is_readable(struct smbcli_tree *tree, const char *fname)
+{
+ int fnum;
+ fnum = smbcli_open(tree, fname, O_RDONLY, DENY_NONE);
+ if (fnum < 0) {
+ return false;
+ }
+ smbcli_close(tree, fnum);
+ return true;
+}
+
+static bool is_writeable(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree,
+ const char *fname)
+{
+ int fnum;
+ fnum = smbcli_open(tree, fname, O_WRONLY, DENY_NONE);
+ if (fnum < 0) {
+ return false;
+ }
+ smbcli_close(tree, fnum);
+ return true;
+}
+
+/*
+ * This is not an exact method because there's a ton of reasons why a getatr
+ * might fail. But for our purposes it's sufficient.
+ */
+
+static bool smbcli_file_exists(struct smbcli_tree *tree, const char *fname)
+{
+ return NT_STATUS_IS_OK(smbcli_getatr(tree, fname, NULL, NULL, NULL));
+}
+
+static NTSTATUS smbcli_chmod(struct smbcli_tree *tree, const char *fname,
+ uint64_t permissions)
+{
+ union smb_setfileinfo sfinfo;
+ init_unixinfo_nochange(&sfinfo);
+ sfinfo.unix_basic.in.file.path = fname;
+ sfinfo.unix_basic.in.permissions = permissions;
+ return smb_raw_setpathinfo(tree, &sfinfo);
+}
+
+bool torture_samba3_hide(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ const char *fname = "test.txt";
+ int fnum;
+ NTSTATUS status;
+ struct smbcli_tree *hideunread;
+ struct smbcli_tree *hideunwrite;
+
+ if (!torture_open_connection_share(
+ torture, &cli, torture, torture_setting_string(torture, "host", NULL),
+ torture_setting_string(torture, "share", NULL), torture->ev)) {
+ d_printf("torture_open_connection_share failed\n");
+ return false;
+ }
+
+ status = torture_second_tcon(torture, cli->session, "hideunread",
+ &hideunread);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("second_tcon(hideunread) failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = torture_second_tcon(torture, cli->session, "hideunwrite",
+ &hideunwrite);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("second_tcon(hideunwrite) failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smbcli_unlink(cli->tree, fname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ smbcli_setatr(cli->tree, fname, 0, -1);
+ smbcli_unlink(cli->tree, fname);
+ }
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Failed to create %s - %s\n", fname,
+ smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ smbcli_close(cli->tree, fnum);
+
+ if (!smbcli_file_exists(cli->tree, fname)) {
+ d_printf("%s does not exist\n", fname);
+ return false;
+ }
+
+ /* R/W file should be visible everywhere */
+
+ status = smbcli_chmod(cli->tree, fname, UNIX_R_USR|UNIX_W_USR);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ if (!is_writeable(torture, cli->tree, fname)) {
+ d_printf("File not writable\n");
+ return false;
+ }
+ if (!is_readable(cli->tree, fname)) {
+ d_printf("File not readable\n");
+ return false;
+ }
+ if (!is_visible(cli->tree, fname)) {
+ d_printf("r/w file not visible via normal share\n");
+ return false;
+ }
+ if (!is_visible(hideunread, fname)) {
+ d_printf("r/w file not visible via hide unreadable\n");
+ return false;
+ }
+ if (!is_visible(hideunwrite, fname)) {
+ d_printf("r/w file not visible via hide unwriteable\n");
+ return false;
+ }
+
+ /* R/O file should not be visible via hide unwriteable files */
+
+ status = smbcli_chmod(cli->tree, fname, UNIX_R_USR);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ if (is_writeable(torture, cli->tree, fname)) {
+ d_printf("r/o is writable\n");
+ return false;
+ }
+ if (!is_readable(cli->tree, fname)) {
+ d_printf("r/o not readable\n");
+ return false;
+ }
+ if (!is_visible(cli->tree, fname)) {
+ d_printf("r/o file not visible via normal share\n");
+ return false;
+ }
+ if (!is_visible(hideunread, fname)) {
+ d_printf("r/o file not visible via hide unreadable\n");
+ return false;
+ }
+ if (is_visible(hideunwrite, fname)) {
+ d_printf("r/o file visible via hide unwriteable\n");
+ return false;
+ }
+
+ /* inaccessible file should be only visible on normal share */
+
+ status = smbcli_chmod(cli->tree, fname, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ if (is_writeable(torture, cli->tree, fname)) {
+ d_printf("inaccessible file is writable\n");
+ return false;
+ }
+ if (is_readable(cli->tree, fname)) {
+ d_printf("inaccessible file is readable\n");
+ return false;
+ }
+ if (!is_visible(cli->tree, fname)) {
+ d_printf("inaccessible file not visible via normal share\n");
+ return false;
+ }
+ if (is_visible(hideunread, fname)) {
+ d_printf("inaccessible file visible via hide unreadable\n");
+ return false;
+ }
+ if (is_visible(hideunwrite, fname)) {
+ d_printf("inaccessible file visible via hide unwriteable\n");
+ return false;
+ }
+
+ smbcli_chmod(cli->tree, fname, UNIX_R_USR|UNIX_W_USR);
+ smbcli_unlink(cli->tree, fname);
+
+ return true;
+}
+
+/*
+ * Try to force smb_close to return an error. The only way I can think of is
+ * to open a file with delete on close, chmod the parent dir to 000 and then
+ * close. smb_close should return NT_STATUS_ACCESS_DENIED.
+ */
+
+bool torture_samba3_closeerr(struct torture_context *tctx)
+{
+ struct smbcli_state *cli = NULL;
+ bool result = false;
+ NTSTATUS status;
+ const char *dname = "closeerr.dir";
+ const char *fname = "closeerr.dir\\closerr.txt";
+ int fnum;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ goto fail;
+ }
+
+ smbcli_deltree(cli->tree, dname);
+
+ torture_assert_ntstatus_ok(
+ tctx, smbcli_mkdir(cli->tree, dname),
+ talloc_asprintf(tctx, "smbcli_mdir failed: (%s)\n",
+ smbcli_errstr(cli->tree)));
+
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR,
+ DENY_NONE);
+ torture_assert(tctx, fnum != -1,
+ talloc_asprintf(tctx, "smbcli_open failed: %s\n",
+ smbcli_errstr(cli->tree)));
+ smbcli_close(cli->tree, fnum);
+
+ fnum = smbcli_nt_create_full(cli->tree, fname, 0,
+ SEC_RIGHTS_FILE_ALL,
+ FILE_ATTRIBUTE_NORMAL,
+ NTCREATEX_SHARE_ACCESS_DELETE,
+ NTCREATEX_DISP_OPEN, 0, 0);
+
+ torture_assert(tctx, fnum != -1,
+ talloc_asprintf(tctx, "smbcli_open failed: %s\n",
+ smbcli_errstr(cli->tree)));
+
+ status = smbcli_nt_delete_on_close(cli->tree, fnum, true);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "setting delete_on_close on file failed !");
+
+ status = smbcli_chmod(cli->tree, dname, 0);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "smbcli_chmod on file failed !");
+
+ status = smbcli_close(cli->tree, fnum);
+
+ smbcli_chmod(cli->tree, dname, UNIX_R_USR|UNIX_W_USR|UNIX_X_USR);
+ smbcli_deltree(cli->tree, dname);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_ACCESS_DENIED,
+ "smbcli_close");
+
+ result = true;
+
+ fail:
+ if (cli) {
+ torture_close_connection(cli);
+ }
+ return result;
+}
diff --git a/source4/torture/raw/samba3misc.c b/source4/torture/raw/samba3misc.c
new file mode 100644
index 0000000000..27b4d42dd8
--- /dev/null
+++ b/source4/torture/raw/samba3misc.c
@@ -0,0 +1,891 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test some misc Samba3 code paths
+ Copyright (C) Volker Lendecke 2006
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ } \
+} while (0)
+
+bool torture_samba3_checkfsp(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ const char *fname = "test.txt";
+ const char *dirname = "testdir";
+ int fnum;
+ NTSTATUS status;
+ bool ret = true;
+ TALLOC_CTX *mem_ctx;
+ ssize_t nread;
+ char buf[16];
+ struct smbcli_tree *tree2;
+
+ if ((mem_ctx = talloc_init("torture_samba3_checkfsp")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ if (!torture_open_connection_share(
+ torture, &cli, torture, torture_setting_string(torture, "host", NULL),
+ torture_setting_string(torture, "share", NULL), torture->ev)) {
+ d_printf("torture_open_connection_share failed\n");
+ ret = false;
+ goto done;
+ }
+
+ smbcli_deltree(cli->tree, dirname);
+
+ status = torture_second_tcon(torture, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ &tree2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ /* Try a read on an invalid FID */
+
+ nread = smbcli_read(cli->tree, 4711, buf, 0, sizeof(buf));
+ CHECK_STATUS(smbcli_nt_error(cli->tree), NT_STATUS_INVALID_HANDLE);
+
+ /* Try a read on a directory handle */
+
+ status = smbcli_mkdir(cli->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_mkdir failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ /* Open the directory */
+ {
+ union smb_open io;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.fname = dirname;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smb_open on the directory failed: %s\n",
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ fnum = io.ntcreatex.out.file.fnum;
+ }
+
+ /* Try a read on the directory */
+
+ nread = smbcli_read(cli->tree, fnum, buf, 0, sizeof(buf));
+ if (nread >= 0) {
+ d_printf("smbcli_read on a directory succeeded, expected "
+ "failure\n");
+ ret = false;
+ }
+
+ CHECK_STATUS(smbcli_nt_error(cli->tree),
+ NT_STATUS_INVALID_DEVICE_REQUEST);
+
+ /* Same test on the second tcon */
+
+ nread = smbcli_read(tree2, fnum, buf, 0, sizeof(buf));
+ if (nread >= 0) {
+ d_printf("smbcli_read on a directory succeeded, expected "
+ "failure\n");
+ ret = false;
+ }
+
+ CHECK_STATUS(smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE);
+
+ smbcli_close(cli->tree, fnum);
+
+ /* Try a normal file read on a second tcon */
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Failed to create %s - %s\n", fname,
+ smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ nread = smbcli_read(tree2, fnum, buf, 0, sizeof(buf));
+ CHECK_STATUS(smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE);
+
+ smbcli_close(cli->tree, fnum);
+
+ done:
+ smbcli_deltree(cli->tree, dirname);
+ torture_close_connection(cli);
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+static NTSTATUS raw_smbcli_open(struct smbcli_tree *tree, const char *fname, int flags, int share_mode, int *fnum)
+{
+ union smb_open open_parms;
+ uint_t openfn=0;
+ uint_t accessmode=0;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ if (flags & O_CREAT) {
+ openfn |= OPENX_OPEN_FUNC_CREATE;
+ }
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC) {
+ openfn |= OPENX_OPEN_FUNC_TRUNC;
+ } else {
+ openfn |= OPENX_OPEN_FUNC_OPEN;
+ }
+ }
+
+ accessmode = (share_mode<<OPENX_MODE_DENY_SHIFT);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= OPENX_MODE_ACCESS_RDWR;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= OPENX_MODE_ACCESS_WRITE;
+ } else if ((flags & O_ACCMODE) == O_RDONLY) {
+ accessmode |= OPENX_MODE_ACCESS_READ;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= OPENX_MODE_WRITE_THRU;
+ }
+#endif
+
+ if (share_mode == DENY_FCB) {
+ accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+ }
+
+ open_parms.openx.level = RAW_OPEN_OPENX;
+ open_parms.openx.in.flags = 0;
+ open_parms.openx.in.open_mode = accessmode;
+ open_parms.openx.in.search_attrs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ open_parms.openx.in.file_attrs = 0;
+ open_parms.openx.in.write_time = 0;
+ open_parms.openx.in.open_func = openfn;
+ open_parms.openx.in.size = 0;
+ open_parms.openx.in.timeout = 0;
+ open_parms.openx.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ talloc_free(mem_ctx);
+
+ if (fnum && NT_STATUS_IS_OK(status)) {
+ *fnum = open_parms.openx.out.file.fnum;
+ }
+
+ return status;
+}
+
+static NTSTATUS raw_smbcli_t2open(struct smbcli_tree *tree, const char *fname, int flags, int share_mode, int *fnum)
+{
+ union smb_open io;
+ uint_t openfn=0;
+ uint_t accessmode=0;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_t2open");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ if (flags & O_CREAT) {
+ openfn |= OPENX_OPEN_FUNC_CREATE;
+ }
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC) {
+ openfn |= OPENX_OPEN_FUNC_TRUNC;
+ } else {
+ openfn |= OPENX_OPEN_FUNC_OPEN;
+ }
+ }
+
+ accessmode = (share_mode<<OPENX_MODE_DENY_SHIFT);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= OPENX_MODE_ACCESS_RDWR;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= OPENX_MODE_ACCESS_WRITE;
+ } else if ((flags & O_ACCMODE) == O_RDONLY) {
+ accessmode |= OPENX_MODE_ACCESS_READ;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= OPENX_MODE_WRITE_THRU;
+ }
+#endif
+
+ if (share_mode == DENY_FCB) {
+ accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+ }
+
+ memset(&io, '\0', sizeof(io));
+ io.t2open.level = RAW_OPEN_T2OPEN;
+ io.t2open.in.flags = 0;
+ io.t2open.in.open_mode = accessmode;
+ io.t2open.in.search_attrs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ io.t2open.in.file_attrs = 0;
+ io.t2open.in.write_time = 0;
+ io.t2open.in.open_func = openfn;
+ io.t2open.in.size = 0;
+ io.t2open.in.timeout = 0;
+ io.t2open.in.fname = fname;
+
+ io.t2open.in.num_eas = 1;
+ io.t2open.in.eas = talloc_array(mem_ctx, struct ea_struct, io.t2open.in.num_eas);
+ io.t2open.in.eas[0].flags = 0;
+ io.t2open.in.eas[0].name.s = ".CLASSINFO";
+ io.t2open.in.eas[0].value = data_blob_talloc(mem_ctx, "first value", 11);
+
+ status = smb_raw_open(tree, mem_ctx, &io);
+ talloc_free(mem_ctx);
+
+ if (fnum && NT_STATUS_IS_OK(status)) {
+ *fnum = io.openx.out.file.fnum;
+ }
+
+ return status;
+}
+
+static NTSTATUS raw_smbcli_ntcreate(struct smbcli_tree *tree, const char *fname, int *fnum)
+{
+ union smb_open io;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_t2open");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ memset(&io, '\0', sizeof(io));
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &io);
+ talloc_free(mem_ctx);
+
+ if (fnum && NT_STATUS_IS_OK(status)) {
+ *fnum = io.openx.out.file.fnum;
+ }
+
+ return status;
+}
+
+
+bool torture_samba3_badpath(struct torture_context *torture)
+{
+ struct smbcli_state *cli_nt;
+ struct smbcli_state *cli_dos;
+ const char *fname = "test.txt";
+ const char *fname1 = "test1.txt";
+ const char *dirname = "testdir";
+ char *fpath;
+ char *fpath1;
+ int fnum;
+ NTSTATUS status;
+ bool ret = true;
+ TALLOC_CTX *mem_ctx;
+ bool nt_status_support;
+
+ if (!(mem_ctx = talloc_init("torture_samba3_badpath"))) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ nt_status_support = lp_nt_status_support(torture->lp_ctx);
+
+ if (!lp_set_cmdline(torture->lp_ctx, "nt status support", "yes")) {
+ printf("Could not set 'nt status support = yes'\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli_nt, torture, 0)) {
+ goto fail;
+ }
+
+ if (!lp_set_cmdline(torture->lp_ctx, "nt status support", "no")) {
+ printf("Could not set 'nt status support = yes'\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli_dos, torture, 1)) {
+ goto fail;
+ }
+
+ if (!lp_set_cmdline(torture->lp_ctx, "nt status support",
+ nt_status_support ? "yes":"no")) {
+ printf("Could not reset 'nt status support = yes'");
+ goto fail;
+ }
+
+ smbcli_deltree(cli_nt->tree, dirname);
+
+ status = smbcli_mkdir(cli_nt->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_mkdir failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ status = smbcli_chkpath(cli_nt->tree, dirname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_chkpath(cli_nt->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla", dirname));
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ status = smbcli_chkpath(cli_dos->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla", dirname));
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla\\blub",
+ dirname));
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ status = smbcli_chkpath(cli_dos->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla\\blub",
+ dirname));
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ if (!(fpath = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname))) {
+ goto fail;
+ }
+ fnum = smbcli_open(cli_nt->tree, fpath, O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Could not create file %s: %s\n", fpath,
+ smbcli_errstr(cli_nt->tree));
+ goto fail;
+ }
+ smbcli_close(cli_nt->tree, fnum);
+
+ if (!(fpath1 = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname1))) {
+ goto fail;
+ }
+ fnum = smbcli_open(cli_nt->tree, fpath1, O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Could not create file %s: %s\n", fpath1,
+ smbcli_errstr(cli_nt->tree));
+ goto fail;
+ }
+ smbcli_close(cli_nt->tree, fnum);
+
+ /*
+ * Do a whole bunch of error code checks on chkpath
+ */
+
+ status = smbcli_chkpath(cli_nt->tree, fpath);
+ CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
+ status = smbcli_chkpath(cli_dos->tree, fpath);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "..");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ status = smbcli_chkpath(cli_dos->tree, "..");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath));
+
+ status = smbcli_chkpath(cli_nt->tree, ".");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, ".");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "\t");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "\t");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "\t\\bla");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "\t\\bla");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "<");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "<");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "<\\bla");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "<\\bla");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ /*
+ * .... And the same gang against getatr. Note that the DOS error codes
+ * differ....
+ */
+
+ status = smbcli_getatr(cli_nt->tree, fpath, NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smbcli_getatr(cli_dos->tree, fpath, NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_getatr(cli_nt->tree, "..", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ status = smbcli_getatr(cli_dos->tree, "..", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath));
+
+ status = smbcli_getatr(cli_nt->tree, ".", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, ".", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "\t", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "\t", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "\t\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "\t\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "<", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "<", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "<\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "<\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ /* Try the same set with openX. */
+
+ status = raw_smbcli_open(cli_nt->tree, "..", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ status = raw_smbcli_open(cli_dos->tree, "..", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath));
+
+ status = raw_smbcli_open(cli_nt->tree, ".", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, ".", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "\t", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "\t", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "\t\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "\t\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "<", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "<", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "<\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "<\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ /* Let's test EEXIST error code mapping. */
+ status = raw_smbcli_open(cli_nt->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = raw_smbcli_open(cli_dos->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS,ERRfilexists));
+
+ status = raw_smbcli_t2open(cli_nt->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED)
+ || !torture_setting_bool(torture, "samba3", false)) {
+ /* Against samba3, treat EAS_NOT_SUPPORTED as acceptable */
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ }
+ status = raw_smbcli_t2open(cli_dos->tree, fpath, O_RDONLY | O_CREAT| O_EXCL, DENY_NONE, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS,ERReasnotsupported))
+ || !torture_setting_bool(torture, "samba3", false)) {
+ /* Against samba3, treat EAS_NOT_SUPPORTED as acceptable */
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS,ERRfilexists));
+ }
+
+ status = raw_smbcli_ntcreate(cli_nt->tree, fpath, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = raw_smbcli_ntcreate(cli_dos->tree, fpath, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS,ERRfilexists));
+
+ /* Try the rename test. */
+ {
+ union smb_rename io;
+ memset(&io, '\0', sizeof(io));
+ io.rename.in.pattern1 = fpath1;
+ io.rename.in.pattern2 = fpath;
+
+ /* Try with SMBmv rename. */
+ status = smb_raw_rename(cli_nt->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = smb_raw_rename(cli_dos->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS,ERRrename));
+
+ /* Try with NT rename. */
+ io.generic.level = RAW_RENAME_NTRENAME;
+ io.ntrename.in.old_name = fpath1;
+ io.ntrename.in.new_name = fpath;
+ io.ntrename.in.attrib = 0;
+ io.ntrename.in.cluster_size = 0;
+ io.ntrename.in.flags = RENAME_FLAG_RENAME;
+
+ status = smb_raw_rename(cli_nt->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = smb_raw_rename(cli_dos->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS,ERRrename));
+ }
+
+ goto done;
+
+ fail:
+ ret = false;
+
+ done:
+ if (cli_nt != NULL) {
+ smbcli_deltree(cli_nt->tree, dirname);
+ torture_close_connection(cli_nt);
+ }
+ if (cli_dos != NULL) {
+ torture_close_connection(cli_dos);
+ }
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+static void count_fn(struct clilist_file_info *info, const char *name,
+ void *private_data)
+{
+ int *counter = (int *)private_data;
+ *counter += 1;
+}
+
+bool torture_samba3_caseinsensitive(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ const char *dirname = "insensitive";
+ const char *ucase_dirname = "InSeNsItIvE";
+ const char *fname = "foo";
+ char *fpath;
+ int fnum;
+ int counter = 0;
+ bool ret = true;
+
+ if (!(mem_ctx = talloc_init("torture_samba3_caseinsensitive"))) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, torture, 0)) {
+ goto done;
+ }
+
+ smbcli_deltree(cli->tree, dirname);
+
+ status = smbcli_mkdir(cli->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_mkdir failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ if (!(fpath = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname))) {
+ goto done;
+ }
+ fnum = smbcli_open(cli->tree, fpath, O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("Could not create file %s: %s\n", fpath,
+ smbcli_errstr(cli->tree));
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+
+ smbcli_list(cli->tree, talloc_asprintf(
+ mem_ctx, "%s\\*", ucase_dirname),
+ FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN
+ |FILE_ATTRIBUTE_SYSTEM,
+ count_fn, (void *)&counter);
+
+ if (counter == 3) {
+ ret = true;
+ }
+ else {
+ d_fprintf(stderr, "expected 3 entries, got %d\n", counter);
+ ret = false;
+ }
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Check that Samba3 correctly deals with conflicting posix byte range locks
+ * on an underlying file
+ */
+
+bool torture_samba3_posixtimedlock(struct torture_context *tctx)
+{
+ struct smbcli_state *cli;
+ NTSTATUS status;
+ bool ret = true;
+ const char *dirname = "posixlock";
+ const char *fname = "locked";
+ const char *fpath;
+ const char *localdir;
+ const char *localname;
+ int fnum = -1;
+
+ int fd = -1;
+ struct flock posix_lock;
+
+ union smb_lock io;
+ struct smb_lock_entry lock_entry;
+ struct smbcli_request *req;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ ret = false;
+ goto done;
+ }
+
+ smbcli_deltree(cli->tree, dirname);
+
+ status = smbcli_mkdir(cli->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "smbcli_mkdir failed: %s\n",
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!(fpath = talloc_asprintf(tctx, "%s\\%s", dirname, fname))) {
+ torture_warning(tctx, "talloc failed\n");
+ ret = false;
+ goto done;
+ }
+ fnum = smbcli_open(cli->tree, fpath, O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ torture_warning(tctx, "Could not create file %s: %s\n", fpath,
+ smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ if (!(localdir = torture_setting_string(tctx, "localdir", NULL))) {
+ torture_warning(tctx, "Need 'localdir' setting\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!(localname = talloc_asprintf(tctx, "%s/%s/%s", localdir, dirname,
+ fname))) {
+ torture_warning(tctx, "talloc failed\n");
+ ret = false;
+ goto done;
+ }
+
+ /*
+ * Lock a byte range from posix
+ */
+
+ fd = open(localname, O_RDWR);
+ if (fd == -1) {
+ torture_warning(tctx, "open(%s) failed: %s\n",
+ localname, strerror(errno));
+ goto done;
+ }
+
+ posix_lock.l_type = F_WRLCK;
+ posix_lock.l_whence = SEEK_SET;
+ posix_lock.l_start = 0;
+ posix_lock.l_len = 1;
+
+ if (fcntl(fd, F_SETLK, &posix_lock) == -1) {
+ torture_warning(tctx, "fcntl failed: %s\n", strerror(errno));
+ ret = false;
+ goto done;
+ }
+
+ /*
+ * Try a cifs brlock without timeout to see if posix locking = yes
+ */
+
+ io.lockx.in.ulock_cnt = 0;
+ io.lockx.in.lock_cnt = 1;
+
+ lock_entry.count = 1;
+ lock_entry.offset = 0;
+ lock_entry.pid = cli->tree->session->pid;
+
+ io.lockx.level = RAW_LOCK_LOCKX;
+ io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ io.lockx.in.timeout = 0;
+ io.lockx.in.locks = &lock_entry;
+ io.lockx.in.file.fnum = fnum;
+
+ status = smb_raw_lock(cli->tree, &io);
+
+ ret = true;
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (!ret) {
+ goto done;
+ }
+
+ /*
+ * Now fire off a timed brlock, unlock the posix lock and see if the
+ * timed lock gets through.
+ */
+
+ io.lockx.in.timeout = 5000;
+
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ torture_warning(tctx, "smb_raw_lock_send failed\n");
+ ret = false;
+ goto done;
+ }
+
+ /*
+ * Ship the async timed request to the server
+ */
+ event_loop_once(req->transport->socket->event.ctx);
+ msleep(500);
+
+ close(fd);
+
+ status = smbcli_request_simple_recv(req);
+
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ done:
+ if (fnum != -1) {
+ smbcli_close(cli->tree, fnum);
+ }
+ if (fd != -1) {
+ close(fd);
+ }
+ smbcli_deltree(cli->tree, dirname);
+ return ret;
+}
+
+bool torture_samba3_rootdirfid(struct torture_context *tctx)
+{
+ struct smbcli_state *cli;
+ NTSTATUS status;
+ uint16_t dnum;
+ union smb_open io;
+ const char *fname = "testfile";
+ bool ret = false;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ ret = false;
+ goto done;
+ }
+
+ smbcli_unlink(cli->tree, fname);
+
+ ZERO_STRUCT(io);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.access_mask =
+ SEC_STD_SYNCHRONIZE | SEC_FILE_EXECUTE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ
+ | NTCREATEX_SHARE_ACCESS_READ;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.fname = "\\";
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smb_open on the directory failed: %s\n",
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ dnum = io.ntcreatex.out.file.fnum;
+
+ io.ntcreatex.in.flags =
+ NTCREATEX_FLAGS_REQUEST_OPLOCK
+ | NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.root_fid = dnum;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.fname = fname;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smb_open on the file %s failed: %s\n",
+ fname, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ smbcli_close(cli->tree, dnum);
+ smbcli_unlink(cli->tree, fname);
+
+ ret = true;
+ done:
+ return ret;
+}
+
diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c
new file mode 100644
index 0000000000..2a6aef20b5
--- /dev/null
+++ b/source4/torture/raw/search.c
@@ -0,0 +1,1391 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_SEARCH_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+
+#define BASEDIR "\\testsearch"
+
+/*
+ callback function for single_search
+*/
+static bool single_search_callback(void *private, const union smb_search_data *file)
+{
+ union smb_search_data *data = (union smb_search_data *)private;
+
+ *data = *file;
+
+ return true;
+}
+
+/*
+ do a single file (non-wildcard) search
+*/
+NTSTATUS torture_single_search(struct smbcli_state *cli,
+ TALLOC_CTX *tctx,
+ const char *pattern,
+ enum smb_search_level level,
+ enum smb_search_data_level data_level,
+ uint16_t attrib,
+ union smb_search_data *data)
+{
+ union smb_search_first io;
+ union smb_search_close c;
+ NTSTATUS status;
+
+ switch (level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ case RAW_SEARCH_FUNIQUE:
+ io.search_first.level = level;
+ io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ io.search_first.in.max_count = 1;
+ io.search_first.in.search_attrib = attrib;
+ io.search_first.in.pattern = pattern;
+ break;
+
+ case RAW_SEARCH_TRANS2:
+ io.t2ffirst.level = RAW_SEARCH_TRANS2;
+ io.t2ffirst.data_level = data_level;
+ io.t2ffirst.in.search_attrib = attrib;
+ io.t2ffirst.in.max_count = 1;
+ io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
+ io.t2ffirst.in.storage_type = 0;
+ io.t2ffirst.in.pattern = pattern;
+ break;
+
+ case RAW_SEARCH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_search_first(cli->tree, tctx,
+ &io, (void *)data, single_search_callback);
+
+ if (NT_STATUS_IS_OK(status) && level == RAW_SEARCH_FFIRST) {
+ c.fclose.level = RAW_FINDCLOSE_FCLOSE;
+ c.fclose.in.max_count = 1;
+ c.fclose.in.search_attrib = 0;
+ c.fclose.in.id = data->search.id;
+ status = smb_raw_search_close(cli->tree, &c);
+ }
+
+ return status;
+}
+
+
+static struct {
+ const char *name;
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ int name_offset;
+ int resume_key_offset;
+ uint32_t capability_mask;
+ NTSTATUS status;
+ union smb_search_data data;
+} levels[] = {
+ {"FFIRST",
+ RAW_SEARCH_FFIRST, RAW_SEARCH_DATA_SEARCH,
+ offsetof(union smb_search_data, search.name),
+ -1,
+ },
+ {"FUNIQUE",
+ RAW_SEARCH_FUNIQUE, RAW_SEARCH_DATA_SEARCH,
+ offsetof(union smb_search_data, search.name),
+ -1,
+ },
+ {"SEARCH",
+ RAW_SEARCH_SEARCH, RAW_SEARCH_DATA_SEARCH,
+ offsetof(union smb_search_data, search.name),
+ -1,
+ },
+ {"STANDARD",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_STANDARD,
+ offsetof(union smb_search_data, standard.name.s),
+ offsetof(union smb_search_data, standard.resume_key),
+ },
+ {"EA_SIZE",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_EA_SIZE,
+ offsetof(union smb_search_data, ea_size.name.s),
+ offsetof(union smb_search_data, ea_size.resume_key),
+ },
+ {"DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_DIRECTORY_INFO,
+ offsetof(union smb_search_data, directory_info.name.s),
+ offsetof(union smb_search_data, directory_info.file_index),
+ },
+ {"FULL_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,
+ offsetof(union smb_search_data, full_directory_info.name.s),
+ offsetof(union smb_search_data, full_directory_info.file_index),
+ },
+ {"NAME_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_NAME_INFO,
+ offsetof(union smb_search_data, name_info.name.s),
+ offsetof(union smb_search_data, name_info.file_index),
+ },
+ {"BOTH_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
+ offsetof(union smb_search_data, both_directory_info.name.s),
+ offsetof(union smb_search_data, both_directory_info.file_index),
+ },
+ {"ID_FULL_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO,
+ offsetof(union smb_search_data, id_full_directory_info.name.s),
+ offsetof(union smb_search_data, id_full_directory_info.file_index),
+ },
+ {"ID_BOTH_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO,
+ offsetof(union smb_search_data, id_both_directory_info.name.s),
+ offsetof(union smb_search_data, id_both_directory_info.file_index),
+ },
+ {"UNIX_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_UNIX_INFO,
+ offsetof(union smb_search_data, unix_info.name),
+ offsetof(union smb_search_data, unix_info.file_index),
+ CAP_UNIX}
+};
+
+
+/*
+ return level name
+*/
+static const char *level_name(enum smb_search_level level,
+ enum smb_search_data_level data_level)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (level == levels[i].level &&
+ data_level == levels[i].data_level) {
+ return levels[i].name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ extract the name from a smb_data structure and level
+*/
+static const char *extract_name(void *data, enum smb_search_level level,
+ enum smb_search_data_level data_level)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (level == levels[i].level &&
+ data_level == levels[i].data_level) {
+ return *(const char **)(levels[i].name_offset + (char *)data);
+ }
+ }
+ return NULL;
+}
+
+/*
+ extract the name from a smb_data structure and level
+*/
+static uint32_t extract_resume_key(void *data, enum smb_search_level level,
+ enum smb_search_data_level data_level)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (level == levels[i].level &&
+ data_level == levels[i].data_level) {
+ return *(uint32_t *)(levels[i].resume_key_offset + (char *)data);
+ }
+ }
+ return 0;
+}
+
+/* find a level in the table by name */
+static union smb_search_data *find(const char *name)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (NT_STATUS_IS_OK(levels[i].status) &&
+ strcmp(levels[i].name, name) == 0) {
+ return &levels[i].data;
+ }
+ }
+ return NULL;
+}
+
+/*
+ basic testing of all RAW_SEARCH_* calls using a single file
+*/
+static bool test_one_file(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ int fnum;
+ const char *fname = "\\torture_search.txt";
+ const char *fname2 = "\\torture_search-NOTEXIST.txt";
+ NTSTATUS status;
+ int i;
+ union smb_fileinfo all_info, alt_info, name_info, internal_info;
+ union smb_search_data *s;
+
+ fnum = create_complex_file(cli, tctx, fname);
+ if (fnum == -1) {
+ printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ /* call all the levels */
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ NTSTATUS expected_status;
+ uint32_t cap = cli->transport->negotiate.capabilities;
+
+ torture_comment(tctx, "testing %s\n", levels[i].name);
+
+ levels[i].status = torture_single_search(cli, tctx, fname,
+ levels[i].level,
+ levels[i].data_level,
+ 0,
+ &levels[i].data);
+
+ /* see if this server claims to support this level */
+ if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
+ printf("search level %s(%d) not supported by server\n",
+ levels[i].name, (int)levels[i].level);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(levels[i].status)) {
+ printf("search level %s(%d) failed - %s\n",
+ levels[i].name, (int)levels[i].level,
+ nt_errstr(levels[i].status));
+ ret = false;
+ continue;
+ }
+
+ status = torture_single_search(cli, tctx, fname2,
+ levels[i].level,
+ levels[i].data_level,
+ 0,
+ &levels[i].data);
+
+ expected_status = NT_STATUS_NO_SUCH_FILE;
+ if (levels[i].level == RAW_SEARCH_SEARCH ||
+ levels[i].level == RAW_SEARCH_FFIRST ||
+ levels[i].level == RAW_SEARCH_FUNIQUE) {
+ expected_status = STATUS_NO_MORE_FILES;
+ }
+ if (!NT_STATUS_EQUAL(status, expected_status)) {
+ printf("search level %s(%d) should fail with %s - %s\n",
+ levels[i].name, (int)levels[i].level,
+ nt_errstr(expected_status),
+ nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ /* get the all_info file into to check against */
+ all_info.generic.level = RAW_FILEINFO_ALL_INFO;
+ all_info.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, tctx, &all_info);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_ALL_INFO failed");
+
+ alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
+ alt_info.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, tctx, &alt_info);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_ALT_NAME_INFO failed");
+
+ internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
+ internal_info.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, tctx, &internal_info);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_INTERNAL_INFORMATION failed");
+
+ name_info.generic.level = RAW_FILEINFO_NAME_INFO;
+ name_info.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, tctx, &name_info);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_NAME_INFO failed");
+
+#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
+ s = find(name); \
+ if (s) { \
+ if ((s->sname1.field1) != (v.sname2.out.field2)) { \
+ printf("(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \
+ __location__, \
+ #sname1, #field1, (int)s->sname1.field1, \
+ #sname2, #field2, (int)v.sname2.out.field2); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
+ s = find(name); \
+ if (s) { \
+ if (s->sname1.field1 != (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
+ printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
+ __location__, \
+ #sname1, #field1, timestring(tctx, s->sname1.field1), \
+ #sname2, #field2, nt_time_string(tctx, v.sname2.out.field2)); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
+ s = find(name); \
+ if (s) { \
+ if (s->sname1.field1 != v.sname2.out.field2) { \
+ printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
+ __location__, \
+ #sname1, #field1, nt_time_string(tctx, s->sname1.field1), \
+ #sname2, #field2, nt_time_string(tctx, v.sname2.out.field2)); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
+ s = find(name); \
+ if (s) { \
+ if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
+ printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
+ __location__, \
+ #sname1, #field1, s->sname1.field1, \
+ #sname2, #field2, v.sname2.out.field2.s); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
+ s = find(name); \
+ if (s) { \
+ if (!s->sname1.field1.s || \
+ strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \
+ wire_bad_flags(&s->sname1.field1, flags, cli->transport)) { \
+ printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
+ __location__, \
+ #sname1, #field1, s->sname1.field1.s, \
+ #sname2, #field2, v.sname2.out.field2.s); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_NAME(name, sname1, field1, fname, flags) do { \
+ s = find(name); \
+ if (s) { \
+ if (!s->sname1.field1.s || \
+ strcmp(s->sname1.field1.s, fname) || \
+ wire_bad_flags(&s->sname1.field1, flags, cli->transport)) { \
+ printf("(%s) %s/%s [%s] != %s\n", \
+ __location__, \
+ #sname1, #field1, s->sname1.field1.s, \
+ fname); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
+ s = find(name); \
+ if (s) { \
+ if (!s->sname1.field1 || \
+ strcmp(s->sname1.field1, fname)) { \
+ printf("(%s) %s/%s [%s] != %s\n", \
+ __location__, \
+ #sname1, #field1, s->sname1.field1, \
+ fname); \
+ ret = false; \
+ } \
+ }} while (0)
+
+ /* check that all the results are as expected */
+ CHECK_VAL("SEARCH", search, attrib, all_info, all_info, attrib&0xFFF);
+ CHECK_VAL("STANDARD", standard, attrib, all_info, all_info, attrib&0xFFF);
+ CHECK_VAL("EA_SIZE", ea_size, attrib, all_info, all_info, attrib&0xFFF);
+ CHECK_VAL("DIRECTORY_INFO", directory_info, attrib, all_info, all_info, attrib);
+ CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib);
+ CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib);
+ CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, attrib, all_info, all_info, attrib);
+ CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, attrib, all_info, all_info, attrib);
+
+ CHECK_TIME("SEARCH", search, write_time, all_info, all_info, write_time);
+ CHECK_TIME("STANDARD", standard, write_time, all_info, all_info, write_time);
+ CHECK_TIME("EA_SIZE", ea_size, write_time, all_info, all_info, write_time);
+ CHECK_TIME("STANDARD", standard, create_time, all_info, all_info, create_time);
+ CHECK_TIME("EA_SIZE", ea_size, create_time, all_info, all_info, create_time);
+ CHECK_TIME("STANDARD", standard, access_time, all_info, all_info, access_time);
+ CHECK_TIME("EA_SIZE", ea_size, access_time, all_info, all_info, access_time);
+
+ CHECK_NTTIME("DIRECTORY_INFO", directory_info, write_time, all_info, all_info, write_time);
+ CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time);
+ CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time);
+ CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info, all_info, write_time);
+ CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info, all_info, write_time);
+
+ CHECK_NTTIME("DIRECTORY_INFO", directory_info, create_time, all_info, all_info, create_time);
+ CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
+ CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
+ CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info, all_info, create_time);
+ CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info, all_info, create_time);
+
+ CHECK_NTTIME("DIRECTORY_INFO", directory_info, access_time, all_info, all_info, access_time);
+ CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time);
+ CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time);
+ CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info, all_info, access_time);
+ CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info, all_info, access_time);
+
+ CHECK_NTTIME("DIRECTORY_INFO", directory_info, change_time, all_info, all_info, change_time);
+ CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info, all_info, change_time);
+ CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info, all_info, change_time);
+ CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info, all_info, change_time);
+ CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info, all_info, change_time);
+
+ CHECK_VAL("SEARCH", search, size, all_info, all_info, size);
+ CHECK_VAL("STANDARD", standard, size, all_info, all_info, size);
+ CHECK_VAL("EA_SIZE", ea_size, size, all_info, all_info, size);
+ CHECK_VAL("DIRECTORY_INFO", directory_info, size, all_info, all_info, size);
+ CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size);
+ CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size);
+ CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, size, all_info, all_info, size);
+ CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, size, all_info, all_info, size);
+ CHECK_VAL("UNIX_INFO", unix_info, size, all_info, all_info, size);
+
+ CHECK_VAL("STANDARD", standard, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("EA_SIZE", ea_size, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("DIRECTORY_INFO", directory_info, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, alloc_size, all_info, all_info, alloc_size);
+ CHECK_VAL("UNIX_INFO", unix_info, alloc_size, all_info, all_info, alloc_size);
+
+ CHECK_VAL("EA_SIZE", ea_size, ea_size, all_info, all_info, ea_size);
+ CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size);
+ CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size);
+ CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, ea_size, all_info, all_info, ea_size);
+ CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, ea_size, all_info, all_info, ea_size);
+
+ CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname);
+ CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
+
+ CHECK_NAME("STANDARD", standard, name, fname+1, 0);
+ CHECK_NAME("EA_SIZE", ea_size, name, fname+1, 0);
+ CHECK_NAME("DIRECTORY_INFO", directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("NAME_INFO", name_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_UNIX_NAME("UNIX_INFO", unix_info, name, fname+1, STR_TERMINATE_ASCII);
+
+ CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id);
+ CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id);
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+struct multiple_result {
+ TALLOC_CTX *tctx;
+ int count;
+ union smb_search_data *list;
+};
+
+/*
+ callback function for multiple_search
+*/
+static bool multiple_search_callback(void *private, const union smb_search_data *file)
+{
+ struct multiple_result *data = (struct multiple_result *)private;
+
+
+ data->count++;
+ data->list = talloc_realloc(data->tctx,
+ data->list,
+ union smb_search_data,
+ data->count);
+
+ data->list[data->count-1] = *file;
+
+ return true;
+}
+
+enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY};
+
+/*
+ do a single file (non-wildcard) search
+*/
+static NTSTATUS multiple_search(struct smbcli_state *cli,
+ TALLOC_CTX *tctx,
+ const char *pattern,
+ enum smb_search_data_level data_level,
+ enum continue_type cont_type,
+ void *data)
+{
+ union smb_search_first io;
+ union smb_search_next io2;
+ NTSTATUS status;
+ const int per_search = 100;
+ struct multiple_result *result = (struct multiple_result *)data;
+
+ if (data_level == RAW_SEARCH_DATA_SEARCH) {
+ io.search_first.level = RAW_SEARCH_SEARCH;
+ io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ io.search_first.in.max_count = per_search;
+ io.search_first.in.search_attrib = 0;
+ io.search_first.in.pattern = pattern;
+ } else {
+ io.t2ffirst.level = RAW_SEARCH_TRANS2;
+ io.t2ffirst.data_level = data_level;
+ io.t2ffirst.in.search_attrib = 0;
+ io.t2ffirst.in.max_count = per_search;
+ io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+ io.t2ffirst.in.storage_type = 0;
+ io.t2ffirst.in.pattern = pattern;
+ if (cont_type == CONT_RESUME_KEY) {
+ io.t2ffirst.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME |
+ FLAG_TRANS2_FIND_BACKUP_INTENT;
+ }
+ }
+
+ status = smb_raw_search_first(cli->tree, tctx,
+ &io, data, multiple_search_callback);
+
+
+ while (NT_STATUS_IS_OK(status)) {
+ if (data_level == RAW_SEARCH_DATA_SEARCH) {
+ io2.search_next.level = RAW_SEARCH_SEARCH;
+ io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ io2.search_next.in.max_count = per_search;
+ io2.search_next.in.search_attrib = 0;
+ io2.search_next.in.id = result->list[result->count-1].search.id;
+ } else {
+ io2.t2fnext.level = RAW_SEARCH_TRANS2;
+ io2.t2fnext.data_level = data_level;
+ io2.t2fnext.in.handle = io.t2ffirst.out.handle;
+ io2.t2fnext.in.max_count = per_search;
+ io2.t2fnext.in.resume_key = 0;
+ io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+ io2.t2fnext.in.last_name = "";
+ switch (cont_type) {
+ case CONT_RESUME_KEY:
+ io2.t2fnext.in.resume_key = extract_resume_key(&result->list[result->count-1],
+ io2.t2fnext.level, io2.t2fnext.data_level);
+ if (io2.t2fnext.in.resume_key == 0) {
+ printf("Server does not support resume by key for level %s\n",
+ level_name(io2.t2fnext.level, io2.t2fnext.data_level));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME |
+ FLAG_TRANS2_FIND_BACKUP_INTENT;
+ break;
+ case CONT_NAME:
+ io2.t2fnext.in.last_name = extract_name(&result->list[result->count-1],
+ io2.t2fnext.level, io2.t2fnext.data_level);
+ break;
+ case CONT_FLAGS:
+ io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_CONTINUE;
+ break;
+ }
+ }
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &io2, data, multiple_search_callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ if (data_level == RAW_SEARCH_DATA_SEARCH) {
+ if (io2.search_next.out.count == 0) {
+ break;
+ }
+ } else if (io2.t2fnext.out.count == 0 ||
+ io2.t2fnext.out.end_of_search) {
+ break;
+ }
+ }
+
+ return status;
+}
+
+#define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status")
+
+#define CHECK_VALUE(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value");
+
+#define CHECK_STRING(v, correct) torture_assert_casestr_equal(tctx, v, correct, "incorrect value");
+
+
+static enum smb_search_data_level compare_data_level;
+
+static int search_compare(union smb_search_data *d1, union smb_search_data *d2)
+{
+ const char *s1, *s2;
+ enum smb_search_level level;
+
+ if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
+ level = RAW_SEARCH_SEARCH;
+ } else {
+ level = RAW_SEARCH_TRANS2;
+ }
+
+ s1 = extract_name(d1, level, compare_data_level);
+ s2 = extract_name(d2, level, compare_data_level);
+ return strcmp_safe(s1, s2);
+}
+
+
+
+/*
+ basic testing of search calls using many files
+*/
+static bool test_many_files(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ const int num_files = 700;
+ int i, fnum, t;
+ char *fname;
+ bool ret = true;
+ NTSTATUS status;
+ struct multiple_result result;
+ struct {
+ const char *name;
+ const char *cont_name;
+ enum smb_search_data_level data_level;
+ enum continue_type cont_type;
+ } search_types[] = {
+ {"SEARCH", "ID", RAW_SEARCH_DATA_SEARCH, CONT_RESUME_KEY},
+ {"BOTH_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_NAME},
+ {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_FLAGS},
+ {"BOTH_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY},
+ {"STANDARD", "FLAGS", RAW_SEARCH_DATA_STANDARD, CONT_FLAGS},
+ {"STANDARD", "KEY", RAW_SEARCH_DATA_STANDARD, CONT_RESUME_KEY},
+ {"STANDARD", "NAME", RAW_SEARCH_DATA_STANDARD, CONT_NAME},
+ {"EA_SIZE", "FLAGS", RAW_SEARCH_DATA_EA_SIZE, CONT_FLAGS},
+ {"EA_SIZE", "KEY", RAW_SEARCH_DATA_EA_SIZE, CONT_RESUME_KEY},
+ {"EA_SIZE", "NAME", RAW_SEARCH_DATA_EA_SIZE, CONT_NAME},
+ {"DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_FLAGS},
+ {"DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESUME_KEY},
+ {"DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_NAME},
+ {"FULL_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_FLAGS},
+ {"FULL_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESUME_KEY},
+ {"FULL_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_NAME},
+ {"ID_FULL_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_FLAGS},
+ {"ID_FULL_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESUME_KEY},
+ {"ID_FULL_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_NAME},
+ {"ID_BOTH_DIRECTORY_INFO", "NAME", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_NAME},
+ {"ID_BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_FLAGS},
+ {"ID_BOTH_DIRECTORY_INFO", "KEY", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY}
+ };
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ torture_comment(tctx, "Testing with %d files\n", num_files);
+
+ for (i=0;i<num_files;i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ torture_assert(tctx, fnum != -1, "Failed to create");
+ talloc_free(fname);
+ smbcli_close(cli->tree, fnum);
+ }
+
+
+ for (t=0;t<ARRAY_SIZE(search_types);t++) {
+ ZERO_STRUCT(result);
+
+ if ((search_types[t].cont_type == CONT_RESUME_KEY) &&
+ (search_types[t].data_level != RAW_SEARCH_DATA_SEARCH) &&
+ torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx,
+ "SKIP: Continue %s via %s\n",
+ search_types[t].name, search_types[t].cont_name);
+ continue;
+ }
+
+ result.tctx = talloc_new(tctx);
+
+ torture_comment(tctx,
+ "Continue %s via %s\n", search_types[t].name, search_types[t].cont_name);
+
+ status = multiple_search(cli, tctx, BASEDIR "\\*.*",
+ search_types[t].data_level,
+ search_types[t].cont_type,
+ &result);
+
+ torture_assert_ntstatus_ok(tctx, status, "search failed");
+ CHECK_VALUE(result.count, num_files);
+
+ compare_data_level = search_types[t].data_level;
+
+ qsort(result.list, result.count, sizeof(result.list[0]),
+ QSORT_CAST search_compare);
+
+ for (i=0;i<result.count;i++) {
+ const char *s;
+ enum smb_search_level level;
+ if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
+ level = RAW_SEARCH_SEARCH;
+ } else {
+ level = RAW_SEARCH_TRANS2;
+ }
+ s = extract_name(&result.list[i], level, compare_data_level);
+ fname = talloc_asprintf(cli, "t%03d-%d.txt", i, i);
+ torture_assert_str_equal(tctx, fname, s, "Incorrect name");
+ talloc_free(fname);
+ }
+ talloc_free(result.tctx);
+ }
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+
+/*
+ check a individual file result
+*/
+static bool check_result(struct multiple_result *result, const char *name, bool exist, uint32_t attrib)
+{
+ int i;
+ for (i=0;i<result->count;i++) {
+ if (strcmp(name, result->list[i].both_directory_info.name.s) == 0) break;
+ }
+ if (i == result->count) {
+ if (exist) {
+ printf("failed: '%s' should exist with attribute %s\n",
+ name, attrib_string(result->list, attrib));
+ return false;
+ }
+ return true;
+ }
+
+ if (!exist) {
+ printf("failed: '%s' should NOT exist (has attribute %s)\n",
+ name, attrib_string(result->list, result->list[i].both_directory_info.attrib));
+ return false;
+ }
+
+ if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
+ printf("failed: '%s' should have attribute 0x%x (has 0x%x)\n",
+ name,
+ attrib, result->list[i].both_directory_info.attrib);
+ return false;
+ }
+ return true;
+}
+
+/*
+ test what happens when the directory is modified during a search
+*/
+static bool test_modify_search(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ const int num_files = 20;
+ int i, fnum;
+ char *fname;
+ bool ret = true;
+ NTSTATUS status;
+ struct multiple_result result;
+ union smb_search_first io;
+ union smb_search_next io2;
+ union smb_setfileinfo sfinfo;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Creating %d files\n", num_files);
+
+ for (i=num_files-1;i>=0;i--) {
+ fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ talloc_free(fname);
+ smbcli_close(cli->tree, fnum);
+ }
+
+ printf("pulling the first file\n");
+ ZERO_STRUCT(result);
+ result.tctx = talloc_new(tctx);
+
+ io.t2ffirst.level = RAW_SEARCH_TRANS2;
+ io.t2ffirst.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ io.t2ffirst.in.search_attrib = 0;
+ io.t2ffirst.in.max_count = 0;
+ io.t2ffirst.in.flags = 0;
+ io.t2ffirst.in.storage_type = 0;
+ io.t2ffirst.in.pattern = BASEDIR "\\*.*";
+
+ status = smb_raw_search_first(cli->tree, tctx,
+ &io, &result, multiple_search_callback);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(result.count, 1);
+
+ printf("pulling the second file\n");
+ io2.t2fnext.level = RAW_SEARCH_TRANS2;
+ io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ io2.t2fnext.in.handle = io.t2ffirst.out.handle;
+ io2.t2fnext.in.max_count = 1;
+ io2.t2fnext.in.resume_key = 0;
+ io2.t2fnext.in.flags = 0;
+ io2.t2fnext.in.last_name = result.list[result.count-1].both_directory_info.name.s;
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &io2, &result, multiple_search_callback);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(result.count, 2);
+
+ result.count = 0;
+
+ printf("Changing attributes and deleting\n");
+ smbcli_open(cli->tree, BASEDIR "\\T003-03.txt.2", O_CREAT|O_RDWR, DENY_NONE);
+ smbcli_open(cli->tree, BASEDIR "\\T013-13.txt.2", O_CREAT|O_RDWR, DENY_NONE);
+ fnum = create_complex_file(cli, tctx, BASEDIR "\\T013-13.txt.3");
+ smbcli_unlink(cli->tree, BASEDIR "\\T014-14.txt");
+ torture_set_file_attribute(cli->tree, BASEDIR "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
+ torture_set_file_attribute(cli->tree, BASEDIR "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
+ torture_set_file_attribute(cli->tree, BASEDIR "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);
+ torture_set_file_attribute(cli->tree, BASEDIR "\\T018-18.txt", 0);
+ sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
+ sfinfo.generic.in.file.fnum = fnum;
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io2.t2fnext.level = RAW_SEARCH_TRANS2;
+ io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ io2.t2fnext.in.handle = io.t2ffirst.out.handle;
+ io2.t2fnext.in.max_count = num_files + 3;
+ io2.t2fnext.in.resume_key = 0;
+ io2.t2fnext.in.flags = 0;
+ io2.t2fnext.in.last_name = ".";
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &io2, &result, multiple_search_callback);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(result.count, 20);
+
+ ret &= check_result(&result, "t009-9.txt", true, FILE_ATTRIBUTE_ARCHIVE);
+ ret &= check_result(&result, "t014-14.txt", false, 0);
+ ret &= check_result(&result, "t015-15.txt", false, 0);
+ ret &= check_result(&result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL);
+ ret &= check_result(&result, "t017-17.txt", false, 0);
+ ret &= check_result(&result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE);
+ ret &= check_result(&result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE);
+ ret &= check_result(&result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE);
+ ret &= check_result(&result, "T003-3.txt.2", false, 0);
+ ret &= check_result(&result, "T013-13.txt.3", true, FILE_ATTRIBUTE_ARCHIVE);
+
+ if (!ret) {
+ for (i=0;i<result.count;i++) {
+ printf("%s %s (0x%x)\n",
+ result.list[i].both_directory_info.name.s,
+ attrib_string(tctx, result.list[i].both_directory_info.attrib),
+ result.list[i].both_directory_info.attrib);
+ }
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+
+
+/*
+ testing if directories always come back sorted
+*/
+static bool test_sorted(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ const int num_files = 700;
+ int i, fnum;
+ char *fname;
+ bool ret = true;
+ NTSTATUS status;
+ struct multiple_result result;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Creating %d files\n", num_files);
+
+ for (i=0;i<num_files;i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\%s.txt", generate_random_str_list(tctx, 10, "abcdefgh"));
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ talloc_free(fname);
+ smbcli_close(cli->tree, fnum);
+ }
+
+
+ ZERO_STRUCT(result);
+ result.tctx = tctx;
+
+ status = multiple_search(cli, tctx, BASEDIR "\\*.*",
+ RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
+ CONT_NAME, &result);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(result.count, num_files);
+
+ for (i=0;i<num_files-1;i++) {
+ const char *name1, *name2;
+ name1 = result.list[i].both_directory_info.name.s;
+ name2 = result.list[i+1].both_directory_info.name.s;
+ if (strcasecmp_m(name1, name2) > 0) {
+ printf("non-alphabetical order at entry %d '%s' '%s'\n",
+ i, name1, name2);
+ printf("Server does not produce sorted directory listings (not an error)\n");
+ goto done;
+ }
+ }
+
+ talloc_free(result.list);
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+
+
+
+/*
+ basic testing of many old style search calls using separate dirs
+*/
+static bool test_many_dirs(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ const int num_dirs = 20;
+ int i, fnum, n;
+ char *fname, *dname;
+ bool ret = true;
+ NTSTATUS status;
+ union smb_search_data *file, *file2, *file3;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Creating %d dirs\n", num_dirs);
+
+ for (i=0;i<num_dirs;i++) {
+ dname = talloc_asprintf(cli, BASEDIR "\\d%d", i);
+ status = smbcli_mkdir(cli->tree, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) Failed to create %s - %s\n",
+ __location__, dname, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ for (n=0;n<3;n++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\d%d\\f%d-%d.txt", i, i, n);
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ if (fnum == -1) {
+ printf("(%s) Failed to create %s - %s\n",
+ __location__, fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ talloc_free(fname);
+ smbcli_close(cli->tree, fnum);
+ }
+
+ talloc_free(dname);
+ }
+
+ file = talloc_zero_array(tctx, union smb_search_data, num_dirs);
+ file2 = talloc_zero_array(tctx, union smb_search_data, num_dirs);
+ file3 = talloc_zero_array(tctx, union smb_search_data, num_dirs);
+
+ printf("Search first on %d dirs\n", num_dirs);
+
+ for (i=0;i<num_dirs;i++) {
+ union smb_search_first io;
+ io.search_first.level = RAW_SEARCH_SEARCH;
+ io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ io.search_first.in.max_count = 1;
+ io.search_first.in.search_attrib = 0;
+ io.search_first.in.pattern = talloc_asprintf(tctx, BASEDIR "\\d%d\\*.txt", i);
+ fname = talloc_asprintf(tctx, "f%d-", i);
+
+ io.search_first.out.count = 0;
+
+ status = smb_raw_search_first(cli->tree, tctx,
+ &io, (void *)&file[i], single_search_callback);
+ if (io.search_first.out.count != 1) {
+ printf("(%s) search first gave %d entries for dir %d - %s\n",
+ __location__, io.search_first.out.count, i, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (strncasecmp(file[i].search.name, fname, strlen(fname)) != 0) {
+ printf("(%s) incorrect name '%s' expected '%s'[12].txt\n",
+ __location__, file[i].search.name, fname);
+ ret = false;
+ goto done;
+ }
+
+ talloc_free(fname);
+ }
+
+ printf("Search next on %d dirs\n", num_dirs);
+
+ for (i=0;i<num_dirs;i++) {
+ union smb_search_next io2;
+
+ io2.search_next.level = RAW_SEARCH_SEARCH;
+ io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ io2.search_next.in.max_count = 1;
+ io2.search_next.in.search_attrib = 0;
+ io2.search_next.in.id = file[i].search.id;
+ fname = talloc_asprintf(tctx, "f%d-", i);
+
+ io2.search_next.out.count = 0;
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &io2, (void *)&file2[i], single_search_callback);
+ if (io2.search_next.out.count != 1) {
+ printf("(%s) search next gave %d entries for dir %d - %s\n",
+ __location__, io2.search_next.out.count, i, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (strncasecmp(file2[i].search.name, fname, strlen(fname)) != 0) {
+ printf("(%s) incorrect name '%s' expected '%s'[12].txt\n",
+ __location__, file2[i].search.name, fname);
+ ret = false;
+ goto done;
+ }
+
+ talloc_free(fname);
+ }
+
+
+ printf("Search next (rewind) on %d dirs\n", num_dirs);
+
+ for (i=0;i<num_dirs;i++) {
+ union smb_search_next io2;
+
+ io2.search_next.level = RAW_SEARCH_SEARCH;
+ io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ io2.search_next.in.max_count = 1;
+ io2.search_next.in.search_attrib = 0;
+ io2.search_next.in.id = file[i].search.id;
+ fname = talloc_asprintf(tctx, "f%d-", i);
+ io2.search_next.out.count = 0;
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &io2, (void *)&file3[i], single_search_callback);
+ if (io2.search_next.out.count != 1) {
+ printf("(%s) search next gave %d entries for dir %d - %s\n",
+ __location__, io2.search_next.out.count, i, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (strncasecmp(file3[i].search.name, file2[i].search.name, 3) != 0) {
+ printf("(%s) incorrect name '%s' on rewind at dir %d\n",
+ __location__, file2[i].search.name, i);
+ ret = false;
+ goto done;
+ }
+
+ if (strcmp(file3[i].search.name, file2[i].search.name) != 0) {
+ printf("(%s) server did not rewind - got '%s' expected '%s'\n",
+ __location__, file3[i].search.name, file2[i].search.name);
+ ret = false;
+ goto done;
+ }
+
+ talloc_free(fname);
+ }
+
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+
+
+/*
+ testing of OS/2 style delete
+*/
+static bool test_os2_delete(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ const int num_files = 700;
+ const int delete_count = 4;
+ int total_deleted = 0;
+ int i, fnum;
+ char *fname;
+ bool ret = true;
+ NTSTATUS status;
+ union smb_search_first io;
+ union smb_search_next io2;
+ struct multiple_result result;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing OS/2 style delete on %d files\n", num_files);
+
+ for (i=0;i<num_files;i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\file%u.txt", i);
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ talloc_free(fname);
+ smbcli_close(cli->tree, fnum);
+ }
+
+
+ ZERO_STRUCT(result);
+ result.tctx = tctx;
+
+ io.t2ffirst.level = RAW_SEARCH_TRANS2;
+ io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_SIZE;
+ io.t2ffirst.in.search_attrib = 0;
+ io.t2ffirst.in.max_count = 100;
+ io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
+ io.t2ffirst.in.storage_type = 0;
+ io.t2ffirst.in.pattern = BASEDIR "\\*";
+
+ status = smb_raw_search_first(cli->tree, tctx,
+ &io, &result, multiple_search_callback);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ for (i=0;i<MIN(result.count, delete_count);i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ total_deleted++;
+ talloc_free(fname);
+ }
+
+ io2.t2fnext.level = RAW_SEARCH_TRANS2;
+ io2.t2fnext.data_level = RAW_SEARCH_DATA_EA_SIZE;
+ io2.t2fnext.in.handle = io.t2ffirst.out.handle;
+ io2.t2fnext.in.max_count = 100;
+ io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key;
+ io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
+ io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s;
+
+ do {
+ ZERO_STRUCT(result);
+ result.tctx = tctx;
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &io2, &result, multiple_search_callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ for (i=0;i<MIN(result.count, delete_count);i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ total_deleted++;
+ talloc_free(fname);
+ }
+
+ if (i>0) {
+ io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key;
+ io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s;
+ }
+ } while (NT_STATUS_IS_OK(status) && result.count != 0);
+
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (total_deleted != num_files) {
+ printf("error: deleted %d - expected to delete %d\n",
+ total_deleted, num_files);
+ ret = false;
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+
+
+static int ealist_cmp(union smb_search_data *r1, union smb_search_data *r2)
+{
+ return strcmp(r1->ea_list.name.s, r2->ea_list.name.s);
+}
+
+/*
+ testing of the rather strange ea_list level
+*/
+static bool test_ea_list(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ int fnum;
+ bool ret = true;
+ NTSTATUS status;
+ union smb_search_first io;
+ union smb_search_next nxt;
+ struct multiple_result result;
+ union smb_setfileinfo setfile;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_SEARCH_EA_LIST level\n");
+
+ fnum = smbcli_open(cli->tree, BASEDIR "\\file1.txt", O_CREAT|O_RDWR, DENY_NONE);
+ smbcli_close(cli->tree, fnum);
+
+ fnum = smbcli_open(cli->tree, BASEDIR "\\file2.txt", O_CREAT|O_RDWR, DENY_NONE);
+ smbcli_close(cli->tree, fnum);
+
+ fnum = smbcli_open(cli->tree, BASEDIR "\\file3.txt", O_CREAT|O_RDWR, DENY_NONE);
+ smbcli_close(cli->tree, fnum);
+
+ setfile.generic.level = RAW_SFILEINFO_EA_SET;
+ setfile.generic.in.file.path = BASEDIR "\\file2.txt";
+ setfile.ea_set.in.num_eas = 2;
+ setfile.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 2);
+ setfile.ea_set.in.eas[0].flags = 0;
+ setfile.ea_set.in.eas[0].name.s = "EA ONE";
+ setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE 1");
+ setfile.ea_set.in.eas[1].flags = 0;
+ setfile.ea_set.in.eas[1].name.s = "SECOND EA";
+ setfile.ea_set.in.eas[1].value = data_blob_string_const("Value Two");
+
+ status = smb_raw_setpathinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ setfile.generic.in.file.path = BASEDIR "\\file3.txt";
+ status = smb_raw_setpathinfo(cli->tree, &setfile);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(result);
+ result.tctx = tctx;
+
+ io.t2ffirst.level = RAW_SEARCH_TRANS2;
+ io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_LIST;
+ io.t2ffirst.in.search_attrib = 0;
+ io.t2ffirst.in.max_count = 2;
+ io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
+ io.t2ffirst.in.storage_type = 0;
+ io.t2ffirst.in.pattern = BASEDIR "\\*";
+ io.t2ffirst.in.num_names = 2;
+ io.t2ffirst.in.ea_names = talloc_array(tctx, struct ea_name, 2);
+ io.t2ffirst.in.ea_names[0].name.s = "SECOND EA";
+ io.t2ffirst.in.ea_names[1].name.s = "THIRD EA";
+
+ status = smb_raw_search_first(cli->tree, tctx,
+ &io, &result, multiple_search_callback);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(result.count, 2);
+
+ nxt.t2fnext.level = RAW_SEARCH_TRANS2;
+ nxt.t2fnext.data_level = RAW_SEARCH_DATA_EA_LIST;
+ nxt.t2fnext.in.handle = io.t2ffirst.out.handle;
+ nxt.t2fnext.in.max_count = 2;
+ nxt.t2fnext.in.resume_key = result.list[1].ea_list.resume_key;
+ nxt.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | FLAG_TRANS2_FIND_CONTINUE;
+ nxt.t2fnext.in.last_name = result.list[1].ea_list.name.s;
+ nxt.t2fnext.in.num_names = 2;
+ nxt.t2fnext.in.ea_names = talloc_array(tctx, struct ea_name, 2);
+ nxt.t2fnext.in.ea_names[0].name.s = "SECOND EA";
+ nxt.t2fnext.in.ea_names[1].name.s = "THIRD EA";
+
+ status = smb_raw_search_next(cli->tree, tctx,
+ &nxt, &result, multiple_search_callback);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* we have to sort the result as different servers can return directories
+ in different orders */
+ qsort(result.list, result.count, sizeof(result.list[0]),
+ (comparison_fn_t)ealist_cmp);
+
+ CHECK_VALUE(result.count, 3);
+ CHECK_VALUE(result.list[0].ea_list.eas.num_eas, 2);
+ CHECK_STRING(result.list[0].ea_list.name.s, "file1.txt");
+ CHECK_STRING(result.list[0].ea_list.eas.eas[0].name.s, "SECOND EA");
+ CHECK_VALUE(result.list[0].ea_list.eas.eas[0].value.length, 0);
+ CHECK_STRING(result.list[0].ea_list.eas.eas[1].name.s, "THIRD EA");
+ CHECK_VALUE(result.list[0].ea_list.eas.eas[1].value.length, 0);
+
+ CHECK_STRING(result.list[1].ea_list.name.s, "file2.txt");
+ CHECK_STRING(result.list[1].ea_list.eas.eas[0].name.s, "SECOND EA");
+ CHECK_VALUE(result.list[1].ea_list.eas.eas[0].value.length, 9);
+ CHECK_STRING((const char *)result.list[1].ea_list.eas.eas[0].value.data, "Value Two");
+ CHECK_STRING(result.list[1].ea_list.eas.eas[1].name.s, "THIRD EA");
+ CHECK_VALUE(result.list[1].ea_list.eas.eas[1].value.length, 0);
+
+ CHECK_STRING(result.list[2].ea_list.name.s, "file3.txt");
+ CHECK_STRING(result.list[2].ea_list.eas.eas[0].name.s, "SECOND EA");
+ CHECK_VALUE(result.list[2].ea_list.eas.eas[0].value.length, 9);
+ CHECK_STRING((const char *)result.list[2].ea_list.eas.eas[0].value.data, "Value Two");
+ CHECK_STRING(result.list[2].ea_list.eas.eas[1].name.s, "THIRD EA");
+ CHECK_VALUE(result.list[2].ea_list.eas.eas[1].value.length, 0);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+
+
+
+/*
+ basic testing of all RAW_SEARCH_* calls using a single file
+*/
+struct torture_suite *torture_raw_search(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SEARCH");
+
+ torture_suite_add_1smb_test(suite, "one file search", test_one_file);
+ torture_suite_add_1smb_test(suite, "many files", test_many_files);
+ torture_suite_add_1smb_test(suite, "sorted", test_sorted);
+ torture_suite_add_1smb_test(suite, "modify search", test_modify_search);
+ torture_suite_add_1smb_test(suite, "many dirs", test_many_dirs);
+ torture_suite_add_1smb_test(suite, "os2 delete", test_os2_delete);
+ torture_suite_add_1smb_test(suite, "ea list", test_ea_list);
+
+ return suite;
+}
diff --git a/source4/torture/raw/seek.c b/source4/torture/raw/seek.c
new file mode 100644
index 0000000000..3ba022feef
--- /dev/null
+++ b/source4/torture/raw/seek.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+ seek test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%d) Incorrect status %s - should be %s\n", \
+ __LINE__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%d) Incorrect value %s=%d - should be %d\n", \
+ __LINE__, #v, (int)v, (int)correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define BASEDIR "\\testseek"
+
+/*
+ test seek ops
+*/
+static bool test_seek(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ union smb_seek io;
+ union smb_fileinfo finfo;
+ union smb_setfileinfo sfinfo;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum, fnum2;
+ const char *fname = BASEDIR "\\test.txt";
+ uint8_t c[2];
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to open test.txt - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum;
+
+ printf("Trying bad handle\n");
+ io.lseek.in.file.fnum = fnum+1;
+ io.lseek.in.mode = SEEK_MODE_START;
+ io.lseek.in.offset = 0;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("Trying simple seek\n");
+ io.lseek.in.file.fnum = fnum;
+ io.lseek.in.mode = SEEK_MODE_START;
+ io.lseek.in.offset = 17;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, 17);
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 0);
+
+ printf("Trying relative seek\n");
+ io.lseek.in.file.fnum = fnum;
+ io.lseek.in.mode = SEEK_MODE_CURRENT;
+ io.lseek.in.offset = -3;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, 14);
+
+ printf("Trying end seek\n");
+ io.lseek.in.file.fnum = fnum;
+ io.lseek.in.mode = SEEK_MODE_END;
+ io.lseek.in.offset = 0;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.all_info.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, finfo.all_info.out.size);
+
+ printf("Trying max seek\n");
+ io.lseek.in.file.fnum = fnum;
+ io.lseek.in.mode = SEEK_MODE_START;
+ io.lseek.in.offset = -1;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, 0xffffffff);
+
+ printf("Testing position information change\n");
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 0);
+
+ printf("Trying max overflow\n");
+ io.lseek.in.file.fnum = fnum;
+ io.lseek.in.mode = SEEK_MODE_CURRENT;
+ io.lseek.in.offset = 1000;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, 999);
+
+ printf("Testing position information change\n");
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 0);
+
+ printf("trying write to update offset\n");
+ ZERO_STRUCT(c);
+ if (smbcli_write(cli->tree, fnum, 0, c, 0, 2) != 2) {
+ printf("Write failed - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Testing position information change\n");
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 0);
+
+ io.lseek.in.file.fnum = fnum;
+ io.lseek.in.mode = SEEK_MODE_CURRENT;
+ io.lseek.in.offset = 0;
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, 2);
+
+ if (smbcli_read(cli->tree, fnum, c, 0, 1) != 1) {
+ printf("Read failed - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Testing position information change\n");
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 1);
+
+ status = smb_raw_seek(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.lseek.out.offset, 1);
+
+ printf("Testing position information\n");
+ fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("2nd open failed - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
+ sfinfo.position_information.in.file.fnum = fnum2;
+ sfinfo.position_information.in.position = 25;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum2;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 25);
+
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 1);
+
+ printf("position_information via paths\n");
+
+ sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
+ sfinfo.position_information.in.file.path = fname;
+ sfinfo.position_information.in.position = 32;
+ status = smb_raw_setpathinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.fnum = fnum2;
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 25);
+
+ finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ finfo.position_information.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.position_information.out.position, 0);
+
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ basic testing of seek calls
+*/
+bool torture_raw_seek(struct torture_context *torture, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ ret &= test_seek(cli, torture);
+
+ return ret;
+}
diff --git a/source4/torture/raw/setfileinfo.c b/source4/torture/raw/setfileinfo.c
new file mode 100644
index 0000000000..10eaa6710d
--- /dev/null
+++ b/source4/torture/raw/setfileinfo.c
@@ -0,0 +1,703 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_SFILEINFO_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "system/time.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "torture/raw/proto.h"
+
+#define BASEDIR "\\testsfileinfo"
+
+/* basic testing of all RAW_SFILEINFO_* calls
+ for each call we test that it succeeds, and where possible test
+ for consistency between the calls.
+*/
+bool torture_raw_sfileinfo(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ int fnum = -1;
+ char *fnum_fname;
+ char *fnum_fname_new;
+ char *path_fname;
+ char *path_fname_new;
+ union smb_fileinfo finfo1, finfo2;
+ union smb_setfileinfo sfinfo;
+ NTSTATUS status, status2;
+ const char *call_name;
+ time_t basetime = (time(NULL) - 86400) & ~1;
+ bool check_fnum;
+ int n = time(NULL) % 100;
+
+ asprintf(&path_fname, BASEDIR "\\fname_test_%d.txt", n);
+ asprintf(&path_fname_new, BASEDIR "\\fname_test_new_%d.txt", n);
+ asprintf(&fnum_fname, BASEDIR "\\fnum_test_%d.txt", n);
+ asprintf(&fnum_fname_new, BASEDIR "\\fnum_test_new_%d.txt", n);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+#define RECREATE_FILE(fname) do { \
+ if (fnum != -1) smbcli_close(cli->tree, fnum); \
+ fnum = create_complex_file(cli, torture, fname); \
+ if (fnum == -1) { \
+ printf("(%s) ERROR: open of %s failed (%s)\n", \
+ __location__, fname, smbcli_errstr(cli->tree)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define RECREATE_BOTH do { \
+ RECREATE_FILE(path_fname); \
+ smbcli_close(cli->tree, fnum); \
+ RECREATE_FILE(fnum_fname); \
+ } while (0)
+
+ RECREATE_BOTH;
+
+#define CHECK_CALL_FNUM(call, rightstatus) do { \
+ check_fnum = true; \
+ call_name = #call; \
+ sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
+ sfinfo.generic.in.file.fnum = fnum; \
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
+ if (!NT_STATUS_EQUAL(status, rightstatus)) { \
+ printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
+ nt_errstr(status), nt_errstr(rightstatus)); \
+ ret = false; \
+ } \
+ finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
+ finfo1.generic.in.file.fnum = fnum; \
+ status2 = smb_raw_fileinfo(cli->tree, torture, &finfo1); \
+ if (!NT_STATUS_IS_OK(status2)) { \
+ printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_CALL_PATH(call, rightstatus) do { \
+ check_fnum = false; \
+ call_name = #call; \
+ sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
+ sfinfo.generic.in.file.path = path_fname; \
+ status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+ sfinfo.generic.in.file.path = path_fname_new; \
+ status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+ } \
+ if (!NT_STATUS_EQUAL(status, rightstatus)) { \
+ printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
+ nt_errstr(status), nt_errstr(rightstatus)); \
+ ret = false; \
+ } \
+ finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
+ finfo1.generic.in.file.path = path_fname; \
+ status2 = smb_raw_pathinfo(cli->tree, torture, &finfo1); \
+ if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+ finfo1.generic.in.file.path = path_fname_new; \
+ status2 = smb_raw_pathinfo(cli->tree, torture, &finfo1); \
+ } \
+ if (!NT_STATUS_IS_OK(status2)) { \
+ printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status2)); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK1(call) \
+ do { if (NT_STATUS_IS_OK(status)) { \
+ finfo2.generic.level = RAW_FILEINFO_ ## call; \
+ if (check_fnum) { \
+ finfo2.generic.in.file.fnum = fnum; \
+ status2 = smb_raw_fileinfo(cli->tree, torture, &finfo2); \
+ } else { \
+ finfo2.generic.in.file.path = path_fname; \
+ status2 = smb_raw_pathinfo(cli->tree, torture, &finfo2); \
+ if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+ finfo2.generic.in.file.path = path_fname_new; \
+ status2 = smb_raw_pathinfo(cli->tree, torture, &finfo2); \
+ } \
+ } \
+ if (!NT_STATUS_IS_OK(status2)) { \
+ printf("%s - %s\n", #call, nt_errstr(status2)); \
+ ret = false; \
+ } \
+ }} while (0)
+
+#define CHECK_VALUE(call, stype, field, value) do { \
+ CHECK1(call); \
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && finfo2.stype.out.field != value) { \
+ printf("(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \
+ call_name, #stype, #field, \
+ (uint_t)value, (uint_t)finfo2.stype.out.field); \
+ dump_all_info(torture, &finfo1); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_TIME(call, stype, field, value) do { \
+ CHECK1(call); \
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && nt_time_to_unix(finfo2.stype.out.field) != value) { \
+ printf("(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \
+ call_name, #stype, #field, \
+ (uint_t)value, \
+ (uint_t)nt_time_to_unix(finfo2.stype.out.field)); \
+ printf("\t%s", timestring(torture, value)); \
+ printf("\t%s\n", nt_time_string(torture, finfo2.stype.out.field)); \
+ dump_all_info(torture, &finfo1); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_STR(call, stype, field, value) do { \
+ CHECK1(call); \
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2) && strcmp(finfo2.stype.out.field, value) != 0) { \
+ printf("(%s) %s - %s/%s should be '%s' - '%s'\n", __location__, \
+ call_name, #stype, #field, \
+ value, \
+ finfo2.stype.out.field); \
+ dump_all_info(torture, &finfo1); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+ printf("test setattr\n");
+ sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY;
+ sfinfo.setattr.in.write_time = basetime;
+ CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+ CHECK_VALUE (ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY);
+ CHECK_TIME (ALL_INFO, all_info, write_time, basetime);
+
+ printf("setting to NORMAL doesn't do anything\n");
+ sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_NORMAL;
+ sfinfo.setattr.in.write_time = 0;
+ CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY);
+ CHECK_TIME (ALL_INFO, all_info, write_time, basetime);
+
+ printf("a zero write_time means don't change\n");
+ sfinfo.setattr.in.attrib = 0;
+ sfinfo.setattr.in.write_time = 0;
+ CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL);
+ CHECK_TIME (ALL_INFO, all_info, write_time, basetime);
+
+ printf("test setattre\n");
+ sfinfo.setattre.in.create_time = basetime + 20;
+ sfinfo.setattre.in.access_time = basetime + 30;
+ sfinfo.setattre.in.write_time = basetime + 40;
+ CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40);
+
+ sfinfo.setattre.in.create_time = 0;
+ sfinfo.setattre.in.access_time = 0;
+ sfinfo.setattre.in.write_time = 0;
+ CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40);
+
+ printf("test standard level\n");
+ sfinfo.standard.in.create_time = basetime + 100;
+ sfinfo.standard.in.access_time = basetime + 200;
+ sfinfo.standard.in.write_time = basetime + 300;
+ CHECK_CALL_FNUM(STANDARD, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+
+ printf("test basic_info level\n");
+ basetime += 86400;
+ unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100);
+ unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200);
+ unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300);
+ unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400);
+ sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
+ CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+ CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY);
+
+ printf("a zero time means don't change\n");
+ unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0);
+ unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0);
+ unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0);
+ unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0);
+ sfinfo.basic_info.in.attrib = 0;
+ CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+ CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY);
+
+ printf("test basic_information level\n");
+ basetime += 86400;
+ unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100);
+ unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200);
+ unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300);
+ unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400);
+ sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+ CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+ CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL);
+
+ CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+ CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL);
+
+ printf("a zero time means don't change\n");
+ unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0);
+ unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0);
+ unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0);
+ unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0);
+ sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+ CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+ CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL);
+
+ CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK);
+ CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+ CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+ CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300);
+
+ /* interesting - w2k3 leaves change_time as current time for 0 change time
+ in setpathinfo
+ CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+ */
+ CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL);
+
+ printf("test disposition_info level\n");
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+ CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+
+ sfinfo.disposition_info.in.delete_on_close = 0;
+ CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+ CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+ printf("test disposition_information level\n");
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+ CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+
+ /* this would delete the file! */
+ /*
+ CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+ CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+ */
+
+ sfinfo.disposition_info.in.delete_on_close = 0;
+ CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+ CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+ CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+ CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+ printf("test allocation_info level\n");
+ sfinfo.allocation_info.in.alloc_size = 0;
+ CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 0);
+ CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+ sfinfo.allocation_info.in.alloc_size = 4096;
+ CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096);
+ CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+ RECREATE_BOTH;
+ sfinfo.allocation_info.in.alloc_size = 0;
+ CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 0);
+ CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+ CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 0);
+ CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+ sfinfo.allocation_info.in.alloc_size = 4096;
+ CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096);
+ CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+ /* setting the allocation size up via setpathinfo seems
+ to be broken in w2k3 */
+ CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+ CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+ printf("test end_of_file_info level\n");
+ sfinfo.end_of_file_info.in.size = 37;
+ CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+ sfinfo.end_of_file_info.in.size = 7;
+ CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+ sfinfo.end_of_file_info.in.size = 37;
+ CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+ CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+ sfinfo.end_of_file_info.in.size = 7;
+ CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+ CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+ printf("test position_information level\n");
+ sfinfo.position_information.in.position = 123456;
+ CHECK_CALL_FNUM(POSITION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456);
+
+ CHECK_CALL_PATH(POSITION_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(POSITION_INFORMATION, position_information, position, 0);
+
+ printf("test mode_information level\n");
+ sfinfo.mode_information.in.mode = 2;
+ CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2);
+
+ CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+ sfinfo.mode_information.in.mode = 1;
+ CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+ CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+
+ sfinfo.mode_information.in.mode = 0;
+ CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+ CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK);
+ CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+#if 0
+ printf("test unix_basic level\n");
+ CHECK_CALL_FNUM(UNIX_BASIC, NT_STATUS_OK);
+ CHECK_CALL_PATH(UNIX_BASIC, NT_STATUS_OK);
+
+ printf("test unix_link level\n");
+ CHECK_CALL_FNUM(UNIX_LINK, NT_STATUS_OK);
+ CHECK_CALL_PATH(UNIX_LINK, NT_STATUS_OK);
+#endif
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_close(cli->tree, fnum);
+ if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fnum_fname))) {
+ printf("Failed to delete %s - %s\n", fnum_fname, smbcli_errstr(cli->tree));
+ }
+ if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, path_fname))) {
+ printf("Failed to delete %s - %s\n", path_fname, smbcli_errstr(cli->tree));
+ }
+
+ return ret;
+}
+
+/*
+ * basic testing of all RAW_SFILEINFO_RENAME call
+ */
+bool torture_raw_sfileinfo_rename(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ int fnum_saved, d_fnum, fnum2, fnum = -1;
+ char *fnum_fname;
+ char *fnum_fname_new;
+ char *path_fname;
+ char *path_fname_new;
+ char *path_dname;
+ char *path_dname_new;
+ char *saved_name;
+ char *saved_name_new;
+ union smb_fileinfo finfo1, finfo2;
+ union smb_setfileinfo sfinfo;
+ NTSTATUS status, status2;
+ const char *call_name;
+ bool check_fnum;
+ int n = time(NULL) % 100;
+
+ asprintf(&path_fname, BASEDIR "\\fname_test_%d.txt", n);
+ asprintf(&path_fname_new, BASEDIR "\\fname_test_new_%d.txt", n);
+ asprintf(&fnum_fname, BASEDIR "\\fnum_test_%d.txt", n);
+ asprintf(&fnum_fname_new, BASEDIR "\\fnum_test_new_%d.txt", n);
+ asprintf(&path_dname, BASEDIR "\\dname_test_%d", n);
+ asprintf(&path_dname_new, BASEDIR "\\dname_test_new_%d", n);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ RECREATE_BOTH;
+
+ ZERO_STRUCT(sfinfo);
+
+ smbcli_close(cli->tree, create_complex_file(cli, torture, fnum_fname_new));
+ smbcli_close(cli->tree, create_complex_file(cli, torture, path_fname_new));
+
+ sfinfo.rename_information.in.overwrite = 0;
+ sfinfo.rename_information.in.root_fid = 0;
+ sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION);
+
+ sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1;
+ CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION);
+
+ sfinfo.rename_information.in.new_name = fnum_fname_new;
+ sfinfo.rename_information.in.overwrite = 1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_NOT_SUPPORTED);
+
+ sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new);
+
+ printf("Trying rename with dest file open\n");
+ fnum2 = create_complex_file(cli, torture, fnum_fname);
+ sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_ACCESS_DENIED);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new);
+
+ fnum_saved = fnum;
+ fnum = fnum2;
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+ fnum = fnum_saved;
+
+ printf("Trying rename with dest file open and delete_on_close\n");
+ sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_ACCESS_DENIED);
+
+ smbcli_close(cli->tree, fnum2);
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+
+ printf("Trying rename with source file open twice\n");
+ sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+
+ fnum2 = create_complex_file(cli, torture, fnum_fname);
+ sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 0;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new);
+ smbcli_close(cli->tree, fnum2);
+
+ sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 0;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+
+ sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 1;
+ CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new);
+
+ sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+
+ sfinfo.rename_information.in.new_name = path_fname+strlen(BASEDIR)+1;
+ CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_fname);
+
+ printf("Trying rename with a root fid\n");
+ status = create_directory_handle(cli->tree, BASEDIR, &d_fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.root_fid = d_fnum;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+ CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+ smbcli_close(cli->tree, d_fnum);
+
+ printf("Trying rename directory\n");
+ if (!torture_setup_dir(cli, path_dname)) {
+ ret = false;
+ goto done;
+ }
+ saved_name = path_fname;
+ saved_name_new = path_fname_new;
+ path_fname = path_dname;
+ path_fname_new = path_dname_new;
+ sfinfo.rename_information.in.new_name = path_dname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 0;
+ sfinfo.rename_information.in.root_fid = 0;
+ CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_dname_new);
+ path_fname = saved_name;
+ path_fname_new = saved_name_new;
+
+ if (torture_setting_bool(torture, "samba3", false)) {
+ printf("SKIP: Trying rename directory with a handle\n");
+ printf("SKIP: Trying rename by path while a handle is open\n");
+ printf("SKIP: Trying rename directory by path while a handle is open\n");
+ goto done;
+ }
+
+ printf("Trying rename directory with a handle\n");
+ status = create_directory_handle(cli->tree, path_dname_new, &d_fnum);
+ fnum_saved = fnum;
+ fnum = d_fnum;
+ saved_name = fnum_fname;
+ saved_name_new = fnum_fname_new;
+ fnum_fname = path_dname;
+ fnum_fname_new = path_dname_new;
+ sfinfo.rename_information.in.new_name = path_dname+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 0;
+ sfinfo.rename_information.in.root_fid = 0;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_dname);
+ smbcli_close(cli->tree, d_fnum);
+ fnum = fnum_saved;
+ fnum_fname = saved_name;
+ fnum_fname_new = saved_name_new;
+
+ printf("Trying rename by path while a handle is open\n");
+ fnum_saved = fnum;
+ fnum = create_complex_file(cli, torture, path_fname);
+ sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 0;
+ sfinfo.rename_information.in.root_fid = 0;
+ CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new);
+ /* check that the handle returns the same name */
+ check_fnum = true;
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new);
+ /* rename it back on the handle */
+ sfinfo.rename_information.in.new_name = path_fname+strlen(BASEDIR)+1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_fname);
+ check_fnum = false;
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_fname);
+ smbcli_close(cli->tree, fnum);
+ fnum = fnum_saved;
+
+ printf("Trying rename directory by path while a handle is open\n");
+ status = create_directory_handle(cli->tree, path_dname, &d_fnum);
+ fnum_saved = fnum;
+ fnum = d_fnum;
+ saved_name = path_fname;
+ saved_name_new = path_fname_new;
+ path_fname = path_dname;
+ path_fname_new = path_dname_new;
+ sfinfo.rename_information.in.new_name = path_dname_new+strlen(BASEDIR)+1;
+ sfinfo.rename_information.in.overwrite = 0;
+ sfinfo.rename_information.in.root_fid = 0;
+ CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_dname_new);
+ path_fname = saved_name;
+ path_fname_new = saved_name_new;
+ saved_name = fnum_fname;
+ saved_name_new = fnum_fname_new;
+ fnum_fname = path_dname;
+ fnum_fname_new = path_dname_new;
+ /* check that the handle returns the same name */
+ check_fnum = true;
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_dname_new);
+ /* rename it back on the handle */
+ sfinfo.rename_information.in.new_name = path_dname+strlen(BASEDIR)+1;
+ CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_dname);
+ fnum_fname = saved_name;
+ fnum_fname_new = saved_name_new;
+ saved_name = path_fname;
+ saved_name_new = path_fname_new;
+ path_fname = path_dname;
+ path_fname_new = path_dname_new;
+ check_fnum = false;
+ CHECK_STR(NAME_INFO, name_info, fname.s, path_dname);
+ smbcli_close(cli->tree, d_fnum);
+ fnum = fnum_saved;
+ path_fname = saved_name;
+ path_fname_new = saved_name_new;
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+/*
+ look for the w2k3 setpathinfo STANDARD bug
+*/
+bool torture_raw_sfileinfo_bug(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ const char *fname = "\\bug3.txt";
+ union smb_setfileinfo sfinfo;
+ NTSTATUS status;
+ int fnum;
+
+ if (!torture_setting_bool(torture, "dangerous", false))
+ torture_skip(torture,
+ "torture_raw_sfileinfo_bug disabled - enable dangerous tests to use\n");
+
+ fnum = create_complex_file(cli, torture, fname);
+ smbcli_close(cli->tree, fnum);
+
+ sfinfo.generic.level = RAW_SFILEINFO_STANDARD;
+ sfinfo.generic.in.file.path = fname;
+
+ sfinfo.standard.in.create_time = 0;
+ sfinfo.standard.in.access_time = 0;
+ sfinfo.standard.in.write_time = 0;
+
+ status = smb_raw_setpathinfo(cli->tree, &sfinfo);
+ printf("%s - %s\n", fname, nt_errstr(status));
+
+ printf("now try and delete %s\n", fname);
+
+ return true;
+}
diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c
new file mode 100644
index 0000000000..8b2d327653
--- /dev/null
+++ b/source4/torture/raw/streams.c
@@ -0,0 +1,649 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test alternate data streams
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\teststreams"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %d\n", \
+ __location__, #v, (int)v, (int)correct); \
+ ret = false; \
+ }} while (0)
+
+/*
+ check that a stream has the right contents
+*/
+static bool check_stream(struct smbcli_state *cli, const char *location,
+ TALLOC_CTX *mem_ctx,
+ const char *fname, const char *sname,
+ const char *value)
+{
+ int fnum;
+ const char *full_name;
+ uint8_t *buf;
+ ssize_t ret;
+
+ full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
+
+ fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
+
+ if (value == NULL) {
+ if (fnum != -1) {
+ printf("(%s) should have failed stream open of %s\n",
+ location, full_name);
+ return false;
+ }
+ return true;
+ }
+
+ if (fnum == -1) {
+ printf("(%s) Failed to open stream '%s' - %s\n",
+ location, full_name, smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11);
+
+ ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11);
+ if (ret != strlen(value)) {
+ printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
+ location, (long)strlen(value), full_name, (int)ret);
+ return false;
+ }
+
+ if (memcmp(buf, value, strlen(value)) != 0) {
+ printf("(%s) Bad data in stream\n", location);
+ return false;
+ }
+
+ smbcli_close(cli->tree, fnum);
+ return true;
+}
+
+static int qsort_string(const void *v1, const void *v2)
+{
+ char * const *s1 = v1;
+ char * const *s2 = v2;
+ return strcmp(*s1, *s2);
+}
+
+static int qsort_stream(const void *v1, const void *v2)
+{
+ const struct stream_struct * s1 = v1;
+ const struct stream_struct * s2 = v2;
+ return strcmp(s1->stream_name.s, s2->stream_name.s);
+}
+
+static bool check_stream_list(struct smbcli_state *cli, const char *fname,
+ int num_exp, const char **exp)
+{
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(cli);
+ char **exp_sort;
+ struct stream_struct *stream_sort;
+ bool ret = false;
+
+ finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
+ finfo.generic.in.file.path = fname;
+
+ status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "(%s) smb_raw_pathinfo failed: %s\n",
+ __location__, nt_errstr(status));
+ goto fail;
+ }
+
+ if (finfo.stream_info.out.num_streams != num_exp) {
+ d_fprintf(stderr, "(%s) expected %d streams, got %d\n",
+ __location__, num_exp,
+ finfo.stream_info.out.num_streams);
+ goto fail;
+ }
+
+ if (num_exp == 0) {
+ ret = true;
+ goto fail;
+ }
+
+ exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
+
+ if (exp_sort == NULL) {
+ goto fail;
+ }
+
+ qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
+
+ stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
+ finfo.stream_info.out.num_streams *
+ sizeof(*stream_sort));
+
+ if (stream_sort == NULL) {
+ goto fail;
+ }
+
+ qsort(stream_sort, finfo.stream_info.out.num_streams,
+ sizeof(*stream_sort), qsort_stream);
+
+ for (i=0; i<num_exp; i++) {
+ if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
+ d_fprintf(stderr, "(%s) expected stream name %s, got "
+ "%s\n", __location__, exp_sort[i],
+ stream_sort[i].stream_name.s);
+ goto fail;
+ }
+ }
+
+ ret = true;
+ fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ test bahavior of streams on directories
+*/
+static bool test_stream_dir(struct torture_context *tctx,
+ struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\stream.txt";
+ const char *sname1;
+ bool ret = true;
+ const char *basedir_data;
+
+ basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", BASEDIR);
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+
+ printf("(%s) opening non-existant directory stream\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = sname1;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
+
+ printf("(%s) opening basedir stream\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = basedir_data;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
+
+ printf("(%s) opening basedir ::$DATA stream\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0x10;
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = 0;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = basedir_data;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ printf("(%s) list the streams on the basedir\n", __location__);
+ ret &= check_stream_list(cli, BASEDIR, 0, NULL);
+done:
+ return ret;
+}
+
+/*
+ test basic behavior of streams on directories
+*/
+static bool test_stream_io(struct torture_context *tctx,
+ struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\stream.txt";
+ const char *sname1, *sname2;
+ bool ret = true;
+ int fnum = -1;
+ ssize_t retsize;
+
+ const char *one[] = { "::$DATA" };
+ const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
+ const char *three[] = { "::$DATA", ":Stream One:$DATA",
+ ":Second Stream:$DATA" };
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+ sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
+
+ printf("(%s) creating a stream on a non-existant file\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = sname1;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", NULL);
+
+ printf("(%s) check that open of base file is allowed\n", __location__);
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.fname = fname;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ printf("(%s) writing to stream\n", __location__);
+ retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
+ CHECK_VALUE(retsize, 9);
+
+ smbcli_close(cli->tree, fnum);
+
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test data");
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.fname = sname1;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("(%s) modifying stream\n", __location__);
+ retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
+ CHECK_VALUE(retsize, 10);
+
+ smbcli_close(cli->tree, fnum);
+
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$FOO", NULL);
+
+ printf("(%s) creating a stream2 on a existing file\n", __location__);
+ io.ntcreatex.in.fname = sname2;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("(%s) modifying stream\n", __location__);
+ retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
+ CHECK_VALUE(retsize, 13);
+
+ smbcli_close(cli->tree, fnum);
+
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test MORE DATA ");
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
+ if (!torture_setting_bool(tctx, "samba4", false)) {
+ ret &= check_stream(cli, __location__, mem_ctx, fname,
+ "SECOND STREAM:$DATA", "SECOND STREAM");
+ }
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
+
+ check_stream_list(cli, fname, 3, three);
+
+ printf("(%s) deleting stream\n", __location__);
+ status = smbcli_unlink(cli->tree, sname1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ check_stream_list(cli, fname, 2, two);
+
+ printf("(%s) delete a stream via delete-on-close\n", __location__);
+ io.ntcreatex.in.fname = sname2;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ smbcli_close(cli->tree, fnum);
+ status = smbcli_unlink(cli->tree, sname2);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ check_stream_list(cli, fname, 1, one);
+
+ if (!torture_setting_bool(tctx, "samba4", false)) {
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.fname = sname1;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ io.ntcreatex.in.fname = sname2;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ }
+
+ printf("(%s) deleting file\n", __location__);
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ test stream sharemodes
+*/
+static bool test_stream_sharemodes(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\stream.txt";
+ const char *sname1, *sname2;
+ bool ret = true;
+ int fnum1 = -1;
+ int fnum2 = -1;
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+ sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
+
+ printf("(%s) testing stream share mode conflicts\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = sname1;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ /*
+ * A different stream does not give a sharing violation
+ */
+
+ io.ntcreatex.in.fname = sname2;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ /*
+ * ... whereas the same stream does with unchanged access/share_access
+ * flags
+ */
+
+ io.ntcreatex.in.fname = sname1;
+ io.ntcreatex.in.open_disposition = 0;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ io.ntcreatex.in.fname = sname2;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+done:
+ if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
+ if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
+ status = smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+/*
+ * Test FILE_SHARE_DELETE on streams
+ *
+ * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
+ * with SEC_STD_DELETE.
+ *
+ * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
+ * be opened with SEC_STD_DELETE.
+ *
+ * A stream held open with FILE_SHARE_DELETE allows the file to be
+ * deleted. After the main file is deleted, access to the open file descriptor
+ * still works, but all name-based access to both the main file as well as the
+ * stream is denied with DELETE ending.
+ *
+ * This means, an open of the main file with SEC_STD_DELETE should walk all
+ * streams and also open them with SEC_STD_DELETE. If any of these opens gives
+ * SHARING_VIOLATION, the main open fails.
+ *
+ * Closing the main file after delete_on_close has been set does not really
+ * unlink it but leaves the corresponding share mode entry with
+ * delete_on_close being set around until all streams are closed.
+ *
+ * Opening a stream must also look at the main file's share mode entry, look
+ * at the delete_on_close bit and potentially return DELETE_PENDING.
+ */
+
+static bool test_stream_delete(struct torture_context *tctx,
+ struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\stream.txt";
+ const char *sname1;
+ bool ret = true;
+ int fnum = -1;
+ uint8_t buf[9];
+ ssize_t retsize;
+ union smb_fileinfo finfo;
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+
+ printf("(%s) opening non-existant file stream\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = sname1;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
+ CHECK_VALUE(retsize, 9);
+
+ /*
+ * One stream opened without FILE_SHARE_DELETE prevents the main file
+ * to be deleted or even opened with DELETE access
+ */
+
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.access_mask = SEC_STD_DELETE;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ smbcli_close(cli->tree, fnum);
+
+ /*
+ * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
+ */
+
+ io.ntcreatex.in.fname = sname1;
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /*
+ * file access still works on the stream while the main file is closed
+ */
+
+ retsize = smbcli_read(cli->tree, fnum, buf, 0, 9);
+ CHECK_VALUE(retsize, 9);
+
+ finfo.generic.level = RAW_FILEINFO_STANDARD;
+ finfo.generic.in.file.path = fname;
+
+ /*
+ * name-based access to both the main file and the stream does not
+ * work anymore but gives DELETE_PENDING
+ */
+
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+
+ /*
+ * S3 doesn't do this yet
+ */
+
+ finfo.generic.in.file.path = sname1;
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
+ }
+
+ /*
+ * fd-based qfileinfo on the stream still works, the stream does not
+ * have the delete-on-close bit set. This could mean that open on the
+ * stream first opens the main file
+ */
+
+ finfo.all_info.level = RAW_FILEINFO_ALL_INFO;
+ finfo.all_info.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
+
+ smbcli_close(cli->tree, fnum);
+
+ /*
+ * After closing the stream the file is really gone.
+ */
+
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA
+ |SEC_STD_DELETE;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ basic testing of streams calls
+*/
+bool torture_raw_streams(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_stream_dir(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_io(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_sharemodes(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ if (!torture_setting_bool(torture, "samba4", false)) {
+ ret &= test_stream_delete(torture, cli, torture);
+ }
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/tconrate.c b/source4/torture/raw/tconrate.c
new file mode 100644
index 0000000000..6f0ba0d617
--- /dev/null
+++ b/source4/torture/raw/tconrate.c
@@ -0,0 +1,201 @@
+/*
+ SMB tree connection rate test
+
+ Copyright (C) 2006-2007 James Peach
+
+ 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/libcli.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/smbtorture.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+#include "system/filesys.h"
+#include "system/shmem.h"
+
+#define TIME_LIMIT_SECS 30
+#define usec_to_sec(s) ((s) / 1000000)
+#define sec_to_usec(s) ((s) * 1000000)
+
+/* Map a shared memory buffer of at least nelem counters. */
+static void * map_count_buffer(unsigned nelem, size_t elemsz)
+{
+ void * buf;
+ size_t bufsz;
+ size_t pagesz = getpagesize();
+
+ bufsz = nelem * elemsz;
+ bufsz = (bufsz + pagesz) % pagesz; /* round up to pagesz */
+
+#ifdef MAP_ANON
+ /* BSD */
+ buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED,
+ -1 /* fd */, 0 /* offset */);
+#else
+ buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
+ open("/dev/zero", O_RDWR), 0 /* offset */);
+#endif
+
+ if (buf == MAP_FAILED) {
+ printf("failed to map count buffer: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ return buf;
+
+}
+
+static int fork_tcon_client(struct torture_context *tctx,
+ int *tcon_count, unsigned tcon_timelimit,
+ const char *host, const char *share)
+{
+ pid_t child;
+ struct smbcli_state *cli;
+ struct timeval end;
+ struct timeval now;
+ struct smbcli_options options;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+
+ child = fork();
+ if (child == -1) {
+ printf("failed to fork child: %s\n,", strerror(errno));
+ return -1;
+ } else if (child != 0) {
+ /* Parent, just return. */
+ return 0;
+ }
+
+ /* Child. Just make as many connections as possible within the
+ * time limit. Don't bother synchronising the child start times
+ * because it's probably not work the effort, and a bit of startup
+ * jitter is probably a more realistic test.
+ */
+
+
+ end = timeval_current();
+ now = timeval_current();
+ end.tv_sec += tcon_timelimit;
+ *tcon_count = 0;
+
+ while (timeval_compare(&now, &end) == -1) {
+ NTSTATUS status;
+
+ status = smbcli_full_connection(NULL, &cli,
+ host, lp_smb_ports(tctx->lp_ctx), share,
+ NULL, cmdline_credentials,
+ lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to connect to //%s/%s: %s\n",
+ host, share, nt_errstr(status));
+ goto done;
+ }
+
+ smbcli_tdis(cli);
+
+ *tcon_count = *tcon_count + 1;
+ now = timeval_current();
+ }
+
+done:
+ exit(0);
+}
+
+static bool children_remain(void)
+{
+ /* Reap as many children as possible. */
+ for (;;) {
+ pid_t ret = waitpid(-1, NULL, WNOHANG);
+ if (ret == 0) {
+ /* no children ready */
+ return true;
+ }
+ if (ret == -1) {
+ /* no children left. maybe */
+ return errno == ECHILD ? false : true;
+ }
+ }
+
+ /* notreached */
+ return false;
+}
+
+static double rate_convert_secs(unsigned count,
+ const struct timeval *start, const struct timeval *end)
+{
+ return (double)count /
+ usec_to_sec((double)usec_time_diff(end, start));
+}
+
+/* Test the rate at which the server will accept connections. */
+bool torture_bench_treeconnect(struct torture_context *tctx)
+{
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ const char *share = torture_setting_string(tctx, "share", NULL);
+
+ int timelimit = torture_setting_int(tctx, "timelimit",
+ TIME_LIMIT_SECS);
+ int nprocs = torture_setting_int(tctx, "nprocs", 4);
+
+ int *curr_counts = map_count_buffer(nprocs, sizeof(int));
+ int *last_counts = talloc_array(NULL, int, nprocs);
+
+ struct timeval now, last, start;
+ int i, delta;
+
+ torture_assert(tctx, nprocs > 0, "bad proc count");
+ torture_assert(tctx, timelimit > 0, "bad timelimit");
+ torture_assert(tctx, curr_counts, "allocation failure");
+ torture_assert(tctx, last_counts, "allocation failure");
+
+ start = last = timeval_current();
+ for (i = 0; i < nprocs; ++i) {
+ fork_tcon_client(tctx, &curr_counts[i], timelimit, host, share);
+ }
+
+ while (children_remain()) {
+
+ sleep(1);
+ now = timeval_current();
+
+ for (i = 0, delta = 0; i < nprocs; ++i) {
+ delta += curr_counts[i] - last_counts[i];
+ }
+
+ printf("%u connections/sec\n",
+ (unsigned)rate_convert_secs(delta, &last, &now));
+
+ memcpy(last_counts, curr_counts, nprocs * sizeof(int));
+ last = timeval_current();
+ }
+
+ now = timeval_current();
+
+ for (i = 0, delta = 0; i < nprocs; ++i) {
+ delta += curr_counts[i];
+ }
+
+ printf("TOTAL: %u connections/sec over %u secs\n",
+ (unsigned)rate_convert_secs(delta, &start, &now),
+ timelimit);
+ return true;
+}
+
+/* vim: set sts=8 sw=8 : */
diff --git a/source4/torture/raw/unlink.c b/source4/torture/raw/unlink.c
new file mode 100644
index 0000000000..ddddeba41b
--- /dev/null
+++ b/source4/torture/raw/unlink.c
@@ -0,0 +1,450 @@
+/*
+ Unix SMB/CIFS implementation.
+ unlink test suite
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define BASEDIR "\\testunlink"
+
+/*
+ test unlink ops
+*/
+static bool test_unlink(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ union smb_unlink io;
+ NTSTATUS status;
+ bool ret = true;
+ const char *fname = BASEDIR "\\test.txt";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Trying non-existant file\n");
+ io.unlink.in.pattern = fname;
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
+
+ io.unlink.in.pattern = fname;
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying a hidden file\n");
+ smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
+ torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN);
+
+ io.unlink.in.pattern = fname;
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.unlink.in.pattern = fname;
+ io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.unlink.in.pattern = fname;
+ io.unlink.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ printf("Trying a directory\n");
+ io.unlink.in.pattern = BASEDIR;
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+
+ io.unlink.in.pattern = BASEDIR;
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+
+ printf("Trying a bad path\n");
+ io.unlink.in.pattern = "..";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+ io.unlink.in.pattern = "\\..";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+ io.unlink.in.pattern = BASEDIR "\\..\\..";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+ io.unlink.in.pattern = BASEDIR "\\..";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+
+ printf("Trying wildcards\n");
+ smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
+ io.unlink.in.pattern = BASEDIR "\\t*.t";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.unlink.in.pattern = BASEDIR "\\z*";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.unlink.in.pattern = BASEDIR "\\z*";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ /*
+ * In Samba3 we gave up upon getting the error codes in
+ * wildcard unlink correct. Trying gentest showed that this is
+ * irregular beyond our capabilities. So for
+ * FILE_ATTRIBUTE_DIRECTORY we always return NAME_INVALID.
+ * Tried by jra and vl. If others feel like solving this
+ * puzzle, please tell us :-)
+ */
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+ }
+
+ io.unlink.in.pattern = BASEDIR "\\*";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+ io.unlink.in.pattern = BASEDIR "\\?";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+ io.unlink.in.pattern = BASEDIR "\\t*";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
+
+ io.unlink.in.pattern = BASEDIR "\\*.dat";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+ }
+
+ io.unlink.in.pattern = BASEDIR "\\*.tx?";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test delete on close
+*/
+static bool test_delete_on_close(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_open op;
+ union smb_unlink io;
+ struct smb_rmdir dio;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum, fnum2;
+ const char *fname = BASEDIR "\\test.txt";
+ const char *dname = BASEDIR "\\test.dir";
+ const char *inside = BASEDIR "\\test.dir\\test.txt";
+ union smb_setfileinfo sfinfo;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ dio.in.path = dname;
+
+ io.unlink.in.pattern = fname;
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ printf("Testing with delete_on_close 0\n");
+ fnum = create_complex_file(cli, tctx, fname);
+
+ sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+ sfinfo.disposition_info.in.file.fnum = fnum;
+ sfinfo.disposition_info.in.delete_on_close = 0;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Testing with delete_on_close 1\n");
+ fnum = create_complex_file(cli, tctx, fname);
+ sfinfo.disposition_info.in.file.fnum = fnum;
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+
+ printf("Testing with directory and delete_on_close 0\n");
+ status = create_directory_handle(cli->tree, dname, &fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sfinfo.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+ sfinfo.disposition_info.in.file.fnum = fnum;
+ sfinfo.disposition_info.in.delete_on_close = 0;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Testing with directory delete_on_close 1\n");
+ status = create_directory_handle(cli->tree, dname, &fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sfinfo.disposition_info.in.file.fnum = fnum;
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+
+ /*
+ * Known deficiency, also skipped in base-delete.
+ */
+
+ printf("Testing with non-empty directory delete_on_close\n");
+ status = create_directory_handle(cli->tree, dname, &fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ fnum2 = create_complex_file(cli, tctx, inside);
+
+ sfinfo.disposition_info.in.file.fnum = fnum;
+ sfinfo.disposition_info.in.delete_on_close = 1;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+ sfinfo.disposition_info.in.file.fnum = fnum2;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sfinfo.disposition_info.in.file.fnum = fnum;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+ smbcli_close(cli->tree, fnum2);
+
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+
+ printf("Testing open dir with delete_on_close\n");
+ status = create_directory_handle(cli->tree, dname, &fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+ fnum2 = create_complex_file(cli, tctx, inside);
+ smbcli_close(cli->tree, fnum2);
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = dname;
+
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+ smbcli_deltree(cli->tree, dname);
+
+ printf("Testing double open dir with second delete_on_close\n");
+ status = create_directory_handle(cli->tree, dname, &fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, fnum);
+
+ fnum2 = create_complex_file(cli, tctx, inside);
+ smbcli_close(cli->tree, fnum2);
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = dname;
+
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = op.ntcreatex.out.file.fnum;
+
+ smbcli_close(cli->tree, fnum2);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+ smbcli_deltree(cli->tree, dname);
+
+ printf("Testing pre-existing open dir with second delete_on_close\n");
+ status = create_directory_handle(cli->tree, dname, &fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ fnum = create_complex_file(cli, tctx, inside);
+ smbcli_close(cli->tree, fnum);
+
+ /* we have a dir with a file in it, no handles open */
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 0;
+ op.ntcreatex.in.flags = 0;
+ op.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY |NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
+ op.ntcreatex.in.alloc_size = 0;
+ op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ op.ntcreatex.in.security_flags = 0;
+ op.ntcreatex.in.fname = dname;
+
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = op.ntcreatex.out.file.fnum;
+
+ /* open without delete on close */
+ op.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ status = smb_raw_open(cli->tree, tctx, &op);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = op.ntcreatex.out.file.fnum;
+
+ /* close 2nd file handle */
+ smbcli_close(cli->tree, fnum2);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_rmdir(cli->tree, &dio);
+ CHECK_STATUS(status, NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+done:
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ basic testing of unlink calls
+*/
+struct torture_suite *torture_raw_unlink(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "UNLINK");
+
+ torture_suite_add_1smb_test(suite, "unlink", test_unlink);
+ torture_suite_add_1smb_test(suite, "delete_on_close", test_delete_on_close);
+
+ return suite;
+}
diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c
new file mode 100644
index 0000000000..5d3628ca86
--- /dev/null
+++ b/source4/torture/raw/write.c
@@ -0,0 +1,724 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for various write operations
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ printf("(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_BUFFER(buf, seed, len) do { \
+ if (!check_buffer(buf, seed, len, __location__)) { \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_ALL_INFO(v, field) do { \
+ finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \
+ finfo.all_info.in.file.path = fname; \
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ if ((v) != finfo.all_info.out.field) { \
+ printf("(%s) wrong value for field %s %.0f - %.0f\n", \
+ __location__, #field, (double)v, (double)finfo.all_info.out.field); \
+ dump_all_info(tctx, &finfo); \
+ ret = false; \
+ }} while (0)
+
+
+#define BASEDIR "\\testwrite"
+
+
+/*
+ setup a random buffer based on a seed
+*/
+static void setup_buffer(uint8_t *buf, uint_t seed, int len)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) buf[i] = random();
+}
+
+/*
+ check a random buffer based on a seed
+*/
+static bool check_buffer(uint8_t *buf, uint_t seed, int len, const char *location)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) {
+ uint8_t v = random();
+ if (buf[i] != v) {
+ printf("Buffer incorrect at %s! ofs=%d buf=0x%x correct=0x%x\n",
+ location, i, buf[i], v);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ test write ops
+*/
+static bool test_write(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_write io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_WRITE_WRITE\n");
+ io.generic.level = RAW_WRITE_WRITE;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying zero write\n");
+ io.write.in.file.fnum = fnum;
+ io.write.in.count = 0;
+ io.write.in.offset = 0;
+ io.write.in.remaining = 0;
+ io.write.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.write.out.nwritten, 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying small write\n");
+ io.write.in.count = 9;
+ io.write.in.offset = 4;
+ io.write.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.write.out.nwritten, io.write.in.count);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying large write\n");
+ io.write.in.count = 4000;
+ io.write.in.offset = 0;
+ io.write.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.write.out.nwritten, 4000);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("Trying bad fnum\n");
+ io.write.in.file.fnum = fnum+1;
+ io.write.in.count = 4000;
+ io.write.in.offset = 0;
+ io.write.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("Setting file as sparse\n");
+ status = torture_set_sparse(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("Trying 2^32 offset\n");
+ setup_buffer(buf, seed, maxsize);
+ io.write.in.file.fnum = fnum;
+ io.write.in.count = 4000;
+ io.write.in.offset = 0xFFFFFFFF - 2000;
+ io.write.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.write.out.nwritten, 4000);
+ CHECK_ALL_INFO(io.write.in.count + (uint64_t)io.write.in.offset, size);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, io.write.in.offset, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test writex ops
+*/
+static bool test_writex(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_write io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum, i;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+ int max_bits=63;
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ max_bits=33;
+ torture_comment(tctx, "dangerous not set - limiting range of test to 2^%d\n", max_bits);
+ }
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_WRITE_WRITEX\n");
+ io.generic.level = RAW_WRITE_WRITEX;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying zero write\n");
+ io.writex.in.file.fnum = fnum;
+ io.writex.in.offset = 0;
+ io.writex.in.wmode = 0;
+ io.writex.in.remaining = 0;
+ io.writex.in.count = 0;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying small write\n");
+ io.writex.in.count = 9;
+ io.writex.in.offset = 4;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying large write\n");
+ io.writex.in.count = 4000;
+ io.writex.in.offset = 0;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, 4000);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("Trying bad fnum\n");
+ io.writex.in.file.fnum = fnum+1;
+ io.writex.in.count = 4000;
+ io.writex.in.offset = 0;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("Testing wmode\n");
+ io.writex.in.file.fnum = fnum;
+ io.writex.in.count = 1;
+ io.writex.in.offset = 0;
+ io.writex.in.wmode = 1;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);
+
+ io.writex.in.wmode = 2;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);
+
+
+ printf("Trying locked region\n");
+ cli->session->pid++;
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 3, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ cli->session->pid--;
+ io.writex.in.wmode = 0;
+ io.writex.in.count = 4;
+ io.writex.in.offset = 0;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ printf("Setting file as sparse\n");
+ status = torture_set_sparse(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("Trying 2^32 offset\n");
+ setup_buffer(buf, seed, maxsize);
+ io.writex.in.file.fnum = fnum;
+ io.writex.in.count = 4000;
+ io.writex.in.offset = 0xFFFFFFFF - 2000;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, 4000);
+ CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ for (i=33;i<max_bits;i++) {
+ printf("Trying 2^%d offset\n", i);
+ setup_buffer(buf, seed+1, maxsize);
+ io.writex.in.file.fnum = fnum;
+ io.writex.in.count = 4000;
+ io.writex.in.offset = ((uint64_t)1) << i;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ if (i>33 &&
+ NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ break;
+ }
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writex.out.nwritten, 4000);
+ CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed+1, 4000);
+ }
+ printf("limit is 2^%d\n", i);
+
+ setup_buffer(buf, seed, maxsize);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test write unlock ops
+*/
+static bool test_writeunlock(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_write io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_WRITE_WRITEUNLOCK\n");
+ io.generic.level = RAW_WRITE_WRITEUNLOCK;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying zero write\n");
+ io.writeunlock.in.file.fnum = fnum;
+ io.writeunlock.in.count = 0;
+ io.writeunlock.in.offset = 0;
+ io.writeunlock.in.remaining = 0;
+ io.writeunlock.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying small write\n");
+ io.writeunlock.in.count = 9;
+ io.writeunlock.in.offset = 4;
+ io.writeunlock.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+ smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count,
+ 0, WRITE_LOCK);
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying large write\n");
+ io.writeunlock.in.count = 4000;
+ io.writeunlock.in.offset = 0;
+ io.writeunlock.in.data = buf;
+ smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count,
+ 0, WRITE_LOCK);
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeunlock.out.nwritten, 4000);
+
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("Trying bad fnum\n");
+ io.writeunlock.in.file.fnum = fnum+1;
+ io.writeunlock.in.count = 4000;
+ io.writeunlock.in.offset = 0;
+ io.writeunlock.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("Setting file as sparse\n");
+ status = torture_set_sparse(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("Trying 2^32 offset\n");
+ setup_buffer(buf, seed, maxsize);
+ io.writeunlock.in.file.fnum = fnum;
+ io.writeunlock.in.count = 4000;
+ io.writeunlock.in.offset = 0xFFFFFFFF - 2000;
+ io.writeunlock.in.data = buf;
+ smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count,
+ 0, WRITE_LOCK);
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeunlock.out.nwritten, 4000);
+ CHECK_ALL_INFO(io.writeunlock.in.count + (uint64_t)io.writeunlock.in.offset, size);
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+
+/*
+ test write close ops
+*/
+static bool test_writeclose(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ union smb_write io;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum;
+ uint8_t *buf;
+ const int maxsize = 90000;
+ const char *fname = BASEDIR "\\test.txt";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_WRITE_WRITECLOSE\n");
+ io.generic.level = RAW_WRITE_WRITECLOSE;
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("Trying zero write\n");
+ io.writeclose.in.file.fnum = fnum;
+ io.writeclose.in.count = 0;
+ io.writeclose.in.offset = 0;
+ io.writeclose.in.mtime = 0;
+ io.writeclose.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);
+
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying small write\n");
+ io.writeclose.in.count = 9;
+ io.writeclose.in.offset = 4;
+ io.writeclose.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
+ io.writeclose.in.file.fnum = fnum;
+
+ if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
+ io.writeclose.in.file.fnum = fnum;
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("Trying large write\n");
+ io.writeclose.in.count = 4000;
+ io.writeclose.in.offset = 0;
+ io.writeclose.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeclose.out.nwritten, 4000);
+
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
+ io.writeclose.in.file.fnum = fnum;
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("Trying bad fnum\n");
+ io.writeclose.in.file.fnum = fnum+1;
+ io.writeclose.in.count = 4000;
+ io.writeclose.in.offset = 0;
+ io.writeclose.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("Setting file as sparse\n");
+ status = torture_set_sparse(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("Trying 2^32 offset\n");
+ setup_buffer(buf, seed, maxsize);
+ io.writeclose.in.file.fnum = fnum;
+ io.writeclose.in.count = 4000;
+ io.writeclose.in.offset = 0xFFFFFFFF - 2000;
+ io.writeclose.in.data = buf;
+ status = smb_raw_write(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(io.writeclose.out.nwritten, 4000);
+ CHECK_ALL_INFO(io.writeclose.in.count + (uint64_t)io.writeclose.in.offset, size);
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
+ io.writeclose.in.file.fnum = fnum;
+
+ memset(buf, 0, maxsize);
+ if (smbcli_read(cli->tree, fnum, buf, io.writeclose.in.offset, 4000) != 4000) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
+/*
+ basic testing of write calls
+*/
+struct torture_suite *torture_raw_write(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "WRITE");
+
+ torture_suite_add_1smb_test(suite, "write", test_write);
+ torture_suite_add_1smb_test(suite, "write unlock", test_writeunlock);
+ torture_suite_add_1smb_test(suite, "write close", test_writeclose);
+ torture_suite_add_1smb_test(suite, "writex", test_writex);
+
+ return suite;
+}