diff options
Diffstat (limited to 'source4/torture/raw')
-rw-r--r-- | source4/torture/raw/chkpath.c | 142 | ||||
-rw-r--r-- | source4/torture/raw/close.c | 170 | ||||
-rw-r--r-- | source4/torture/raw/context.c | 388 | ||||
-rw-r--r-- | source4/torture/raw/ioctl.c | 156 | ||||
-rw-r--r-- | source4/torture/raw/lock.c | 216 | ||||
-rw-r--r-- | source4/torture/raw/missing.txt | 157 | ||||
-rw-r--r-- | source4/torture/raw/mkdir.c | 141 | ||||
-rw-r--r-- | source4/torture/raw/mux.c | 298 | ||||
-rw-r--r-- | source4/torture/raw/notify.c | 140 | ||||
-rw-r--r-- | source4/torture/raw/open.c | 895 | ||||
-rw-r--r-- | source4/torture/raw/oplock.c | 288 | ||||
-rw-r--r-- | source4/torture/raw/qfileinfo.c | 701 | ||||
-rw-r--r-- | source4/torture/raw/qfsinfo.c | 295 | ||||
-rw-r--r-- | source4/torture/raw/read.c | 732 | ||||
-rw-r--r-- | source4/torture/raw/rename.c | 128 | ||||
-rw-r--r-- | source4/torture/raw/search.c | 610 | ||||
-rw-r--r-- | source4/torture/raw/seek.c | 152 | ||||
-rw-r--r-- | source4/torture/raw/setfileinfo.c | 498 | ||||
-rw-r--r-- | source4/torture/raw/unlink.c | 148 | ||||
-rw-r--r-- | source4/torture/raw/write.c | 702 |
20 files changed, 6957 insertions, 0 deletions
diff --git a/source4/torture/raw/chkpath.c b/source4/torture/raw/chkpath.c new file mode 100644 index 0000000000..3364c39a73 --- /dev/null +++ b/source4/torture/raw/chkpath.c @@ -0,0 +1,142 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#define BASEDIR "\\rawchkpath" + +#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) + + +static BOOL test_chkpath(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_chkpath io; + NTSTATUS status; + BOOL ret = True; + int fnum = -1; + + io.in.path = BASEDIR; + + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = BASEDIR "\\nodir"; + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + fnum = create_complex_file(cli, mem_ctx, BASEDIR "\\test.txt"); + if (fnum == -1) { + printf("failed to open test.txt - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + io.in.path = BASEDIR "\\test.txt"; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + if (!torture_set_file_attribute(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN)) { + printf("failed to set basedir hidden\n"); + ret = False; + goto done; + } + + io.in.path = BASEDIR; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = ""; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = "."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.in.path = "\\"; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = "\\."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.in.path = "\\.."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + io.in.path = BASEDIR "\\.."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + cli_close(cli, fnum); + return ret; +} + +/* + basic testing of chkpath calls +*/ +BOOL torture_raw_chkpath(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_chkpath"); + + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to clean " BASEDIR "\n"); + return False; + } + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + if (!test_chkpath(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/close.c b/source4/torture/raw/close.c new file mode 100644 index 0000000000..40bb57f303 --- /dev/null +++ b/source4/torture/raw/close.c @@ -0,0 +1,170 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/* basic testing of all RAW_CLOSE_* calls +*/ +BOOL torture_raw_close(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + union smb_close io; + struct 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; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_close"); + +#define REOPEN do { \ + fnum = create_complex_file(cli, mem_ctx, 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.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.in.fname = fname; + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &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", + time_string(mem_ctx, basetime), + nt_time_string(mem_ctx, &finfo.all_info.out.write_time)); + dump_all_info(mem_ctx, &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(mem_ctx, &finfo); + ret = False; + } + + + cli_unlink(cli, fname); + REOPEN; + + finfo2.generic.in.fname = fname; + finfo2.generic.level = RAW_FILEINFO_ALL_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); + CHECK_STATUS(status, NT_STATUS_OK); + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.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.in.fname = fname; + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &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(mem_ctx, &finfo); + ret = False; + } + + printf("testing splclose\n"); + + /* check splclose on a file */ + REOPEN; + io.splclose.level = RAW_CLOSE_SPLCLOSE; + io.splclose.in.fnum = fnum; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + printf("testing flush\n"); + cli_close(cli, fnum); + + io_flush.in.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + io_flush.in.fnum = 0xffff; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_OK); + + REOPEN; + + io_flush.in.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.in.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/context.c b/source4/torture/raw/context.c new file mode 100644 index 0000000000..c19fea458d --- /dev/null +++ b/source4/torture/raw/context.c @@ -0,0 +1,388 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#define BASEDIR "\\rawcontext" + +#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, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + + +/* + test session ops +*/ +static BOOL test_session(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + BOOL ret = True; + char *username, *domain, *password; + struct cli_session *session; + struct cli_tree *tree; + union smb_sesssetup setup; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + char c = 1; + + printf("TESTING SESSION HANDLING\n"); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + username = lp_parm_string(-1, "torture", "username"); + password = lp_parm_string(-1, "torture", "password"); + domain = lp_workgroup(); + + printf("create a second security context on the same transport\n"); + session = cli_session_init(cli->transport); + setup.generic.level = RAW_SESSSETUP_GENERIC; + setup.generic.in.sesskey = cli->transport->negotiate.sesskey; + setup.generic.in.capabilities = 0; /* ignored in secondary session setup */ + setup.generic.in.password = password; + setup.generic.in.user = username; + setup.generic.in.domain = domain; + + status = smb_raw_session_setup(session, mem_ctx, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + + session->vuid = setup.generic.out.vuid; + + printf("use the same tree as the existing connection\n"); + tree = cli_tree_init(session); + tree->tid = cli->tree->tid; + cli->tree->reference_count++; + + printf("vuid1=%d vuid2=%d\n", cli->session->vuid, session->vuid); + + 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_RIGHT_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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + printf("write using the old vuid\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.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("the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.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, which will also close the session + as the reference count will be 0 */ + cli_tree_close(tree); + +done: + return ret; +} + + +/* + test tree ops +*/ +static BOOL test_tree(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + BOOL ret = True; + char *share; + struct cli_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"; + char c = 1; + + printf("TESTING TREE HANDLING\n"); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + share = lp_parm_string(-1, "torture", "share"); + + printf("create a second tree context on the same session\n"); + tree = cli_tree_init(cli->session); + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = share; + tcon.tconx.in.device = "A:"; + status = smb_tree_connect(tree, mem_ctx, &tcon); + CHECK_STATUS(status, NT_STATUS_OK); + + tree->tid = tcon.tconx.out.cnum; + 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_tree_connect(tree, mem_ctx, &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_RIGHT_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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + printf("write using the old tid\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.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.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 */ + cli_tree_close(tree); + +done: + return ret; +} + + +/* + test pid ops +*/ +static BOOL test_pid(struct cli_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"; + char c = 1; + uint16 pid1, pid2; + + printf("TESTING PID HANDLING\n"); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("create a second pid\n"); + pid1 = cli->session->pid; + pid2 = pid1+1; + + printf("pid1=%d pid2=%d\n", pid1, pid2); + + printf("create a file using the new pid\n"); + cli->session->pid = pid2; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHT_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.fnum; + + printf("write using the old pid\n"); + cli->session->pid = pid1; + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.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("write with the new pid\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 the old pid\n"); + cli->session->pid = pid1; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the fnum should still be accessible\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); + + printf("exit the new pid\n"); + cli->session->pid = pid2; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the fnum should not now be accessible\n"); + cli->session->pid = pid1; + 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.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; +} + + +/* + basic testing of session/tree context calls +*/ +BOOL torture_raw_context(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_context"); + + if (!test_session(cli, mem_ctx)) { + ret = False; + } + + if (!test_tree(cli, mem_ctx)) { + ret = False; + } + + if (!test_pid(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/ioctl.c b/source4/torture/raw/ioctl.c new file mode 100644 index 0000000000..d55db4c1e6 --- /dev/null +++ b/source4/torture/raw/ioctl.c @@ -0,0 +1,156 @@ +/* + Unix SMB/CIFS implementation. + ioctl 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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 cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct 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", cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying QUERY_JOB_INFO\n"); + ctl.in.fnum = fnum; + ctl.in.request = IOCTL_QUERY_JOB_INFO; + + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + printf("Trying bad handle\n"); + ctl.in.fnum = fnum+1; + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + +done: + cli_close(cli, fnum); + return ret; +} + +/* test some filesystem control functions */ +static BOOL test_fsctl(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + int fnum; + NTSTATUS status; + BOOL ret = True; + const char *fname = BASEDIR "\\test.dat"; + struct smb_ntioctl 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", cli_errstr(cli)); + ret = False; + goto done; + } + + printf("trying sparse file\n"); + nt.in.function = FSCTL_SET_SPARSE; + nt.in.fnum = fnum; + nt.in.fsctl = True; + nt.in.filter = 0; + + status = smb_raw_ntioctl(cli->tree, &nt); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying bad handle\n"); + nt.in.fnum = fnum+1; + status = smb_raw_ntioctl(cli->tree, &nt); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +#if 0 + nt.in.fnum = fnum; + for (i=0;i<100;i++) { + nt.in.function = FSCTL_FILESYSTEM + (i<<2); + status = smb_raw_ntioctl(cli->tree, &nt); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + printf("filesystem fsctl 0x%x - %s\n", + i, nt_errstr(status)); + } + } +#endif + +done: + cli_close(cli, fnum); + return ret; +} + +/* + basic testing of some ioctl calls +*/ +BOOL torture_raw_ioctl(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_ioctl"); + + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to clean " BASEDIR "\n"); + return False; + } + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + if (!test_ioctl(cli, mem_ctx)) { + ret = False; + } + + if (!test_fsctl(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c new file mode 100644 index 0000000000..b0b0b56451 --- /dev/null +++ b/source4/torture/raw/lock.c @@ -0,0 +1,216 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testlock" + + +/* + test SMBlock and SMBunlock ops +*/ +static BOOL test_lock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_LOCK_LOCK\n"); + io.generic.level = RAW_LOCK_LOCK; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying 0/0 lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.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.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 max lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.fnum = fnum; + io.lock.in.count = 4000; + 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_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.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: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test locking&X ops +*/ +static BOOL test_lockx(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + struct smb_lock_entry lock[1]; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_LOCK_LOCKX\n"); + io.generic.level = RAW_LOCK_LOCKX; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.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); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of lock calls +*/ +BOOL torture_raw_lock(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_lock"); + + if (!test_lockx(cli, mem_ctx)) { + ret = False; + } + + if (!test_lock(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/missing.txt b/source4/torture/raw/missing.txt new file mode 100644 index 0000000000..8e026b78ff --- /dev/null +++ b/source4/torture/raw/missing.txt @@ -0,0 +1,157 @@ +- 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 +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..52120f0542 --- /dev/null +++ b/source4/torture/raw/mkdir.c @@ -0,0 +1,141 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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) + +/* + test mkdir ops +*/ +static BOOL test_mkdir(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_mkdir md; + struct smb_rmdir rd; + const char *path = "\\test_mkdir.dir"; + NTSTATUS status; + BOOL ret = True; + + /* cleanup */ + cli_rmdir(cli, path); + cli_unlink(cli, path); + + /* + 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 */ + cli_close(cli, create_complex_file(cli, mem_ctx, 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); + + cli_unlink(cli, 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_UNSUCCESSFUL); + + printf("testing t2mkdir with EAs\n"); + + /* with EAs */ + md.t2mkdir.in.num_eas = 1; + md.t2mkdir.in.eas = talloc(mem_ctx, sizeof(md.t2mkdir.in.eas[0])); + md.t2mkdir.in.eas[0].flags = 0; + md.t2mkdir.in.eas[0].name.s = "EAONE"; + md.t2mkdir.in.eas[0].value = data_blob_talloc(mem_ctx, "1", 1); + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + +done: + cli_rmdir(cli, path); + cli_unlink(cli, path); + return ret; +} + + +/* + basic testing of all RAW_MKDIR_* calls +*/ +BOOL torture_raw_mkdir(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_mkdir"); + + if (!test_mkdir(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/mux.c b/source4/torture/raw/mux.c new file mode 100644 index 0000000000..05c5e3de09 --- /dev/null +++ b/source4/torture/raw/mux.c @@ -0,0 +1,298 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#define BASEDIR "\\test_mux" + +#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 the delayed reply to a open that leads to a sharing violation +*/ +static BOOL test_mux_open(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + NTSTATUS status; + int fnum; + BOOL ret = True; + struct cli_request *req; + + printf("testing multiplexed open/open/close\n"); + + /* + file open with no share access + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + 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 = BASEDIR "\\open.dat"; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + /* send an open that will conflict */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + /* + same request, but async + */ + req = smb_raw_open_send(cli->tree, &io); + + /* and close the file */ + cli_close(cli, fnum); + + /* see if the async open succeeded */ + status = smb_raw_open_recv(req, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + cli_close(cli, io.ntcreatex.out.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 cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + int fnum; + BOOL ret = True; + struct cli_request *req; + + printf("testing multiplexed lock/write/close\n"); + + fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("open failed in mux_write - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + cli->session->pid = 1; + + /* lock a range */ + if (!cli_lock(cli, fnum, 0, 4, 0, WRITE_LOCK)) { + printf("lock failed in mux_write - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + cli->session->pid = 2; + + /* send an async write */ + io.generic.level = RAW_WRITE_WRITEX; + io.writex.in.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 = (void *)&fnum; + req = smb_raw_write_send(cli->tree, &io); + + /* unlock the range */ + cli->session->pid = 1; + cli_unlock(cli, fnum, 0, 4); + + /* and recv the async write reply */ + status = smb_raw_write_recv(req, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + cli_close(cli, fnum); + +done: + return ret; +} + + +/* + test a lock that conflicts with an existing lock +*/ +static BOOL test_mux_lock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + NTSTATUS status; + int fnum; + BOOL ret = True; + struct cli_request *req; + struct smb_lock_entry lock[1]; + + printf("TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\n"); + + fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("open failed in mux_write - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + printf("establishing a lock\n"); + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.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"); + 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 = cli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("reopening with an exit\n"); + smb_raw_exit(cli->session); + fnum = cli_open(cli, 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.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); + + 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 = cli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + cli_close(cli, fnum); + +done: + return ret; +} + + + +/* + basic testing of multiplexing notify +*/ +BOOL torture_raw_mux(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_mux"); + + /* cleanup */ + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to cleanup " BASEDIR "\n"); + ret = False; + goto done; + } + + + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create %s\n", BASEDIR); + ret = False; + goto done; + } + + if (!test_mux_open(cli, mem_ctx)) { + ret = False; + } + + if (!test_mux_write(cli, mem_ctx)) { + ret = False; + } + + if (!test_mux_lock(cli, mem_ctx)) { + ret = False; + } + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/notify.c b/source4/torture/raw/notify.c new file mode 100644 index 0000000000..a123dc6702 --- /dev/null +++ b/source4/torture/raw/notify.c @@ -0,0 +1,140 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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 - 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)) { \ + printf("(%d) %s [%s] != %s\n", __LINE__, #field, field.s, value); \ + ret = False; \ + goto done; \ + }} while (0) + + +/* + basic testing of change notify +*/ +BOOL torture_raw_notify(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + struct smb_notify notify; + union smb_open io; + int fnum = -1; + struct cli_request *req; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_notify"); + + /* cleanup */ + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to cleanup " BASEDIR "\n"); + ret = False; + goto done; + } + + /* + 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 = SA_RIGHT_FILE_ALL_ACCESS; + 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_CREATE; + 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.fnum; + + /* ask for a change notify */ + notify.in.buffer_size = 4096; + notify.in.completion_filter = 0xFF; + notify.in.fnum = fnum; + notify.in.recursive = True; + + printf("testing notify mkdir\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + cli_mkdir(cli, BASEDIR "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.out.num_changes, 1); + CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE); + + printf("testing notify rmdir\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + cli_rmdir(cli, BASEDIR "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.out.num_changes, 1); + CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED); + CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE); + + printf("testing notify cancel\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smb_raw_ntcancel(req); + cli_mkdir(cli, BASEDIR "\\subdir-name"); + status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c new file mode 100644 index 0000000000..9f77eb3f8e --- /dev/null +++ b/source4/torture/raw/open.c @@ -0,0 +1,895 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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 cli_state *cli, int fnum) +{ + char c = 1; + BOOL can_read = (cli_read(cli, fnum, &c, 0, 1) == 1); + BOOL can_write = (cli_write(cli, 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("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CREATE_FILE do { \ + fnum = create_complex_file(cli, mem_ctx, fname); \ + if (fnum == -1) { \ + printf("(%d) Failed to create %s - %s\n", __LINE__, fname, cli_errstr(cli)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_RDWR(fnum, correct) do { \ + enum rdwr_mode m = check_rdwr(cli, fnum); \ + if (m != correct) { \ + printf("(%d) Incorrect readwrite mode %s - expected %s\n", \ + __LINE__, 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.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &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("(%d) wrong time for field %s %s - %s\n", \ + __LINE__, #field, \ + time_string(mem_ctx, t1), \ + time_string(mem_ctx, t2)); \ + dump_all_info(mem_ctx, &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.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (!nt_time_equal(&t, &t2)) { \ + printf("(%d) wrong time for field %s %s - %s\n", \ + __LINE__, #field, \ + nt_time_string(mem_ctx, &t), \ + nt_time_string(mem_ctx, &t2)); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != finfo.all_info.out.field) { \ + printf("(%d) wrong value for field %s 0x%x - 0x%x\n", \ + __LINE__, #field, (int)v, (int)finfo.all_info.out.field); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) wrong value for %s 0x%x - 0x%x\n", \ + __LINE__, #v, (int)v, (int)correct); \ + ret = False; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.generic.file.fname = fname; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + printf("(%d) Failed to set attrib 0x%x on %s\n", \ + __LINE__, sattrib, fname); \ + }} while (0) + +/* + test RAW_OPEN_OPEN +*/ +static BOOL test_open(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_open.txt"; + NTSTATUS status; + int fnum, fnum2; + BOOL ret = True; + + printf("Checking RAW_OPEN_OPEN\n"); + + io.open.level = RAW_OPEN_OPEN; + io.open.in.fname = fname; + io.open.in.flags = OPEN_FLAGS_FCB; + io.open.in.search_attrs = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + fnum = io.open.out.fnum; + + cli_unlink(cli, fname); + CREATE_FILE; + cli_close(cli, fnum); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.open.out.fnum; + CHECK_RDWR(fnum2, RDWR_RDWR); + cli_close(cli, fnum2); + cli_close(cli, fnum); + + /* check the read/write modes */ + io.open.level = RAW_OPEN_OPEN; + io.open.in.fname = fname; + io.open.in.search_attrs = 0; + + io.open.in.flags = OPEN_FLAGS_OPEN_READ; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDONLY); + cli_close(cli, fnum); + + io.open.in.flags = OPEN_FLAGS_OPEN_WRITE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_WRONLY); + cli_close(cli, fnum); + + io.open.in.flags = OPEN_FLAGS_OPEN_RDWR; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + cli_close(cli, fnum); + + /* check the share modes roughly - not a complete matrix */ + io.open.in.flags = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_WRITE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + + if (io.open.in.flags != io.open.out.rmode) { + printf("(%d) rmode should equal flags - 0x%x 0x%x\n", + __LINE__, io.open.out.rmode, io.open.in.flags); + } + + io.open.in.flags = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.open.in.flags = OPEN_FLAGS_OPEN_READ | OPEN_FLAGS_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.open.out.fnum; + CHECK_RDWR(fnum2, RDWR_RDONLY); + cli_close(cli, fnum); + cli_close(cli, fnum2); + + + /* check the returned write time */ + io.open.level = RAW_OPEN_OPEN; + io.open.in.fname = fname; + io.open.in.search_attrs = 0; + io.open.in.flags = OPEN_FLAGS_OPEN_READ; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + + /* check other reply fields */ + CHECK_TIME(io.open.out.write_time, write_time); + CHECK_ALL_INFO(io.open.out.size, size); + CHECK_ALL_INFO(io.open.out.attrib, attrib); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_OPENX +*/ +static BOOL test_openx(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_openx.txt"; + NTSTATUS status; + int fnum, fnum2; + BOOL ret = True; + int i; + struct { + uint16 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_INVALID_LOCK_SEQUENCE }, + { OPENX_OPEN_FUNC_FAIL, False, NT_STATUS_INVALID_LOCK_SEQUENCE }, + { 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"); + cli_unlink(cli, 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, mem_ctx, fname); + if (fnum == -1) { + d_printf("Failed to create file %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + cli_close(cli, fnum); + } + io.openx.in.open_func = open_funcs[i].open_func; + status = smb_raw_open(cli->tree, mem_ctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n", + __LINE__, 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) || open_funcs[i].with_file) { + cli_close(cli, io.openx.out.fnum); + cli_unlink(cli, 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.fnum; + + CHECK_ALL_INFO(io.openx.out.size, size); + CHECK_VAL(io.openx.out.size, 1024*1024); + CHECK_ALL_INFO(io.openx.in.size, size); + CHECK_TIME(io.openx.out.write_time, write_time); + CHECK_ALL_INFO(io.openx.out.attrib, attrib); + 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); + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* check the fields when the file already existed */ + fnum2 = create_complex_file(cli, mem_ctx, fname); + if (fnum2 == -1) { + ret = False; + goto done; + } + cli_close(cli, fnum2); + + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.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); + cli_close(cli, 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.openx.out.fnum); + + io.openx.in.search_attrs = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.openx.out.fnum); + + SET_ATTRIB(FILE_ATTRIBUTE_NORMAL); + cli_unlink(cli, 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE, attrib); + cli_close(cli, io.openx.out.fnum); + cli_unlink(cli, 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.fnum; + + io.openx.in.timeout = 20000; + start_timer(); + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + if (end_timer() > 3) { + printf("(%d) Incorrect timing in openx with timeout - waited %d seconds\n", + __LINE__, (int)end_timer()); + ret = False; + } + cli_close(cli, fnum); + cli_unlink(cli, 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.openx.out.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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.openx.out.access_mask, STD_RIGHT_ALL_ACCESS); + cli_close(cli, io.openx.out.fnum); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_T2OPEN + + I can't work out how to get win2003 to accept a create file via TRANS2_OPEN, which + is why you see all the ACCESS_DENIED results below. When we finally work this out then this + test will make more sense +*/ +static BOOL test_t2open(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_t2open.txt"; + NTSTATUS status; + int fnum; + BOOL ret = True; + int i; + struct { + uint16 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_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_FAIL, True, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL, False, NT_STATUS_ACCESS_DENIED }, + { 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_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_TRUNC, True, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_TRUNC, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED }, + }; + + printf("Checking RAW_OPEN_T2OPEN\n"); + + io.t2open.level = RAW_OPEN_T2OPEN; + io.t2open.in.fname = fname; + io.t2open.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | + OPENX_FLAGS_EA_LEN | OPENX_FLAGS_EXTENDED_RETURN; + 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.file_attrs = 0; + io.t2open.in.write_time = 0; + io.t2open.in.size = 0; + io.t2open.in.timeout = 0; + + io.t2open.in.eas = talloc(mem_ctx, sizeof(io.t2open.in.eas[0])); + io.t2open.in.num_eas = 1; + io.t2open.in.eas[0].flags = 0; + io.t2open.in.eas[0].name.s = "EAONE"; + io.t2open.in.eas[0].value = data_blob_talloc(mem_ctx, "1", 1); + + /* 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, mem_ctx, fname); + if (fnum == -1) { + d_printf("Failed to create file %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + cli_close(cli, fnum); + } + io.t2open.in.open_func = open_funcs[i].open_func; + status = smb_raw_open(cli->tree, mem_ctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n", + __LINE__, 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) || open_funcs[i].with_file) { + cli_close(cli, io.t2open.out.fnum); + cli_unlink(cli, fname); + } + } + + /* check the basic return fields */ + fnum = create_complex_file(cli, mem_ctx, fname); + cli_close(cli, fnum); + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.t2open.out.fnum; + + CHECK_ALL_INFO(io.t2open.out.size, size); + CHECK_VAL(io.t2open.out.write_time, 0); + CHECK_ALL_INFO(io.t2open.out.attrib, attrib); + 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_EXISTED); + cli_close(cli, fnum); + + /* 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.t2open.out.fnum); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.t2open.out.fnum); + + SET_ATTRIB(FILE_ATTRIBUTE_NORMAL); + cli_unlink(cli, 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + /* 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.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_NTCREATEX +*/ +static BOOL test_ntcreatex(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + 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; + BOOL ret = True; + int i; + struct { + uint32 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 = GENERIC_RIGHTS_FILE_ALL_ACCESS; + 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 = cli_open(cli, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE); + if (fnum == -1) { + d_printf("Failed to create file %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + cli_close(cli, fnum); + } + io.ntcreatex.in.open_disposition = open_funcs[i].open_disp; + status = smb_raw_open(cli->tree, mem_ctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_disp=%d)\n", + __LINE__, 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) { + cli_close(cli, io.ntcreatex.out.fnum); + cli_unlink(cli, fname); + } + } + + /* basic field testing */ + 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.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 */ + cli_close(cli, fnum); + cli_unlink(cli, fname); + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + ret = False; + goto done; + } + cli_close(cli, fnum); + + 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.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); + cli_close(cli, fnum); + cli_unlink(cli, fname); + + + /* create a directory */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + 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; + + cli_rmdir(cli, fname); + cli_unlink(cli, fname); + + io.ntcreatex.in.access_mask = SEC_RIGHT_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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.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_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); + cli_unlink(cli, fname); + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_MKNEW +*/ +static BOOL test_mknew(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_mknew.txt"; + NTSTATUS status; + int fnum; + 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.fnum; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* make sure write_time works */ + io.mknew.in.write_time = basetime; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.fnum; + CHECK_TIME(basetime, write_time); + + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* make sure file_attrs works */ + io.mknew.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.fnum; + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, attrib); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_CTEMP +*/ +static BOOL test_ctemp(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + NTSTATUS status; + int fnum; + 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, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ctemp.out.fnum; + + name = io.ctemp.out.name; + + finfo.generic.level = RAW_FILEINFO_NAME_INFO; + finfo.generic.in.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &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); + + CHECK_TIME(basetime, write_time); + +done: + cli_close(cli, fnum); + if (fname) { + cli_unlink(cli, fname); + } + + return ret; +} + +/* basic testing of all RAW_OPEN_* calls +*/ +BOOL torture_raw_open(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_open"); + + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to clean " BASEDIR "\n"); + return False; + } + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + if (!test_open(cli, mem_ctx)) { + ret = False; + } + + if (!test_openx(cli, mem_ctx)) { + ret = False; + } + + if (!test_ntcreatex(cli, mem_ctx)) { + ret = False; + } + + if (!test_t2open(cli, mem_ctx)) { + ret = False; + } + + if (!test_mknew(cli, mem_ctx)) { + ret = False; + } + + if (!test_ctemp(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c new file mode 100644 index 0000000000..888fcbdc13 --- /dev/null +++ b/source4/torture/raw/oplock.c @@ -0,0 +1,288 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) wrong value for %s 0x%x - 0x%x\n", \ + __LINE__, #v, (int)v, (int)correct); \ + ret = False; \ + }} 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) + + +static struct { + int fnum; + unsigned char level; + int count; +} break_info; + +/* + a handler function for oplock break requests +*/ +static BOOL oplock_handler_ack(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private) +{ + struct cli_tree *tree = private; + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + printf("Acking in oplock handler\n"); + + return cli_oplock_ack(tree, fnum, level == 1? 0x102 : 2); +} + +/* + a handler function for oplock break requests - close the file +*/ +static BOOL oplock_handler_close(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private) +{ + union smb_close io; + NTSTATUS status; + struct cli_tree *tree = private; + + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.fnum = fnum; + io.close.in.write_time = 0; + status = smb_raw_close(tree, &io); + + printf("Closing in oplock handler\n"); + + if (!NT_STATUS_IS_OK(status)) { + printf("close failed in oplock_handler_close\n"); + return False; + } + return True; +} + +/* + test oplock ops +*/ +static BOOL test_oplock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + const char *fname = "\\test_oplock.dat"; + NTSTATUS status; + BOOL ret = True; + union smb_open io; + struct smb_unlink unl; + union smb_read rd; + uint16 fnum, fnum2; + + /* cleanup */ + cli_unlink(cli, fname); + + cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + 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; + + printf("open a file with a normal oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + printf("unlink it - should be no break\n"); + unl.in.pattern = fname; + unl.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &unl); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + CHECK_VAL(break_info.count, 0); + + cli_close(cli, fnum); + + /* + with a batch oplock we get a break + */ + printf("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(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + printf("unlink should generate a break\n"); + unl.in.pattern = fname; + unl.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &unl); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 2); + CHECK_VAL(break_info.count, 1); + + + cli_close(cli, fnum); + + printf("if we close on break then the unlink can succeed\n"); + ZERO_STRUCT(break_info); + cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + unl.in.pattern = fname; + unl.in.attrib = 0; + ZERO_STRUCT(break_info); + status = smb_raw_unlink(cli->tree, &unl); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 2); + CHECK_VAL(break_info.count, 1); + + printf("a self read should not cause a break\n"); + ZERO_STRUCT(break_info); + cli_close(cli, fnum); + cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + rd.read.level = RAW_READ_READ; + rd.read.in.fnum = fnum; + rd.read.in.count = 1; + rd.read.in.offset = 0; + rd.read.in.remaining = 0; + status = smb_raw_read(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + + + printf("a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + cli_close(cli, fnum); + cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.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(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 2); + + printf("a 2nd open should get an oplock when we close instead of ack\n"); + ZERO_STRUCT(break_info); + cli_close(cli, fnum); + cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.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(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.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, 2); + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + return ret; +} + + +/* + basic testing of oplocks +*/ +BOOL torture_raw_oplock(int dummy) +{ + struct cli_state *cli1; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli1)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_oplock"); + + if (!test_oplock(cli1, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli1); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c new file mode 100644 index 0000000000..b1f508cae8 --- /dev/null +++ b/source4/torture/raw/qfileinfo.c @@ -0,0 +1,701 @@ +/* + Unix SMB/CIFS implementation. + RAW_FILEINFO_* 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +static struct { + const char *name; + enum fileinfo_level level; + unsigned only_paths:1; + unsigned only_handles:1; + uint32 capability_mask; + NTSTATUS fnum_status, fname_status; + union smb_fileinfo fnum_finfo, fname_finfo; +} levels[] = { + { "GETATTR", RAW_FILEINFO_GETATTR, 1, 0, }, + { "GETATTRE", RAW_FILEINFO_GETATTRE, 0, 1, }, + { "STANDARD", RAW_FILEINFO_STANDARD, }, + { "EA_SIZE", RAW_FILEINFO_EA_SIZE, }, + { "ALL_EAS", RAW_FILEINFO_ALL_EAS, }, + { "IS_NAME_VALID", RAW_FILEINFO_IS_NAME_VALID, 1, 0, }, + { "BASIC_INFO", RAW_FILEINFO_BASIC_INFO, }, + { "STANDARD_INFO", RAW_FILEINFO_STANDARD_INFO, }, + { "EA_INFO", RAW_FILEINFO_EA_INFO, }, + { "NAME_INFO", RAW_FILEINFO_NAME_INFO, }, + { "ALL_INFO", RAW_FILEINFO_ALL_INFO, }, + { "ALT_NAME_INFO", RAW_FILEINFO_ALT_NAME_INFO, }, + { "STREAM_INFO", RAW_FILEINFO_STREAM_INFO, }, + { "COMPRESSION_INFO", RAW_FILEINFO_COMPRESSION_INFO, }, + { "UNIX_BASIC_INFO", RAW_FILEINFO_UNIX_BASIC, 0, 0, CAP_UNIX}, + { "UNIX_LINK_INFO", RAW_FILEINFO_UNIX_LINK, 0, 0, CAP_UNIX}, + { "BASIC_INFORMATION", RAW_FILEINFO_BASIC_INFORMATION, }, + { "STANDARD_INFORMATION", RAW_FILEINFO_STANDARD_INFORMATION, }, + { "INTERNAL_INFORMATION", RAW_FILEINFO_INTERNAL_INFORMATION, }, + { "EA_INFORMATION", RAW_FILEINFO_EA_INFORMATION, }, + { "ACCESS_INFORMATION", RAW_FILEINFO_ACCESS_INFORMATION, }, + { "NAME_INFORMATION", RAW_FILEINFO_NAME_INFORMATION, }, + { "POSITION_INFORMATION", RAW_FILEINFO_POSITION_INFORMATION, }, + { "MODE_INFORMATION", RAW_FILEINFO_MODE_INFORMATION, }, + { "ALIGNMENT_INFORMATION", RAW_FILEINFO_ALIGNMENT_INFORMATION, }, + { "ALL_INFORMATION", RAW_FILEINFO_ALL_INFORMATION, }, + { "ALT_NAME_INFORMATION", RAW_FILEINFO_ALT_NAME_INFORMATION, }, + { "STREAM_INFORMATION", RAW_FILEINFO_STREAM_INFORMATION, }, + { "COMPRESSION_INFORMATION", RAW_FILEINFO_COMPRESSION_INFORMATION, }, + { "NETWORK_OPEN_INFORMATION", RAW_FILEINFO_NETWORK_OPEN_INFORMATION, }, + { "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, }, + { NULL, } +}; + +/* + compare a dos time (2 second resolution) to a nt time +*/ +static int dos_nt_time_cmp(time_t t, const 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(const char *name) +{ + int i; + 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(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 */ +#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_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +BOOL torture_raw_qfileinfo(int dummy) +{ + struct cli_state *cli; + int i; + BOOL ret = True; + int count; + union smb_fileinfo *s1, *s2; + TALLOC_CTX *mem_ctx; + int fnum; + const char *fname = "\\torture_qfileinfo.txt"; + NTTIME correct_time; + large_t correct_size; + uint32 correct_attrib; + const char *correct_name; + BOOL skip_streams = False; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_qfileinfo"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + + /* 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.fnum = fnum; + levels[i].fnum_status = smb_raw_fileinfo(cli->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.fname = talloc_strdup(mem_ctx, fname); + levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx, + &levels[i].fname_finfo); + } + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32 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 (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) { + printf("ERROR: 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: 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 > 32) { + printf("too many level failures - giving up\n"); + goto done; + } + } + + /* see if we can do streams */ + s1 = fnum_find("STREAM_INFO"); + if (!s1 || s1->stream_info.out.num_streams == 0) { + 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(sname1); s2 = fname_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fnum_find(sname1); s2 = fname_find(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(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, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(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, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + +#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, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(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, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + + /* 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; + printf("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; + printf("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; + printf("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; + printf("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, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_size); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_size); \ + ret = False; \ + }} while (0) + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.size; + printf("size: %u\n", (unsigned)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; + printf("alloc_size: %u\n", (unsigned)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, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_attrib); \ + ret = False; \ + } \ + s1 = fname_find(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, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_attrib); \ + ret = False; \ + }} while (0) + + s1 = fnum_find("BASIC_INFO"); + correct_attrib = s1->basic_info.out.attrib; + printf("attrib: 0x%x\n", (unsigned)correct_attrib); + + ATTRIB_CHECK("GETATTR", getattr, attrib); + ATTRIB_CHECK("GETATTRE", getattre, attrib); + ATTRIB_CHECK("STANDARD", standard, attrib); + ATTRIB_CHECK("BASIC_INFO", basic_info, attrib); + ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib); + ATTRIB_CHECK("EA_SIZE", ea_size, 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; + printf("name: %s\n", correct_name); + +#define NAME_CHECK(sname, stype, tfield, flags) do { \ + s1 = fnum_find(sname); \ + if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name) != 0) || \ + wire_bad_flags(&s1->stype.out.tfield, flags)) { \ + 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(sname); \ + if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name)) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags)) { \ + 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) { + printf("ALL_INFO didn't give a filename\n"); + ret = False; + } + 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(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)) { + printf("Should not null terminate all_info/fname\n"); + ret = False; + } + } + + s1 = fnum_find("ALT_NAME_INFO"); + correct_name = s1->alt_name_info.out.fname.s; + printf("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 */ + cli_close(cli, fnum); + fnum = cli_nt_create_full(cli, correct_name, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, + 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", cli_errstr(cli)); + ret = False; + } + + if (!skip_streams) { + correct_name = "::$DATA"; + printf("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, + s1->all_eas.out.eas[i].value.length, + 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", + ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas), + s2->all_info.out.ea_size); + } + } + } + s2 = fname_find("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(sname1); s2 = fname_find(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(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(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); + VAL_CHECK("EA_INFO", ea_info, ea_size, + "ALL_INFO", all_info, ea_size); + 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(sname); s2 = fnum_find(sname); \ + VAL_EQUAL(stype, field, stype, field); \ +} while (0) + + NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, device); + NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, inode); + NAME_PATH_CHECK("POSITION_INFORMATION", position_information, 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 + +#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, \ + (unsigned)s1->stype.out.tfield); \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned)s1->stype.out.tfield); \ + }} while (0) + + /* 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 */ + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/qfsinfo.c b/source4/torture/raw/qfsinfo.c new file mode 100644 index 0000000000..274d1073af --- /dev/null +++ b/source4/torture/raw/qfsinfo.c @@ -0,0 +1,295 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +static struct { + const char *name; + enum fsinfo_level level; + uint32 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, }, + {"OBJECTID_INFORMATION", RAW_QFS_OBJECTID_INFORMATION, }, + { 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) { + 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 STR_EQUAL(n1, v1, n2, v2) do {if (!s1->n1.out.v1 && !s2->n2.out.v2) return True; \ + if (!s1->n1.out.v1 || !s2->n2.out.v2) return False; \ + if (strcmp(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(int dummy) +{ + struct cli_state *cli; + int i; + BOOL ret = True; + int count; + union smb_fsinfo *s1, *s2; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_qfsinfo"); + + /* scan all the levels, pulling the results */ + for (i=0; levels[i].name; i++) { + printf("Running level %s\n", levels[i].name); + levels[i].fsinfo.generic.level = levels[i].level; + levels[i].status = smb_raw_fsinfo(cli->tree, mem_ctx, &levels[i].fsinfo); + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32 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) { + ret = False; + printf("%d levels failed\n", count); + if (count > 10) { + printf("too many level failures - giving up\n"); + goto done; + } + } + + printf("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_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); + printf("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); + printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s); + } + + printf("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; + } + printf("total disk = %.0f MB\n", size1*scale/1.0e6); + } + + printf("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; + } + printf("free disk = %.0f MB\n", size1*scale/1.0e6); + } + + printf("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_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 (wire_bad_flags(&s1->stype.out.field, flags)) { \ + printf("(%d) incorrect string termination in %s/%s\n", \ + __LINE__, #stype, #field); \ + ret = False; \ + } \ + }} while (0) + + printf("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); + +done: + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c new file mode 100644 index 0000000000..c231f52c9d --- /dev/null +++ b/source4/torture/raw/read.c @@ -0,0 +1,732 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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, v, 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(char *buf, unsigned 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(char *buf, unsigned seed, int len, int line) +{ + int i; + srandom(seed); + for (i=0;i<len;i++) { + char 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 cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_READ\n"); + io.generic.level = RAW_READ_READ; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.read.in.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.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.read.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.read.in.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; + } + + 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); + cli_write(cli, 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 (!cli_lock(cli, 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: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test lockread ops +*/ +static BOOL test_lockread(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_LOCKREAD\n"); + io.generic.level = RAW_READ_LOCKREAD; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.lockread.in.fnum = fnum; + io.lockread.in.count = 1; + io.lockread.in.offset = 0; + 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); + + printf("Trying zero file read\n"); + io.lockread.in.count = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + printf("Trying bad fnum\n"); + io.lockread.in.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.lockread.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.lockread.in.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_FILE_LOCK_CONFLICT); + + cli_unlock(cli, fnum, 0, 1); + + 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); + cli_unlock(cli, 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; + } + + 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); + cli_write(cli, 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); + cli_unlock(cli, 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); + cli_unlock(cli, fnum, 0, 0xFFFF); + + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, 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: + cli_close(cli, fnum); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test readx ops +*/ +static BOOL test_readx(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_READX\n"); + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.generic.level = RAW_READ_READX; + io.readx.in.fnum = fnum; + io.readx.in.mincnt = 1; + io.readx.in.maxcnt = 1; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + 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.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.readx.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.readx.in.fnum = fnum; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + 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; + } + + 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); + + setup_buffer(buf, seed, maxsize); + cli_write(cli, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.readx.in.offset = 0; + 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.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 = 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); + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, 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); + +#ifdef LARGE_SMB_OFF_T + printf("Trying large offset read\n"); + io.readx.in.offset = ((SMB_BIG_UINT)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 (!cli_lock64(cli, 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); +#endif + +done: + cli_close(cli, fnum); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test readbraw ops +*/ +static BOOL test_readbraw(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_READBRAW\n"); + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.generic.level = RAW_READ_READBRAW; + io.readbraw.in.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.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.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.readbraw.in.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; + } + + 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); + cli_write(cli, 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 (!cli_lock(cli, 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); + +#ifdef LARGE_SMB_OFF_T + printf("Trying large offset read\n"); + io.readbraw.in.offset = ((SMB_BIG_UINT)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); +#endif + +done: + cli_close(cli, fnum); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of read calls +*/ +BOOL torture_raw_read(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_read"); + + if (!test_read(cli, mem_ctx)) { + ret = False; + } + + if (!test_readx(cli, mem_ctx)) { + ret = False; + } + + if (!test_lockread(cli, mem_ctx)) { + ret = False; + } + + if (!test_readbraw(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/rename.c b/source4/torture/raw/rename.c new file mode 100644 index 0000000000..4cfa1c95c2 --- /dev/null +++ b/source4/torture/raw/rename.c @@ -0,0 +1,128 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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 BASEDIR "\\testrename" + +/* + test SMBmv ops +*/ +static BOOL test_mv(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_rename io; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname1 = BASEDIR "\\test1.txt"; + const char *fname2 = BASEDIR "\\test2.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Trying simple rename\n"); + + fnum = create_complex_file(cli, mem_ctx, fname1); + + io.in.pattern1 = fname1; + io.in.pattern2 = fname2; + io.in.attrib = 0; + + 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 wildcard rename\n"); + io.in.pattern1 = BASEDIR "\\*.txt"; + io.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.in.pattern1 = BASEDIR "\\*.txt"; + io.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.in.pattern1 = BASEDIR "\\test1.bak"; + io.in.pattern2 = BASEDIR "\\*.txt"; + io.in.attrib = 0; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of rename calls +*/ +BOOL torture_raw_rename(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_rename"); + + if (!test_mv(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c new file mode 100644 index 0000000000..6cfdd2b3ff --- /dev/null +++ b/source4/torture/raw/search.c @@ -0,0 +1,610 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +#define BASEDIR "\\testsearch" + +/* + callback function for single_search +*/ +static BOOL single_search_callback(void *private, union smb_search_data *file) +{ + union smb_search_data *data = private; + + *data = *file; + + return True; +} + +/* + do a single file (non-wildcard) search +*/ +static NTSTATUS single_search(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *pattern, + enum search_level level, + union smb_search_data *data) +{ + union smb_search_first io; + NTSTATUS status; + + io.generic.level = level; + if (level == RAW_SEARCH_SEARCH) { + io.search_first.in.max_count = 1; + io.search_first.in.search_attrib = 0; + io.search_first.in.pattern = pattern; + } else { + 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, (void *)data, single_search_callback); + + return status; +} + + +static struct { + const char *name; + enum search_level level; + uint32 capability_mask; + NTSTATUS status; + union smb_search_data data; +} levels[] = { + {"SEARCH", RAW_SEARCH_SEARCH, }, + {"STANDARD", RAW_SEARCH_STANDARD, }, + {"EA_SIZE", RAW_SEARCH_EA_SIZE, }, + {"DIRECTORY_INFO", RAW_SEARCH_DIRECTORY_INFO, }, + {"FULL_DIRECTORY_INFO", RAW_SEARCH_FULL_DIRECTORY_INFO, }, + {"NAME_INFO", RAW_SEARCH_NAME_INFO, }, + {"BOTH_DIRECTORY_INFO", RAW_SEARCH_BOTH_DIRECTORY_INFO, }, + {"LEVEL_261", RAW_SEARCH_261, }, + {"LEVEL_262", RAW_SEARCH_262, }, + {"UNIX_INFO", RAW_SEARCH_UNIX_INFO, CAP_UNIX} +}; + +/* 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 cli_state *cli, TALLOC_CTX *mem_ctx) +{ + BOOL ret = True; + int fnum; + const char *fname = "\\torture_search.txt"; + NTSTATUS status; + int i; + union smb_fileinfo all_info, alt_info, name_info; + union smb_search_data *s; + + printf("Testing one file searches\n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + /* call all the levels */ + for (i=0;i<ARRAY_SIZE(levels);i++) { + uint32 cap = cli->transport->negotiate.capabilities; + + levels[i].status = single_search(cli, mem_ctx, fname, + levels[i].level, &levels[i].data); + + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + printf("testing %s\n", levels[i].name); + + 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; + } + } + + /* get the all_info file into to check against */ + all_info.generic.level = RAW_FILEINFO_ALL_INFO; + all_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + + alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO; + alt_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + + name_info.generic.level = RAW_FILEINFO_NAME_INFO; + name_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + +#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != v.sname2.out.field2) { \ + printf("(%d) %s/%s [%d] != %s/%s [%d]\n", \ + __LINE__, \ + #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("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, time_string(mem_ctx, s->sname1.field1), \ + #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (memcmp(&s->sname1.field1, &v.sname2.out.field2, sizeof(NTTIME))) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, nt_time_string(mem_ctx, &s->sname1.field1), \ + #sname2, #field2, nt_time_string(mem_ctx, &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("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #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)) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #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)) { \ + printf("(%d) %s/%s [%s] != %s\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1.s, \ + fname); \ + ret = False; \ + } \ + }} while (0) + + /* check that all the results are as expected */ + CHECK_VAL("SEARCH", search, attrib, all_info, all_info, attrib); + CHECK_VAL("STANDARD", standard, attrib, all_info, all_info, attrib); + CHECK_VAL("EA_SIZE", ea_size, attrib, all_info, all_info, attrib); + 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("LEVEL_261", level_261, attrib, all_info, all_info, attrib); + CHECK_VAL("LEVEL_262", level_262, 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("LEVEL_261", level_261, write_time, all_info, all_info, write_time); + CHECK_NTTIME("LEVEL_262", level_262, 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("LEVEL_261", level_261, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_262", level_262, 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("LEVEL_261", level_261, access_time, all_info, all_info, access_time); + CHECK_NTTIME("LEVEL_262", level_262, access_time, all_info, all_info, access_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("LEVEL_261", level_261, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_262", level_262, create_time, all_info, all_info, create_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("LEVEL_261", level_261, size, all_info, all_info, size); + CHECK_VAL("LEVEL_262", level_262, 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("LEVEL_261", level_261, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("LEVEL_262", level_262, 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("LEVEL_261", level_261, ea_size, all_info, all_info, ea_size); + CHECK_VAL("LEVEL_262", level_262, 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("LEVEL_261", level_261, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("LEVEL_262", level_262, name, fname+1, STR_TERMINATE_ASCII); + +done: + smb_raw_exit(cli->session); + cli_unlink(cli, fname); + + return ret; +} + + +struct multiple_result { + TALLOC_CTX *mem_ctx; + int count; + union smb_search_data *list; +}; + +/* + callback function for multiple_search +*/ +static BOOL multiple_search_callback(void *private, union smb_search_data *file) +{ + struct multiple_result *data = private; + + + data->count++; + data->list = talloc_realloc(data->mem_ctx, + data->list, + data->count * (sizeof(data->list[0]))); + + 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 cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *pattern, + enum search_level level, + enum continue_type cont_type, + void *data) +{ + union smb_search_first io; + union smb_search_next io2; + NTSTATUS status; + const int per_search = 300; + struct multiple_result *result = data; + + io.generic.level = level; + if (level == RAW_SEARCH_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.in.search_attrib = 0; + io.t2ffirst.in.max_count = per_search; + io.t2ffirst.in.flags = 0; + 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, mem_ctx, + &io, data, multiple_search_callback); + + + while (NT_STATUS_IS_OK(status)) { + io2.generic.level = level; + if (level == RAW_SEARCH_SEARCH) { + io2.search_next.in.max_count = per_search; + io2.search_next.in.search_attrib = 0; + io2.search_next.in.search_id = result->list[result->count-1].search.search_id; + } else { + 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 = 0; + io2.t2fnext.in.last_name = ""; + switch (cont_type) { + case CONT_RESUME_KEY: + if (level == RAW_SEARCH_STANDARD) { + io2.t2fnext.in.resume_key = + result->list[result->count-1].standard.resume_key; + } else { + io2.t2fnext.in.resume_key = + result->list[result->count-1].both_directory_info.file_index; + } + io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | + FLAG_TRANS2_FIND_BACKUP_INTENT; + break; + case CONT_NAME: + if (level == RAW_SEARCH_STANDARD) { + io2.t2fnext.in.last_name = + result->list[result->count-1].standard.name.s; + } else { + io2.t2fnext.in.last_name = + result->list[result->count-1].both_directory_info.name.s; + } + break; + case CONT_FLAGS: + io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CONTINUE; + break; + } + } + + status = smb_raw_search_next(cli->tree, mem_ctx, + &io2, data, multiple_search_callback); + if (!NT_STATUS_IS_OK(status)) { + break; + } + if (level == RAW_SEARCH_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) 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, v, correct); \ + ret = False; \ + }} while (0) + + +static int search_both_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + return strcmp(d1->both_directory_info.name.s, d2->both_directory_info.name.s); +} + +static int search_standard_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + return strcmp(d1->standard.name.s, d2->standard.name.s); +} + +static int search_old_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + return strcmp(d1->search.name, d2->search.name); +} + + +/* + basic testing of search calls using many files +*/ +static BOOL test_many_files(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + 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 search_level level; + enum continue_type cont_type; + } search_types[] = { + {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_FLAGS}, + {"BOTH_DIRECTORY_INFO", "KEY", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY}, + {"BOTH_DIRECTORY_INFO", "NAME", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_NAME}, + {"STANDARD", "FLAGS", RAW_SEARCH_STANDARD, CONT_FLAGS}, + {"STANDARD", "KEY", RAW_SEARCH_STANDARD, CONT_RESUME_KEY}, + {"STANDARD", "NAME", RAW_SEARCH_STANDARD, CONT_NAME}, + {"SEARCH", "ID", RAW_SEARCH_SEARCH, CONT_RESUME_KEY} + }; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + printf("Creating %d files\n", num_files); + + for (i=0;i<num_files;i++) { + asprintf(&fname, BASEDIR "\\test%03d.txt", i); + fnum = cli_open(cli, fname, O_CREAT|O_RDWR, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + free(fname); + cli_close(cli, fnum); + } + + + for (t=0;t<ARRAY_SIZE(search_types);t++) { + ZERO_STRUCT(result); + result.mem_ctx = mem_ctx; + + printf("Continue %s via %s\n", search_types[t].name, search_types[t].cont_name); + + status = multiple_search(cli, mem_ctx, BASEDIR "\\*.*", + search_types[t].level, + search_types[t].cont_type, + &result); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(result.count, num_files); + + if (search_types[t].level == RAW_SEARCH_BOTH_DIRECTORY_INFO) { + qsort(result.list, result.count, sizeof(result.list[0]), search_both_compare); + } else if (search_types[t].level == RAW_SEARCH_STANDARD) { + qsort(result.list, result.count, sizeof(result.list[0]), search_standard_compare); + } else { + qsort(result.list, result.count, sizeof(result.list[0]), search_old_compare); + } + + for (i=0;i<num_files;i++) { + const char *s; + if (search_types[t].level == RAW_SEARCH_BOTH_DIRECTORY_INFO) { + s = result.list[i].both_directory_info.name.s; + } else if (search_types[t].level == RAW_SEARCH_STANDARD) { + s = result.list[i].standard.name.s; + } else { + s = result.list[i].search.name; + } + asprintf(&fname, "test%03d.txt", i); + if (strcmp(fname, s)) { + printf("Incorrect name %s at entry %d\n", s, i); + ret = False; + break; + } + free(fname); + } + } + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + return ret; +} + + +/* + basic testing of all RAW_SEARCH_* calls using a single file +*/ +BOOL torture_raw_search(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_search"); + + if (!test_one_file(cli, mem_ctx)) { + ret = False; + } + + if (!test_many_files(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + + return ret; +} diff --git a/source4/torture/raw/seek.c b/source4/torture/raw/seek.c new file mode 100644 index 0000000000..9379b676ab --- /dev/null +++ b/source4/torture/raw/seek.c @@ -0,0 +1,152 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testseek" + +/* + test seek ops +*/ +static BOOL test_seek(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_seek io; + union smb_fileinfo finfo; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("Failed to open test.txt - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.fnum = fnum; + + printf("Trying bad handle\n"); + io.in.fnum = fnum+1; + io.in.mode = SEEK_MODE_START; + io.in.offset = 0; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Trying simple seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_START; + io.in.offset = 17; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.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.in.fnum = fnum; + io.in.mode = SEEK_MODE_CURRENT; + io.in.offset = -3; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 14); + + printf("Trying end seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_END; + io.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.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, finfo.all_info.out.size); + + printf("Trying max seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_START; + io.in.offset = -1; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 0xffffffff); + + printf("Trying max overflow\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_CURRENT; + io.in.offset = 1000; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 999); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of seek calls +*/ +BOOL torture_raw_seek(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_seek"); + + if (!test_seek(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/setfileinfo.c b/source4/torture/raw/setfileinfo.c new file mode 100644 index 0000000000..c169895020 --- /dev/null +++ b/source4/torture/raw/setfileinfo.c @@ -0,0 +1,498 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + 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_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_sfileinfo"); + + cli_deltree(cli, BASEDIR); + cli_mkdir(cli, BASEDIR); + +#define RECREATE_FILE(fname) do { \ + if (fnum != -1) cli_close(cli, fnum); \ + fnum = create_complex_file(cli, mem_ctx, fname); \ + if (fnum == -1) { \ + printf("(%d) ERROR: open of %s failed (%s)\n", \ + __LINE__, fname, cli_errstr(cli)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define RECREATE_BOTH do { \ + RECREATE_FILE(path_fname); \ + cli_close(cli, 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.file.fnum = fnum; \ + status = smb_raw_setfileinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = False; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1); \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%d) %s pathinfo - %s\n", __LINE__, #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.file.fname = path_fname; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + sfinfo.generic.file.fname = path_fname_new; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + } \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = False; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.fname = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo1.generic.in.fname = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%d) %s pathinfo - %s\n", __LINE__, #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.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2); \ + } else { \ + finfo2.generic.in.fname = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo2.generic.in.fname = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \ + } \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("%s - %s\n", #call, nt_errstr(status2)); \ + } \ + }} while (0) + +#define CHECK_VALUE(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && finfo2.stype.out.field != value) { \ + printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \ + call_name, #stype, #field, \ + (uint_t)value, (uint_t)finfo2.stype.out.field); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + +#define CHECK_TIME(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && nt_time_to_unix(&finfo2.stype.out.field) != value) { \ + printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \ + call_name, #stype, #field, \ + (uint_t)value, \ + (uint_t)nt_time_to_unix(&finfo2.stype.out.field)); \ + printf("\t%s", http_timestring(mem_ctx, value)); \ + printf("\t%s\n", nt_time_string(mem_ctx, &finfo2.stype.out.field)); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + +#define CHECK_STR(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && strcmp(finfo2.stype.out.field, value) != 0) { \ + printf("(%d) %s - %s/%s should be '%s' - '%s'\n", __LINE__, \ + call_name, #stype, #field, \ + value, \ + finfo2.stype.out.field); \ + dump_all_info(mem_ctx, &finfo1); \ + }} 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 = FILE_ATTRIBUTE_NORMAL; + 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_NORMAL); + + 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_READONLY; + 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_READONLY); + + 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_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 = 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); + + printf("finally the rename_information level\n"); + cli_close(cli, create_complex_file(cli, mem_ctx, fnum_fname_new)); + cli_close(cli, create_complex_file(cli, mem_ctx, 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+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); + + sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+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); + +#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: + cli_close(cli, fnum); + if (!cli_unlink(cli, fnum_fname)) { + printf("Failed to delete %s - %s\n", fnum_fname, cli_errstr(cli)); + } + if (!cli_unlink(cli, path_fname)) { + printf("Failed to delete %s - %s\n", path_fname, cli_errstr(cli)); + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} + + +/* + look for the w2k3 setpathinfo STANDARD bug +*/ +BOOL torture_raw_sfileinfo_bug(int dummy) +{ + struct cli_state *cli; + TALLOC_CTX *mem_ctx; + const char *fname = "\\bug3.txt"; + union smb_setfileinfo sfinfo; + NTSTATUS status; + int fnum; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_sfileinfo"); + + fnum = create_complex_file(cli, mem_ctx, fname); + cli_close(cli, fnum); + + sfinfo.generic.level = RAW_SFILEINFO_STANDARD; + sfinfo.generic.file.fname = 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/unlink.c b/source4/torture/raw/unlink.c new file mode 100644 index 0000000000..9cae91fe41 --- /dev/null +++ b/source4/torture/raw/unlink.c @@ -0,0 +1,148 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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 BASEDIR "\\testunlink" + +/* + test unlink ops +*/ +static BOOL test_unlink(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_unlink io; + NTSTATUS status; + BOOL ret = True; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Trying non-existant file\n"); + io.in.pattern = fname; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE)); + + io.in.pattern = fname; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying a hidden file\n"); + cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE)); + torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN); + + io.in.pattern = fname; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.pattern = fname; + io.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying a directory\n"); + io.in.pattern = BASEDIR; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + io.in.pattern = BASEDIR; + io.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.in.pattern = ".."; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + printf("Trying wildcards\n"); + cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE)); + io.in.pattern = BASEDIR "\\t*.t"; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.pattern = BASEDIR "\\*"; + io.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.in.pattern = BASEDIR "\\*.dat"; + io.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.pattern = BASEDIR "\\*.tx?"; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + 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); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of unlink calls +*/ +BOOL torture_raw_unlink(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_unlink"); + + if (!test_unlink(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c new file mode 100644 index 0000000000..117b322530 --- /dev/null +++ b/source4/torture/raw/write.c @@ -0,0 +1,702 @@ +/* + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.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, v, 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 CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != finfo.all_info.out.field) { \ + printf("(%d) wrong value for field %s %.0f - %.0f\n", \ + __LINE__, #field, (double)v, (double)finfo.all_info.out.field); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + + +#define BASEDIR "\\testwrite" + + +/* + setup a random buffer based on a seed +*/ +static void setup_buffer(char *buf, unsigned 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(char *buf, unsigned seed, int len, int line) +{ + int i; + srandom(seed); + for (i=0;i<len;i++) { + char v = random(); + if (buf[i] != v) { + printf("Buffer incorrect at line %d! ofs=%d buf=0x%x correct=0x%x\n", + line, i, buf[i], v); + return False; + } + } + return True; +} + +/* + test write ops +*/ +static BOOL test_write(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITE\n"); + io.generic.level = RAW_WRITE_WRITE; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.write.in.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 (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + 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 (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.write.in.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); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.write.in.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 + (SMB_BIG_UINT)io.write.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.write.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test writex ops +*/ +static BOOL test_writex(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITEX\n"); + io.generic.level = RAW_WRITE_WRITEX; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.writex.in.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 (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + 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 (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.writex.in.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.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 (!cli_lock(cli, fnum, 3, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + 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); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writex.in.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 + (SMB_BIG_UINT)io.writex.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writex.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying 2^43 offset\n"); + setup_buffer(buf, seed+1, maxsize); + io.writex.in.fnum = fnum; + io.writex.in.count = 4000; + io.writex.in.offset = ((SMB_BIG_UINT)1) << 43; + 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 + (SMB_BIG_UINT)io.writex.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writex.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed+1, 4000); + + + setup_buffer(buf, seed, maxsize); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test write unlock ops +*/ +static BOOL test_writeunlock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITEUNLOCK\n"); + io.generic.level = RAW_WRITE_WRITEUNLOCK; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.writeunlock.in.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 (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + cli_lock(cli, 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 (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + 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; + cli_lock(cli, 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 (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.writeunlock.in.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); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writeunlock.in.fnum = fnum; + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0xFFFFFFFF - 2000; + io.writeunlock.in.data = buf; + cli_lock(cli, 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 + (SMB_BIG_UINT)io.writeunlock.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test write close ops +*/ +static BOOL test_writeclose(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITECLOSE\n"); + io.generic.level = RAW_WRITE_WRITECLOSE; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.writeclose.in.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 = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + 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 = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + 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 = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.writeclose.in.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); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writeclose.in.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 + (SMB_BIG_UINT)io.writeclose.in.offset, size); + + fnum = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writeclose.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of write calls +*/ +BOOL torture_raw_write(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_write"); + + if (!test_write(cli, mem_ctx)) { + ret = False; + } + + if (!test_writeunlock(cli, mem_ctx)) { + ret = False; + } + + if (!test_writeclose(cli, mem_ctx)) { + ret = False; + } + + if (!test_writex(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} |