summaryrefslogtreecommitdiffstats
path: root/source3/torture
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/torture
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/torture')
-rw-r--r--source3/torture/bench_pthreadpool.c68
-rw-r--r--source3/torture/cmd_vfs.c2362
-rw-r--r--source3/torture/denytest.c1600
-rw-r--r--source3/torture/locktest2.c610
-rw-r--r--source3/torture/mangle_test.c223
-rw-r--r--source3/torture/msg_sink.c285
-rw-r--r--source3/torture/msg_source.c159
-rw-r--r--source3/torture/msgtest.c171
-rw-r--r--source3/torture/nbench.c504
-rw-r--r--source3/torture/nbio.c377
-rw-r--r--source3/torture/pdbtest.c736
-rw-r--r--source3/torture/proto.h192
-rw-r--r--source3/torture/scanner.c515
-rw-r--r--source3/torture/test_addrchange.c94
-rw-r--r--source3/torture/test_async_echo.c148
-rw-r--r--source3/torture/test_authinfo_structs.c218
-rw-r--r--source3/torture/test_buffersize.c55
-rw-r--r--source3/torture/test_case_insensitive.c80
-rw-r--r--source3/torture/test_chain3.c296
-rw-r--r--source3/torture/test_cleanup.c240
-rw-r--r--source3/torture/test_ctdbd_conn.c312
-rw-r--r--source3/torture/test_dbwrap_ctdb.c163
-rw-r--r--source3/torture/test_dbwrap_do_locked.c161
-rw-r--r--source3/torture/test_dbwrap_watch.c467
-rw-r--r--source3/torture/test_g_lock.c1403
-rw-r--r--source3/torture/test_hidenewfiles.c234
-rw-r--r--source3/torture/test_idmap_cache.c122
-rw-r--r--source3/torture/test_idmap_tdb_common.c1047
-rw-r--r--source3/torture/test_matching.c276
-rw-r--r--source3/torture/test_messaging_fd_passing.c397
-rw-r--r--source3/torture/test_messaging_read.c706
-rw-r--r--source3/torture/test_messaging_send_all.c279
-rw-r--r--source3/torture/test_namemap_cache.c270
-rw-r--r--source3/torture/test_notify.c731
-rw-r--r--source3/torture/test_notify_online.c293
-rw-r--r--source3/torture/test_nttrans_create.c108
-rw-r--r--source3/torture/test_nttrans_fsctl.c288
-rw-r--r--source3/torture/test_oplock_cancel.c168
-rw-r--r--source3/torture/test_posix.c1972
-rw-r--r--source3/torture/test_posix_append.c100
-rw-r--r--source3/torture/test_pthreadpool_tevent.c82
-rw-r--r--source3/torture/test_readdir_timestamp.c533
-rw-r--r--source3/torture/test_rpc_scale.c301
-rw-r--r--source3/torture/test_smb1_dfs.c4284
-rw-r--r--source3/torture/test_smb2.c5471
-rw-r--r--source3/torture/test_smbsock_any_connect.c47
-rw-r--r--source3/torture/test_tdb_validate.c68
-rw-r--r--source3/torture/torture.c16512
-rw-r--r--source3/torture/utable.c210
-rw-r--r--source3/torture/vfstest.c650
-rw-r--r--source3/torture/vfstest.h51
-rw-r--r--source3/torture/vfstest_chain.c342
-rw-r--r--source3/torture/wbc_async.c758
-rw-r--r--source3/torture/wbc_async.h171
-rw-r--r--source3/torture/wscript_build135
55 files changed, 48045 insertions, 0 deletions
diff --git a/source3/torture/bench_pthreadpool.c b/source3/torture/bench_pthreadpool.c
new file mode 100644
index 0000000..3d581e9
--- /dev/null
+++ b/source3/torture/bench_pthreadpool.c
@@ -0,0 +1,68 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Little pthreadpool benchmark
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../lib/pthreadpool/pthreadpool_pipe.h"
+#include "proto.h"
+
+extern int torture_numops;
+
+static void null_job(void *private_data)
+{
+ return;
+}
+
+bool run_bench_pthreadpool(int dummy)
+{
+ struct pthreadpool_pipe *pool;
+ int i, ret;
+
+ ret = pthreadpool_pipe_init(1, &pool);
+ if (ret != 0) {
+ d_fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
+ strerror(ret));
+ return false;
+ }
+
+ for (i=0; i<torture_numops; i++) {
+ int jobid;
+
+ ret = pthreadpool_pipe_add_job(pool, 0, null_job, NULL);
+ if (ret != 0) {
+ d_fprintf(stderr, "pthreadpool_pipe_add_job "
+ "failed: %s\n", strerror(ret));
+ break;
+ }
+ ret = pthreadpool_pipe_finished_jobs(pool, &jobid, 1);
+ if (ret < 0) {
+ d_fprintf(stderr, "pthreadpool_pipe_finished_job "
+ "failed: %s\n", strerror(-ret));
+ break;
+ }
+ }
+
+ if (ret != 1) {
+ return false;
+ }
+
+ ret = pthreadpool_pipe_destroy(pool);
+
+ return (ret == 0);
+}
diff --git a/source3/torture/cmd_vfs.c b/source3/torture/cmd_vfs.c
new file mode 100644
index 0000000..95b1e21
--- /dev/null
+++ b/source3/torture/cmd_vfs.c
@@ -0,0 +1,2362 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module functions
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/smbd.h"
+#include "system/passwd.h"
+#include "system/filesys.h"
+#include "vfstest.h"
+#include "../lib/util/util_pw.h"
+#include "libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "source3/smbd/dir.h"
+
+static const char *null_string = "";
+
+static uint32_t ssf_flags(void)
+{
+ return lp_posix_pathnames() ? SMB_FILENAME_POSIX_PATH : 0;
+}
+
+static NTSTATUS cmd_load_module(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int i;
+
+ if (argc < 2) {
+ printf("Usage: load <modules>\n");
+ return NT_STATUS_OK;
+ }
+
+ for (i=argc-1;i>0;i--) {
+ if (!vfs_init_custom(vfs->conn, argv[i])) {
+ DEBUG(0, ("load: (vfs_init_custom failed for %s)\n", argv[i]));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ printf("load: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_populate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char c;
+ size_t size;
+ if (argc != 3) {
+ printf("Usage: populate <char> <size>\n");
+ return NT_STATUS_OK;
+ }
+ c = argv[1][0];
+ size = atoi(argv[2]);
+ vfs->data = talloc_array(mem_ctx, char, size);
+ if (vfs->data == NULL) {
+ printf("populate: error=-1 (not enough memory)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ memset(vfs->data, c, size);
+ vfs->data_size = size;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_show_data(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ size_t offset;
+ size_t len;
+ if (argc != 1 && argc != 3) {
+ printf("Usage: showdata [<offset> <len>]\n");
+ return NT_STATUS_OK;
+ }
+ if (vfs->data == NULL || vfs->data_size == 0) {
+ printf("show_data: error=-1 (buffer empty)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (argc == 3) {
+ offset = atoi(argv[1]);
+ len = atoi(argv[2]);
+ } else {
+ offset = 0;
+ len = vfs->data_size;
+ }
+ if ((offset + len) > vfs->data_size) {
+ printf("show_data: error=-1 (not enough data in buffer)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ dump_data(0, (uint8_t *)(vfs->data) + offset, len);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_connect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ SMB_VFS_CONNECT(vfs->conn, lp_servicename(talloc_tos(), lp_sub, SNUM(vfs->conn)), "vfstest");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disconnect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_VFS_DISCONNECT(vfs->conn);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disk_free(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = NULL;
+ uint64_t diskfree, bsize, dfree, dsize;
+ if (argc != 2) {
+ printf("Usage: disk_free <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname(talloc_tos(),
+ argv[1],
+ NULL,
+ NULL,
+ 0,
+ ssf_flags());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ diskfree = SMB_VFS_DISK_FREE(vfs->conn, smb_fname,
+ &bsize, &dfree, &dsize);
+ printf("disk_free: %lu, bsize = %lu, dfree = %lu, dsize = %lu\n",
+ (unsigned long)diskfree,
+ (unsigned long)bsize,
+ (unsigned long)dfree,
+ (unsigned long)dsize);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_opendir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 2) {
+ printf("Usage: opendir <fname>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname(talloc_tos(),
+ argv[1],
+ NULL,
+ NULL,
+ 0,
+ ssf_flags());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = OpenDir(vfs->conn,
+ vfs->conn,
+ smb_fname,
+ NULL,
+ 0,
+ &vfs->currentdir);
+ if (!NT_STATUS_IS_OK(status)) {
+ int err = map_errno_from_nt_status(status);
+ printf("opendir error=%d (%s)\n", err, strerror(err));
+ TALLOC_FREE(smb_fname);
+ errno = err;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ TALLOC_FREE(smb_fname);
+ printf("opendir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_Dir *currentdir = vfs->currentdir;
+ files_struct *dirfsp = dir_hnd_fetch_fsp(currentdir);
+ connection_struct *conn = dirfsp->conn;
+ SMB_STRUCT_STAT st;
+ const char *dname = NULL;
+ struct smb_filename *fname = NULL;
+ char *talloced = NULL;
+ int ret;
+
+ if (vfs->currentdir == NULL) {
+ printf("readdir: error=-1 (no open directory)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dname = ReadDirName(vfs->currentdir, &talloced);
+ if (dname == NULL) {
+ printf("readdir: NULL\n");
+ return NT_STATUS_OK;
+ }
+
+ fname = synthetic_smb_fname(
+ talloc_tos(), dname, NULL, 0, 0, ssf_flags());
+ if (fname == NULL) {
+ printf("readdir: no memory\n");
+ return NT_STATUS_OK;
+ }
+
+ printf("readdir: %s\n", dname);
+
+ ret = SMB_VFS_FSTATAT(conn, dirfsp, fname, &st, AT_SYMLINK_NOFOLLOW);
+
+ if ((ret == 0) && VALID_STAT(st)) {
+ time_t tmp_time;
+ printf(" stat available");
+ if (S_ISREG(st.st_ex_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_ex_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_ex_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_ex_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_ex_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_ex_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_ex_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_ex_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_ex_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_ex_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_ex_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ex_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_ex_nlink);
+ printf(" Access: %05o", (int)((st.st_ex_mode) & 007777));
+ printf(" Uid: %5lu Gid: %5lu\n",
+ (unsigned long)st.st_ex_uid,
+ (unsigned long)st.st_ex_gid);
+ tmp_time = convert_timespec_to_time_t(st.st_ex_atime);
+ printf(" Access: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_mtime);
+ printf(" Modify: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_ctime);
+ printf(" Change: %s", ctime(&tmp_time));
+ }
+
+ TALLOC_FREE(talloced);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_mkdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = NULL;
+ int ret;
+
+ if (argc != 2) {
+ printf("Usage: mkdir <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname(talloc_tos(),
+ argv[1],
+ NULL,
+ NULL,
+ 0,
+ ssf_flags());
+
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = SMB_VFS_MKDIRAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ smb_fname,
+ 00755);
+ if (ret == -1) {
+ printf("mkdir error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("mkdir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_closedir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (vfs->currentdir == NULL) {
+ printf("closedir: failure (no directory open)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ TALLOC_FREE(vfs->currentdir);
+
+ printf("closedir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_open(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct vfs_open_how how = { .mode = 0400, };
+ const char *flagstr;
+ files_struct *fsp;
+ struct files_struct *fspcwd = NULL;
+ struct smb_filename *smb_fname = NULL;
+ NTSTATUS status;
+ int fd;
+
+ if (argc < 3 || argc > 5) {
+ printf("Usage: open <filename> <flags> <mode>\n");
+ printf(" flags: O = O_RDONLY\n");
+ printf(" R = O_RDWR\n");
+ printf(" W = O_WRONLY\n");
+ printf(" C = O_CREAT\n");
+ printf(" E = O_EXCL\n");
+ printf(" T = O_TRUNC\n");
+ printf(" A = O_APPEND\n");
+ printf(" N = O_NONBLOCK/O_NDELAY\n");
+#ifdef O_SYNC
+ printf(" S = O_SYNC\n");
+#endif
+#ifdef O_NOFOLLOW
+ printf(" F = O_NOFOLLOW\n");
+#endif
+ printf(" mode: see open.2\n");
+ printf(" mode is ignored if C flag not present\n");
+ printf(" mode defaults to 00400\n");
+ return NT_STATUS_OK;
+ }
+ flagstr = argv[2];
+ while (*flagstr) {
+ switch (*flagstr) {
+ case 'O':
+ how.flags |= O_RDONLY;
+ break;
+ case 'R':
+ how.flags |= O_RDWR;
+ break;
+ case 'W':
+ how.flags |= O_WRONLY;
+ break;
+ case 'C':
+ how.flags |= O_CREAT;
+ break;
+ case 'E':
+ how.flags |= O_EXCL;
+ break;
+ case 'T':
+ how.flags |= O_TRUNC;
+ break;
+ case 'A':
+ how.flags |= O_APPEND;
+ break;
+ case 'N':
+ how.flags |= O_NONBLOCK;
+ break;
+#ifdef O_SYNC
+ case 'S':
+ how.flags |= O_SYNC;
+ break;
+#endif
+#ifdef O_NOFOLLOW
+ case 'F':
+ how.flags |= O_NOFOLLOW;
+ break;
+#endif
+ default:
+ printf("open: error=-1 (invalid flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ flagstr++;
+ }
+ if ((how.flags & O_CREAT) && argc == 4) {
+ short _mode = 0;
+
+ if (sscanf(argv[3], "%ho", &_mode) == 0) {
+ printf("open: error=-1 (invalid mode!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ how.mode = _mode;
+ }
+
+ fsp = talloc_zero(vfs, struct files_struct);
+ if (fsp == NULL) {
+ goto nomem;
+ }
+ fsp->fh = fd_handle_create(fsp);
+ if (fsp->fh == NULL) {
+ goto nomem;
+ }
+ fsp->conn = vfs->conn;
+
+ smb_fname = synthetic_smb_fname_split(NULL,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ goto nomem;
+ }
+
+ fsp->fsp_name = smb_fname;
+
+ status = vfs_at_fspcwd(fsp, vfs->conn, &fspcwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (is_named_stream(smb_fname)) {
+ struct smb_filename *base_name = NULL;
+
+ base_name = cp_smb_filename_nostream(NULL, smb_fname);
+ if (base_name == NULL) {
+ goto nomem;
+ }
+
+ status = openat_pathref_fsp(fspcwd, base_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ TALLOC_FREE(fspcwd);
+
+ fsp->base_fsp = base_name->fsp;
+ }
+
+ fd = SMB_VFS_OPENAT(vfs->conn,
+ fspcwd,
+ smb_fname,
+ fsp,
+ &how);
+ if (fd == -1) {
+ printf("open: error=%d (%s)\n", errno, strerror(errno));
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ fsp_set_fd(fsp, fd);
+
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* If we have an fd, this stat should succeed. */
+ DEBUG(0,("Error doing fstat on open file %s "
+ "(%s)\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status) ));
+ } else if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+ errno = EISDIR;
+ status = NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ fd_close(fsp);
+ goto fail;
+ }
+
+ fsp->file_id = vfs_file_id_from_sbuf(vfs->conn, &smb_fname->st);
+ fsp->vuid = UID_FIELD_INVALID;
+ fsp->file_pid = 0;
+ fsp->fsp_flags.can_lock = true;
+ fsp->fsp_flags.can_read = true;
+ fsp->fsp_flags.can_write = CAN_WRITE(vfs->conn);
+ fsp->print_file = NULL;
+ fsp->fsp_flags.modified = false;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->fsp_flags.is_directory = false;
+
+ vfs->files[fsp_get_pathref_fd(fsp)] = fsp;
+ printf("open: fd=%d\n", fsp_get_pathref_fd(fsp));
+ return NT_STATUS_OK;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+fail:
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(fsp);
+ return status;
+}
+
+
+static NTSTATUS cmd_pathfunc(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = NULL;
+ int ret = -1;
+
+ if (argc != 2) {
+ printf("Usage: %s <path>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname(talloc_tos(),
+ argv[1],
+ NULL,
+ NULL,
+ 0,
+ ssf_flags());
+
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (strcmp("rmdir", argv[0]) == 0 ) {
+ ret = SMB_VFS_UNLINKAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ smb_fname,
+ AT_REMOVEDIR);
+ TALLOC_FREE(smb_fname);
+ } else if (strcmp("unlink", argv[0]) == 0 ) {
+ TALLOC_FREE(smb_fname);
+ /* unlink can be a stream:name */
+ smb_fname = synthetic_smb_fname_split(talloc_tos(),
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = SMB_VFS_UNLINKAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ smb_fname,
+ 0);
+ TALLOC_FREE(smb_fname);
+ } else if (strcmp("chdir", argv[0]) == 0 ) {
+ ret = SMB_VFS_CHDIR(vfs->conn, smb_fname);
+ TALLOC_FREE(smb_fname);
+ } else {
+ printf("%s: error=%d (invalid function name!)\n", argv[0], errno);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ret == -1) {
+ printf("%s: error=%d (%s)\n", argv[0], errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("%s: ok\n", argv[0]);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_close(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ NTSTATUS status;
+
+ if (argc != 2) {
+ printf("Usage: close <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (vfs->files[fd] == NULL) {
+ printf("close: error=-1 (invalid file descriptor)\n");
+ return NT_STATUS_OK;
+ }
+
+ status = fd_close(vfs->files[fd]);
+ if (!NT_STATUS_IS_OK(status))
+ printf("close: error=%s\n", nt_errstr(status));
+ else
+ printf("close: ok\n");
+
+ TALLOC_FREE(vfs->files[fd]);
+ vfs->files[fd] = NULL;
+ return status;
+}
+
+
+static NTSTATUS cmd_read(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ size_t size;
+ ssize_t rsize;
+
+ if (argc != 3) {
+ printf("Usage: read <fd> <size>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* do some error checking on these */
+ fd = atoi(argv[1]);
+ size = atoi(argv[2]);
+ vfs->data = talloc_array(mem_ctx, char, size);
+ if (vfs->data == NULL) {
+ printf("read: error=-1 (not enough memory)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ vfs->data_size = size;
+
+ rsize = read_file(vfs->files[fd], vfs->data, 0, size);
+ if (rsize == -1) {
+ printf("read: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("read: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_write(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, wsize;
+ size_t size;
+
+ if (argc != 3) {
+ printf("Usage: write <fd> <size>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* some error checking should go here */
+ fd = atoi(argv[1]);
+ size = atoi(argv[2]);
+ if (vfs->data == NULL) {
+ printf("write: error=-1 (buffer empty, please populate it before writing)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (vfs->data_size < size) {
+ printf("write: error=-1 (buffer too small, please put some more data in)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ wsize = write_file(NULL, vfs->files[fd], vfs->data, 0, size);
+
+ if (wsize == -1) {
+ printf("write: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("write: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lseek(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, offset, whence;
+ off_t pos;
+
+ if (argc != 4) {
+ printf("Usage: lseek <fd> <offset> <whence>\n...where whence is 1 => SEEK_SET, 2 => SEEK_CUR, 3 => SEEK_END\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ offset = atoi(argv[2]);
+ whence = atoi(argv[3]);
+ switch (whence) {
+ case 1: whence = SEEK_SET; break;
+ case 2: whence = SEEK_CUR; break;
+ default: whence = SEEK_END;
+ }
+
+ pos = SMB_VFS_LSEEK(vfs->files[fd], offset, whence);
+ if (pos == (off_t)-1) {
+ printf("lseek: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("lseek: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_rename(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ struct smb_filename *smb_fname_src = NULL;
+ struct smb_filename *smb_fname_dst = NULL;
+
+ if (argc != 3) {
+ printf("Usage: rename <old> <new>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname_src = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname_src == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smb_fname_dst = synthetic_smb_fname_split(mem_ctx,
+ argv[2],
+ lp_posix_pathnames());
+ if (smb_fname_dst == NULL) {
+ TALLOC_FREE(smb_fname_src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = SMB_VFS_RENAMEAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ smb_fname_src,
+ vfs->conn->cwd_fsp,
+ smb_fname_dst);
+
+ TALLOC_FREE(smb_fname_src);
+ TALLOC_FREE(smb_fname_dst);
+ if (ret == -1) {
+ printf("rename: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("rename: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_fsync(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret, fd;
+ if (argc != 2) {
+ printf("Usage: fsync <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ ret = smb_vfs_fsync_sync(vfs->files[fd]);
+ if (ret == -1) {
+ printf("fsync: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fsync: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_stat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ struct smb_filename *smb_fname = NULL;
+ SMB_STRUCT_STAT st;
+ time_t tmp_time;
+
+ if (argc != 2) {
+ printf("Usage: stat <fname>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = SMB_VFS_STAT(vfs->conn, smb_fname);
+ if (ret == -1) {
+ printf("stat: error=%d (%s)\n", errno, strerror(errno));
+ TALLOC_FREE(smb_fname);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ st = smb_fname->st;
+ TALLOC_FREE(smb_fname);
+
+ pwd = getpwuid(st.st_ex_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = getgrgid(st.st_ex_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("stat: ok\n");
+ printf(" File: %s", argv[1]);
+ if (S_ISREG(st.st_ex_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_ex_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_ex_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_ex_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_ex_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_ex_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_ex_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_ex_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_ex_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_ex_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_ex_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ex_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_ex_nlink);
+ printf(" Access: %05o", (int)((st.st_ex_mode) & 007777));
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_ex_uid, user,
+ (unsigned long)st.st_ex_gid, group);
+ tmp_time = convert_timespec_to_time_t(st.st_ex_atime);
+ printf(" Access: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_mtime);
+ printf(" Modify: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_ctime);
+ printf(" Change: %s", ctime(&tmp_time));
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+ time_t tmp_time;
+
+ if (argc != 2) {
+ printf("Usage: fstat <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd >= 1024) {
+ printf("fstat: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (vfs->files[fd] == NULL) {
+ printf("fstat: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FSTAT(vfs->files[fd], &st) == -1) {
+ printf("fstat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = getpwuid(st.st_ex_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = getgrgid(st.st_ex_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("fstat: ok\n");
+ if (S_ISREG(st.st_ex_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_ex_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_ex_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_ex_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_ex_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_ex_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_ex_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_ex_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_ex_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_ex_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_ex_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ex_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_ex_nlink);
+ printf(" Access: %05o", (int)((st.st_ex_mode) & 007777));
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_ex_uid, user,
+ (unsigned long)st.st_ex_gid, group);
+ tmp_time = convert_timespec_to_time_t(st.st_ex_atime);
+ printf(" Access: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_mtime);
+ printf(" Modify: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_ctime);
+ printf(" Change: %s", ctime(&tmp_time));
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ struct smb_filename *smb_fname = NULL;
+ SMB_STRUCT_STAT st;
+ time_t tmp_time;
+
+ if (argc != 2) {
+ printf("Usage: lstat <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (SMB_VFS_LSTAT(vfs->conn, smb_fname) == -1) {
+ printf("lstat: error=%d (%s)\n", errno, strerror(errno));
+ TALLOC_FREE(smb_fname);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ st = smb_fname->st;
+ TALLOC_FREE(smb_fname);
+
+ pwd = getpwuid(st.st_ex_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = getgrgid(st.st_ex_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("lstat: ok\n");
+ if (S_ISREG(st.st_ex_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_ex_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_ex_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_ex_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_ex_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_ex_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_ex_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_ex_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_ex_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_ex_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_ex_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ex_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_ex_nlink);
+ printf(" Access: %05o", (int)((st.st_ex_mode) & 007777));
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_ex_uid, user,
+ (unsigned long)st.st_ex_gid, group);
+ tmp_time = convert_timespec_to_time_t(st.st_ex_atime);
+ printf(" Access: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_mtime);
+ printf(" Modify: %s", ctime(&tmp_time));
+ tmp_time = convert_timespec_to_time_t(st.st_ex_ctime);
+ printf(" Change: %s", ctime(&tmp_time));
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = NULL;
+ mode_t mode;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+ if (argc != 3) {
+ printf("Usage: chmod <path> <mode>\n");
+ return NT_STATUS_OK;
+ }
+
+ mode = atoi(argv[2]);
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (SMB_VFS_FCHMOD(pathref_fname->fsp, mode) == -1) {
+ printf("chmod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("chmod: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ mode_t mode;
+ if (argc != 3) {
+ printf("Usage: fchmod <fd> <mode>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ mode = atoi(argv[2]);
+ if (fd < 0 || fd >= 1024) {
+ printf("fchmod: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fchmod: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FCHMOD(vfs->files[fd], mode) == -1) {
+ printf("fchmod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fchmod: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_fchown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ uid_t uid;
+ gid_t gid;
+ int fd;
+ if (argc != 4) {
+ printf("Usage: fchown <fd> <uid> <gid>\n");
+ return NT_STATUS_OK;
+ }
+
+ uid = atoi(argv[2]);
+ gid = atoi(argv[3]);
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd >= 1024) {
+ printf("fchown: failure=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fchown: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (SMB_VFS_FCHOWN(vfs->files[fd], uid, gid) == -1) {
+ printf("fchown error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fchown: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_getwd(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = SMB_VFS_GETWD(vfs->conn, talloc_tos());
+ if (smb_fname == NULL) {
+ printf("getwd: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("getwd: %s\n", smb_fname->base_name);
+ TALLOC_FREE(smb_fname);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_utime(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_file_time ft;
+ struct files_struct *dirfsp = NULL;
+ struct smb_filename *smb_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 4) {
+ printf("Usage: utime <path> <access> <modify>\n");
+ return NT_STATUS_OK;
+ }
+
+ init_smb_file_time(&ft);
+
+ ft.atime = time_t_to_full_timespec(atoi(argv[2]));
+ ft.mtime = time_t_to_full_timespec(atoi(argv[3]));
+
+ status = filename_convert_dirfsp(mem_ctx,
+ vfs->conn,
+ argv[1],
+ 0, /* ucf_flags */
+ 0, /* twrp */
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("utime: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ if (SMB_VFS_FNTIMES(smb_fname->fsp, &ft) != 0) {
+ printf("utime: error=%d (%s)\n", errno, strerror(errno));
+ TALLOC_FREE(smb_fname);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ TALLOC_FREE(smb_fname);
+ printf("utime: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_ftruncate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ off_t off;
+ if (argc != 3) {
+ printf("Usage: ftruncate <fd> <length>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ off = atoi(argv[2]);
+ if (fd < 0 || fd >= 1024) {
+ printf("ftruncate: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("ftruncate: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FTRUNCATE(vfs->files[fd], off) == -1) {
+ printf("ftruncate: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("ftruncate: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lock(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ int op;
+ long offset;
+ long count;
+ int type;
+ const char *typestr;
+
+ if (argc != 6) {
+ printf("Usage: lock <fd> <op> <offset> <count> <type>\n");
+ printf(" ops: G = F_GETLK\n");
+ printf(" S = F_SETLK\n");
+ printf(" W = F_SETLKW\n");
+ printf(" type: R = F_RDLCK\n");
+ printf(" W = F_WRLCK\n");
+ printf(" U = F_UNLCK\n");
+ return NT_STATUS_OK;
+ }
+
+ if (sscanf(argv[1], "%d", &fd) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ op = 0;
+ switch (*argv[2]) {
+ case 'G':
+ op = F_GETLK;
+ break;
+ case 'S':
+ op = F_SETLK;
+ break;
+ case 'W':
+ op = F_SETLKW;
+ break;
+ default:
+ printf("lock: error=-1 (invalid op flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[3], "%ld", &offset) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[4], "%ld", &count) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ type = 0;
+ typestr = argv[5];
+ while(*typestr) {
+ switch (*typestr) {
+ case 'R':
+ type |= F_RDLCK;
+ break;
+ case 'W':
+ type |= F_WRLCK;
+ break;
+ case 'U':
+ type |= F_UNLCK;
+ break;
+ default:
+ printf("lock: error=-1 (invalid type flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ typestr++;
+ }
+
+ printf("lock: debug lock(fd=%d, op=%d, offset=%ld, count=%ld, type=%d))\n", fd, op, offset, count, type);
+
+ if (SMB_VFS_LOCK(vfs->files[fd], op, offset, count, type) == False) {
+ printf("lock: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("lock: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_symlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ char *target = NULL;
+ struct smb_filename target_fname;
+ struct smb_filename *new_smb_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 3) {
+ printf("Usage: symlink <path> <link>\n");
+ return NT_STATUS_OK;
+ }
+
+ new_smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[2],
+ lp_posix_pathnames());
+ if (new_smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ target = talloc_strdup(mem_ctx, argv[1]);
+ if (target == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ target_fname = (struct smb_filename) {
+ .base_name = target,
+ };
+
+ /* Removes @GMT tokens if any */
+ status = canonicalize_snapshot_path(&target_fname, UCF_GMT_PATHNAME, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ret = SMB_VFS_SYMLINKAT(vfs->conn,
+ &target_fname,
+ vfs->conn->cwd_fsp,
+ new_smb_fname);
+ if (ret == -1) {
+ printf("symlink: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("symlink: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char buffer[PATH_MAX];
+ struct smb_filename *smb_fname = NULL;
+ int size;
+
+ if (argc != 2) {
+ printf("Usage: readlink <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ size = SMB_VFS_READLINKAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ smb_fname,
+ buffer,
+ PATH_MAX);
+
+ if (size == -1) {
+ printf("readlink: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buffer[size] = '\0';
+ printf("readlink: %s\n", buffer);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_link(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *old_smb_fname = NULL;
+ struct smb_filename *new_smb_fname = NULL;
+ int ret;
+
+ if (argc != 3) {
+ printf("Usage: link <path> <link>\n");
+ return NT_STATUS_OK;
+ }
+
+ old_smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (old_smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ new_smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[2],
+ lp_posix_pathnames());
+ if (new_smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = SMB_VFS_LINKAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ old_smb_fname,
+ vfs->conn->cwd_fsp,
+ new_smb_fname,
+ 0);
+ if (ret == -1) {
+ printf("link: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("link: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_mknod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ short _mode = 0;
+ mode_t mode;
+ unsigned int dev_val;
+ SMB_DEV_T dev;
+ struct smb_filename *smb_fname = NULL;
+ int ret;
+
+ if (argc != 4) {
+ printf("Usage: mknod <path> <mode> <dev>\n");
+ printf(" mode is octal\n");
+ printf(" dev is hex\n");
+ return NT_STATUS_OK;
+ }
+
+ if (sscanf(argv[2], "%ho", &_mode) == 0) {
+ printf("open: error=-1 (invalid mode!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ mode = _mode;
+
+ if (sscanf(argv[3], "%x", &dev_val) == 0) {
+ printf("open: error=-1 (invalid dev!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ dev = (SMB_DEV_T)dev_val;
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = SMB_VFS_MKNODAT(vfs->conn,
+ vfs->conn->cwd_fsp,
+ smb_fname,
+ mode,
+ dev);
+
+ if (ret == -1) {
+ printf("mknod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("mknod: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_realpath(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct smb_filename *smb_fname = NULL;
+
+ if (argc != 2) {
+ printf("Usage: realpath <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (SMB_VFS_REALPATH(vfs->conn, mem_ctx, smb_fname) == NULL) {
+ printf("realpath: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("realpath: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_getxattr(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint8_t *buf;
+ ssize_t ret;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 3) {
+ printf("Usage: getxattr <path> <xattr>\n");
+ return NT_STATUS_OK;
+ }
+
+ buf = NULL;
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ ret = SMB_VFS_FGETXATTR(pathref_fname->fsp,
+ argv[2],
+ buf,
+ talloc_get_size(buf));
+ if (ret == -1) {
+ int err = errno;
+ printf("getxattr returned (%s)\n", strerror(err));
+ return map_nt_error_from_unix(err);
+ }
+ buf = talloc_array(mem_ctx, uint8_t, ret);
+ if (buf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = SMB_VFS_FGETXATTR(pathref_fname->fsp,
+ argv[2],
+ buf,
+ talloc_get_size(buf));
+ if (ret == -1) {
+ int err = errno;
+ printf("getxattr returned (%s)\n", strerror(err));
+ return map_nt_error_from_unix(err);
+ }
+ dump_data_file(buf, talloc_get_size(buf), false, stdout);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_listxattr(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ char *buf, *p;
+ ssize_t ret;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+ if (argc != 2) {
+ printf("Usage: listxattr <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ buf = NULL;
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ret = SMB_VFS_FLISTXATTR(pathref_fname->fsp,
+ buf, talloc_get_size(buf));
+ if (ret == -1) {
+ int err = errno;
+ printf("listxattr returned (%s)\n", strerror(err));
+ return map_nt_error_from_unix(err);
+ }
+ buf = talloc_array(mem_ctx, char, ret);
+ if (buf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = SMB_VFS_FLISTXATTR(pathref_fname->fsp,
+ buf, talloc_get_size(buf));
+ if (ret == -1) {
+ int err = errno;
+ printf("listxattr returned (%s)\n", strerror(err));
+ return map_nt_error_from_unix(err);
+ }
+ if (ret == 0) {
+ return NT_STATUS_OK;
+ }
+ if (buf[ret-1] != '\0') {
+ printf("listxattr returned non 0-terminated strings\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ p = buf;
+ while (p < buf+ret) {
+ printf("%s\n", p);
+ p = strchr(p, 0);
+ p += 1;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_fsetxattr(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ ssize_t ret;
+ int flags = 0;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+
+ if ((argc < 4) || (argc > 5)) {
+ printf("Usage: setxattr <path> <xattr> <value> [flags]\n");
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 5) {
+ flags = atoi(argv[4]);
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ret = SMB_VFS_FSETXATTR(pathref_fname->fsp, argv[2],
+ argv[3], strlen(argv[3]), flags);
+ if (ret == -1) {
+ int err = errno;
+ printf("fsetxattr returned (%s)\n", strerror(err));
+ return map_nt_error_from_unix(err);
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_removexattr(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ ssize_t ret;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 3) {
+ printf("Usage: removexattr <path> <xattr>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ ret = SMB_VFS_FREMOVEXATTR(pathref_fname->fsp, argv[2]);
+ if (ret == -1) {
+ int err = errno;
+ printf("removexattr returned (%s)\n", strerror(err));
+ return map_nt_error_from_unix(err);
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_fget_nt_acl(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ int fd;
+ NTSTATUS status;
+ struct security_descriptor *sd;
+
+ if (argc != 2) {
+ printf("Usage: fget_nt_acl <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd >= 1024) {
+ printf("fget_nt_acl: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fget_nt_acl: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ status = SMB_VFS_FGET_NT_ACL(metadata_fsp(vfs->files[fd]),
+ SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL,
+ talloc_tos(), &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("fget_nt_acl returned (%s)\n", nt_errstr(status));
+ return status;
+ }
+ printf("%s\n", sddl_encode(talloc_tos(), sd, get_global_sam_sid()));
+ TALLOC_FREE(sd);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_get_nt_acl(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct security_descriptor *sd;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+
+ if (argc != 2) {
+ printf("Usage: get_nt_acl <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname(talloc_tos(),
+ argv[1],
+ NULL,
+ NULL,
+ 0,
+ ssf_flags());
+
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_fname);
+ return status;
+ }
+ status = SMB_VFS_FGET_NT_ACL(pathref_fname->fsp,
+ SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL,
+ talloc_tos(),
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("get_nt_acl returned (%s)\n", nt_errstr(status));
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return status;
+ }
+ printf("%s\n", sddl_encode(talloc_tos(), sd, get_global_sam_sid()));
+ TALLOC_FREE(sd);
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_fset_nt_acl(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ int fd;
+ NTSTATUS status;
+ struct security_descriptor *sd;
+
+ if (argc != 3) {
+ printf("Usage: fset_nt_acl <fd> <sddl>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd >= 1024) {
+ printf("fset_nt_acl: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fset_nt_acl: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ sd = sddl_decode(talloc_tos(), argv[2], get_global_sam_sid());
+ if (!sd) {
+ printf("sddl_decode failed to parse %s as SDDL\n", argv[2]);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = SMB_VFS_FSET_NT_ACL(
+ metadata_fsp(vfs->files[fd]),
+ SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL,
+ sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("fset_nt_acl returned (%s)\n", nt_errstr(status));
+ return status;
+ }
+ TALLOC_FREE(sd);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_set_nt_acl(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct vfs_open_how how = { .mode = 0400, };
+ files_struct *fsp;
+ struct files_struct *fspcwd = NULL;
+ struct smb_filename *smb_fname = NULL;
+ NTSTATUS status;
+ struct security_descriptor *sd = NULL;
+ int fd;
+
+ if (argc != 3) {
+ printf("Usage: set_nt_acl <file> <sddl>\n");
+ return NT_STATUS_OK;
+ }
+
+
+ fsp = talloc_zero(vfs, struct files_struct);
+ if (fsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ fsp->fh = fd_handle_create(fsp);
+ if (fsp->fh == NULL) {
+ TALLOC_FREE(fsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+ fsp->conn = vfs->conn;
+
+ smb_fname = synthetic_smb_fname_split(NULL,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ TALLOC_FREE(fsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fsp->fsp_name = smb_fname;
+
+ status = vfs_at_fspcwd(fsp, vfs->conn, &fspcwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ how.flags = O_RDWR;
+ fd = SMB_VFS_OPENAT(vfs->conn,
+ fspcwd,
+ smb_fname,
+ fsp,
+ &how);
+ if (fd == -1 && errno == EISDIR) {
+#ifdef O_DIRECTORY
+ how.flags = O_RDONLY|O_DIRECTORY;
+#else
+ /* POSIX allows us to open a directory with O_RDONLY. */
+ how.flags = O_RDONLY;
+#endif
+ fd = SMB_VFS_OPENAT(vfs->conn,
+ fspcwd,
+ smb_fname,
+ fsp,
+ &how);
+ }
+ if (fd == -1) {
+ printf("open: error=%d (%s)\n", errno, strerror(errno));
+ TALLOC_FREE(fsp);
+ TALLOC_FREE(smb_fname);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ fsp_set_fd(fsp, fd);
+
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* If we have an fd, this stat should succeed. */
+ DEBUG(0,("Error doing fstat on open file %s "
+ "(%s)\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status) ));
+ goto out;
+ }
+
+ fsp->file_id = vfs_file_id_from_sbuf(vfs->conn, &smb_fname->st);
+ fsp->vuid = UID_FIELD_INVALID;
+ fsp->file_pid = 0;
+ fsp->fsp_flags.can_lock = true;
+ fsp->fsp_flags.can_read = true;
+ fsp->fsp_flags.can_write = true;
+ fsp->print_file = NULL;
+ fsp->fsp_flags.modified = false;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->fsp_flags.is_directory = S_ISDIR(smb_fname->st.st_ex_mode);
+
+ sd = sddl_decode(talloc_tos(), argv[2], get_global_sam_sid());
+ if (!sd) {
+ printf("sddl_decode failed to parse %s as SDDL\n", argv[2]);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ status = SMB_VFS_FSET_NT_ACL(
+ metadata_fsp(fsp),
+ SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL,
+ sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("fset_nt_acl returned (%s)\n", nt_errstr(status));
+ goto out;
+ }
+out:
+ TALLOC_FREE(sd);
+
+ status = fd_close(fsp);
+ if (!NT_STATUS_IS_OK(status))
+ printf("close: error= (%s)\n", nt_errstr(status));
+
+ TALLOC_FREE(fsp);
+
+ return status;
+}
+
+
+
+static NTSTATUS cmd_sys_acl_get_fd(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ int fd;
+ SMB_ACL_T acl;
+ char *acl_text;
+
+ if (argc != 2) {
+ printf("Usage: sys_acl_get_fd <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd >= 1024) {
+ printf("sys_acl_get_fd: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("sys_acl_get_fd: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ acl = SMB_VFS_SYS_ACL_GET_FD(vfs->files[fd],
+ SMB_ACL_TYPE_ACCESS,
+ talloc_tos());
+ if (!acl) {
+ printf("sys_acl_get_fd failed (%s)\n", strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ acl_text = sys_acl_to_text(acl, NULL);
+ printf("%s", acl_text);
+ TALLOC_FREE(acl);
+ SAFE_FREE(acl_text);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_sys_acl_get_file(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ SMB_ACL_T acl;
+ char *acl_text;
+ int type;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 3) {
+ printf("Usage: sys_acl_get_file <path> <type>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(talloc_tos(),
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ type = atoi(argv[2]);
+
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_fname);
+ return status;
+ }
+
+ acl = SMB_VFS_SYS_ACL_GET_FD(pathref_fname->fsp,
+ type, talloc_tos());
+ if (!acl) {
+ printf("sys_acl_get_fd failed (%s)\n", strerror(errno));
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ acl_text = sys_acl_to_text(acl, NULL);
+ printf("%s", acl_text);
+ TALLOC_FREE(acl);
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ SAFE_FREE(acl_text);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_sys_acl_blob_get_file(struct vfs_state *vfs,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ char *description;
+ DATA_BLOB blob;
+ int ret;
+ size_t i;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 2) {
+ printf("Usage: sys_acl_blob_get_file <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_fname);
+ return status;
+ }
+
+ ret = SMB_VFS_SYS_ACL_BLOB_GET_FD(pathref_fname->fsp,
+ talloc_tos(),
+ &description,
+ &blob);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ printf("sys_acl_blob_get_file failed (%s)\n", strerror(errno));
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return status;
+ }
+ printf("Description: %s\n", description);
+ for (i = 0; i < blob.length; i++) {
+ printf("%.2x ", blob.data[i]);
+ }
+ printf("\n");
+
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_sys_acl_blob_get_fd(struct vfs_state *vfs,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ int fd;
+ char *description;
+ DATA_BLOB blob;
+ int ret;
+ size_t i;
+
+ if (argc != 2) {
+ printf("Usage: sys_acl_blob_get_fd <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd >= 1024) {
+ printf("sys_acl_blob_get_fd: error=%d "
+ "(file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("sys_acl_blob_get_fd: error=%d "
+ "(invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_SYS_ACL_BLOB_GET_FD(vfs->files[fd], talloc_tos(),
+ &description, &blob);
+ if (ret != 0) {
+ printf("sys_acl_blob_get_fd failed (%s)\n", strerror(errno));
+ return map_nt_error_from_unix(errno);
+ }
+ printf("Description: %s\n", description);
+ for (i = 0; i < blob.length; i++) {
+ printf("%.2x ", blob.data[i]);
+ }
+ printf("\n");
+
+ return NT_STATUS_OK;
+}
+
+
+
+static NTSTATUS cmd_sys_acl_delete_def_file(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ int ret;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *pathref_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 2) {
+ printf("Usage: sys_acl_delete_def_file <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ smb_fname = synthetic_smb_fname_split(mem_ctx,
+ argv[1],
+ lp_posix_pathnames());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = synthetic_pathref(mem_ctx,
+ vfs->conn->cwd_fsp,
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_fname);
+ return status;
+ }
+ if (!pathref_fname->fsp->fsp_flags.is_directory) {
+ printf("sys_acl_delete_def_file - %s is not a directory\n",
+ smb_fname->base_name);
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = SMB_VFS_SYS_ACL_DELETE_DEF_FD(pathref_fname->fsp);
+ if (ret == -1) {
+ int err = errno;
+ printf("sys_acl_delete_def_file failed (%s)\n", strerror(err));
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return map_nt_error_from_unix(err);
+ }
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(pathref_fname);
+ return NT_STATUS_OK;
+}
+
+/* Afaik translate name was first introduced with vfs_catia, to be able
+ to translate unix file/dir-names, containing invalid windows characters,
+ to valid windows names.
+ The used translation direction is always unix --> windows
+*/
+static NTSTATUS cmd_translate_name(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ const char *dname = NULL;
+ char *dname_talloced = NULL;
+ bool found = false;
+ char *translated = NULL;
+ struct smb_filename *smb_fname = NULL;
+ NTSTATUS status;
+
+ if (argc != 2) {
+ DEBUG(0, ("Usage: translate_name unix_filename\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ smb_fname = synthetic_smb_fname(talloc_tos(),
+ ".",
+ NULL,
+ NULL,
+ 0,
+ ssf_flags());
+ if (smb_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = OpenDir(vfs->conn,
+ vfs->conn,
+ smb_fname,
+ NULL,
+ 0,
+ &vfs->currentdir);
+ if (!NT_STATUS_IS_OK(status)) {
+ int err = map_errno_from_nt_status(status);
+ DEBUG(0, ("cmd_translate_name: opendir error=%d (%s)\n",
+ err, strerror(err)));
+ TALLOC_FREE(smb_fname);
+ errno = err;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ while (true) {
+ /* ReadDirName() returns Windows "encoding" */
+ dname = ReadDirName(vfs->currentdir, &dname_talloced);
+ if (dname == NULL) {
+ break;
+ }
+
+ /* Convert Windows "encoding" from ReadDirName() to UNIX */
+ status = SMB_VFS_TRANSLATE_NAME(vfs->conn,
+ dname,
+ vfs_translate_to_unix,
+ talloc_tos(),
+ &translated);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("file '%s' cannot be translated\n", argv[1]);
+ goto cleanup;
+ }
+
+ /*
+ * argv[1] uses UNIX "encoding", so compare with translation
+ * result.
+ */
+ if (strcmp(translated, argv[1]) == 0) {
+ found = true;
+ break;
+ }
+ TALLOC_FREE(dname_talloced);
+ TALLOC_FREE(translated);
+ };
+
+ if (!found) {
+ DEBUG(0, ("cmd_translate_name: file '%s' not found.\n",
+ argv[1]));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto cleanup;
+ }
+
+ /* translation success. But that could also mean
+ that translating "aaa" to "aaa" was successful :-(
+ */
+ DBG_ERR("file '%s' --> '%s'\n", argv[1], dname);
+ status = NT_STATUS_OK;
+
+cleanup:
+ TALLOC_FREE(dname_talloced);
+ TALLOC_FREE(translated);
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(vfs->currentdir);
+ return status;
+}
+
+/*
+ * This is a quick hack to demonstrate a crash in the full_audit
+ * module when passing fsp->smb_fname into SMB_VFS_CREATE_FILE leading
+ * to an error.
+ *
+ * Feel free to expand with more options as needed
+ */
+static NTSTATUS cmd_create_file(
+ struct vfs_state *vfs,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct smb_filename *fname = NULL;
+ struct files_struct *fsp = NULL;
+ int info, ret;
+ NTSTATUS status;
+
+ if (argc != 2) {
+ DBG_ERR("Usage: create_file filename\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fname = synthetic_smb_fname(
+ talloc_tos(), argv[1], NULL, NULL, 0, 0);
+ if (fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = vfs_stat(vfs->conn, fname);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("vfs_stat() failed: %s\n", strerror(errno));
+ TALLOC_FREE(fname);
+ return status;
+ }
+
+ status = openat_pathref_fsp(vfs->conn->cwd_fsp, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not open %s: %s\n",
+ fname->base_name,
+ nt_errstr(status));
+ TALLOC_FREE(fname);
+ return status;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ vfs->conn,
+ NULL,
+ NULL,
+
+ /*
+ * Using fname->fsp->fsp_name seems to be legal,
+ * there's code to handle this in
+ * create_file_unixpath(). And it is actually very
+ * worthwhile re-using the fsp_name, we can save quite
+ * a few copies of smb_filename with that.
+ */
+ fname->fsp->fsp_name,
+ SEC_FILE_ALL,
+ FILE_SHARE_NONE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ &fsp,
+ &info,
+ NULL,
+ NULL
+ );
+ DBG_DEBUG("create_file returned %s\n", nt_errstr(status));
+
+ TALLOC_FREE(fname);
+
+ return NT_STATUS_OK;
+}
+
+struct cmd_set vfs_commands[] = {
+
+ { .name = "VFS Commands" },
+
+ { "load", cmd_load_module, "Load a module", "load <module.so>" },
+ { "populate", cmd_populate, "Populate a data buffer", "populate <char> <size>" },
+ { "showdata", cmd_show_data, "Show data currently in data buffer", "show_data [<offset> <len>]"},
+ { "connect", cmd_connect, "VFS connect()", "connect" },
+ { "disconnect", cmd_disconnect, "VFS disconnect()", "disconnect" },
+ { "disk_free", cmd_disk_free, "VFS disk_free()", "disk_free <path>" },
+ { "opendir", cmd_opendir, "VFS opendir()", "opendir <fname>" },
+ { "readdir", cmd_readdir, "VFS readdir()", "readdir" },
+ { "mkdir", cmd_mkdir, "VFS mkdir()", "mkdir <path>" },
+ { "rmdir", cmd_pathfunc, "VFS rmdir()", "rmdir <path>" },
+ { "closedir", cmd_closedir, "VFS closedir()", "closedir" },
+ { "open", cmd_open, "VFS open()", "open <fname> <flags> <mode>" },
+ { "close", cmd_close, "VFS close()", "close <fd>" },
+ { "read", cmd_read, "VFS read()", "read <fd> <size>" },
+ { "write", cmd_write, "VFS write()", "write <fd> <size>" },
+ { "lseek", cmd_lseek, "VFS lseek()", "lseek <fd> <offset> <whence>" },
+ { "rename", cmd_rename, "VFS rename()", "rename <old> <new>" },
+ { "fsync", cmd_fsync, "VFS fsync()", "fsync <fd>" },
+ { "stat", cmd_stat, "VFS stat()", "stat <fname>" },
+ { "fstat", cmd_fstat, "VFS fstat()", "fstat <fd>" },
+ { "lstat", cmd_lstat, "VFS lstat()", "lstat <fname>" },
+ { "unlink", cmd_pathfunc, "VFS unlink()", "unlink <fname>" },
+ { "chmod", cmd_chmod, "VFS chmod()", "chmod <path> <mode>" },
+ { "fchmod", cmd_fchmod, "VFS fchmod()", "fchmod <fd> <mode>" },
+ { "fchown", cmd_fchown, "VFS fchown()", "fchown <fd> <uid> <gid>" },
+ { "chdir", cmd_pathfunc, "VFS chdir()", "chdir <path>" },
+ { "getwd", cmd_getwd, "VFS getwd()", "getwd" },
+ { "utime", cmd_utime, "VFS utime()", "utime <path> <access> <modify>" },
+ { "ftruncate", cmd_ftruncate, "VFS ftruncate()", "ftruncate <fd> <length>" },
+ { "lock", cmd_lock, "VFS lock()", "lock <f> <op> <offset> <count> <type>" },
+ { "symlink", cmd_symlink, "VFS symlink()", "symlink <old> <new>" },
+ { "readlink", cmd_readlink, "VFS readlink()", "readlink <path>" },
+ { "link", cmd_link, "VFS link()", "link <oldpath> <newpath>" },
+ { "mknod", cmd_mknod, "VFS mknod()", "mknod <path> <mode> <dev>" },
+ { "realpath", cmd_realpath, "VFS realpath()", "realpath <path>" },
+ { "getxattr", cmd_getxattr, "VFS getxattr()",
+ "getxattr <path> <name>" },
+ { "listxattr", cmd_listxattr, "VFS listxattr()",
+ "listxattr <path>" },
+ { "fsetxattr", cmd_fsetxattr, "VFS fsetxattr()",
+ "fsetxattr <path> <name> <value> [<flags>]" },
+ { "removexattr", cmd_removexattr, "VFS removexattr()",
+ "removexattr <path> <name>\n" },
+ { "fget_nt_acl", cmd_fget_nt_acl, "VFS fget_nt_acl()",
+ "fget_nt_acl <fd>\n" },
+ { "get_nt_acl", cmd_get_nt_acl, "VFS get_nt_acl()",
+ "get_nt_acl <path>\n" },
+ { "fset_nt_acl", cmd_fset_nt_acl, "VFS fset_nt_acl()",
+ "fset_nt_acl <fd>\n" },
+ { "set_nt_acl", cmd_set_nt_acl, "VFS open() and fset_nt_acl()",
+ "set_nt_acl <file>\n" },
+ { "sys_acl_get_file", cmd_sys_acl_get_file, "VFS sys_acl_get_file()", "sys_acl_get_file <path>" },
+ { "sys_acl_get_fd", cmd_sys_acl_get_fd, "VFS sys_acl_get_fd()", "sys_acl_get_fd <fd>" },
+ { "sys_acl_blob_get_file", cmd_sys_acl_blob_get_file,
+ "VFS sys_acl_blob_get_file()", "sys_acl_blob_get_file <path>" },
+ { "sys_acl_blob_get_fd", cmd_sys_acl_blob_get_fd,
+ "VFS sys_acl_blob_get_fd()", "sys_acl_blob_get_fd <path>" },
+ { "sys_acl_delete_def_file", cmd_sys_acl_delete_def_file, "VFS sys_acl_delete_def_file()", "sys_acl_delete_def_file <path>" },
+
+
+#if defined(WITH_SMB1SERVER)
+ { "test_chain", cmd_test_chain, "test chain code",
+ "test_chain" },
+#endif
+ { "translate_name", cmd_translate_name, "VFS translate_name()", "translate_name unix_filename" },
+ { "create_file",
+ cmd_create_file,
+ "VFS create_file()",
+ "create_file <filename>"
+ },
+ {0}
+};
diff --git a/source3/torture/denytest.c b/source3/torture/denytest.c
new file mode 100644
index 0000000..34497c3
--- /dev/null
+++ b/source3/torture/denytest.c
@@ -0,0 +1,1600 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - scanning functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+
+extern bool torture_showall;
+
+enum deny_result {A_0=0, A_X=1, A_R=2, A_W=3, A_RW=5};
+
+
+static const char *denystr(int denymode)
+{
+ struct {
+ int v;
+ const char *name;
+ } deny_modes[] = {
+ {DENY_DOS, "DENY_DOS"},
+ {DENY_ALL, "DENY_ALL"},
+ {DENY_WRITE, "DENY_WRITE"},
+ {DENY_READ, "DENY_READ"},
+ {DENY_NONE, "DENY_NONE"},
+ {DENY_FCB, "DENY_FCB"},
+ {-1, NULL}};
+ int i;
+ for (i=0;deny_modes[i].name;i++) {
+ if (deny_modes[i].v == denymode) return deny_modes[i].name;
+ }
+ return "DENY_XXX";
+}
+
+static const char *openstr(int mode)
+{
+ struct {
+ int v;
+ const char *name;
+ } open_modes[] = {
+ {O_RDWR, "O_RDWR"},
+ {O_RDONLY, "O_RDONLY"},
+ {O_WRONLY, "O_WRONLY"},
+ {-1, NULL}};
+ int i;
+ for (i=0;open_modes[i].name;i++) {
+ if (open_modes[i].v == mode) return open_modes[i].name;
+ }
+ return "O_XXX";
+}
+
+static const char *resultstr(enum deny_result res)
+{
+ struct {
+ enum deny_result res;
+ const char *name;
+ } results[] = {
+ {A_X, "X"},
+ {A_0, "-"},
+ {A_R, "R"},
+ {A_W, "W"},
+ {A_RW,"RW"}};
+ size_t i;
+ for (i=0;i<ARRAY_SIZE(results);i++) {
+ if (results[i].res == res) return results[i].name;
+ }
+ return "*";
+}
+
+static struct {
+ int isexe;
+ int mode1, deny1;
+ int mode2, deny2;
+ enum deny_result result;
+} denytable2[] = {
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0}
+};
+
+
+static struct {
+ int isexe;
+ int mode1, deny1;
+ int mode2, deny2;
+ enum deny_result result;
+} denytable1[] = {
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW}
+};
+
+
+static void progress_bar(unsigned i, unsigned total)
+{
+ if (i % 10 != 0) return;
+ printf("%5d/%5d\r", i, total);
+ fflush(stdout);
+}
+
+/*
+ this produces a matrix of deny mode behaviour for 1 connection
+ */
+bool torture_denytest1(int dummy)
+{
+ struct cli_state *cli1;
+ uint16_t fnum1, fnum2;
+ bool correct = True;
+ NTSTATUS ret1, ret2, status;
+ const char *fnames[2] = {"\\denytest1.dat", "\\denytest1.exe"};
+ size_t i, nread;
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ printf("starting denytest1\n");
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i], FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_openx(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE, &fnum1);
+ cli_writeall(cli1, fnum1, 0, (const uint8_t *)fnames[i], 0,
+ strlen(fnames[i]), NULL);
+ cli_close(cli1, fnum1);
+ }
+
+ printf("testing %ld entries\n", (unsigned long)ARRAY_SIZE(denytable1));
+
+ for (i=0; i<ARRAY_SIZE(denytable1); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable1[i].isexe];
+
+ progress_bar(i, ARRAY_SIZE(denytable1));
+
+ ret1 = cli_openx(cli1, fname,
+ denytable1[i].mode1,
+ denytable1[i].deny1, &fnum1);
+ ret2 = cli_openx(cli1, fname,
+ denytable1[i].mode2,
+ denytable1[i].deny2, &fnum2);
+
+ if (!NT_STATUS_IS_OK(ret1)) {
+ res = A_X;
+ } else if (!NT_STATUS_IS_OK(ret2)) {
+ res = A_0;
+ } else {
+ char x = 1;
+ res = A_0;
+
+ status = cli_read(cli1, fnum2, (char *)&x, 0, 1,
+ &nread);
+ if (NT_STATUS_IS_OK(status) && nread == 1) {
+ res += A_R;
+ }
+ if (NT_STATUS_IS_OK(cli_writeall(cli1, fnum2, 0,
+ (uint8_t *)&x, 0, 1,
+ NULL))) {
+ res += A_W;
+ }
+ }
+
+ if (res != denytable1[i].result) {
+ correct = False;
+ }
+
+ if (torture_showall || res != denytable1[i].result) {
+ printf("%s %8s %10s %8s %10s %s (correct=%s)\n",
+ fname,
+ denystr(denytable1[i].deny1),
+ openstr(denytable1[i].mode1),
+ denystr(denytable1[i].deny2),
+ openstr(denytable1[i].mode2),
+ resultstr(res),
+ resultstr(denytable1[i].result));
+ }
+
+ if (NT_STATUS_IS_OK(ret1)) {
+ cli_close(cli1, fnum1);
+ }
+ if (NT_STATUS_IS_OK(ret2)) {
+ cli_close(cli1, fnum2);
+ }
+ }
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i], FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ printf("finished denytest1\n");
+ return correct;
+}
+
+
+/*
+ this produces a matrix of deny mode behaviour with 2 connections
+ */
+bool torture_denytest2(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ uint16_t fnum1, fnum2;
+ int i;
+ bool correct = True;
+ NTSTATUS ret1, ret2, status;
+ const char *fnames[2] = {"\\denytest2.dat", "\\denytest2.exe"};
+ size_t nread;
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+
+ printf("starting denytest2\n");
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i], FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_openx(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE, &fnum1);
+ cli_writeall(cli1, fnum1, 0, (const uint8_t *)fnames[i], 0,
+ strlen(fnames[i]), NULL);
+ cli_close(cli1, fnum1);
+ }
+
+ for (i=0; i<ARRAY_SIZE(denytable2); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable2[i].isexe];
+
+ progress_bar(i, ARRAY_SIZE(denytable2));
+
+ ret1 = cli_openx(cli1, fname,
+ denytable2[i].mode1,
+ denytable2[i].deny1, &fnum1);
+ ret2 = cli_openx(cli2, fname,
+ denytable2[i].mode2,
+ denytable2[i].deny2, &fnum2);
+
+ if (!NT_STATUS_IS_OK(ret1)) {
+ res = A_X;
+ } else if (!NT_STATUS_IS_OK(ret2)) {
+ res = A_0;
+ } else {
+ char x = 1;
+ res = A_0;
+
+ status = cli_read(cli2, fnum2, (char *)&x, 0, 1,
+ &nread);
+ if (NT_STATUS_IS_OK(status) && nread == 1) {
+ res += A_R;
+ }
+ if (NT_STATUS_IS_OK(cli_writeall(cli2, fnum2, 0,
+ (uint8_t *)&x, 0, 1,
+ NULL))) {
+ res += A_W;
+ }
+ }
+
+ if (res != denytable2[i].result) {
+ correct = False;
+ }
+
+ if (torture_showall || res != denytable2[i].result) {
+ printf("%s %8s %10s %8s %10s %s (correct=%s)\n",
+ fname,
+ denystr(denytable2[i].deny1),
+ openstr(denytable2[i].mode1),
+ denystr(denytable2[i].deny2),
+ openstr(denytable2[i].mode2),
+ resultstr(res),
+ resultstr(denytable2[i].result));
+ }
+
+ if (NT_STATUS_IS_OK(ret1)) {
+ cli_close(cli1, fnum1);
+ }
+ if (NT_STATUS_IS_OK(ret2)) {
+ cli_close(cli2, fnum2);
+ }
+ }
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i], FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished denytest2\n");
+ return correct;
+}
+
diff --git a/source3/torture/locktest2.c b/source3/torture/locktest2.c
new file mode 100644
index 0000000..851d77b
--- /dev/null
+++ b/source3/torture/locktest2.c
@@ -0,0 +1,610 @@
+/*
+ Unix SMB/CIFS implementation.
+ byte range lock tester - with local filesystem support
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "system/filesys.h"
+#include "locking/share_mode_lock.h"
+#include "locking/proto.h"
+#include "lib/util/string_wrappers.h"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int numops = 1000;
+static bool showall;
+static bool analyze;
+static bool hide_unlock_fails;
+static bool use_oplocks;
+
+extern char *optarg;
+extern int optind;
+
+#define FILENAME "\\locktest.dat"
+#define LOCKRANGE 100
+#define LOCKBASE 0
+
+/*
+#define LOCKBASE (0x40000000 - 50)
+*/
+
+#define READ_PCT 50
+#define LOCK_PCT 25
+#define UNLOCK_PCT 65
+#define RANGE_MULTIPLE 1
+
+#define NSERVERS 2
+#define NCONNECTIONS 2
+#define NUMFSTYPES 2
+#define NFILES 2
+#define LOCK_TIMEOUT 0
+
+#define FSTYPE_SMB 0
+#define FSTYPE_NFS 1
+
+struct record {
+ char r1, r2;
+ char conn, f, fstype;
+ unsigned start, len;
+ char needed;
+};
+
+static struct record *recorded;
+
+static int try_open(struct cli_state *c, char *nfs, int fstype, const char *fname, int flags)
+{
+ char *path;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ {
+ uint16_t fd;
+ if (!NT_STATUS_IS_OK(cli_openx(c, fname, flags, DENY_NONE, &fd))) {
+ return -1;
+ }
+ return fd;
+ }
+
+ case FSTYPE_NFS:
+ if (asprintf(&path, "%s%s", nfs, fname) > 0) {
+ int ret;
+ string_replace(path,'\\', '/');
+ ret = open(path, flags, 0666);
+ SAFE_FREE(path);
+ return ret;
+ }
+ break;
+ }
+
+ return -1;
+}
+
+static bool try_close(struct cli_state *c, int fstype, int fd)
+{
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return NT_STATUS_IS_OK(cli_close(c, fd));
+
+ case FSTYPE_NFS:
+ return close(fd) == 0;
+ }
+
+ return False;
+}
+
+static bool try_lock(struct cli_state *c, int fstype,
+ int fd, unsigned start, unsigned len,
+ enum brl_type op)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return NT_STATUS_IS_OK(cli_lock32(c, fd, start, len,
+ LOCK_TIMEOUT, op));
+
+ case FSTYPE_NFS:
+ lock.l_type = (op==READ_LOCK) ? F_RDLCK:F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = getpid();
+ return fcntl(fd,F_SETLK,&lock) == 0;
+ }
+
+ return False;
+}
+
+static bool try_unlock(struct cli_state *c, int fstype,
+ int fd, unsigned start, unsigned len)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return NT_STATUS_IS_OK(cli_unlock(c, fd, start, len));
+
+ case FSTYPE_NFS:
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = getpid();
+ return fcntl(fd,F_SETLK,&lock) == 0;
+ }
+
+ return False;
+}
+
+static void print_brl(struct file_id id, struct server_id pid,
+ enum brl_type lock_type,
+ enum brl_flavour lock_flav,
+ br_off start, br_off size,
+ void *private_data)
+{
+ struct file_id_buf idbuf;
+
+ printf("%6d %s %s %.0f:%.0f(%.0f)\n",
+ (int)procid_to_pid(&pid),
+ file_id_str_buf(id, &idbuf),
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)start+size-1,(double)size);
+
+}
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share)
+{
+ struct cli_state *c;
+ char *server_n;
+ fstring server;
+ fstring myname;
+ static int count;
+ NTSTATUS nt_status;
+ bool use_kerberos = false;
+ bool fallback_after_kerberos = false;
+ bool use_ccache = false;
+ bool pw_nt_hash = false;
+ struct cli_credentials *creds = NULL;
+
+ fstrcpy(server,share+2);
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ if (!got_pass) {
+ char pwd[256] = {0};
+ int rc;
+
+ rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
+ if (rc == 0) {
+ fstrcpy(password, pwd);
+ }
+ }
+
+ creds = cli_session_creds_init(NULL,
+ username,
+ lp_workgroup(),
+ NULL, /* realm (use default) */
+ password,
+ use_kerberos,
+ fallback_after_kerberos,
+ use_ccache,
+ pw_nt_hash);
+ if (creds == NULL) {
+ DEBUG(0, ("cli_session_creds_init failed\n"));
+ return NULL;
+ }
+
+ slprintf(myname,sizeof(myname), "lock-%lu-%u", (unsigned long)getpid(), count++);
+
+ nt_status = cli_full_connection_creds(&c,
+ myname,
+ server_n,
+ NULL,
+ 0,
+ share,
+ "?????",
+ creds,
+ 0);
+ TALLOC_FREE(creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("cli_full_connection failed with error %s\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ c->use_oplocks = use_oplocks;
+
+ return c;
+}
+
+
+static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ char *share1, char *share2)
+{
+ int server, conn, f, fstype;
+ char *share[2];
+ share[0] = share1;
+ share[1] = share2;
+
+ fstype = FSTYPE_SMB;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++) {
+ if (cli[server][conn]) {
+ for (f=0;f<NFILES;f++) {
+ cli_close(cli[server][conn], fnum[server][fstype][conn][f]);
+ }
+ cli_ulogoff(cli[server][conn]);
+ cli_shutdown(cli[server][conn]);
+ }
+ cli[server][conn] = connect_one(share[server]);
+ if (!cli[server][conn]) {
+ DEBUG(0,("Failed to connect to %s\n", share[server]));
+ exit(1);
+ }
+ }
+}
+
+
+
+static bool test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ struct record *rec)
+{
+ unsigned conn = rec->conn;
+ unsigned f = rec->f;
+ unsigned fstype = rec->fstype;
+ unsigned start = rec->start;
+ unsigned len = rec->len;
+ unsigned r1 = rec->r1;
+ unsigned r2 = rec->r2;
+ enum brl_type op;
+ int server;
+ bool ret[NSERVERS];
+
+ if (r1 < READ_PCT) {
+ op = READ_LOCK;
+ } else {
+ op = WRITE_LOCK;
+ }
+
+ if (fstype >= NUMFSTYPES) {
+ return false;
+ }
+
+ if (r2 < LOCK_PCT) {
+ /* set a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = try_lock(cli[server][conn], fstype,
+ fnum[server][fstype][conn][f],
+ start, len, op);
+ }
+ if (showall || ret[0] != ret[1]) {
+ printf("lock conn=%u fstype=%u f=%u range=%u:%u(%u) op=%s -> %u:%u\n",
+ conn, fstype, f,
+ start, start+len-1, len,
+ op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
+ ret[0], ret[1]);
+ }
+ if (showall) brl_forall(print_brl, NULL);
+ if (ret[0] != ret[1]) return False;
+ } else if (r2 < LOCK_PCT+UNLOCK_PCT) {
+ /* unset a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = try_unlock(cli[server][conn], fstype,
+ fnum[server][fstype][conn][f],
+ start, len);
+ }
+ if (showall || (!hide_unlock_fails && (ret[0] != ret[1]))) {
+ printf("unlock conn=%u fstype=%u f=%u range=%u:%u(%u) -> %u:%u\n",
+ conn, fstype, f,
+ start, start+len-1, len,
+ ret[0], ret[1]);
+ }
+ if (showall) brl_forall(print_brl, NULL);
+ if (!hide_unlock_fails && ret[0] != ret[1]) return False;
+ } else {
+ /* reopen the file */
+ for (server=0;server<NSERVERS;server++) {
+ try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+ fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+ O_RDWR|O_CREAT);
+ if (fnum[server][fstype][conn][f] == -1) {
+ printf("failed to reopen on share1\n");
+ return False;
+ }
+ }
+ if (showall) {
+ printf("reopen conn=%u fstype=%u f=%u\n",
+ conn, fstype, f);
+ brl_forall(print_brl, NULL);
+ }
+ }
+ return True;
+}
+
+static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+ int server, conn, f, fstype;
+
+ for (server=0;server<NSERVERS;server++)
+ for (fstype=0;fstype<NUMFSTYPES;fstype++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][fstype][conn][f] != -1) {
+ try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+ fnum[server][fstype][conn][f] = -1;
+ }
+ }
+ for (server=0;server<NSERVERS;server++) {
+ cli_unlink(cli[server][0], FILENAME, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ }
+}
+
+static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+ int server, fstype, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (fstype=0;fstype<NUMFSTYPES;fstype++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+ O_RDWR|O_CREAT);
+ if (fnum[server][fstype][conn][f] == -1) {
+ fprintf(stderr,"Failed to open fnum[%u][%u][%u][%u]\n",
+ server, fstype, conn, f);
+ exit(1);
+ }
+ }
+}
+
+
+static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ int n)
+{
+ int i;
+ printf("testing %u ...\n", n);
+ for (i=0; i<n; i++) {
+ if (i && i % 100 == 0) {
+ printf("%u\n", i);
+ }
+
+ if (recorded[i].needed &&
+ !test_one(cli, nfs, fnum, &recorded[i])) return i;
+ }
+ return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+ descriptors open on the file - 8 file descriptors in total
+
+ we then do random locking ops in tamdem on the 4 fnums from each
+ server and ensure that the results match
+ */
+static void test_locks(char *share1, char *share2, char *nfspath1, char *nfspath2)
+{
+ struct cli_state *cli[NSERVERS][NCONNECTIONS];
+ char *nfs[NSERVERS];
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES];
+ int n, i, n1;
+
+ nfs[0] = nfspath1;
+ nfs[1] = nfspath2;
+
+ ZERO_STRUCT(fnum);
+ ZERO_STRUCT(cli);
+
+ recorded = SMB_MALLOC_ARRAY(struct record, numops);
+
+ for (n=0; n<numops; n++) {
+ recorded[n].conn = random() % NCONNECTIONS;
+ recorded[n].fstype = random() % NUMFSTYPES;
+ recorded[n].f = random() % NFILES;
+ recorded[n].start = LOCKBASE + ((unsigned)random() % (LOCKRANGE-1));
+ recorded[n].len = 1 +
+ random() % (LOCKRANGE-(recorded[n].start-LOCKBASE));
+ recorded[n].start *= RANGE_MULTIPLE;
+ recorded[n].len *= RANGE_MULTIPLE;
+ recorded[n].r1 = random() % 100;
+ recorded[n].r2 = random() % 100;
+ recorded[n].needed = True;
+ }
+
+ reconnect(cli, nfs, fnum, share1, share2);
+ open_files(cli, nfs, fnum);
+ n = retest(cli, nfs, fnum, numops);
+
+ if (n == numops || !analyze) return;
+ n++;
+
+ while (1) {
+ n1 = n;
+
+ close_files(cli, nfs, fnum);
+ reconnect(cli, nfs, fnum, share1, share2);
+ open_files(cli, nfs, fnum);
+
+ for (i=0;i<n-1;i++) {
+ int m;
+ recorded[i].needed = False;
+
+ close_files(cli, nfs, fnum);
+ open_files(cli, nfs, fnum);
+
+ m = retest(cli, nfs, fnum, n);
+ if (m == n) {
+ recorded[i].needed = True;
+ } else {
+ if (i < m) {
+ memmove(&recorded[i], &recorded[i+1],
+ (m-i)*sizeof(recorded[0]));
+ }
+ n = m;
+ i--;
+ }
+ }
+
+ if (n1 == n) break;
+ }
+
+ close_files(cli, nfs, fnum);
+ reconnect(cli, nfs, fnum, share1, share2);
+ open_files(cli, nfs, fnum);
+ showall = True;
+ n1 = retest(cli, nfs, fnum, n);
+ if (n1 != n-1) {
+ printf("ERROR - inconsistent result (%u %u)\n", n1, n);
+ }
+ close_files(cli, nfs, fnum);
+
+ for (i=0;i<n;i++) {
+ printf("{%u, %u, %u, %u, %u, %u, %u, %u},\n",
+ recorded[i].r1,
+ recorded[i].r2,
+ recorded[i].conn,
+ recorded[i].fstype,
+ recorded[i].f,
+ recorded[i].start,
+ recorded[i].len,
+ recorded[i].needed);
+ }
+}
+
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ locktest //server1/share1 //server2/share2 /path1 /path2 [options..]\n\
+ options:\n\
+ -U user%%pass\n\
+ -s seed\n\
+ -o numops\n\
+ -u hide unlock fails\n\
+ -a (show all ops)\n\
+ -O use oplocks\n\
+");
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share1, *share2, *nfspath1, *nfspath2;
+ int opt;
+ char *p;
+ int seed;
+
+ setlinebuf(stdout);
+
+ if (argc < 5 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ setup_logging(argv[0], DEBUG_STDOUT);
+
+ share1 = argv[1];
+ share2 = argv[2];
+ nfspath1 = argv[3];
+ nfspath2 = argv[4];
+
+ all_string_sub(share1,"/","\\",0);
+ all_string_sub(share2,"/","\\",0);
+
+ argc -= 4;
+ argv += 4;
+
+ lp_load_global(get_dyn_CONFIGFILE());
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username,getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "U:s:ho:aAW:O")) != EOF) {
+ switch (opt) {
+ case 'U':
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ got_pass = 1;
+ }
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'u':
+ hide_unlock_fails = True;
+ break;
+ case 'o':
+ numops = atoi(optarg);
+ break;
+ case 'O':
+ use_oplocks = True;
+ break;
+ case 'a':
+ showall = True;
+ break;
+ case 'A':
+ analyze = True;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ DEBUG(0,("seed=%u\n", seed));
+ srandom(seed);
+
+ locking_init_readonly();
+ test_locks(share1, share2, nfspath1, nfspath2);
+
+ return(0);
+}
diff --git a/source3/torture/mangle_test.c b/source3/torture/mangle_test.c
new file mode 100644
index 0000000..92754b9
--- /dev/null
+++ b/source3/torture/mangle_test.c
@@ -0,0 +1,223 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - mangling test
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "util_tdb.h"
+#include "lib/util/string_wrappers.h"
+
+extern int torture_numops;
+
+static TDB_CONTEXT *tdb;
+
+#define NAME_LENGTH 20
+
+static unsigned total, collisions, failures;
+
+static bool test_one(struct cli_state *cli, const char *name)
+{
+ uint16_t fnum;
+ fstring shortname;
+ fstring name2;
+ NTSTATUS status;
+ TDB_DATA data;
+
+ total++;
+
+ status = cli_openx(cli, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", name, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close of %s failed (%s)\n", name, nt_errstr(status));
+ return False;
+ }
+
+ /* get the short name */
+ status = cli_qpathinfo_alt_name(cli, name, shortname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("query altname of %s failed (%s)\n", name, nt_errstr(status));
+ return False;
+ }
+
+ fstr_sprintf(name2, "\\mangle_test\\%s", shortname);
+ status = cli_unlink(cli, name2, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink of %s (%s) failed (%s)\n",
+ name2, name, nt_errstr(status));
+ return False;
+ }
+
+ /* recreate by short name */
+ status = cli_openx(cli, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open2 of %s failed (%s)\n", name2, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close of %s failed (%s)\n", name, nt_errstr(status));
+ return False;
+ }
+
+ /* and unlink by long name */
+ status = cli_unlink(cli, name, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink2 of %s (%s) failed (%s)\n",
+ name, name2, nt_errstr(status));
+ failures++;
+ cli_unlink(cli, name2, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ return True;
+ }
+
+ /* see if the short name is already in the tdb */
+ data = tdb_fetch_bystring(tdb, shortname);
+ if (data.dptr) {
+ /* maybe its a duplicate long name? */
+ if (!strequal(name, (const char *)data.dptr)) {
+ /* we have a collision */
+ collisions++;
+ printf("Collision between %s and %s -> %s "
+ " (coll/tot: %u/%u)\n",
+ name, data.dptr, shortname, collisions, total);
+ }
+ free(data.dptr);
+ } else {
+ TDB_DATA namedata;
+ /* store it for later */
+ namedata.dptr = discard_const_p(uint8_t, name);
+ namedata.dsize = strlen(name)+1;
+ tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE);
+ }
+
+ return True;
+}
+
+
+static void gen_name(char *name)
+{
+ const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~... ";
+ unsigned max_idx = strlen(chars);
+ unsigned len;
+ int i;
+ char *p;
+
+ fstrcpy(name, "\\mangle_test\\");
+ p = name + strlen(name);
+
+ len = 1 + random() % NAME_LENGTH;
+
+ for (i=0;i<len;i++) {
+ p[i] = chars[random() % max_idx];
+ }
+
+ p[i] = 0;
+
+ if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
+ p[0] = '_';
+ }
+
+ /* have a high probability of a common lead char */
+ if (random() % 2 == 0) {
+ p[0] = 'A';
+ }
+
+ /* and a medium probability of a common lead string */
+ if (random() % 10 == 0) {
+ if (strlen(p) <= 5) {
+ fstrcpy(p, "ABCDE");
+ } else {
+ /* try not to kill off the null termination */
+ memcpy(p, "ABCDE", 5);
+ }
+ }
+
+ /* and a high probability of a good extension length */
+ if (random() % 2 == 0) {
+ char *s = strrchr(p, '.');
+ if (s) {
+ s[4] = 0;
+ }
+ }
+
+ /* ..... and a 100% proability of a file not ending in "." */
+ if (p[strlen(p)-1] == '.')
+ p[strlen(p)-1] = '_';
+}
+
+
+bool torture_mangle(int dummy)
+{
+ static struct cli_state *cli;
+ int i;
+ bool ret = True;
+
+ printf("starting mangle test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ /* we will use an internal tdb to store the names we have used */
+ tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0);
+ if (!tdb) {
+ printf("ERROR: Failed to open tdb\n");
+ return False;
+ }
+
+ torture_deltree(cli, "\\mangle_test");
+
+ if (!NT_STATUS_IS_OK(cli_mkdir(cli, "\\mangle_test"))) {
+ printf("ERROR: Failed to make directory\n");
+ return False;
+ }
+
+ for (i=0;i<torture_numops;i++) {
+ fstring name;
+ ZERO_STRUCT(name);
+
+ gen_name(name);
+
+ if (!test_one(cli, name)) {
+ ret = False;
+ break;
+ }
+ if (total && total % 100 == 0) {
+ printf("collisions %u/%u - %.2f%% (%u failures)\r",
+ collisions, total, (100.0*collisions) / total, failures);
+ }
+ }
+
+ torture_deltree(cli, "\\mangle_test");
+
+ printf("\nTotal collisions %u/%u - %.2f%% (%u failures)\n",
+ collisions, total, (100.0*collisions) / total, failures);
+
+ torture_close_connection(cli);
+
+ printf("mangle test finished\n");
+ return (ret && (failures == 0));
+}
diff --git a/source3/torture/msg_sink.c b/source3/torture/msg_sink.c
new file mode 100644
index 0000000..3c3dda3
--- /dev/null
+++ b/source3/torture/msg_sink.c
@@ -0,0 +1,285 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Receive and count messages
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "includes.h"
+#include "lib/util/server_id.h"
+#include "messages.h"
+#include "lib/util/tevent_unix.h"
+#include <stdio.h>
+
+struct sink_state {
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ int msg_type;
+ unsigned *counter;
+};
+
+static void sink_done(struct tevent_req *subreq);
+
+static struct tevent_req *sink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ int msg_type, unsigned *counter)
+{
+ struct tevent_req *req, *subreq;
+ struct sink_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct sink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->msg_ctx = msg_ctx;
+ state->msg_type = msg_type;
+ state->counter = counter;
+
+ subreq = messaging_read_send(state, state->ev, state->msg_ctx,
+ state->msg_type);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, sink_done, req);
+ return req;
+}
+
+static void sink_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct sink_state *state = tevent_req_data(
+ req, struct sink_state);
+ int ret;
+
+ ret = messaging_read_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ *state->counter += 1;
+
+ subreq = messaging_read_send(state, state->ev, state->msg_ctx,
+ state->msg_type);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, sink_done, req);
+}
+
+static int sink_recv(struct tevent_req *req)
+{
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+ return 0;
+}
+
+struct prcount_state {
+ struct tevent_context *ev;
+ struct timeval interval;
+ unsigned *counter;
+};
+
+static void prcount_waited(struct tevent_req *subreq);
+
+static struct tevent_req *prcount_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct timeval interval,
+ unsigned *counter)
+{
+ struct tevent_req *req, *subreq;
+ struct prcount_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct prcount_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->interval = interval;
+ state->counter = counter;
+
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ timeval_current_ofs(state->interval.tv_sec,
+ state->interval.tv_usec));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, prcount_waited, req);
+ return req;
+}
+
+static void prcount_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct prcount_state *state = tevent_req_data(
+ req, struct prcount_state);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ printf("%u\n", *state->counter);
+
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ timeval_current_ofs(state->interval.tv_sec,
+ state->interval.tv_usec));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, prcount_waited, req);
+}
+
+static int prcount_recv(struct tevent_req *req)
+{
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+ return 0;
+}
+
+struct msgcount_state {
+ unsigned count;
+};
+
+static void msgcount_sunk(struct tevent_req *subreq);
+static void msgcount_printed(struct tevent_req *subreq);
+
+static struct tevent_req *msgcount_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ int msg_type, struct timeval interval)
+{
+ struct tevent_req *req, *subreq;
+ struct msgcount_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct msgcount_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = sink_send(state, ev, msg_ctx, msg_type, &state->count);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, msgcount_sunk, req);
+
+ subreq = prcount_send(state, ev, interval, &state->count);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, msgcount_printed, req);
+
+ return req;
+}
+
+static void msgcount_sunk(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+
+ ret = sink_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void msgcount_printed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+
+ ret = prcount_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static int msgcount_recv(struct tevent_req *req)
+{
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+ return 0;
+}
+
+int main(void)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ struct tevent_req *req;
+ int ret;
+ struct server_id id;
+ struct server_id_buf tmp;
+
+ lp_load_global(get_dyn_CONFIGFILE());
+
+ ev = tevent_context_init(frame);
+ if (ev == NULL) {
+ perror("tevent_context_init failed");
+ return -1;
+ }
+
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ perror("messaging_init failed");
+ return -1;
+ }
+
+ id = messaging_server_id(msg_ctx);
+
+ printf("server_id: %s\n", server_id_str_buf(id, &tmp));
+
+ req = msgcount_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY,
+ timeval_set(1, 0));
+ if (req == NULL) {
+ perror("msgcount_send failed");
+ return -1;
+ }
+
+ if (!tevent_req_poll(req, ev)) {
+ perror("tevent_req_poll failed");
+ return -1;
+ }
+
+ ret = msgcount_recv(req);
+ printf("msgcount_recv returned %d\n", ret);
+
+ return 0;
+}
diff --git a/source3/torture/msg_source.c b/source3/torture/msg_source.c
new file mode 100644
index 0000000..e718018
--- /dev/null
+++ b/source3/torture/msg_source.c
@@ -0,0 +1,159 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Send messages once a second
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "includes.h"
+#include "lib/util/server_id.h"
+#include "messages.h"
+#include "lib/util/tevent_unix.h"
+#include <stdio.h>
+
+struct source_state {
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ int msg_type;
+ struct timeval interval;
+ struct server_id dst;
+};
+
+static void source_waited(struct tevent_req *subreq);
+
+static struct tevent_req *source_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ int msg_type,
+ struct timeval interval,
+ struct server_id dst)
+{
+ struct tevent_req *req, *subreq;
+ struct source_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct source_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->msg_ctx = msg_ctx;
+ state->msg_type = msg_type;
+ state->interval = interval;
+ state->dst = dst;
+
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ timeval_current_ofs(state->interval.tv_sec,
+ state->interval.tv_usec));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, source_waited, req);
+ return req;
+}
+
+static void source_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct source_state *state = tevent_req_data(
+ req, struct source_state);
+ bool ok;
+ uint8_t buf[200] = { };
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ messaging_send_buf(state->msg_ctx, state->dst, state->msg_type,
+ buf, sizeof(buf));
+
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ timeval_current_ofs(state->interval.tv_sec,
+ state->interval.tv_usec));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, source_waited, req);
+}
+
+static int source_recv(struct tevent_req *req)
+{
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ struct tevent_req *req;
+ int ret;
+ struct server_id my_id, id;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <dst>\n", argv[0]);
+ return -1;
+ }
+
+ lp_load_global(get_dyn_CONFIGFILE());
+
+ ev = tevent_context_init(frame);
+ if (ev == NULL) {
+ perror("tevent_context_init failed");
+ return -1;
+ }
+
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ perror("messaging_init failed");
+ return -1;
+ }
+ my_id = messaging_server_id(msg_ctx);
+
+ id = server_id_from_string(my_id.vnn, argv[1]);
+ if (!procid_valid(&id)) {
+ fprintf(stderr, "pid %s invalid\n", argv[1]);
+ return -1;
+ }
+
+ req = source_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY,
+ timeval_set(0, 10000), id);
+ if (req == NULL) {
+ perror("source_send failed");
+ return -1;
+ }
+
+ if (!tevent_req_poll(req, ev)) {
+ perror("tevent_req_poll failed");
+ return -1;
+ }
+
+ ret = source_recv(req);
+
+ printf("source_recv returned %d\n", ret);
+
+ return 0;
+}
diff --git a/source3/torture/msgtest.c b/source3/torture/msgtest.c
new file mode 100644
index 0000000..1d2a8a9
--- /dev/null
+++ b/source3/torture/msgtest.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ test code for internal messaging
+ */
+
+#include "includes.h"
+#include "messages.h"
+
+static int pong_count;
+
+
+/****************************************************************************
+a useful function for testing the message system
+****************************************************************************/
+static void pong_message(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ pong_count++;
+}
+
+ int main(int argc, char *argv[])
+{
+ struct tevent_context *evt_ctx;
+ struct messaging_context *msg_ctx;
+ pid_t pid;
+ int i, n;
+ char buf[12];
+ int ret;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ smb_init_locale();
+
+ setup_logging(argv[0], DEBUG_STDOUT);
+
+ lp_load_global(get_dyn_CONFIGFILE());
+
+ if (!(evt_ctx = samba_tevent_context_init(NULL)) ||
+ !(msg_ctx = messaging_init(NULL, evt_ctx))) {
+ fprintf(stderr, "could not init messaging context\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ if (argc != 3) {
+ fprintf(stderr, "%s: Usage - %s pid count\n", argv[0],
+ argv[0]);
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ pid = atoi(argv[1]);
+ n = atoi(argv[2]);
+
+ messaging_register(msg_ctx, NULL, MSG_PONG, pong_message);
+
+ for (i=0;i<n;i++) {
+ messaging_send(msg_ctx, pid_to_procid(pid), MSG_PING,
+ &data_blob_null);
+ }
+
+ while (pong_count < i) {
+ ret = tevent_loop_once(evt_ctx);
+ if (ret != 0) {
+ break;
+ }
+ }
+
+ /* Ensure all messages get through to ourselves. */
+ pong_count = 0;
+
+ strlcpy(buf, "1234567890", sizeof(buf));
+
+ for (i=0;i<n;i++) {
+ messaging_send(msg_ctx, messaging_server_id(msg_ctx), MSG_PING,
+ &data_blob_null);
+ messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
+ MSG_PING,(uint8_t *)buf, 11);
+ }
+
+ /*
+ * We have to loop at least 2 times for
+ * each message as local ping messages are
+ * handled by an immediate callback, that
+ * has to be dispatched, which sends a pong
+ * message, which also has to be dispatched.
+ * Above we sent 2*n messages, which means
+ * we have to dispatch 4*n times.
+ */
+
+ while (pong_count < n*2) {
+ ret = tevent_loop_once(evt_ctx);
+ if (ret != 0) {
+ break;
+ }
+ }
+
+ if (pong_count != 2*n) {
+ fprintf(stderr, "Message count failed (%d).\n", pong_count);
+ }
+
+ /* Speed testing */
+
+ pong_count = 0;
+
+ {
+ struct timeval tv = timeval_current();
+ size_t timelimit = n;
+ size_t ping_count = 0;
+
+ printf("Sending pings for %d seconds\n", (int)timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ if(NT_STATUS_IS_OK(messaging_send_buf(
+ msg_ctx, pid_to_procid(pid),
+ MSG_PING,
+ (uint8_t *)buf, 11)))
+ ping_count++;
+ if(NT_STATUS_IS_OK(messaging_send(
+ msg_ctx, pid_to_procid(pid),
+ MSG_PING, &data_blob_null)))
+ ping_count++;
+
+ while (ping_count > pong_count + 20) {
+ ret = tevent_loop_once(evt_ctx);
+ if (ret != 0) {
+ break;
+ }
+ }
+ }
+
+ printf("waiting for %d remaining replies (done %d)\n",
+ (int)(ping_count - pong_count), pong_count);
+ while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
+ ret = tevent_loop_once(evt_ctx);
+ if (ret != 0) {
+ break;
+ }
+ }
+
+ if (ping_count != pong_count) {
+ fprintf(stderr, "ping test failed! received %d, sent "
+ "%d\n", pong_count, (int)ping_count);
+ }
+
+ printf("ping rate of %.0f messages/sec\n",
+ (ping_count+pong_count)/timeval_elapsed(&tv));
+ }
+
+ TALLOC_FREE(frame);
+ return (0);
+}
+
diff --git a/source3/torture/nbench.c b/source3/torture/nbench.c
new file mode 100644
index 0000000..8646d7a
--- /dev/null
+++ b/source3/torture/nbench.c
@@ -0,0 +1,504 @@
+/*
+ Unix SMB/CIFS implementation.
+ In-memory cache
+ Copyright (C) Volker Lendecke 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "../lib/util/tevent_ntstatus.h"
+
+static long long int ival(const char *str)
+{
+ return strtoll(str, NULL, 0);
+}
+
+struct nbench_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *cliname;
+ FILE *loadfile;
+ struct ftable *ftable;
+ void (*bw_report)(size_t nread,
+ size_t nwritten,
+ void *private_data);
+ void *bw_report_private;
+};
+
+struct lock_info {
+ struct lock_info *next, *prev;
+ off_t offset;
+ int size;
+};
+
+struct createx_params {
+ char *fname;
+ unsigned int cr_options;
+ unsigned int cr_disposition;
+ int handle;
+};
+
+struct ftable {
+ struct ftable *next, *prev;
+ struct createx_params cp;
+ struct lock_info *locks;
+ uint16_t fnum; /* the fd that we got back from the server */
+};
+
+enum nbench_cmd {
+ NBENCH_CMD_NTCREATEX,
+ NBENCH_CMD_CLOSE,
+ NBENCH_CMD_RENAME,
+ NBENCH_CMD_UNLINK,
+ NBENCH_CMD_DELTREE,
+ NBENCH_CMD_RMDIR,
+ NBENCH_CMD_MKDIR,
+ NBENCH_CMD_QUERY_PATH_INFORMATION,
+ NBENCH_CMD_QUERY_FILE_INFORMATION,
+ NBENCH_CMD_QUERY_FS_INFORMATION,
+ NBENCH_CMD_SET_FILE_INFORMATION,
+ NBENCH_CMD_FIND_FIRST,
+ NBENCH_CMD_WRITEX,
+ NBENCH_CMD_WRITE,
+ NBENCH_CMD_LOCKX,
+ NBENCH_CMD_UNLOCKX,
+ NBENCH_CMD_READX,
+ NBENCH_CMD_FLUSH,
+ NBENCH_CMD_SLEEP,
+};
+
+struct nbench_cmd_struct {
+ char **params;
+ int num_params;
+ NTSTATUS status;
+ enum nbench_cmd cmd;
+};
+
+static struct nbench_cmd_struct *nbench_parse(TALLOC_CTX *mem_ctx,
+ const char *line)
+{
+ struct nbench_cmd_struct *result;
+ char *cmd;
+ char *status;
+
+ result = talloc(mem_ctx, struct nbench_cmd_struct);
+ if (result == NULL) {
+ return NULL;
+ }
+ result->params = str_list_make_shell(mem_ctx, line, " ");
+ if (result->params == NULL) {
+ goto fail;
+ }
+ result->num_params = talloc_array_length(result->params) - 1;
+ if (result->num_params < 2) {
+ goto fail;
+ }
+ status = result->params[result->num_params-1];
+ if (strncmp(status, "NT_STATUS_", 10) != 0 &&
+ strncmp(status, "0x", 2) != 0) {
+ goto fail;
+ }
+ /* accept numeric or string status codes */
+ if (strncmp(status, "0x", 2) == 0) {
+ result->status = NT_STATUS(strtoul(status, NULL, 16));
+ } else {
+ result->status = nt_status_string_to_code(status);
+ }
+
+ cmd = result->params[0];
+
+ if (!strcmp(cmd, "NTCreateX")) {
+ result->cmd = NBENCH_CMD_NTCREATEX;
+ } else if (!strcmp(cmd, "Close")) {
+ result->cmd = NBENCH_CMD_CLOSE;
+ } else if (!strcmp(cmd, "Rename")) {
+ result->cmd = NBENCH_CMD_RENAME;
+ } else if (!strcmp(cmd, "Unlink")) {
+ result->cmd = NBENCH_CMD_UNLINK;
+ } else if (!strcmp(cmd, "Deltree")) {
+ result->cmd = NBENCH_CMD_DELTREE;
+ } else if (!strcmp(cmd, "Rmdir")) {
+ result->cmd = NBENCH_CMD_RMDIR;
+ } else if (!strcmp(cmd, "Mkdir")) {
+ result->cmd = NBENCH_CMD_MKDIR;
+ } else if (!strcmp(cmd, "QUERY_PATH_INFORMATION")) {
+ result->cmd = NBENCH_CMD_QUERY_PATH_INFORMATION;
+ } else if (!strcmp(cmd, "QUERY_FILE_INFORMATION")) {
+ result->cmd = NBENCH_CMD_QUERY_FILE_INFORMATION;
+ } else if (!strcmp(cmd, "QUERY_FS_INFORMATION")) {
+ result->cmd = NBENCH_CMD_QUERY_FS_INFORMATION;
+ } else if (!strcmp(cmd, "SET_FILE_INFORMATION")) {
+ result->cmd = NBENCH_CMD_SET_FILE_INFORMATION;
+ } else if (!strcmp(cmd, "FIND_FIRST")) {
+ result->cmd = NBENCH_CMD_FIND_FIRST;
+ } else if (!strcmp(cmd, "WriteX")) {
+ result->cmd = NBENCH_CMD_WRITEX;
+ } else if (!strcmp(cmd, "Write")) {
+ result->cmd = NBENCH_CMD_WRITE;
+ } else if (!strcmp(cmd, "LockX")) {
+ result->cmd = NBENCH_CMD_LOCKX;
+ } else if (!strcmp(cmd, "UnlockX")) {
+ result->cmd = NBENCH_CMD_UNLOCKX;
+ } else if (!strcmp(cmd, "ReadX")) {
+ result->cmd = NBENCH_CMD_READX;
+ } else if (!strcmp(cmd, "Flush")) {
+ result->cmd = NBENCH_CMD_FLUSH;
+ } else if (!strcmp(cmd, "Sleep")) {
+ result->cmd = NBENCH_CMD_SLEEP;
+ } else {
+ goto fail;
+ }
+ return result;
+fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static struct ftable *ft_find(struct ftable *ftlist, int handle)
+{
+ while (ftlist != NULL) {
+ if (ftlist->cp.handle == handle) {
+ return ftlist;
+ }
+ ftlist = ftlist->next;
+ }
+ return NULL;
+}
+
+struct nbench_cmd_state {
+ struct tevent_context *ev;
+ struct nbench_state *state;
+ struct nbench_cmd_struct *cmd;
+ struct ftable *ft;
+ bool eof;
+};
+
+static void nbench_cmd_done(struct tevent_req *subreq);
+
+static struct tevent_req *nbench_cmd_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct nbench_state *nb_state)
+{
+ struct tevent_req *req, *subreq;
+ struct nbench_cmd_state *state;
+ char line[1024];
+ size_t len;
+
+ req = tevent_req_create(mem_ctx, &state, struct nbench_cmd_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->state = nb_state;
+
+ if (fgets(line, sizeof(line), nb_state->loadfile) == NULL) {
+ tevent_req_nterror(req, NT_STATUS_END_OF_FILE);
+ return tevent_req_post(req, ev);
+ }
+ len = strlen(line);
+ if (len == 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (line[len-1] == '\n') {
+ line[len-1] = '\0';
+ }
+
+ state->cmd = nbench_parse(state, line);
+ if (state->cmd == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ switch (state->cmd->cmd) {
+ case NBENCH_CMD_NTCREATEX: {
+ uint32_t desired_access;
+ uint32_t share_mode;
+ unsigned int flags = 0;
+
+ state->ft = talloc(state, struct ftable);
+ if (tevent_req_nomem(state->ft, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->ft->cp.fname = talloc_all_string_sub(
+ state->ft, state->cmd->params[1], "client1",
+ nb_state->cliname);
+ if (tevent_req_nomem(state->ft->cp.fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->ft->cp.cr_options = ival(state->cmd->params[2]);
+ state->ft->cp.cr_disposition = ival(state->cmd->params[3]);
+ state->ft->cp.handle = ival(state->cmd->params[4]);
+
+ if (state->ft->cp.cr_options & FILE_DIRECTORY_FILE) {
+ desired_access = SEC_FILE_READ_DATA;
+ } else {
+ desired_access =
+ SEC_FILE_READ_DATA |
+ SEC_FILE_WRITE_DATA |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_FILE_WRITE_ATTRIBUTE;
+ flags = EXTENDED_RESPONSE_REQUIRED
+ | REQUEST_OPLOCK | REQUEST_BATCH_OPLOCK;
+ }
+ share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ subreq = cli_ntcreate_send(
+ state, ev, nb_state->cli, state->ft->cp.fname, flags,
+ desired_access, 0, share_mode,
+ state->ft->cp.cr_disposition,
+ state->ft->cp.cr_options,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ break;
+ }
+ case NBENCH_CMD_CLOSE: {
+ state->ft = ft_find(state->state->ftable,
+ ival(state->cmd->params[1]));
+ if (state->ft == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ subreq = cli_close_send(state,
+ ev,
+ nb_state->cli,
+ state->ft->fnum,
+ 0);
+ break;
+ }
+ case NBENCH_CMD_MKDIR: {
+ char *fname;
+ fname = talloc_all_string_sub(
+ state, state->cmd->params[1], "client1",
+ nb_state->cliname);
+ if (tevent_req_nomem(state->ft->cp.fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ subreq = cli_mkdir_send(state, ev, nb_state->cli, fname);
+ break;
+ }
+ case NBENCH_CMD_QUERY_PATH_INFORMATION: {
+ char *fname;
+ fname = talloc_all_string_sub(
+ state, state->cmd->params[1], "client1",
+ nb_state->cliname);
+ if (tevent_req_nomem(state->ft->cp.fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ subreq = cli_qpathinfo_send(state, ev, nb_state->cli, fname,
+ ival(state->cmd->params[2]),
+ 0, CLI_BUFFER_SIZE);
+ break;
+ }
+ default:
+ tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+ return tevent_req_post(req, ev);
+ }
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nbench_cmd_done, req);
+ return req;
+}
+
+static bool status_wrong(struct tevent_req *req, NTSTATUS expected,
+ NTSTATUS status)
+{
+ if (NT_STATUS_EQUAL(expected, status)) {
+ return false;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ tevent_req_nterror(req, status);
+ return true;
+}
+
+static void nbench_cmd_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nbench_cmd_state *state = tevent_req_data(
+ req, struct nbench_cmd_state);
+ struct nbench_state *nbstate = state->state;
+ NTSTATUS status;
+
+ switch (state->cmd->cmd) {
+ case NBENCH_CMD_NTCREATEX: {
+ struct ftable *ft;
+ status = cli_ntcreate_recv(subreq, &state->ft->fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (status_wrong(req, state->cmd->status, status)) {
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return;
+ }
+ ft = talloc_move(nbstate, &state->ft);
+ DLIST_ADD(nbstate->ftable, ft);
+ break;
+ }
+ case NBENCH_CMD_CLOSE: {
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (status_wrong(req, state->cmd->status, status)) {
+ return;
+ }
+ DLIST_REMOVE(state->state->ftable, state->ft);
+ TALLOC_FREE(state->ft);
+ break;
+ }
+ case NBENCH_CMD_MKDIR: {
+ status = cli_mkdir_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (status_wrong(req, state->cmd->status, status)) {
+ return;
+ }
+ break;
+ }
+ case NBENCH_CMD_QUERY_PATH_INFORMATION: {
+ status = cli_qpathinfo_recv(subreq, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (status_wrong(req, state->cmd->status, status)) {
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS nbench_cmd_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void nbench_done(struct tevent_req *subreq);
+
+static struct tevent_req *nbench_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *cliname, FILE *loadfile,
+ void (*bw_report)(size_t nread, size_t nwritten, void *private_data),
+ void *bw_report_private)
+{
+ struct tevent_req *req, *subreq;
+ struct nbench_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct nbench_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->cliname = cliname;
+ state->loadfile = loadfile;
+ state->bw_report = bw_report;
+ state->bw_report_private = bw_report_private;
+
+ subreq = nbench_cmd_send(state, ev, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nbench_done, req);
+ return req;
+}
+
+static void nbench_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nbench_state *state = tevent_req_data(
+ req, struct nbench_state);
+ NTSTATUS status;
+
+ status = nbench_cmd_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ tevent_req_done(req);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ subreq = nbench_cmd_send(state, state->ev, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nbench_done, req);
+}
+
+static NTSTATUS nbench_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+bool run_nbench2(int dummy)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct cli_state *cli = NULL;
+ FILE *loadfile;
+ bool ret = false;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ loadfile = fopen("client.txt", "r");
+ if (loadfile == NULL) {
+ fprintf(stderr, "Could not open \"client.txt\": %s\n",
+ strerror(errno));
+ return false;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ if (!torture_open_connection(&cli, 0)) {
+ goto fail;
+ }
+
+ req = nbench_send(talloc_tos(), ev, cli, "client1", loadfile,
+ NULL, NULL);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ goto fail;
+ }
+ status = nbench_recv(req);
+ TALLOC_FREE(req);
+ printf("nbench returned %s\n", nt_errstr(status));
+
+ ret = true;
+fail:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ TALLOC_FREE(ev);
+ if (loadfile != NULL) {
+ fclose(loadfile);
+ loadfile = NULL;
+ }
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/torture/nbio.c b/source3/torture/nbio.c
new file mode 100644
index 0000000..4fedfc5
--- /dev/null
+++ b/source3/torture/nbio.c
@@ -0,0 +1,377 @@
+#define NBDEBUG 0
+
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+
+#define MAX_FILES 1000
+
+static char buf[70000];
+extern int line_count;
+extern int nbio_id;
+static int nprocs;
+static struct timeval nb_start;
+
+static struct {
+ int fd;
+ int handle;
+} ftable[MAX_FILES];
+
+static struct children {
+ double bytes_in, bytes_out;
+ int line;
+ int done;
+} *children;
+
+double nbio_total(void)
+{
+ int i;
+ double total = 0;
+ for (i=0;i<nprocs;i++) {
+ total += children[i].bytes_out + children[i].bytes_in;
+ }
+ return total;
+}
+
+void nb_alarm(int ignore)
+{
+ int i;
+ int lines=0, num_clients=0;
+ if (nbio_id != -1) return;
+
+ for (i=0;i<nprocs;i++) {
+ lines += children[i].line;
+ if (!children[i].done) num_clients++;
+ }
+
+ printf("%4d %8d %.2f MB/sec\r",
+ num_clients, lines/nprocs,
+ 1.0e-6 * nbio_total() / timeval_elapsed(&nb_start));
+
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+}
+
+void nbio_shmem(int n)
+{
+ nprocs = n;
+ children = (struct children *)anonymous_shared_allocate(sizeof(*children) * nprocs);
+ if (!children) {
+ printf("Failed to setup shared memory!\n");
+ exit(1);
+ }
+}
+
+#if 0
+static int ne_find_handle(int handle)
+{
+ int i;
+ children[nbio_id].line = line_count;
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == handle) return i;
+ }
+ return -1;
+}
+#endif
+
+static int find_handle(int handle)
+{
+ int i;
+ children[nbio_id].line = line_count;
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == handle) return i;
+ }
+ printf("(%d) ERROR: handle %d was not found\n",
+ line_count, handle);
+ exit(1);
+
+ return -1; /* Not reached */
+}
+
+
+static struct cli_state *c;
+
+static void sigsegv(int sig)
+{
+ char line[200];
+ printf("segv at line %d\n", line_count);
+ slprintf(line, sizeof(line), "/usr/X11R6/bin/xterm -e gdb /proc/%d/exe %d",
+ (int)getpid(), (int)getpid());
+ if (system(line) == -1) {
+ printf("system() failed\n");
+ }
+ exit(1);
+}
+
+void nb_setup(struct cli_state *cli)
+{
+ signal(SIGSEGV, sigsegv);
+ c = cli;
+ nb_start = timeval_current();
+ children[nbio_id].done = 0;
+}
+
+
+void nb_unlink(const char *fname)
+{
+ NTSTATUS status;
+
+ status = cli_unlink(c, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+#if NBDEBUG
+ printf("(%d) unlink %s failed (%s)\n",
+ line_count, fname, nt_errstr(status));
+#endif
+ }
+}
+
+
+void nb_createx(const char *fname,
+ unsigned create_options, unsigned create_disposition, int handle)
+{
+ uint16_t fd = (uint16_t)-1;
+ int i;
+ NTSTATUS status;
+ uint32_t desired_access;
+
+ if (create_options & FILE_DIRECTORY_FILE) {
+ desired_access = FILE_READ_DATA;
+ } else {
+ desired_access = FILE_READ_DATA | FILE_WRITE_DATA;
+ }
+
+ status = cli_ntcreate(c, fname, 0,
+ desired_access,
+ 0x0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ create_disposition,
+ create_options, 0, &fd, NULL);
+ if (!NT_STATUS_IS_OK(status) && handle != -1) {
+ printf("ERROR: cli_ntcreate failed for %s - %s\n",
+ fname, nt_errstr(status));
+ exit(1);
+ }
+ if (NT_STATUS_IS_OK(status) && handle == -1) {
+ printf("ERROR: cli_ntcreate succeeded for %s\n", fname);
+ exit(1);
+ }
+ if (fd == (uint16_t)-1) return;
+
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == 0) break;
+ }
+ if (i == MAX_FILES) {
+ printf("(%d) file table full for %s\n", line_count,
+ fname);
+ exit(1);
+ }
+ ftable[i].handle = handle;
+ ftable[i].fd = fd;
+}
+
+void nb_writex(int handle, int offset, int size, int ret_size)
+{
+ int i;
+ NTSTATUS status;
+
+ if (buf[0] == 0) memset(buf, 1, sizeof(buf));
+
+ i = find_handle(handle);
+ status = cli_writeall(c, ftable[i].fd, 0, (uint8_t *)buf, offset, size,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%d) ERROR: write failed on handle %d, fd %d "
+ "error %s\n", line_count, handle, ftable[i].fd,
+ nt_errstr(status));
+ exit(1);
+ }
+
+ children[nbio_id].bytes_out += ret_size;
+}
+
+void nb_readx(int handle, int offset, int size, int ret_size)
+{
+ int i;
+ NTSTATUS status;
+ size_t nread;
+
+ i = find_handle(handle);
+ status = cli_read(c, ftable[i].fd, buf, offset, size, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%d) ERROR: read failed on handle %d ofs=%d size=%d "
+ "fd %d nterror %s\n",
+ line_count, handle, offset, size,
+ ftable[i].fd, nt_errstr(status));
+ exit(1);
+ } else if (nread != ret_size) {
+ printf("(%d) ERROR: read failed on handle %d ofs=%d size=%d "
+ "nread=%lu ret_size=%d fd %d\n",
+ line_count, handle, offset, size, (unsigned long)nread,
+ ret_size, ftable[i].fd);
+ exit(1);
+ }
+ children[nbio_id].bytes_in += ret_size;
+}
+
+void nb_close(int handle)
+{
+ int i;
+ i = find_handle(handle);
+ if (!NT_STATUS_IS_OK(cli_close(c, ftable[i].fd))) {
+ printf("(%d) close failed on handle %d\n", line_count, handle);
+ exit(1);
+ }
+ ftable[i].handle = 0;
+}
+
+void nb_rmdir(const char *fname)
+{
+ NTSTATUS status;
+
+ status = cli_rmdir(c, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: rmdir %s failed (%s)\n",
+ fname, nt_errstr(status));
+ exit(1);
+ }
+}
+
+void nb_rename(const char *oldname, const char *newname)
+{
+ NTSTATUS status;
+
+ status = cli_rename(c, oldname, newname, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: rename %s %s failed (%s)\n",
+ oldname, newname, nt_errstr(status));
+ exit(1);
+ }
+}
+
+
+void nb_qpathinfo(const char *fname)
+{
+ cli_qpathinfo1(c, fname, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfileinfo(int fnum)
+{
+ int i;
+ i = find_handle(fnum);
+ cli_qfileinfo_basic(c, ftable[i].fd, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+}
+
+void nb_qfsinfo(int level)
+{
+ uint64_t bsize, total, avail;
+ /* this is not the right call - we need cli_qfsinfo() */
+ cli_disk_size(c, "", &bsize, &total, &avail);
+}
+
+static NTSTATUS find_fn(struct file_info *finfo, const char *name,
+ void *state)
+{
+ /* noop */
+ return NT_STATUS_OK;
+}
+
+void nb_findfirst(const char *mask)
+{
+ cli_list(c, mask, 0, find_fn, NULL);
+}
+
+void nb_flush(int fnum)
+{
+ int i;
+ i = find_handle(fnum);
+
+ cli_flush(NULL, c, i);
+}
+
+static int total_deleted;
+
+static NTSTATUS delete_fn(struct file_info *finfo,
+ const char *name, void *state)
+{
+ NTSTATUS status;
+ char *s, *n;
+ if (finfo->name[0] == '.') {
+ return NT_STATUS_OK;
+ }
+
+ n = SMB_STRDUP(name);
+ n[strlen(n)-1] = 0;
+ if (asprintf(&s, "%s%s", n, finfo->name) == -1) {
+ free(n);
+ printf("asprintf failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ char *s2;
+ if (asprintf(&s2, "%s\\*", s) == -1) {
+ printf("asprintf failed\n");
+ free(s);
+ free(n);
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_list(c, s2, FILE_ATTRIBUTE_DIRECTORY, delete_fn, NULL);
+ free(s2);
+ if (!NT_STATUS_IS_OK(status)) {
+ free(s);
+ free(n);
+ return status;
+ }
+ nb_rmdir(s);
+ } else {
+ total_deleted++;
+ nb_unlink(s);
+ }
+ free(s);
+ free(n);
+ return NT_STATUS_OK;
+}
+
+void nb_deltree(const char *dname)
+{
+ char *mask;
+ if (asprintf(&mask, "%s\\*", dname) == -1) {
+ printf("asprintf failed\n");
+ return;
+ }
+
+ total_deleted = 0;
+ cli_list(c, mask, FILE_ATTRIBUTE_DIRECTORY, delete_fn, NULL);
+ free(mask);
+ cli_rmdir(c, dname);
+
+ if (total_deleted) printf("WARNING: Cleaned up %d files\n", total_deleted);
+}
+
+
+void nb_cleanup(void)
+{
+ cli_rmdir(c, "clients");
+ children[nbio_id].done = 1;
+}
diff --git a/source3/torture/pdbtest.c b/source3/torture/pdbtest.c
new file mode 100644
index 0000000..d153f3e
--- /dev/null
+++ b/source3/torture/pdbtest.c
@@ -0,0 +1,736 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb testing utility
+
+ Copyright (C) Wilco Baan Hofman 2006
+ Copyright (C) Jelmer Vernooij 2006
+ Copyright (C) Andrew Bartlett 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "passdb.h"
+
+#include "../librpc/gen_ndr/drsblobs.h"
+#include "../librpc/gen_ndr/ndr_drsblobs.h"
+#include "../libcli/security/dom_sid.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../auth/common_auth.h"
+#include "lib/tsocket/tsocket.h"
+#include "include/auth.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "auth/auth_sam_reply.h"
+
+#define TRUST_DOM "trustdom"
+#define TRUST_PWD "trustpwd1232"
+#define TRUST_SID "S-1-5-21-1111111111-2222222222-3333333333"
+
+static bool samu_correct(struct samu *s1, struct samu *s2)
+{
+ bool ret = True;
+ uint32_t s1_len, s2_len;
+ const char *s1_buf, *s2_buf;
+ const uint8_t *d1_buf, *d2_buf;
+ const struct dom_sid *s1_sid, *s2_sid;
+
+ /* Check Unix username */
+ s1_buf = pdb_get_username(s1);
+ s2_buf = pdb_get_username(s2);
+ if (s2_buf == NULL && s1_buf != NULL) {
+ DEBUG(0, ("Username is not set\n"));
+ ret = False;
+ } else if (s1_buf == NULL) {
+ /* Do nothing */
+ } else if (strcmp(s1_buf,s2_buf)) {
+ DEBUG(0, ("Username not written correctly, want %s, got \"%s\"\n",
+ pdb_get_username(s1),
+ pdb_get_username(s2)));
+ ret = False;
+ }
+
+ /* Check NT username */
+ s1_buf = pdb_get_nt_username(s1);
+ s2_buf = pdb_get_nt_username(s2);
+ if (s2_buf == NULL && s1_buf != NULL) {
+ DEBUG(0, ("NT Username is not set\n"));
+ ret = False;
+ } else if (s1_buf == NULL) {
+ /* Do nothing */
+ } else if (strcmp(s1_buf, s2_buf)) {
+ DEBUG(0, ("NT Username not written correctly, want \"%s\", got \"%s\"\n",
+ pdb_get_nt_username(s1),
+ pdb_get_nt_username(s2)));
+ ret = False;
+ }
+
+ /* Check acct ctrl */
+ if (pdb_get_acct_ctrl(s1) != pdb_get_acct_ctrl(s2)) {
+ DEBUG(0, ("Acct ctrl field not written correctly, want %d (0x%X), got %d (0x%X)\n",
+ pdb_get_acct_ctrl(s1),
+ pdb_get_acct_ctrl(s1),
+ pdb_get_acct_ctrl(s2),
+ pdb_get_acct_ctrl(s2)));
+ ret = False;
+ }
+
+ /* Check NT password */
+ d1_buf = pdb_get_nt_passwd(s1);
+ d2_buf = pdb_get_nt_passwd(s2);
+ if (d2_buf == NULL && d1_buf != NULL) {
+ DEBUG(0, ("NT password is not set\n"));
+ ret = False;
+ } else if (d1_buf == NULL) {
+ /* Do nothing */
+ } else if (memcmp(d1_buf, d2_buf, NT_HASH_LEN)) {
+ DEBUG(0, ("NT password not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check lanman password */
+ d1_buf = pdb_get_lanman_passwd(s1);
+ d2_buf = pdb_get_lanman_passwd(s2);
+ if (d2_buf == NULL && d1_buf != NULL) {
+ DEBUG(0, ("Lanman password is not set\n"));
+ } else if (d1_buf == NULL) {
+ /* Do nothing */
+ } else if (memcmp(d1_buf, d2_buf, NT_HASH_LEN)) {
+ DEBUG(0, ("Lanman password not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check password history */
+ d1_buf = pdb_get_pw_history(s1, &s1_len);
+ d2_buf = pdb_get_pw_history(s2, &s2_len);
+ if (d2_buf == NULL && d1_buf != NULL) {
+ DEBUG(0, ("Password history is not set\n"));
+ } else if (d1_buf == NULL) {
+ /* Do nothing */
+ } else if (s1_len != s2_len) {
+ DEBUG(0, ("Password history not written correctly, lengths differ, want %d, got %d\n",
+ s1_len, s2_len));
+ ret = False;
+ } else if (strncmp(s1_buf, s2_buf, s1_len)) {
+ DEBUG(0, ("Password history not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check logon time */
+ if (pdb_get_logon_time(s1) != pdb_get_logon_time(s2)) {
+ DEBUG(0, ("Logon time is not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check logoff time */
+ if (pdb_get_logoff_time(s1) != pdb_get_logoff_time(s2)) {
+ DEBUG(0, ("Logoff time is not written correctly: %s vs %s \n",
+ http_timestring(talloc_tos(), pdb_get_logoff_time(s1)),
+ http_timestring(talloc_tos(), pdb_get_logoff_time(s2))));
+ ret = False;
+ }
+
+ /* Check kickoff time */
+ if (pdb_get_kickoff_time(s1) != pdb_get_kickoff_time(s2)) {
+ DEBUG(0, ("Kickoff time is not written correctly: %s vs %s \n",
+ http_timestring(talloc_tos(), pdb_get_kickoff_time(s1)),
+ http_timestring(talloc_tos(), pdb_get_kickoff_time(s2))));
+ ret = False;
+ }
+
+ /* Check bad password time */
+ if (pdb_get_bad_password_time(s1) != pdb_get_bad_password_time(s2)) {
+ DEBUG(0, ("Bad password time is not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check password last set time */
+ if (pdb_get_pass_last_set_time(s1) != pdb_get_pass_last_set_time(s2)) {
+ DEBUG(0, ("Password last set time is not written correctly: %s vs %s \n",
+ http_timestring(talloc_tos(), pdb_get_pass_last_set_time(s1)),
+ http_timestring(talloc_tos(), pdb_get_pass_last_set_time(s2))));
+ ret = False;
+ }
+
+ /* Check password can change time */
+ if (pdb_get_pass_can_change_time(s1) != pdb_get_pass_can_change_time(s2)) {
+ DEBUG(0, ("Password can change time is not written correctly %s vs %s \n",
+ http_timestring(talloc_tos(), pdb_get_pass_can_change_time(s1)),
+ http_timestring(talloc_tos(), pdb_get_pass_can_change_time(s2))));
+ ret = False;
+ }
+
+ /* Check password must change time */
+ if (pdb_get_pass_must_change_time(s1) != pdb_get_pass_must_change_time(s2)) {
+ DEBUG(0, ("Password must change time is not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check logon divs */
+ if (pdb_get_logon_divs(s1) != pdb_get_logon_divs(s2)) {
+ DEBUG(0, ("Logon divs not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check logon hours */
+ if (pdb_get_hours_len(s1) != pdb_get_hours_len(s2)) {
+ DEBUG(0, ("Logon hours length not written correctly\n"));
+ ret = False;
+ } else if (pdb_get_hours_len(s1) != 0) {
+ d1_buf = pdb_get_hours(s1);
+ d2_buf = pdb_get_hours(s2);
+ if (d2_buf == NULL && d1_buf != NULL) {
+ DEBUG(0, ("Logon hours is not set\n"));
+ ret = False;
+ } else if (d1_buf == NULL) {
+ /* Do nothing */
+ } else if (memcmp(d1_buf, d2_buf, MAX_HOURS_LEN)) {
+ DEBUG(0, ("Logon hours is not written correctly\n"));
+ ret = False;
+ }
+ }
+
+ /* Check profile path */
+ s1_buf = pdb_get_profile_path(s1);
+ s2_buf = pdb_get_profile_path(s2);
+ if (s2_buf == NULL && s1_buf != NULL) {
+ DEBUG(0, ("Profile path is not set\n"));
+ ret = False;
+ } else if (s1_buf == NULL) {
+ /* Do nothing */
+ } else if (strcmp(s1_buf, s2_buf)) {
+ DEBUG(0, ("Profile path is not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check home dir */
+ s1_buf = pdb_get_homedir(s1);
+ s2_buf = pdb_get_homedir(s2);
+ if (s2_buf == NULL && s1_buf != NULL) {
+ DEBUG(0, ("Home dir is not set\n"));
+ ret = False;
+ } else if (s1_buf == NULL) {
+ /* Do nothing */
+ } else if (strcmp(s1_buf, s2_buf)) {
+ DEBUG(0, ("Home dir is not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check logon script */
+ s1_buf = pdb_get_logon_script(s1);
+ s2_buf = pdb_get_logon_script(s2);
+ if (s2_buf == NULL && s1_buf != NULL) {
+ DEBUG(0, ("Logon script not set\n"));
+ ret = False;
+ } else if (s1_buf == NULL) {
+ /* Do nothing */
+ } else if (strcmp(s1_buf, s2_buf)) {
+ DEBUG(0, ("Logon script is not written correctly\n"));
+ ret = False;
+ }
+
+ /* Check user and group sids */
+ s1_sid = pdb_get_user_sid(s1);
+ s2_sid = pdb_get_user_sid(s2);
+ if (s2_sid == NULL && s1_sid != NULL) {
+ DEBUG(0, ("USER SID not set\n"));
+ ret = False;
+ } else if (s1_sid == NULL) {
+ /* Do nothing */
+ } else if (!dom_sid_equal(s1_sid, s2_sid)) {
+ DEBUG(0, ("USER SID is not written correctly\n"));
+ ret = False;
+ }
+
+ return ret;
+}
+
+static bool test_auth(TALLOC_CTX *mem_ctx, struct samu *pdb_entry)
+{
+ struct auth_usersupplied_info *user_info;
+ struct auth_context *auth_context;
+ static const uint8_t challenge_8[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+ DATA_BLOB challenge = data_blob_const(challenge_8, sizeof(challenge_8));
+ struct tsocket_address *remote_address;
+ struct tsocket_address *local_address;
+ unsigned char local_nt_response[24];
+ DATA_BLOB nt_resp = data_blob_const(local_nt_response, sizeof(local_nt_response));
+ unsigned char local_nt_session_key[16];
+ struct netr_SamInfo3 *info3_sam, *info3_auth;
+ struct auth_serversupplied_info *server_info;
+ struct wbcAuthUserParams params = { .flags = 0 };
+ struct wbcAuthUserInfo *info = NULL;
+ struct wbcAuthErrorInfo *err = NULL;
+ wbcErr wbc_status;
+ struct netr_SamInfo6 *info6_wbc = NULL;
+ NTSTATUS status;
+ bool ok;
+ uint8_t authoritative = 1;
+ int rc;
+
+ rc = SMBOWFencrypt(pdb_get_nt_passwd(pdb_entry), challenge_8,
+ local_nt_response);
+ if (rc != 0) {
+ return False;
+ }
+
+ SMBsesskeygen_ntv1(pdb_get_nt_passwd(pdb_entry), local_nt_session_key);
+
+ if (tsocket_address_inet_from_strings(NULL, "ip", NULL, 0, &remote_address) != 0) {
+ return False;
+ }
+
+ if (tsocket_address_inet_from_strings(NULL, "ip", NULL, 0, &local_address) != 0) {
+ return False;
+ }
+
+ status = make_user_info(mem_ctx,
+ &user_info, pdb_get_username(pdb_entry), pdb_get_username(pdb_entry),
+ pdb_get_domain(pdb_entry), pdb_get_domain(pdb_entry), lp_netbios_name(),
+ remote_address,local_address, "pdbtest",
+ NULL, &nt_resp, NULL, NULL, NULL,
+ AUTH_PASSWORD_RESPONSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
+ return False;
+ }
+
+ status = check_sam_security_info3(&challenge, NULL, user_info, &info3_sam);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
+ return False;
+ }
+
+ if (memcmp(info3_sam->base.key.key, local_nt_session_key, 16) != 0) {
+ DEBUG(0, ("Returned NT session key is incorrect\n"));
+ return False;
+ }
+
+ status = make_auth3_context_for_ntlm(NULL, &auth_context);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
+ return False;
+ }
+
+ ok = auth3_context_set_challenge(
+ auth_context, challenge.data, "fixed");
+ if (!ok) {
+ DBG_ERR("auth3_context_set_challenge failed\n");
+ return false;
+ }
+
+ status = auth_check_ntlm_password(mem_ctx,
+ auth_context,
+ user_info,
+ &server_info,
+ &authoritative);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to test authentication with auth module: "
+ "%s authoritative[%u].\n",
+ nt_errstr(status), authoritative));
+ return False;
+ }
+
+ info3_auth = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ if (info3_auth == NULL) {
+ return False;
+ }
+
+ status = serverinfo_to_SamInfo3(server_info, info3_auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
+ nt_errstr(status)));
+ return False;
+ }
+
+ if (memcmp(info3_auth->base.key.key, local_nt_session_key, 16) != 0) {
+ DEBUG(0, ("Returned NT session key is incorrect\n"));
+ return False;
+ }
+
+ if (!dom_sid_equal(info3_sam->base.domain_sid, info3_auth->base.domain_sid)) {
+ struct dom_sid_buf buf1, buf2;
+ DEBUG(0, ("domain_sid in SAM info3 %s does not match domain_sid in AUTH info3 %s\n",
+ dom_sid_str_buf(info3_sam->base.domain_sid, &buf1),
+ dom_sid_str_buf(info3_auth->base.domain_sid,
+ &buf2)));
+ return False;
+ }
+
+ /* TODO:
+ * Compare more details from the two info3 structures,
+ * then test that an expired/disabled/pwdmustchange account
+ * returns the correct errors
+ */
+
+ params.parameter_control = user_info->logon_parameters;
+ params.parameter_control |= WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
+ WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+ params.level = WBC_AUTH_USER_LEVEL_RESPONSE;
+
+ params.account_name = user_info->client.account_name;
+ params.domain_name = user_info->client.domain_name;
+ params.workstation_name = user_info->workstation_name;
+
+ memcpy(params.password.response.challenge,
+ challenge.data,
+ sizeof(params.password.response.challenge));
+
+ params.password.response.lm_length =
+ user_info->password.response.lanman.length;
+ params.password.response.nt_length =
+ user_info->password.response.nt.length;
+
+ params.password.response.lm_data =
+ user_info->password.response.lanman.data;
+ params.password.response.nt_data =
+ user_info->password.response.nt.data;
+
+ wbc_status = wbcAuthenticateUserEx(&params, &info, &err);
+ if (wbc_status != WBC_ERR_WINBIND_NOT_AVAILABLE) {
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ if (err) {
+ DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
+ err->nt_string, err->nt_status, err->display_string));
+ status = NT_STATUS(err->nt_status);
+ wbcFreeMemory(err);
+ } else {
+ status = NT_STATUS_LOGON_FAILURE;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ } else if (!WBC_ERROR_IS_OK(wbc_status)) {
+ DEBUG(1, ("wbcAuthenticateUserEx: failed with %u - %s\n",
+ wbc_status, wbcErrorString(wbc_status)));
+ if (err) {
+ DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
+ err->nt_string, err->nt_status, err->display_string));
+ }
+ return false;
+ }
+ info6_wbc = wbcAuthUserInfo_to_netr_SamInfo6(mem_ctx, info);
+ wbcFreeMemory(info);
+ if (!info6_wbc) {
+ DEBUG(1, ("wbcAuthUserInfo_to_netr_SamInfo6 failed\n"));
+ return false;
+ }
+
+ if (memcmp(info6_wbc->base.key.key, local_nt_session_key, 16) != 0) {
+ DEBUG(0, ("Returned NT session key is incorrect\n"));
+ return false;
+ }
+
+ if (!dom_sid_equal(info3_sam->base.domain_sid, info6_wbc->base.domain_sid)) {
+ struct dom_sid_buf buf1, buf2;
+ DEBUG(0, ("domain_sid in SAM info3 %s does not match domain_sid in AUTH info3 %s\n",
+ dom_sid_str_buf(info3_sam->base.domain_sid,
+ &buf1),
+ dom_sid_str_buf(info6_wbc->base.domain_sid,
+ &buf2)));
+ return false;
+ }
+ }
+
+ return True;
+}
+
+static bool test_trusted_domains(TALLOC_CTX *ctx,
+ struct pdb_methods *pdb,
+ bool *error)
+{
+ NTSTATUS rv;
+ /* test trustdom calls */
+ struct pdb_trusted_domain *td;
+ struct pdb_trusted_domain *new_td;
+ struct trustAuthInOutBlob taiob;
+ struct AuthenticationInformation aia;
+ enum ndr_err_code ndr_err;
+ bool ok;
+
+ td = talloc_zero(ctx ,struct pdb_trusted_domain);
+ if (!td) {
+ fprintf(stderr, "talloc failed\n");
+ return false;
+ }
+
+ td->domain_name = talloc_strdup(td, TRUST_DOM);
+ td->netbios_name = talloc_strdup(td, TRUST_DOM);
+ if (!td->domain_name || !td->netbios_name) {
+ fprintf(stderr, "talloc failed\n");
+ return false;
+ }
+ ok = dom_sid_parse("S-1-5-21-123-456-789", &td->security_identifier);
+ if (!ok) {
+ fprintf(stderr, "dom_sid_parse S-1-5-21-123-456-789 failed\n");
+ return false;
+ }
+
+ td->trust_auth_incoming = data_blob_null;
+
+ ZERO_STRUCT(taiob);
+ ZERO_STRUCT(aia);
+ taiob.count = 1;
+ taiob.current.count = 1;
+ taiob.current.array = &aia;
+ unix_to_nt_time(&aia.LastUpdateTime, time(NULL));
+ aia.AuthType = TRUST_AUTH_TYPE_CLEAR;
+ aia.AuthInfo.clear.password = (uint8_t *) talloc_strdup(ctx, TRUST_PWD);
+ aia.AuthInfo.clear.size = strlen(TRUST_PWD);
+
+ taiob.previous.count = 0;
+ taiob.previous.array = NULL;
+
+ ndr_err = ndr_push_struct_blob(&td->trust_auth_outgoing,
+ td, &taiob,
+ (ndr_push_flags_fn_t) ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ fprintf(stderr, "ndr_push_struct_blob failed.\n");
+ return false;
+ }
+
+ td->trust_direction = LSA_TRUST_DIRECTION_OUTBOUND;
+ td->trust_type = LSA_TRUST_TYPE_DOWNLEVEL;
+ td->trust_attributes = 0;
+ td->trust_forest_trust_info = data_blob_null;
+
+ rv = pdb->set_trusted_domain(pdb, TRUST_DOM, td);
+ if (!NT_STATUS_IS_OK(rv)) {
+ fprintf(stderr, "Error in set_trusted_domain %s\n",
+ get_friendly_nt_error_msg(rv));
+ *error = true;
+ }
+
+ rv = pdb->get_trusted_domain(pdb, ctx, TRUST_DOM, &new_td);
+ if (!NT_STATUS_IS_OK(rv)) {
+ fprintf(stderr, "Error in get_trusted_domain %s\n",
+ get_friendly_nt_error_msg(rv));
+ *error = true;
+ }
+
+ if (!strequal(td->domain_name, new_td->domain_name) ||
+ !strequal(td->netbios_name, new_td->netbios_name) ||
+ !dom_sid_equal(&td->security_identifier,
+ &new_td->security_identifier) ||
+ td->trust_direction != new_td->trust_direction ||
+ td->trust_type != new_td->trust_type ||
+ td->trust_attributes != new_td->trust_attributes ||
+ td->trust_auth_incoming.length != new_td->trust_auth_incoming.length ||
+ td->trust_forest_trust_info.length != new_td->trust_forest_trust_info.length ||
+ data_blob_cmp(&td->trust_auth_outgoing, &new_td->trust_auth_outgoing) != 0) {
+ fprintf(stderr, "Old and new trusdet domain data do not match\n");
+ *error = true;
+ }
+
+ rv = pdb->del_trusted_domain(pdb, TRUST_DOM);
+ if (!NT_STATUS_IS_OK(rv)) {
+ fprintf(stderr, "Error in del_trusted_domain %s\n",
+ get_friendly_nt_error_msg(rv));
+ *error = true;
+ }
+
+ return true;
+}
+
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *ctx;
+ struct samu *out = NULL;
+ struct samu *in = NULL;
+ NTSTATUS rv;
+ int i;
+ int opt;
+ struct timeval tv;
+ bool error = False;
+ struct passwd *pwd;
+ uint8_t *buf;
+ uint32_t expire, min_age, history;
+ struct pdb_methods *pdb;
+ poptContext pc;
+ static const char *backend = NULL;
+ static const char *unix_user = "nobody";
+ bool ok;
+ struct poptOption long_options[] = {
+ {"username", 'u', POPT_ARG_STRING, &unix_user, 0, "Unix user to use for testing", "USERNAME" },
+ {"backend", 'b', POPT_ARG_STRING, &backend, 0, "Backend to use if not default", "BACKEND[:SETTINGS]" },
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ ctx = talloc_stackframe();
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ true /* require_smbconf */);
+ if (!ok) {
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(), argc, argv, long_options, 0);
+ if (pc == NULL) {
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "backend[:settings] username");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+
+ if (backend == NULL) {
+ backend = lp_passdb_backend();
+ }
+
+ rv = make_pdb_method_name(&pdb, backend);
+ if (NT_STATUS_IS_ERR(rv)) {
+ fprintf(stderr, "Error initializing '%s': %s\n", backend, get_friendly_nt_error_msg(rv));
+ exit(1);
+ }
+
+ if (!(out = samu_new(ctx))) {
+ fprintf(stderr, "Can't create samu structure.\n");
+ exit(1);
+ }
+
+ if ((pwd = Get_Pwnam_alloc(ctx, unix_user)) == NULL) {
+ fprintf(stderr, "Error getting user information for %s\n", unix_user);
+ exit(1);
+ }
+
+ samu_set_unix(out, pwd);
+
+ pdb_set_profile_path(out, "\\\\torture\\profile", PDB_SET);
+ pdb_set_homedir(out, "\\\\torture\\home", PDB_SET);
+ pdb_set_logon_script(out, "torture_script.cmd", PDB_SET);
+
+ pdb_set_acct_ctrl(out, ACB_NORMAL, PDB_SET);
+
+ pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &history);
+ if (history * PW_HISTORY_ENTRY_LEN < NT_HASH_LEN) {
+ buf = (uint8_t *)TALLOC(ctx, NT_HASH_LEN);
+ } else {
+ buf = (uint8_t *)TALLOC(ctx, history * PW_HISTORY_ENTRY_LEN);
+ }
+
+ /* Generate some random hashes */
+ GetTimeOfDay(&tv);
+ srand(tv.tv_usec);
+ for (i = 0; i < NT_HASH_LEN; i++) {
+ buf[i] = (uint8_t) rand();
+ }
+ pdb_set_nt_passwd(out, buf, PDB_SET);
+ for (i = 0; i < LM_HASH_LEN; i++) {
+ buf[i] = (uint8_t) rand();
+ }
+ pdb_set_lanman_passwd(out, buf, PDB_SET);
+ for (i = 0; i < history * PW_HISTORY_ENTRY_LEN; i++) {
+ buf[i] = (uint8_t) rand();
+ }
+ pdb_set_pw_history(out, buf, history, PDB_SET);
+
+ pdb_get_account_policy(PDB_POLICY_MAX_PASSWORD_AGE, &expire);
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_AGE, &min_age);
+ pdb_set_pass_last_set_time(out, time(NULL), PDB_SET);
+
+ if (min_age == (uint32_t)-1) {
+ pdb_set_pass_can_change_time(out, 0, PDB_SET);
+ } else {
+ pdb_set_pass_can_change_time(out, time(NULL)+min_age, PDB_SET);
+ }
+
+ pdb_set_logon_time(out, time(NULL)-3600, PDB_SET);
+
+ pdb_set_logoff_time(out, time(NULL), PDB_SET);
+
+ pdb_set_kickoff_time(out, time(NULL)+3600, PDB_SET);
+
+ /* Create account */
+ if (!NT_STATUS_IS_OK(rv = pdb->add_sam_account(pdb, out))) {
+ fprintf(stderr, "Error in add_sam_account: %s\n",
+ get_friendly_nt_error_msg(rv));
+ exit(1);
+ }
+
+ if (!(in = samu_new(ctx))) {
+ fprintf(stderr, "Can't create samu structure.\n");
+ exit(1);
+ }
+
+ /* Get account information through getsampwnam() */
+ rv = pdb->getsampwnam(pdb, in, out->username);
+ if (NT_STATUS_IS_ERR(rv)) {
+ fprintf(stderr, "Error getting sampw of added user %s: %s\n",
+ out->username, nt_errstr(rv));
+ if (!NT_STATUS_IS_OK(rv = pdb->delete_sam_account(pdb, out))) {
+ fprintf(stderr, "Error in delete_sam_account %s\n",
+ get_friendly_nt_error_msg(rv));
+ }
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ /* Verify integrity */
+ if (samu_correct(out, in)) {
+ printf("User info written correctly\n");
+ } else {
+ printf("User info NOT written correctly\n");
+ error = True;
+ }
+
+ if (test_auth(ctx, out)) {
+ printf("Authentication module test passed\n");
+ } else {
+ printf("Authentication module test failed!\n");
+ error = True;
+ }
+
+
+ /* Delete account */
+ if (!NT_STATUS_IS_OK(rv = pdb->delete_sam_account(pdb, out))) {
+ fprintf(stderr, "Error in delete_sam_account %s\n",
+ get_friendly_nt_error_msg(rv));
+ }
+
+ if (pdb_capabilities() & PDB_CAP_TRUSTED_DOMAINS_EX) {
+ if (!test_trusted_domains(ctx, pdb, &error)) {
+ fprintf(stderr, "failed testing trusted domains.\n");
+ exit(1);
+ }
+ }
+
+ TALLOC_FREE(ctx);
+
+ if (error) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/source3/torture/proto.h b/source3/torture/proto.h
new file mode 100644
index 0000000..26b5120
--- /dev/null
+++ b/source3/torture/proto.h
@@ -0,0 +1,192 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB torture tester - header file
+
+ Copyright (C) Andrew Tridgell 1997-1998
+ Copyright (C) Jeremy Allison 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TORTURE_H__
+#define __TORTURE_H__
+
+struct cli_state;
+
+/* The following definitions come from torture/denytest.c */
+
+bool torture_denytest1(int dummy);
+bool torture_denytest2(int dummy);
+
+/* The following definitions come from torture/mangle_test.c */
+
+bool torture_mangle(int dummy);
+
+/* The following definitions come from torture/nbio.c */
+
+double nbio_total(void);
+void nb_alarm(int ignore);
+void nbio_shmem(int n);
+void nb_setup(struct cli_state *cli);
+void nb_unlink(const char *fname);
+void nb_createx(const char *fname,
+ unsigned create_options, unsigned create_disposition, int handle);
+void nb_writex(int handle, int offset, int size, int ret_size);
+void nb_readx(int handle, int offset, int size, int ret_size);
+void nb_close(int handle);
+void nb_rmdir(const char *fname);
+void nb_rename(const char *oldname, const char *newname);
+void nb_qpathinfo(const char *fname);
+void nb_qfileinfo(int fnum);
+void nb_qfsinfo(int level);
+void nb_findfirst(const char *mask);
+void nb_flush(int fnum);
+void nb_deltree(const char *dname);
+void nb_cleanup(void);
+
+/* The following definitions come from torture/scanner.c */
+
+bool torture_trans2_scan(int dummy);
+bool torture_nttrans_scan(int dummy);
+
+/* The following definitions come from torture/torture.c */
+
+bool smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
+ char **hostname, char **sharename);
+bool torture_open_connection_flags(struct cli_state **c, int conn_index, int flags);
+bool torture_open_connection(struct cli_state **c, int conn_index);
+bool torture_init_connection(struct cli_state **pcli);
+bool torture_cli_session_setup2(struct cli_state *cli, uint16_t *new_vuid);
+bool torture_close_connection(struct cli_state *c);
+bool torture_ioctl_test(int dummy);
+bool torture_chkpath_test(int dummy);
+NTSTATUS torture_setup_unix_extensions(struct cli_state *cli);
+void torture_conn_set_sockopt(struct cli_state *cli);
+void torture_deltree(struct cli_state *cli, const char *dname);
+
+NTSTATUS cli_qpathinfo1(struct cli_state *cli,
+ const char *fname,
+ time_t *change_time,
+ time_t *access_time,
+ time_t *write_time,
+ off_t *size,
+ uint32_t *pattr);
+
+/* The following definitions come from torture/utable.c */
+
+bool torture_utable(int dummy);
+bool torture_casetable(int dummy);
+
+/*
+ * Misc
+ */
+
+bool run_posix_append(int dummy);
+bool run_posix_ls_wildcard_test(int dummy);
+bool run_posix_ls_single_test(int dummy);
+bool run_posix_readlink_test(int dummy);
+bool run_posix_stat_test(int dummy);
+bool run_posix_symlink_parent_test(int dummy);
+bool run_posix_symlink_chmod_test(int dummy);
+bool run_posix_dir_default_acl_test(int dummy);
+bool run_case_insensitive_create(int dummy);
+bool run_posix_symlink_rename_test(int dummy);
+bool run_posix_symlink_getpathinfo_test(int dummy);
+bool run_posix_symlink_setpathinfo_test(int dummy);
+
+bool run_nbench2(int dummy);
+bool run_async_echo(int dummy);
+bool run_smb_any_connect(int dummy);
+bool run_addrchange(int dummy);
+bool run_str_match_mswild(int dummy);
+bool run_str_match_regex_sub1(int dummy);
+bool run_notify_online(int dummy);
+bool run_nttrans_create(int dummy);
+bool run_nttrans_fsctl(int dummy);
+bool run_smb2_basic(int dummy);
+bool run_smb2_negprot(int dummy);
+bool run_smb2_anonymous(int dummy);
+bool run_smb2_session_reconnect(int dummy);
+bool run_smb2_tcon_dependence(int dummy);
+bool run_smb2_multi_channel(int dummy);
+bool run_smb2_session_reauth(int dummy);
+bool run_smb2_ftruncate(int dummy);
+bool run_smb2_dir_fsync(int dummy);
+bool run_smb2_path_slash(int dummy);
+bool run_smb2_sacl(int dummy);
+bool run_smb2_quota1(int dummy);
+bool run_smb2_stream_acl(int dummy);
+bool run_smb2_dfs_paths(int dummy);
+bool run_smb2_non_dfs_share(int dummy);
+bool run_smb2_dfs_share_non_dfs_path(int dummy);
+bool run_smb2_dfs_filename_leading_backslash(int dummy);
+bool run_smb2_pipe_read_async_disconnect(int dummy);
+bool run_smb2_invalid_pipename(int dummy);
+bool run_smb1_dfs_paths(int dummy);
+bool run_smb1_dfs_search_paths(int dummy);
+bool run_smb1_dfs_operations(int dummy);
+bool run_smb1_dfs_check_badpath(int dummy);
+bool run_list_dir_async_test(int dummy);
+bool run_delete_on_close_non_empty(int dummy);
+bool run_delete_on_close_nonwrite_delete_yes_test(int dummy);
+bool run_delete_on_close_nonwrite_delete_no_test(int dummy);
+bool run_chain3(int dummy);
+bool run_local_conv_auth_info(int dummy);
+bool run_local_sprintf_append(int dummy);
+bool run_cleanup1(int dummy);
+bool run_cleanup2(int dummy);
+bool run_cleanup4(int dummy);
+bool run_notify_bench2(int dummy);
+bool run_notify_bench3(int dummy);
+bool run_dbwrap_watch1(int dummy);
+bool run_dbwrap_watch2(int dummy);
+bool run_dbwrap_watch3(int dummy);
+bool run_dbwrap_watch4(int dummy);
+bool run_dbwrap_do_locked1(int dummy);
+bool run_idmap_tdb_common_test(int dummy);
+bool run_local_dbwrap_ctdb1(int dummy);
+bool run_qpathinfo_bufsize(int dummy);
+bool run_bench_pthreadpool(int dummy);
+bool run_messaging_read1(int dummy);
+bool run_messaging_read2(int dummy);
+bool run_messaging_read3(int dummy);
+bool run_messaging_read4(int dummy);
+bool run_messaging_fdpass1(int dummy);
+bool run_messaging_fdpass2(int dummy);
+bool run_messaging_fdpass2a(int dummy);
+bool run_messaging_fdpass2b(int dummy);
+bool run_messaging_send_all(int dummy);
+bool run_oplock_cancel(int dummy);
+bool run_pthreadpool_tevent(int dummy);
+bool run_g_lock1(int dummy);
+bool run_g_lock2(int dummy);
+bool run_g_lock3(int dummy);
+bool run_g_lock4(int dummy);
+bool run_g_lock4a(int dummy);
+bool run_g_lock5(int dummy);
+bool run_g_lock6(int dummy);
+bool run_g_lock7(int dummy);
+bool run_g_lock8(int dummy);
+bool run_g_lock_ping_pong(int dummy);
+bool run_local_namemap_cache1(int dummy);
+bool run_local_idmap_cache1(int dummy);
+bool run_hidenewfiles(int dummy);
+bool run_hidenewfiles_showdirs(int dummy);
+bool run_readdir_timestamp(int dummy);
+bool run_ctdbd_conn1(int dummy);
+bool run_rpc_scale(int dummy);
+bool run_tdb_validate(int dummy);
+
+#endif /* __TORTURE_H__ */
diff --git a/source3/torture/scanner.c b/source3/torture/scanner.c
new file mode 100644
index 0000000..ad44f33
--- /dev/null
+++ b/source3/torture/scanner.c
@@ -0,0 +1,515 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - scanning functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+#define VERBOSE 0
+#define OP_MIN 0
+#define OP_MAX 20
+
+#define DATA_SIZE 1024
+#define PARAM_SIZE 1024
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void trans2_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_INFO_CLASS)) {
+ return;
+ }
+#if VERBOSE
+ printf("possible %s hit op=%3d level=%5d status=%s\n",
+ format, op, level, nt_errstr(status));
+#endif
+}
+
+/****************************************************************************
+check for existence of a trans2 call
+****************************************************************************/
+static NTSTATUS try_trans2(struct cli_state *cli,
+ int op,
+ uint8_t *param, uint8_t *data,
+ uint32_t param_len, uint32_t data_len,
+ uint32_t *rparam_len, uint32_t *rdata_len)
+{
+ uint16_t setup[1];
+ uint8_t *rparam=NULL, *rdata=NULL;
+ NTSTATUS status;
+
+ SSVAL(setup+0, 0, op);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, -1, /* name, fid */
+ op, 0,
+ NULL, 0, 0, /* setup */
+ param, param_len, 2,
+ data, data_len, CLI_BUFFER_SIZE,
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ &rparam, 0, rparam_len,
+ &rdata, 0, rdata_len);
+
+ TALLOC_FREE(rdata);
+ TALLOC_FREE(rparam);
+
+ return status;
+}
+
+
+static NTSTATUS try_trans2_len(struct cli_state *cli,
+ const char *format,
+ int op, int level,
+ uint8_t *param, uint8_t *data,
+ uint32_t param_len, uint32_t *data_len,
+ uint32_t *rparam_len, uint32_t *rdata_len)
+{
+ NTSTATUS ret=NT_STATUS_OK;
+
+ ret = try_trans2(cli, op, param, data, param_len,
+ DATA_SIZE, rparam_len, rdata_len);
+#if VERBOSE
+ printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
+#endif
+ if (!NT_STATUS_IS_OK(ret)) return ret;
+
+ *data_len = 0;
+ while (*data_len < DATA_SIZE) {
+ ret = try_trans2(cli, op, param, data, param_len,
+ *data_len, rparam_len, rdata_len);
+ if (NT_STATUS_IS_OK(ret)) break;
+ *data_len += 2;
+ }
+ if (NT_STATUS_IS_OK(ret)) {
+ printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
+ format, level, *data_len, *rparam_len, *rdata_len);
+ } else {
+ trans2_check_hit(format, op, level, ret);
+ }
+ return ret;
+}
+
+/****************************************************************************
+check for existence of a trans2 call
+****************************************************************************/
+static bool scan_trans2(struct cli_state *cli, int op, int level,
+ int fnum, int dnum, const char *fname)
+{
+ uint32_t data_len = 0;
+ uint32_t param_len = 0;
+ uint32_t rparam_len, rdata_len;
+ uint8_t *param = NULL;
+ uint8_t data[DATA_SIZE];
+ const char *newfname;
+ const char *dname;
+ NTSTATUS status;
+
+ memset(data, 0, sizeof(data));
+ data_len = 4;
+
+ /* try with a info level only */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 2);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+
+ param_len = talloc_get_size(param);
+ status = try_trans2_len(cli, "void", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file descriptor */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, level);
+ SSVAL(param, 4, 0);
+
+ param_len = talloc_get_size(param);
+ status = try_trans2_len(cli, "fnum", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+
+ /* try with a notify style */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, dnum);
+ SSVAL(param, 2, dnum);
+ SSVAL(param, 4, level);
+
+ param_len = talloc_get_size(param);
+ status = try_trans2_len(cli, "notify", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file name */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ fname, strlen(fname)+1, NULL);
+ if (param == NULL) return True;
+
+ param_len = talloc_get_size(param);
+ status = try_trans2_len(cli, "fname", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a new file name */
+ newfname = "\\newfile.dat";
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ newfname, strlen(newfname)+1, NULL);
+ if (param == NULL) return True;
+
+ param_len = talloc_get_size(param);
+ status = try_trans2_len(cli, "newfile", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_unlink(cli, newfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, newfname);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try dfs style */
+ dname = "\\testdir";
+ cli_mkdir(cli, dname);
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 2);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ dname, strlen(dname)+1, NULL);
+ if (param == NULL) return True;
+
+ param_len = talloc_get_size(param);
+ status = try_trans2_len(cli, "dfs", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_rmdir(cli, dname);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ return False;
+}
+
+
+bool torture_trans2_scan(int dummy)
+{
+ static struct cli_state *cli;
+ int op, level;
+ const char *fname = "\\scanner.dat";
+ uint16_t fnum, dnum;
+
+ printf("starting trans2 scan test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(cli_openx(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE, &fnum))) {
+ printf("open of %s failed\n", fname);
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(cli_openx(cli, "\\", O_RDONLY, DENY_NONE, &dnum))) {
+ printf("open of \\ failed\n");
+ return false;
+ }
+
+ for (op=OP_MIN; op<=OP_MAX; op++) {
+ printf("Scanning op=%d\n", op);
+ for (level = 0; level <= 50; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+ }
+
+ torture_close_connection(cli);
+
+ printf("trans2 scan finished\n");
+ return True;
+}
+
+
+
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void nttrans_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_INFO_CLASS)) {
+ return;
+ }
+#if VERBOSE
+ printf("possible %s hit op=%3d level=%5d status=%s\n",
+ format, op, level, nt_errstr(status));
+#endif
+}
+
+/****************************************************************************
+check for existence of a nttrans call
+****************************************************************************/
+static NTSTATUS try_nttrans(struct cli_state *cli,
+ int op,
+ uint8_t *param, uint8_t *data,
+ int32_t param_len, uint32_t data_len,
+ uint32_t *rparam_len,
+ uint32_t *rdata_len)
+{
+ uint8_t *rparam=NULL, *rdata=NULL;
+ NTSTATUS status;
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, -1, /* name, fid */
+ op, 0,
+ NULL, 0, 0, /* setup */
+ param, param_len, 2,
+ data, data_len, CLI_BUFFER_SIZE,
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ &rparam, 0, rparam_len,
+ &rdata, 0, rdata_len);
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return status;
+}
+
+
+static NTSTATUS try_nttrans_len(struct cli_state *cli,
+ const char *format,
+ int op, int level,
+ uint8_t *param, uint8_t *data,
+ int param_len, uint32_t *data_len,
+ uint32_t *rparam_len, uint32_t *rdata_len)
+{
+ NTSTATUS ret=NT_STATUS_OK;
+
+ ret = try_nttrans(cli, op, param, data, param_len,
+ DATA_SIZE, rparam_len, rdata_len);
+#if VERBOSE
+ printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
+#endif
+ if (!NT_STATUS_IS_OK(ret)) return ret;
+
+ *data_len = 0;
+ while (*data_len < DATA_SIZE) {
+ ret = try_nttrans(cli, op, param, data, param_len,
+ *data_len, rparam_len, rdata_len);
+ if (NT_STATUS_IS_OK(ret)) break;
+ *data_len += 2;
+ }
+ if (NT_STATUS_IS_OK(ret)) {
+ printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
+ format, level, *data_len, *rparam_len, *rdata_len);
+ } else {
+ nttrans_check_hit(format, op, level, ret);
+ }
+ return ret;
+}
+
+/****************************************************************************
+check for existence of a nttrans call
+****************************************************************************/
+static bool scan_nttrans(struct cli_state *cli, int op, int level,
+ int fnum, int dnum, const char *fname)
+{
+ uint32_t data_len = 0;
+ uint32_t param_len = 0;
+ uint32_t rparam_len, rdata_len;
+ uint8_t *param = NULL;
+ uint8_t data[DATA_SIZE];
+ NTSTATUS status;
+ const char *newfname;
+ const char *dname;
+
+ memset(data, 0, sizeof(data));
+ data_len = 4;
+
+ /* try with a info level only */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 2);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+
+ param_len = talloc_get_size(param);
+ status = try_nttrans_len(cli, "void", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file descriptor */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, level);
+ SSVAL(param, 4, 0);
+
+ param_len = talloc_get_size(param);
+ status = try_nttrans_len(cli, "fnum", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+
+ /* try with a notify style */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, dnum);
+ SSVAL(param, 2, dnum);
+ SSVAL(param, 4, level);
+
+ param_len = talloc_get_size(param);
+ status = try_nttrans_len(cli, "notify", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file name */
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ fname, strlen(fname)+1, NULL);
+ if (param == NULL) return True;
+
+ param_len = talloc_get_size(param);
+ status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a new file name */
+ newfname = "\\newfile.dat";
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ newfname, strlen(newfname)+1, NULL);
+ if (param == NULL) return True;
+
+ param_len = talloc_get_size(param);
+ status = try_nttrans_len(cli, "newfile", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_unlink(cli, newfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, newfname);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try dfs style */
+ dname = "\\testdir";
+ cli_mkdir(cli, dname);
+ TALLOC_FREE(param);
+ param = talloc_array(talloc_tos(), uint8_t, 2);
+ if (param == NULL) return True;
+
+ SSVAL(param, 0, level);
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ dname, strlen(dname)+1, NULL);
+ if (param == NULL) return True;
+
+ param_len = talloc_get_size(param);
+ status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_rmdir(cli, dname);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ return False;
+}
+
+
+bool torture_nttrans_scan(int dummy)
+{
+ static struct cli_state *cli;
+ int op, level;
+ const char *fname = "\\scanner.dat";
+ uint16_t fnum, dnum;
+
+ printf("starting nttrans scan test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ cli_openx(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE, &fnum);
+ cli_openx(cli, "\\", O_RDONLY, DENY_NONE, &dnum);
+
+ for (op=OP_MIN; op<=OP_MAX; op++) {
+ printf("Scanning op=%d\n", op);
+ for (level = 0; level <= 50; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+ }
+
+ torture_close_connection(cli);
+
+ printf("nttrans scan finished\n");
+ return True;
+}
diff --git a/source3/torture/test_addrchange.c b/source3/torture/test_addrchange.c
new file mode 100644
index 0000000..9ccca1c
--- /dev/null
+++ b/source3/torture/test_addrchange.c
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+ test for the addrchange functionality
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/addrchange.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "proto.h"
+
+extern int torture_numops;
+
+bool run_addrchange(int dummy)
+{
+ struct addrchange_context *ctx;
+ struct tevent_context *ev;
+ NTSTATUS status;
+ int i;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "tevent_context_init failed\n");
+ return false;
+ }
+
+ status = addrchange_context_create(talloc_tos(), &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "addrchange_context_create failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i<torture_numops; i++) {
+ enum addrchange_type type;
+ struct sockaddr_storage addr;
+ struct tevent_req *req;
+ const char *typestr;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ req = addrchange_send(talloc_tos(), ev, ctx);
+ if (req == NULL) {
+ d_fprintf(stderr, "addrchange_send failed\n");
+ return false;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ d_fprintf(stderr, "tevent_req_poll_ntstatus failed: "
+ "%s\n", nt_errstr(status));
+ TALLOC_FREE(req);
+ return false;
+ }
+
+ status = addrchange_recv(req, &type, &addr);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "addrchange_recv failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ switch(type) {
+ case ADDRCHANGE_ADD:
+ typestr = "add";
+ break;
+ case ADDRCHANGE_DEL:
+ typestr = "del";
+ break;
+ default:
+ typestr = talloc_asprintf(talloc_tos(), "unknown %d",
+ (int)type);
+ break;
+ }
+
+ printf("Got %s %s\n", typestr,
+ print_sockaddr(addrstr, sizeof(addrstr), &addr));
+ }
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(ev);
+ return true;
+}
diff --git a/source3/torture/test_async_echo.c b/source3/torture/test_async_echo.c
new file mode 100644
index 0000000..f21daa4
--- /dev/null
+++ b/source3/torture/test_async_echo.c
@@ -0,0 +1,148 @@
+/*
+ Unix SMB/CIFS implementation.
+ Run the async echo responder
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "rpc_client/cli_pipe.h"
+#include "librpc/gen_ndr/ndr_echo_c.h"
+
+static void rpccli_sleep_done(struct tevent_req *req)
+{
+ int *done = (int *)tevent_req_callback_data_void(req);
+ NTSTATUS status;
+ uint32_t result = UINT32_MAX;
+
+ status = dcerpc_echo_TestSleep_recv(req, talloc_tos(), &result);
+ TALLOC_FREE(req);
+ printf("sleep returned %s, %d\n", nt_errstr(status), (int)result);
+ *done -= 1;
+}
+
+static void cli_echo_done(struct tevent_req *req)
+{
+ int *done = (int *)tevent_req_callback_data_void(req);
+ NTSTATUS status;
+
+ status = cli_echo_recv(req);
+ TALLOC_FREE(req);
+ printf("echo returned %s\n", nt_errstr(status));
+ *done -= 1;
+}
+
+static void write_andx_done(struct tevent_req *req)
+{
+ int *done = (int *)tevent_req_callback_data_void(req);
+ NTSTATUS status;
+ size_t written;
+
+ status = cli_write_andx_recv(req, &written);
+ TALLOC_FREE(req);
+ printf("cli_write_andx returned %s\n", nt_errstr(status));
+ *done -= 1;
+}
+
+bool run_async_echo(int dummy)
+{
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *p;
+ struct dcerpc_binding_handle *b;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+ bool ret = false;
+ int i, num_reqs;
+ uint8_t buf[65536];
+
+ printf("Starting ASYNC_ECHO\n");
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("tevent_context_init failed\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ printf("torture_open_connection failed\n");
+ goto fail;
+ }
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_rpcecho,
+ &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Could not open echo pipe: %s\n", nt_errstr(status));
+ goto fail;
+ }
+ b = p->binding_handle;
+
+ num_reqs = 0;
+
+ req = dcerpc_echo_TestSleep_send(ev, ev, b, 15);
+ if (req == NULL) {
+ printf("rpccli_echo_TestSleep_send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, rpccli_sleep_done, &num_reqs);
+ num_reqs += 1;
+
+ req = cli_echo_send(ev, ev, cli, 1, data_blob_const("hello", 5));
+ if (req == NULL) {
+ printf("cli_echo_send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, cli_echo_done, &num_reqs);
+ num_reqs += 1;
+
+ memset(buf, 0, sizeof(buf));
+
+ for (i=0; i<10; i++) {
+ req = cli_write_andx_send(ev, ev, cli, 4711, 0, buf, 0,
+ sizeof(buf));
+ if (req == NULL) {
+ printf("cli_write_andx_send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, write_andx_done, &num_reqs);
+ num_reqs += 1;
+
+ req = cli_echo_send(ev, ev, cli, 1,
+ data_blob_const("hello", 5));
+ if (req == NULL) {
+ printf("cli_echo_send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, cli_echo_done, &num_reqs);
+ num_reqs += 1;
+ }
+
+ while (num_reqs > 0) {
+ if (tevent_loop_once(ev) != 0) {
+ printf("tevent_loop_once failed\n");
+ goto fail;
+ }
+ }
+
+ TALLOC_FREE(p);
+
+ ret = true;
+fail:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ return ret;
+}
diff --git a/source3/torture/test_authinfo_structs.c b/source3/torture/test_authinfo_structs.c
new file mode 100644
index 0000000..0b5cff7
--- /dev/null
+++ b/source3/torture/test_authinfo_structs.c
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test conversion form struct lsa_TrustDomainInfoAuthInfo to
+ struct trustAuthInOutBlob and back
+ Copyright (C) Sumit Bose 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "libcli/lsarpc/util_lsarpc.h"
+
+static bool cmp_TrustDomainInfoBuffer(struct lsa_TrustDomainInfoBuffer a,
+ struct lsa_TrustDomainInfoBuffer b)
+{
+ if (a.last_update_time != b. last_update_time ||
+ a.AuthType != b.AuthType ||
+ a.data.size != b.data.size ||
+ memcmp(a.data.data, b.data.data, a.data.size) !=0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool cmp_auth_info(struct lsa_TrustDomainInfoAuthInfo *a,
+ struct lsa_TrustDomainInfoAuthInfo *b)
+{
+ size_t c;
+
+ if (a->incoming_count != b->incoming_count ||
+ a->outgoing_count != b->outgoing_count) {
+ return false;
+ }
+
+ for (c = 0; c < a->incoming_count; c++) {
+ if (!cmp_TrustDomainInfoBuffer(a->incoming_current_auth_info[c],
+ b->incoming_current_auth_info[c])) {
+ return false;
+ }
+
+ if (a->incoming_previous_auth_info != NULL &&
+ b->incoming_previous_auth_info != NULL) {
+ if (!cmp_TrustDomainInfoBuffer(a->incoming_previous_auth_info[c],
+ b->incoming_previous_auth_info[c])) {
+ return false;
+ }
+ } else if (a->incoming_previous_auth_info == NULL &&
+ b->incoming_previous_auth_info == NULL) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+
+ for (c = 0; c < a->outgoing_count; c++) {
+ if (!cmp_TrustDomainInfoBuffer(a->outgoing_current_auth_info[c],
+ b->outgoing_current_auth_info[c])) {
+ return false;
+ }
+
+ if (a->outgoing_previous_auth_info != NULL &&
+ b->outgoing_previous_auth_info != NULL) {
+ if (!cmp_TrustDomainInfoBuffer(a->outgoing_previous_auth_info[c],
+ b->outgoing_previous_auth_info[c])) {
+ return false;
+ }
+ } else if (a->outgoing_previous_auth_info == NULL &&
+ b->outgoing_previous_auth_info == NULL) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool covert_and_compare(struct lsa_TrustDomainInfoAuthInfo *auth_info)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+ DATA_BLOB incoming;
+ DATA_BLOB outgoing;
+ struct lsa_TrustDomainInfoAuthInfo auth_info_out;
+ bool result = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return false;
+ }
+
+ status = auth_info_2_auth_blob(tmp_ctx, auth_info, &incoming, &outgoing);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = auth_blob_2_auth_info(tmp_ctx, incoming, outgoing,
+ &auth_info_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ result = cmp_auth_info(auth_info, &auth_info_out);
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+bool run_local_conv_auth_info(int dummy)
+{
+ struct lsa_TrustDomainInfoAuthInfo auth_info;
+ struct lsa_TrustDomainInfoBuffer ic[1];
+ struct lsa_TrustDomainInfoBuffer ip[1];
+ struct lsa_TrustDomainInfoBuffer oc[2];
+ struct lsa_TrustDomainInfoBuffer op[2];
+ uint32_t version = 3;
+
+ ic[0].last_update_time = 12345;
+ ic[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ic[0].data.size = strlen("iPaSsWoRd");
+ ic[0].data.data = discard_const_p(uint8_t, "iPaSsWoRd");
+
+ ip[0].last_update_time = 67890;
+ ip[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ip[0].data.size = strlen("OlDiPaSsWoRd");
+ ip[0].data.data = discard_const_p(uint8_t, "OlDiPaSsWoRd");
+
+ oc[0].last_update_time = 24580;
+ oc[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
+ oc[0].data.size = strlen("oPaSsWoRd");
+ oc[0].data.data = discard_const_p(uint8_t, "oPaSsWoRd");
+ oc[1].last_update_time = 24580;
+ oc[1].AuthType = TRUST_AUTH_TYPE_VERSION;
+ oc[1].data.size = 4;
+ oc[1].data.data = (uint8_t *) &version;
+
+ op[0].last_update_time = 13579;
+ op[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
+ op[0].data.size = strlen("OlDoPaSsWoRd");
+ op[0].data.data = discard_const_p(uint8_t, "OlDoPaSsWoRd");
+ op[1].last_update_time = 24580;
+ op[1].AuthType = TRUST_AUTH_TYPE_VERSION;
+ op[1].data.size = 4;
+ op[1].data.data = (uint8_t *) &version;
+
+ auth_info.incoming_count = 0;
+ auth_info.incoming_current_auth_info = NULL;
+ auth_info.incoming_previous_auth_info = NULL;
+ auth_info.outgoing_count = 0;
+ auth_info.outgoing_current_auth_info = NULL;
+ auth_info.outgoing_previous_auth_info = NULL;
+
+ if (!covert_and_compare(&auth_info)) {
+ return false;
+ }
+
+ auth_info.incoming_count = 1;
+ auth_info.incoming_current_auth_info = ic;
+ auth_info.incoming_previous_auth_info = NULL;
+ auth_info.outgoing_count = 0;
+ auth_info.outgoing_current_auth_info = NULL;
+ auth_info.outgoing_previous_auth_info = NULL;
+
+ if (!covert_and_compare(&auth_info)) {
+ return false;
+ }
+
+ auth_info.incoming_count = 0;
+ auth_info.incoming_current_auth_info = NULL;
+ auth_info.incoming_previous_auth_info = NULL;
+ auth_info.outgoing_count = 2;
+ auth_info.outgoing_current_auth_info = oc;
+ auth_info.outgoing_previous_auth_info = NULL;
+
+ if (!covert_and_compare(&auth_info)) {
+ return false;
+ }
+
+ auth_info.incoming_count = 1;
+ auth_info.incoming_current_auth_info = ic;
+ auth_info.incoming_previous_auth_info = NULL;
+ auth_info.outgoing_count = 2;
+ auth_info.outgoing_current_auth_info = oc;
+ auth_info.outgoing_previous_auth_info = NULL;
+
+ if (!covert_and_compare(&auth_info)) {
+ return false;
+ }
+
+ auth_info.incoming_count = 1;
+ auth_info.incoming_current_auth_info = ic;
+ auth_info.incoming_previous_auth_info = ip;
+ auth_info.outgoing_count = 2;
+ auth_info.outgoing_current_auth_info = oc;
+ auth_info.outgoing_previous_auth_info = op;
+
+ if (!covert_and_compare(&auth_info)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/torture/test_buffersize.c b/source3/torture/test_buffersize.c
new file mode 100644
index 0000000..217b148
--- /dev/null
+++ b/source3/torture/test_buffersize.c
@@ -0,0 +1,55 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test buffer sizes in cli_qpathinfo
+ Copyright (C) Volker Lendecke 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/secdesc.h"
+#include "libcli/security/security.h"
+#include "trans2.h"
+#include "source3/libsmb/clirap.h"
+
+bool run_qpathinfo_bufsize(int dummy)
+{
+ struct cli_state *cli = NULL;
+ bool ret = false;
+ int i;
+
+ printf("Starting qpathinfo_bufsize\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ printf("torture_open_connection failed\n");
+ goto fail;
+ }
+
+ for (i=0; i<500; i++) {
+ uint8_t *rdata;
+ uint32_t num_rdata;
+ cli_qpathinfo(cli, cli, "\\", SMB_FILE_ALL_INFORMATION,
+ 0, i, &rdata, &num_rdata);
+ }
+
+ ret = true;
+fail:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ return ret;
+}
diff --git a/source3/torture/test_case_insensitive.c b/source3/torture/test_case_insensitive.c
new file mode 100644
index 0000000..04e2dce
--- /dev/null
+++ b/source3/torture/test_case_insensitive.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ reproducer for bug 8042
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "libsmb/libsmb.h"
+
+/*
+ * Regression test file creates on case insensitive file systems (e.g. OS/X)
+ * https://bugzilla.samba.org/show_bug.cgi?id=8042
+ */
+
+bool run_case_insensitive_create(int dummy)
+{
+ struct cli_state *cli;
+ uint16_t fnum;
+ NTSTATUS status;
+
+ printf("Starting case_insensitive_create\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ status = cli_mkdir(cli, "x");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_mkdir failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+ status = cli_chkpath(cli, "X");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_chkpath failed: %s\n", nt_errstr(status));
+ goto rmdir;
+ }
+ status = cli_openx(cli, "x\\y", O_RDWR|O_CREAT, 0, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_openx failed: %s\n", nt_errstr(status));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("Bug 8042 reappeared!!\n");
+ }
+ goto unlink;
+ }
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+unlink:
+ status = cli_unlink(cli, "x\\y", 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_unlink failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+rmdir:
+ status = cli_rmdir(cli, "x");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed: %s\n", nt_errstr(status));
+ }
+done:
+ torture_close_connection(cli);
+ return NT_STATUS_IS_OK(status);
+}
diff --git a/source3/torture/test_chain3.c b/source3/torture/test_chain3.c
new file mode 100644
index 0000000..5320ef8
--- /dev/null
+++ b/source3/torture/test_chain3.c
@@ -0,0 +1,296 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test smbd chain routines
+
+ Copyright (C) Volker Lendecke 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "system/filesys.h"
+#include "async_smb.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/security/security.h"
+#include "libcli/smb/smbXcli_base.h"
+
+struct chain3_andx_state {
+ uint16_t fnum;
+ size_t written;
+ char str[6];
+};
+
+static void chain3_andx_open_done(struct tevent_req *subreq);
+static void chain3_andx_write_done(struct tevent_req *subreq);
+static void chain3_andx_close_done(struct tevent_req *subreq);
+
+static struct tevent_req *chain3_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req, *subreq;
+ struct tevent_req *smbreqs[3];
+ struct chain3_andx_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct chain3_andx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ strlcpy(state->str, "hello", sizeof(state->str));
+
+ subreq = cli_openx_create(state, ev, cli, fname,
+ O_CREAT|O_RDWR, 0, &smbreqs[0]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, chain3_andx_open_done, req);
+
+ subreq = cli_write_andx_create(state, ev, cli, 0, 0,
+ (const uint8_t *)state->str, 0,
+ strlen(state->str)+1,
+ smbreqs, 1, &smbreqs[1]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, chain3_andx_write_done, req);
+
+ subreq = cli_smb1_close_create(state, ev, cli, 0, &smbreqs[2]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, chain3_andx_close_done, req);
+
+ status = smb1cli_req_chain_submit(smbreqs, ARRAY_SIZE(smbreqs));
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void chain3_andx_open_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct chain3_andx_state *state = tevent_req_data(
+ req, struct chain3_andx_state);
+ NTSTATUS status;
+
+ status = cli_openx_recv(subreq, &state->fnum);
+ printf("cli_openx returned %s, fnum=%u\n", nt_errstr(status),
+ (unsigned)state->fnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+}
+
+static void chain3_andx_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct chain3_andx_state *state = tevent_req_data(
+ req, struct chain3_andx_state);
+ NTSTATUS status;
+
+ status = cli_write_andx_recv(subreq, &state->written);
+ printf("cli_write_andx returned %s, written=%u\n", nt_errstr(status),
+ (unsigned)state->written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+}
+
+static void chain3_andx_close_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ printf("cli_close returned %s\n", nt_errstr(status));
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS chain3_andx_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct chain3_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname;
+ uint16_t fnum;
+};
+
+static void chain3_got_break(struct tevent_req *subreq);
+static void chain3_ntcreate_done(struct tevent_req *subreq);
+static void chain3_break_close_done(struct tevent_req *subreq);
+static void chain3_andx_done(struct tevent_req *subreq);
+
+static struct tevent_req *chain3_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct tevent_req *req, *subreq;
+ struct chain3_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct chain3_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->fname = "chain3.txt";
+
+ if (!torture_open_connection(&state->cli, 0)) {
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_oplock_break_waiter_send(
+ state, state->ev, state->cli);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, chain3_got_break, req);
+
+ subreq = cli_ntcreate_send(
+ state, state->ev, state->cli, state->fname,
+ REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK,
+ GENERIC_READ_ACCESS|GENERIC_WRITE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, chain3_ntcreate_done, req);
+ return req;
+}
+
+static void chain3_got_break(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct chain3_state *state = tevent_req_data(
+ req, struct chain3_state);
+ uint16_t fnum;
+ uint8_t level;
+ NTSTATUS status;
+
+ status = cli_smb_oplock_break_waiter_recv(subreq, &fnum, &level);
+ TALLOC_FREE(subreq);
+ printf("cli_smb_oplock_break_waiter_recv returned %s\n",
+ nt_errstr(status));
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_close_send(state, state->ev, state->cli, fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, chain3_break_close_done, req);
+}
+
+static void chain3_break_close_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ printf("cli_close_recv returned %s\n", nt_errstr(status));
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+}
+
+static void chain3_ntcreate_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct chain3_state *state = tevent_req_data(
+ req, struct chain3_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
+ TALLOC_FREE(subreq);
+ printf("cli_ntcreate returned %s, fnum=%u\n", nt_errstr(status),
+ (unsigned)state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = chain3_andx_send(state, state->ev, state->cli, state->fname);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, chain3_andx_done, req);
+}
+
+static void chain3_andx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = chain3_andx_recv(subreq);
+ TALLOC_FREE(subreq);
+ printf("chain3_andx_recv returned %s\n", nt_errstr(status));
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS chain3_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+bool run_chain3(int dummy)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = chain3_send(frame, ev);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = chain3_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ printf("run_chain3 returns %s\n", nt_errstr(status));
+ return NT_STATUS_IS_OK(status);
+}
diff --git a/source3/torture/test_cleanup.c b/source3/torture/test_cleanup.c
new file mode 100644
index 0000000..2ff5cf6
--- /dev/null
+++ b/source3/torture/test_cleanup.c
@@ -0,0 +1,240 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test cleanup behaviour
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "locking/proto.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "system/select.h"
+#include "libsmb/libsmb.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/open_files.h"
+
+bool run_cleanup1(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\cleanup1";
+ uint16_t fnum;
+ NTSTATUS status;
+
+ printf("CLEANUP1: Checking that a conflicting share mode is cleaned "
+ "up\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_ALL, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+ status = smbXcli_conn_samba_suicide(cli->conn, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_conn_samba_suicide failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 1)) {
+ return false;
+ }
+ status = cli_ntcreate(
+ cli, fname, 0,
+ FILE_GENERIC_READ|FILE_GENERIC_WRITE|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, FILE_DELETE_ON_CLOSE, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("2nd open of %s failed (%s)\n", fname,
+ nt_errstr(status));
+ return false;
+ }
+ cli_close(cli, fnum);
+
+ torture_close_connection(cli);
+ return NT_STATUS_IS_OK(status);
+}
+
+bool run_cleanup2(int dummy)
+{
+ struct cli_state *cli1, *cli2, *cli3;
+ const char *fname = "\\cleanup2";
+ uint16_t fnum1, fnum2, fnum3;
+ NTSTATUS status;
+ char buf;
+
+ printf("CLEANUP2: Checking that a conflicting brlock is cleaned up\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return false;
+ }
+ status = cli_ntcreate(
+ cli1, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+ status = cli_lock32(cli1, fnum1, 0, 1, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!torture_open_connection(&cli3, 1)) {
+ return false;
+ }
+ status = cli_ntcreate(
+ cli3, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum3, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+ status = cli_lock32(cli3, fnum3, 1, 1, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_lock32(cli1, fnum1, 2, 1, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Check the file is indeed locked
+ */
+ if (!torture_open_connection(&cli2, 1)) {
+ return false;
+ }
+ status = cli_ntcreate(
+ cli2, fname, 0, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+ buf = 'x';
+ status = cli_smbwrite(cli2, fnum2, &buf, 0, 1, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("write succeeded\n");
+ return false;
+ }
+
+ /*
+ * Kill the lock holder
+ */
+ status = smbXcli_conn_samba_suicide(cli1->conn, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_conn_samba_suicide failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Give the suicidal smbd a bit of time to really pass away
+ */
+ smb_msleep(1000);
+
+ status = cli_smbwrite(cli2, fnum2, &buf, 0, 1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+bool run_cleanup4(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\cleanup4";
+ uint16_t fnum1, fnum2;
+ NTSTATUS status;
+
+ printf("CLEANUP4: Checking that a conflicting share mode is cleaned "
+ "up\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return false;
+ }
+ if (!torture_open_connection(&cli2, 0)) {
+ return false;
+ }
+
+ status = cli_ntcreate(
+ cli1, fname, 0,
+ FILE_GENERIC_READ|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("creating file failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = cli_ntcreate(
+ cli2, fname, 0,
+ FILE_GENERIC_READ|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("opening file 1st time failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smbXcli_conn_samba_suicide(cli1->conn, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_conn_samba_suicide failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * The next open will conflict with both opens above. The first open
+ * above will be correctly cleaned up. A bug in smbd iterating over
+ * the share mode array made it skip the share conflict check for the
+ * second open. Trigger this bug.
+ */
+
+ status = cli_ntcreate(
+ cli2, fname, 0,
+ FILE_GENERIC_WRITE|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ printf("opening file 2nd time returned: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/torture/test_ctdbd_conn.c b/source3/torture/test_ctdbd_conn.c
new file mode 100644
index 0000000..124a334
--- /dev/null
+++ b/source3/torture/test_ctdbd_conn.c
@@ -0,0 +1,312 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test async ctdb_req_send/recv
+ * Copyright (C) Volker Lendecke 2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "ctdbd_conn.h"
+#include "lib/cluster_support.h"
+#include "ctdb/include/ctdb_protocol.h"
+#include "lib/util/tevent_unix.h"
+
+extern int torture_nprocs;
+extern int torture_numops;
+
+struct ctdb_echo_state {
+ struct ctdb_req_control_old req;
+ struct iovec iov[2];
+ TDB_DATA echodata;
+};
+
+static void ctdb_echo_done(struct tevent_req *subreq);
+
+static struct tevent_req *ctdb_echo_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdbd_connection *conn,
+ uint32_t delay)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct ctdb_echo_state *state = NULL;
+ struct ctdb_req_header *hdr = NULL;
+ uint32_t datalen;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct ctdb_echo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ hdr = &state->req.hdr;
+ ctdbd_prep_hdr_next_reqid(conn, hdr);
+ hdr->operation = CTDB_REQ_CONTROL;
+ state->req.opcode = CTDB_CONTROL_ECHO_DATA;
+
+ state->iov[0] = (struct iovec) {
+ .iov_base = &state->req,
+ .iov_len = offsetof(struct ctdb_req_control_old, data),
+ };
+
+ datalen = generate_random() % 1024;
+
+ state->echodata.dptr = talloc_array(state, uint8_t, datalen+8);
+ if (tevent_req_nomem(state->echodata.dptr, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->echodata.dsize = talloc_get_size(state->echodata.dptr);
+ generate_random_buffer(
+ state->echodata.dptr, state->echodata.dsize);
+
+ memcpy(state->echodata.dptr, &delay, sizeof(delay));
+ memcpy(state->echodata.dptr+4, &datalen, sizeof(datalen));
+
+ state->req.datalen = state->echodata.dsize;
+
+ state->iov[1] = (struct iovec) {
+ .iov_base = state->echodata.dptr,
+ .iov_len = state->echodata.dsize,
+ };
+
+ hdr->length =
+ offsetof(struct ctdb_req_control_old, data) +
+ state->req.datalen;
+
+ subreq = ctdbd_req_send(
+ state, ev, conn, state->iov, ARRAY_SIZE(state->iov));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ctdb_echo_done, req);
+
+ return req;
+}
+
+static void ctdb_echo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ctdb_echo_state *state = tevent_req_data(
+ req, struct ctdb_echo_state);
+ struct ctdb_req_header *hdr = NULL;
+ struct ctdb_reply_control_old *reply = NULL;
+ int cmp, ret;
+
+ ret = ctdbd_req_recv(subreq, state, &hdr);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ printf("ctdbd_req_recv(%"PRIu32") returned %d (%s)\n",
+ state->req.hdr.reqid,
+ ret,
+ strerror(ret));
+ return;
+ }
+ if (hdr->operation != CTDB_REPLY_CONTROL) {
+ printf("Expected CTDB_REPLY_CONTROL, got %"PRIu32"\n",
+ hdr->operation);
+ tevent_req_error(req, EIO);
+ return;
+ }
+ reply = (struct ctdb_reply_control_old *)hdr;
+ if (reply->status != 0) {
+ printf("reply->status = %"PRIi32"\n", reply->status);
+ tevent_req_error(req, EIO);
+ return;
+ }
+ if (reply->datalen != state->req.datalen) {
+ printf("state->echodata.dsize=%zu datalen=%"PRIu32"\n",
+ state->echodata.dsize,
+ reply->datalen);
+ tevent_req_error(req, EIO);
+ return;
+ }
+ cmp = memcmp(reply->data,
+ state->echodata.dptr,
+ state->echodata.dsize);
+ if (cmp != 0) {
+ printf("data mismatch\n");
+ tevent_req_error(req, EIO);
+ return;
+ }
+ TALLOC_FREE(reply);
+ tevent_req_done(req);
+}
+
+static int ctdb_echo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+struct ctdb_ping_flood_state {
+ struct tevent_context *ev;
+ struct ctdbd_connection *conn;
+ size_t num_running;
+ bool done;
+};
+
+static void ctdb_ping_flood_next(struct tevent_req *subreq);
+static void ctdb_ping_flood_done(struct tevent_req *subreq);
+
+static struct tevent_req *ctdb_ping_flood_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdbd_connection *conn,
+ size_t num_parallel,
+ unsigned usecs)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct ctdb_ping_flood_state *state = NULL;
+ size_t i;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct ctdb_ping_flood_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->conn = conn;
+
+ for (i=0; i<num_parallel; i++) {
+ subreq = ctdb_echo_send(
+ state,
+ state->ev,
+ state->conn,
+ generate_random() % 10);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ctdb_ping_flood_next, req);
+ }
+ state->num_running = num_parallel;
+
+ subreq = tevent_wakeup_send(
+ state,
+ ev,
+ tevent_timeval_current_ofs(0, usecs));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ctdb_ping_flood_done, req);
+
+ return req;
+}
+
+static void ctdb_ping_flood_next(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ctdb_ping_flood_state *state = tevent_req_data(
+ req, struct ctdb_ping_flood_state);
+ int ret;
+
+ ret = ctdb_echo_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ state->num_running -= 1;
+
+ if (state->done) {
+ if (state->num_running == 0) {
+ tevent_req_done(req);
+ }
+ return;
+ }
+
+ subreq = ctdb_echo_send(
+ state,
+ state->ev,
+ state->conn,
+ generate_random() % 10);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, ctdb_ping_flood_next, req);
+ state->num_running += 1;
+}
+
+static void ctdb_ping_flood_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ctdb_ping_flood_state *state = tevent_req_data(
+ req, struct ctdb_ping_flood_state);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ state->done = true;
+}
+
+static int ctdb_ping_flood_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+bool run_ctdbd_conn1(int dummy)
+{
+ struct ctdbd_connection *conn = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ int ret;
+ bool ok;
+ bool result = false;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("samba_tevent_context_init failed\n");
+ goto done;
+ }
+
+ ret = ctdbd_init_async_connection(
+ ev, lp_ctdbd_socket(), 0, &conn);
+ if (ret != 0) {
+ printf("ctdbd_init_async_connection failed: %s\n",
+ strerror(ret));
+ goto done;
+ }
+
+ req = ctdb_ping_flood_send(
+ ev, ev, conn, torture_nprocs, torture_numops * 1000);
+ if (req == NULL) {
+ printf("ctdb_ping_flood_send failed\n");
+ goto done;
+ }
+
+ ok = tevent_req_poll_unix(req, ev, &ret);
+ if (!ok) {
+ printf("tevent_req_poll_unix failed: %s\n",
+ strerror(ret));
+ goto done;
+ }
+
+ ret = ctdb_ping_flood_recv(req);
+ TALLOC_FREE(req);
+ if (ret != 0) {
+ printf("ctdb_ping_flood failed: %s\n", strerror(ret));
+ goto done;
+ }
+
+ result = true;
+done:
+ TALLOC_FREE(conn);
+ return result;
+}
diff --git a/source3/torture/test_dbwrap_ctdb.c b/source3/torture/test_dbwrap_ctdb.c
new file mode 100644
index 0000000..e3a7c6a
--- /dev/null
+++ b/source3/torture/test_dbwrap_ctdb.c
@@ -0,0 +1,163 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test dbwrap_ctdb API
+ * Copyright (C) Volker Lendecke 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_ctdb.h"
+#include "messages.h"
+#include "lib/messages_ctdb.h"
+#include "lib/global_contexts.h"
+
+bool run_local_dbwrap_ctdb1(int dummy)
+{
+ struct db_context *db = NULL;
+ int res;
+ bool ret = false;
+ NTSTATUS status;
+ uint32_t val;
+ struct messaging_context *msg_ctx;
+
+ msg_ctx = global_messaging_context();
+
+ db = db_open_ctdb(
+ talloc_tos(),
+ msg_ctx,
+ "torture.tdb",
+ 0,
+ TDB_DEFAULT,
+ O_RDWR|O_CREAT,
+ 0755,
+ DBWRAP_LOCK_ORDER_1,
+ DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ perror("db_open_ctdb failed");
+ goto fail;
+ }
+
+ res = dbwrap_transaction_start(db);
+ if (res != 0) {
+ fprintf(stderr, "dbwrap_transaction_start failed");
+ goto fail;
+ }
+ res = dbwrap_transaction_cancel(db);
+ if (res != 0) {
+ fprintf(stderr, "dbwrap_transaction_cancel failed");
+ goto fail;
+ }
+
+ res = dbwrap_transaction_start(db);
+ if (res != 0) {
+ fprintf(stderr, "dbwrap_transaction_start failed");
+ goto fail;
+ }
+
+ status = dbwrap_store_uint32_bystring(db, "foo", 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "store_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ status = dbwrap_fetch_uint32_bystring(db, "foo", &val);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (val != 1) {
+ fprintf(stderr, "fetch_uint32 gave %u, expected 1",
+ (unsigned)val);
+ goto fail;
+ }
+
+ status = dbwrap_store_uint32_bystring(db, "bar", 5);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "store_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ status = dbwrap_fetch_uint32_bystring(db, "bar", &val);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (val != 5) {
+ fprintf(stderr, "fetch_uint32 gave %u, expected 5",
+ (unsigned)val);
+ goto fail;
+ }
+
+ status = dbwrap_store_uint32_bystring(db, "foo", 2);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "store_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ status = dbwrap_fetch_uint32_bystring(db, "foo", &val);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (val != 2) {
+ fprintf(stderr, "fetch_uint32 gave %u, expected 2",
+ (unsigned)val);
+ goto fail;
+ }
+
+ res = dbwrap_transaction_commit(db);
+ if (res != 0) {
+ fprintf(stderr, "dbwrap_transaction_commit failed");
+ goto fail;
+ }
+
+ /*
+ * check that the values have reached the disk
+ */
+ status = dbwrap_fetch_uint32_bystring(db, "foo", &val);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (val != 2) {
+ fprintf(stderr, "fetch_uint32 gave %u, expected 1",
+ (unsigned)val);
+ goto fail;
+ }
+
+ status = dbwrap_fetch_uint32_bystring(db, "bar", &val);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (val != 5) {
+ fprintf(stderr, "fetch_uint32 gave %u, expected 1",
+ (unsigned)val);
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(db);
+ return ret;
+}
diff --git a/source3/torture/test_dbwrap_do_locked.c b/source3/torture/test_dbwrap_do_locked.c
new file mode 100644
index 0000000..93648ce
--- /dev/null
+++ b/source3/torture/test_dbwrap_do_locked.c
@@ -0,0 +1,161 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test dbwrap_watch API
+ * Copyright (C) Volker Lendecke 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "lib/dbwrap/dbwrap_watch.h"
+#include "lib/util/util_tdb.h"
+#include "source3/include/util_tdb.h"
+#include "lib/global_contexts.h"
+
+struct do_locked1_state {
+ TDB_DATA value;
+ NTSTATUS status;
+};
+
+static void do_locked1_cb(
+ struct db_record *rec,
+ TDB_DATA value,
+ void *private_data)
+{
+ struct do_locked1_state *state =
+ (struct do_locked1_state *)private_data;
+
+ state->status = dbwrap_record_store(rec, state->value, 0);
+}
+
+static void do_locked1_check(TDB_DATA key, TDB_DATA value,
+ void *private_data)
+{
+ struct do_locked1_state *state =
+ (struct do_locked1_state *)private_data;
+ int ret;
+
+ ret = tdb_data_cmp(value, state->value);
+ if (ret != 0) {
+ state->status = NT_STATUS_DATA_ERROR;
+ return;
+ }
+
+ state->status = NT_STATUS_OK;
+}
+
+static void do_locked1_del(
+ struct db_record *rec,
+ TDB_DATA value,
+ void *private_data)
+{
+ struct do_locked1_state *state =
+ (struct do_locked1_state *)private_data;
+
+ state->status = dbwrap_record_delete(rec);
+}
+
+bool run_dbwrap_do_locked1(int dummy)
+{
+ struct tevent_context *ev;
+ struct messaging_context *msg;
+ struct db_context *backend;
+ struct db_context *db;
+ const char *dbname = "test_do_locked.tdb";
+ const char *keystr = "key";
+ TDB_DATA key = string_term_tdb_data(keystr);
+ const char *valuestr = "value";
+ TDB_DATA value = string_term_tdb_data(valuestr);
+ struct do_locked1_state state = { .value = value };
+ int ret = false;
+ NTSTATUS status;
+
+ ev = global_event_context();
+ if (ev == NULL) {
+ fprintf(stderr, "global_event_context() failed\n");
+ return false;
+ }
+ msg = global_messaging_context();
+ if (msg == NULL) {
+ fprintf(stderr, "global_messaging_context() failed\n");
+ return false;
+ }
+
+ backend = db_open(talloc_tos(), dbname, 0,
+ TDB_CLEAR_IF_FIRST, O_CREAT|O_RDWR, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (backend == NULL) {
+ fprintf(stderr, "db_open failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ db = db_open_watched(talloc_tos(), &backend, msg);
+ if (db == NULL) {
+ fprintf(stderr, "db_open_watched failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+
+ status = dbwrap_do_locked(db, key, do_locked1_cb, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_do_locked failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ fprintf(stderr, "store returned %s\n",
+ nt_errstr(state.status));
+ goto fail;
+ }
+
+ status = dbwrap_parse_record(db, key, do_locked1_check, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_parse_record failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ fprintf(stderr, "data compare returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dbwrap_do_locked(db, key, do_locked1_del, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_do_locked failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ fprintf(stderr, "delete returned %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = dbwrap_parse_record(db, key, do_locked1_check, &state);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ fprintf(stderr, "parse_record returned %s, "
+ "expected NOT_FOUND\n", nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(db);
+ unlink(dbname);
+ return ret;
+}
diff --git a/source3/torture/test_dbwrap_watch.c b/source3/torture/test_dbwrap_watch.c
new file mode 100644
index 0000000..480b2c5
--- /dev/null
+++ b/source3/torture/test_dbwrap_watch.c
@@ -0,0 +1,467 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test dbwrap_watch API
+ Copyright (C) Volker Lendecke 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "lib/dbwrap/dbwrap_watch.h"
+#include "lib/util/util_tdb.h"
+
+static bool test_dbwrap_watch_init(
+ TALLOC_CTX *mem_ctx,
+ const char *dbname,
+ struct tevent_context **pev,
+ struct messaging_context **pmsg,
+ struct db_context **pbackend,
+ struct db_context **pdb)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct db_context *backend = NULL;
+ struct db_context *db = NULL;
+
+ ev = samba_tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ goto fail;
+ }
+
+ msg = messaging_init(ev, ev);
+ if (msg == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ goto fail;
+ }
+
+ backend = db_open(
+ msg,
+ dbname,
+ 0,
+ TDB_CLEAR_IF_FIRST,
+ O_CREAT|O_RDWR,
+ 0644,
+ DBWRAP_LOCK_ORDER_1,
+ DBWRAP_FLAG_NONE);
+ if (backend == NULL) {
+ fprintf(stderr, "db_open failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ {
+ struct db_context *backend_copy = backend;
+
+ db = db_open_watched(ev, &backend_copy, msg);
+ if (db == NULL) {
+ fprintf(stderr, "db_open_watched failed\n");
+ goto fail;
+ }
+ }
+
+ if (pev != NULL) {
+ *pev = ev;
+ }
+ if (pmsg != NULL) {
+ *pmsg = msg;
+ }
+ if (pbackend != NULL) {
+ *pbackend = backend;
+ }
+ if (pdb != NULL) {
+ *pdb = db;
+ }
+ return true;
+
+fail:
+ TALLOC_FREE(backend);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return false;
+}
+
+bool run_dbwrap_watch1(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct db_context *backend = NULL;
+ struct db_context *db = NULL;
+ const char *keystr = "key";
+ TDB_DATA key = string_term_tdb_data(keystr);
+ struct db_record *rec = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status;
+ bool ret = false;
+
+ ret = test_dbwrap_watch_init(
+ talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
+ if (!ret) {
+ goto fail;
+ }
+
+ rec = dbwrap_fetch_locked(db, db, key);
+ if (rec == NULL) {
+ fprintf(stderr, "dbwrap_fetch_locked failed\n");
+ goto fail;
+ }
+ req = dbwrap_watched_watch_send(talloc_tos(), ev, rec,
+ 0, /* resume_instance */
+ (struct server_id){0});
+ if (req == NULL) {
+ fprintf(stderr, "dbwrap_record_watch_send failed\n");
+ goto fail;
+ }
+ TALLOC_FREE(rec);
+
+ status = dbwrap_store_int32_bystring(db, "different_key", 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_store_int32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dbwrap_store_int32_bystring(db, keystr, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_store_int32 failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (!tevent_req_poll(req, ev)) {
+ fprintf(stderr, "tevent_req_poll failed\n");
+ goto fail;
+ }
+
+ status = dbwrap_watched_watch_recv(req, NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_record_watch_recv failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ (void)unlink("test_watch.tdb");
+ ret = true;
+fail:
+ TALLOC_FREE(req);
+ TALLOC_FREE(rec);
+ TALLOC_FREE(db);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+/*
+ * Make sure dbwrap_parse_record does not return NT_STATUS_OK on
+ * invalid data
+ */
+
+bool run_dbwrap_watch2(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct db_context *backend = NULL;
+ struct db_context *db = NULL;
+ const char *keystr = "key";
+ TDB_DATA key = string_term_tdb_data(keystr);
+ NTSTATUS status;
+ bool ret = false;
+
+ ret = test_dbwrap_watch_init(
+ talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
+ if (!ret) {
+ goto fail;
+ }
+
+ /*
+ * Store invalid data (from the dbwrap_watch point of view)
+ * directly into the backend database
+ */
+ status = dbwrap_store_uint32_bystring(backend, keystr, UINT32_MAX);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "dbwrap_store_uint32_bystring failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dbwrap_parse_record(db, key, NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ fprintf(stderr, "dbwrap_parse_record returned %s, expected "
+ "NT_STATUS_NOT_FOUND\n", nt_errstr(status));
+ goto fail;
+ }
+
+ (void)unlink("test_watch.tdb");
+ ret = true;
+fail:
+ TALLOC_FREE(db);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+/*
+ * Test autocleanup of dead watchers
+ */
+
+bool run_dbwrap_watch3(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct db_context *backend = NULL;
+ struct db_context *db = NULL;
+ const char *keystr = "key";
+ TDB_DATA key = string_term_tdb_data(keystr);
+ NTSTATUS status;
+ bool ret = false;
+ pid_t child, waited;
+ int wstatus, exit_status;
+
+ BlockSignals(true, SIGCHLD);
+
+ child = fork();
+ if (child == -1) {
+ fprintf(stderr,
+ "fork failed: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ ret = test_dbwrap_watch_init(
+ talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
+ if (!ret) {
+ goto fail;
+ }
+
+ if (child == 0) {
+ struct db_record *rec = dbwrap_fetch_locked(db, db, key);
+ struct tevent_req *req = NULL;
+
+ if (rec == NULL) {
+ fprintf(stderr, "dbwrap_fetch_locked failed\n");
+ exit(1);
+ }
+
+ req = dbwrap_watched_watch_send(
+ db, ev, rec, 0, (struct server_id) { 0 });
+ if (req == NULL) {
+ fprintf(stderr, "dbwrap_watched_watch_send failed\n");
+ exit(2);
+ }
+
+ exit(0);
+ }
+
+ waited = waitpid(child, &wstatus, 0);
+ if (waited == -1) {
+ fprintf(stderr, "waitpid failed: %s\n", strerror(errno));
+ goto fail;
+ }
+ if (!WIFEXITED(wstatus)) {
+ fprintf(stderr, "child did not exit normally\n");
+ goto fail;
+ }
+
+ exit_status = WEXITSTATUS(wstatus);
+ if (exit_status != 0) {
+ fprintf(stderr, "exit status is %d\n", exit_status);
+ goto fail;
+ }
+
+ status = dbwrap_store_uint32_bystring(db, keystr, 1);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ fprintf(stderr,
+ "dbwrap_store_uint32 returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ (void)unlink("test_watch.tdb");
+ ret = true;
+fail:
+ TALLOC_FREE(db);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+/*
+ * Test that we can't add two watchers in the same
+ * fetch_lock/do_locked round
+ */
+
+struct dbwrap_watch4_state {
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct db_context *db;
+ TDB_DATA key;
+
+ NTSTATUS status;
+
+ struct tevent_req *req1;
+ NTSTATUS status1;
+
+ struct tevent_req *req2;
+ NTSTATUS status2;
+};
+
+static void dbwrap_watch4_done1(struct tevent_req *subreq);
+static void dbwrap_watch4_done2(struct tevent_req *subreq);
+
+static void dbwrap_watch4_fn(struct db_record *rec,
+ TDB_DATA value,
+ void *private_data)
+{
+ struct dbwrap_watch4_state *state = private_data;
+ bool ok;
+
+ state->req1 = dbwrap_watched_watch_send(
+ state->mem_ctx, state->ev, rec, 0, (struct server_id) { .pid=0 });
+ if (state->req1 == NULL) {
+ goto nomem;
+ }
+ tevent_req_set_callback(state->req1, dbwrap_watch4_done1, state);
+ state->status1 = NT_STATUS_EVENT_PENDING;
+
+ ok = tevent_req_set_endtime(
+ state->req1, state->ev, timeval_current_ofs(1, 0));
+ if (!ok) {
+ goto nomem;
+ }
+
+ state->req2 = dbwrap_watched_watch_send(
+ state->mem_ctx, state->ev, rec, 0, (struct server_id) { .pid=0 });
+ if (state->req2 == NULL) {
+ goto nomem;
+ }
+ tevent_req_set_callback(state->req2, dbwrap_watch4_done2, state);
+ state->status2 = NT_STATUS_EVENT_PENDING;
+
+ ok = tevent_req_set_endtime(
+ state->req2, state->ev, timeval_current_ofs(1, 0));
+ if (!ok) {
+ goto nomem;
+ }
+
+ state->status = NT_STATUS_OK;
+ return;
+
+ nomem:
+ state->status = NT_STATUS_NO_MEMORY;
+}
+
+static void dbwrap_watch4_done1(struct tevent_req *subreq)
+{
+ struct dbwrap_watch4_state *state = tevent_req_callback_data_void(subreq);
+ state->status1 = dbwrap_watched_watch_recv(subreq, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ printf("req1 finished: %s\n", nt_errstr(state->status1));
+ state->req1 = NULL;
+}
+
+static void dbwrap_watch4_done2(struct tevent_req *subreq)
+{
+ struct dbwrap_watch4_state *state = tevent_req_callback_data_void(subreq);
+ state->status2 = dbwrap_watched_watch_recv(subreq, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ printf("req2 finished: %s\n", nt_errstr(state->status2));
+ state->req2 = NULL;
+}
+
+bool run_dbwrap_watch4(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct db_context *backend = NULL;
+ struct db_context *db = NULL;
+ const char *keystr = "key";
+ TDB_DATA key = string_term_tdb_data(keystr);
+ struct dbwrap_watch4_state state = { 0 };
+ NTSTATUS status;
+ bool ret = false;
+ bool ok;
+
+ ok = test_dbwrap_watch_init(
+ talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
+ if (!ok) {
+ goto fail;
+ }
+
+ state = (struct dbwrap_watch4_state) {
+ .mem_ctx = talloc_tos(),
+ .ev = ev,
+ .db = db,
+ .key = key,
+ };
+
+ status = dbwrap_do_locked(db, key, dbwrap_watch4_fn, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "dbwrap_do_locked failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ fprintf(stderr,
+ "dbwrap_watch4_fn failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dbwrap_store(db, key, key, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "dbwrap_store failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ while (NT_STATUS_EQUAL(state.status1, NT_STATUS_EVENT_PENDING) ||
+ NT_STATUS_EQUAL(state.status2, NT_STATUS_EVENT_PENDING)) {
+ int res = tevent_loop_once(ev);
+ if (res != 0) {
+ fprintf(stderr,
+ "tevent_loop_once failed: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(state.status1)) {
+ fprintf(stderr,
+ "req1 returned %s\n",
+ nt_errstr(state.status1));
+ goto fail;
+ }
+
+ if (!NT_STATUS_EQUAL(state.status2, NT_STATUS_REQUEST_NOT_ACCEPTED)) {
+ fprintf(stderr,
+ "req2 returned %s\n",
+ nt_errstr(state.status2));
+ goto fail;
+ }
+
+ (void)unlink("test_watch.tdb");
+ ret = true;
+fail:
+ TALLOC_FREE(state.req2);
+ TALLOC_FREE(state.req1);
+ TALLOC_FREE(db);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
diff --git a/source3/torture/test_g_lock.c b/source3/torture/test_g_lock.c
new file mode 100644
index 0000000..fee9127
--- /dev/null
+++ b/source3/torture/test_g_lock.c
@@ -0,0 +1,1403 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test g_lock API
+ * Copyright (C) Volker Lendecke 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "g_lock.h"
+#include "messages.h"
+#include "lib/util/server_id.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/global_contexts.h"
+
+static bool get_g_lock_ctx(TALLOC_CTX *mem_ctx,
+ struct tevent_context **ev,
+ struct messaging_context **msg,
+ struct g_lock_ctx **ctx)
+{
+ *ev = global_event_context();
+ if (*ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ return false;
+ }
+ *msg = global_messaging_context();
+ if (*msg == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ TALLOC_FREE(*ev);
+ return false;
+ }
+ *ctx = g_lock_ctx_init(*ev, *msg);
+ if (*ctx == NULL) {
+ fprintf(stderr, "g_lock_ctx_init failed\n");
+ TALLOC_FREE(*msg);
+ TALLOC_FREE(*ev);
+ return false;
+ }
+
+ return true;
+}
+
+bool run_g_lock1(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock1";
+ NTSTATUS status;
+ bool ret = false;
+ bool ok;
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WAS_LOCKED)) {
+ fprintf(stderr, "Double lock got %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_unlock failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ fprintf(stderr, "g_lock_unlock returned: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+struct lock2_parser_state {
+ uint8_t *rdata;
+ bool ok;
+};
+
+static void lock2_parser(struct server_id exclusive,
+ size_t num_shared,
+ const struct server_id *shared,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct lock2_parser_state *state = private_data;
+
+ if (datalen != sizeof(uint8_t)) {
+ return;
+ }
+ *state->rdata = *data;
+ state->ok = true;
+}
+
+/*
+ * Test g_lock_write_data
+ */
+
+bool run_g_lock2(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock2";
+ uint8_t data = 42;
+ uint8_t rdata;
+ struct lock2_parser_state state = { .rdata = &rdata };
+ NTSTATUS status;
+ bool ret = false;
+ bool ok;
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ status = g_lock_write_data(ctx, string_term_tdb_data(lockname),
+ &data, sizeof(data));
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_LOCKED)) {
+ fprintf(stderr, "unlocked g_lock_write_data returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_write_data(ctx, string_term_tdb_data(lockname),
+ &data, sizeof(data));
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_write_data failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_unlock failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_dump(ctx, string_term_tdb_data(lockname),
+ lock2_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_dump failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (!state.ok) {
+ fprintf(stderr, "Could not parse data\n");
+ goto fail;
+ }
+ if (rdata != data) {
+ fprintf(stderr, "Returned %"PRIu8", expected %"PRIu8"\n",
+ rdata, data);
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+struct lock3_parser_state {
+ struct server_id self;
+ enum g_lock_type lock_type;
+ bool ok;
+};
+
+static void lock3_parser(struct server_id exclusive,
+ size_t num_shared,
+ const struct server_id *shared,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct lock3_parser_state *state = private_data;
+ size_t num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
+ const struct server_id *pid;
+
+ if (datalen != 0) {
+ fprintf(stderr, "datalen=%zu\n", datalen);
+ return;
+ }
+ if (num_locks != 1) {
+ fprintf(stderr, "num_locks=%zu\n", num_locks);
+ return;
+ }
+
+ if (state->lock_type == G_LOCK_WRITE) {
+ if (exclusive.pid == 0) {
+ fprintf(stderr, "Found READ, expected WRITE\n");
+ return;
+ }
+ } else {
+ if (exclusive.pid != 0) {
+ fprintf(stderr, "Found WRITE, expected READ\n");
+ return;
+ }
+ }
+
+ pid = (exclusive.pid != 0) ? &exclusive : &shared[0];
+
+ if (!server_id_equal(pid, &state->self)) {
+ struct server_id_buf tmp1, tmp2;
+ fprintf(stderr, "found pid %s, expected %s\n",
+ server_id_str_buf(*pid, &tmp1),
+ server_id_str_buf(state->self, &tmp2));
+ return;
+ }
+
+ state->ok = true;
+}
+
+/*
+ * Test lock upgrade/downgrade
+ */
+
+bool run_g_lock3(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock3";
+ struct lock3_parser_state state;
+ NTSTATUS status;
+ bool ret = false;
+ bool ok;
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ state.self = messaging_server_id(msg);
+
+ status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_READ,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ state.lock_type = G_LOCK_READ;
+ state.ok = false;
+
+ status = g_lock_dump(ctx, string_term_tdb_data(lockname),
+ lock3_parser, &state);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ fprintf(stderr, "g_lock_dump returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!state.ok) {
+ goto fail;
+ }
+
+ status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_UPGRADE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ state.lock_type = G_LOCK_WRITE;
+ state.ok = false;
+
+ status = g_lock_dump(ctx, string_term_tdb_data(lockname),
+ lock3_parser, &state);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ fprintf(stderr, "g_lock_dump returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!state.ok) {
+ goto fail;
+ }
+
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+static bool lock4_child(const char *lockname,
+ enum g_lock_type lock_type,
+ int ready_pipe,
+ int exit_pipe)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ NTSTATUS status;
+ ssize_t n;
+ bool ok;
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ return false;
+ }
+
+ status = g_lock_lock(
+ ctx,
+ string_term_tdb_data(lockname),
+ lock_type,
+ (struct timeval) { .tv_sec = 1 },
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "child: g_lock_lock returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ n = sys_write(ready_pipe, &ok, sizeof(ok));
+ if (n != sizeof(ok)) {
+ fprintf(stderr, "child: write failed\n");
+ return false;
+ }
+
+ if (ok) {
+ n = sys_read(exit_pipe, &ok, sizeof(ok));
+ if (n != 0) {
+ fprintf(stderr, "child: read failed\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void lock4_done(struct tevent_req *subreq)
+{
+ int *done = tevent_req_callback_data_void(subreq);
+ NTSTATUS status;
+
+ status = g_lock_lock_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock_recv returned %s\n",
+ nt_errstr(status));
+ *done = -1;
+ return;
+ }
+ *done = 1;
+}
+
+static void lock4_waited(struct tevent_req *subreq)
+{
+ int *exit_pipe = tevent_req_callback_data_void(subreq);
+ pid_t child;
+ int status;
+ bool ok;
+
+ printf("waited\n");
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ fprintf(stderr, "tevent_wakeup_recv failed\n");
+ }
+ close(*exit_pipe);
+
+ child = wait(&status);
+
+ printf("child %d exited with %d\n", (int)child, status);
+}
+
+struct lock4_check_state {
+ struct server_id me;
+ bool ok;
+};
+
+static void lock4_check(struct server_id exclusive,
+ size_t num_shared,
+ const struct server_id *shared,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct lock4_check_state *state = private_data;
+ size_t num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
+
+ if (num_locks != 1) {
+ fprintf(stderr, "num_locks=%zu\n", num_locks);
+ return;
+ }
+
+ if (exclusive.pid == 0) {
+ fprintf(stderr, "Wrong lock type, not WRITE\n");
+ return;
+ }
+
+ if (!server_id_equal(&state->me, &exclusive)) {
+ struct server_id_buf buf1, buf2;
+ fprintf(stderr, "me=%s, locker=%s\n",
+ server_id_str_buf(state->me, &buf1),
+ server_id_str_buf(exclusive, &buf2));
+ return;
+ }
+
+ state->ok = true;
+}
+
+/*
+ * Test a lock conflict: Contend with a WRITE lock
+ */
+
+bool run_g_lock4(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock4";
+ TDB_DATA key = string_term_tdb_data(lockname);
+ pid_t child;
+ int ready_pipe[2];
+ int exit_pipe[2];
+ NTSTATUS status;
+ bool ret = false;
+ struct tevent_req *req;
+ bool ok;
+ int done;
+
+ if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
+ perror("pipe failed");
+ return false;
+ }
+
+ child = fork();
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (child == -1) {
+ perror("fork failed");
+ return false;
+ }
+
+ if (child == 0) {
+ close(ready_pipe[0]);
+ close(exit_pipe[1]);
+ ok = lock4_child(
+ lockname, G_LOCK_WRITE, ready_pipe[1], exit_pipe[0]);
+ exit(ok ? 0 : 1);
+ }
+
+ close(ready_pipe[1]);
+ close(exit_pipe[0]);
+
+ if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
+ perror("read failed");
+ return false;
+ }
+
+ if (!ok) {
+ fprintf(stderr, "child returned error\n");
+ return false;
+ }
+
+ status = g_lock_lock(ctx, key, G_LOCK_WRITE,
+ (struct timeval) { .tv_usec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_lock(ctx, key, G_LOCK_READ,
+ (struct timeval) { .tv_usec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ req = g_lock_lock_send(ev, ev, ctx, key, G_LOCK_WRITE, NULL, NULL);
+ if (req == NULL) {
+ fprintf(stderr, "g_lock_lock send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, lock4_done, &done);
+
+ req = tevent_wakeup_send(ev, ev, timeval_current_ofs(1, 0));
+ if (req == NULL) {
+ fprintf(stderr, "tevent_wakeup_send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, lock4_waited, &exit_pipe[1]);
+
+ done = 0;
+
+ while (done == 0) {
+ int tevent_ret = tevent_loop_once(ev);
+ if (tevent_ret != 0) {
+ perror("tevent_loop_once failed");
+ goto fail;
+ }
+ }
+
+ {
+ struct lock4_check_state state = {
+ .me = messaging_server_id(msg)
+ };
+
+ status = g_lock_dump(ctx, key, lock4_check, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_dump failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!state.ok) {
+ fprintf(stderr, "lock4_check failed\n");
+ goto fail;
+ }
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+/*
+ * Test a lock conflict: Contend with a READ lock
+ */
+
+bool run_g_lock4a(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock4a";
+ TDB_DATA key = string_term_tdb_data(lockname);
+ pid_t child;
+ int ready_pipe[2];
+ int exit_pipe[2];
+ NTSTATUS status;
+ bool ret = false;
+ struct tevent_req *req;
+ bool ok;
+ int done;
+
+ if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
+ perror("pipe failed");
+ return false;
+ }
+
+ child = fork();
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (child == -1) {
+ perror("fork failed");
+ return false;
+ }
+
+ if (child == 0) {
+ close(ready_pipe[0]);
+ close(exit_pipe[1]);
+ ok = lock4_child(
+ lockname, G_LOCK_READ, ready_pipe[1], exit_pipe[0]);
+ exit(ok ? 0 : 1);
+ }
+
+ close(ready_pipe[1]);
+ close(exit_pipe[0]);
+
+ if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
+ perror("read failed");
+ return false;
+ }
+
+ if (!ok) {
+ fprintf(stderr, "child returned error\n");
+ return false;
+ }
+
+ status = g_lock_lock(ctx, key, G_LOCK_WRITE,
+ (struct timeval) { .tv_usec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_lock(ctx, key, G_LOCK_READ,
+ (struct timeval) { .tv_usec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = g_lock_unlock(ctx, key);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_unlock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ req = g_lock_lock_send(ev, ev, ctx, key, G_LOCK_WRITE, NULL, NULL);
+ if (req == NULL) {
+ fprintf(stderr, "g_lock_lock send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, lock4_done, &done);
+
+ req = tevent_wakeup_send(ev, ev, timeval_current_ofs(1, 0));
+ if (req == NULL) {
+ fprintf(stderr, "tevent_wakeup_send failed\n");
+ goto fail;
+ }
+ tevent_req_set_callback(req, lock4_waited, &exit_pipe[1]);
+
+ done = 0;
+
+ while (done == 0) {
+ int tevent_ret = tevent_loop_once(ev);
+ if (tevent_ret != 0) {
+ perror("tevent_loop_once failed");
+ goto fail;
+ }
+ }
+
+ {
+ struct lock4_check_state state = {
+ .me = messaging_server_id(msg)
+ };
+
+ status = g_lock_dump(ctx, key, lock4_check, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_dump failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!state.ok) {
+ fprintf(stderr, "lock4_check failed\n");
+ goto fail;
+ }
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+struct lock5_parser_state {
+ size_t num_locks;
+};
+
+static void lock5_parser(struct server_id exclusive,
+ size_t num_shared,
+ const struct server_id *shared,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct lock5_parser_state *state = private_data;
+ state->num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
+}
+
+/*
+ * Test heuristic cleanup
+ */
+
+bool run_g_lock5(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock5";
+ pid_t child;
+ int exit_pipe[2], ready_pipe[2];
+ NTSTATUS status;
+ size_t i, nprocs;
+ int ret;
+ bool ok;
+ ssize_t nread;
+ char c;
+
+ nprocs = 5;
+
+ if ((pipe(exit_pipe) != 0) || (pipe(ready_pipe) != 0)) {
+ perror("pipe failed");
+ return false;
+ }
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ fprintf(stderr, "get_g_lock_ctx failed");
+ return false;
+ }
+
+ for (i=0; i<nprocs; i++) {
+
+ child = fork();
+
+ if (child == -1) {
+ perror("fork failed");
+ return false;
+ }
+
+ if (child == 0) {
+ TALLOC_FREE(ctx);
+
+ status = reinit_after_fork(msg, ev, false);
+
+ close(ready_pipe[0]);
+ close(exit_pipe[1]);
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ fprintf(stderr, "get_g_lock_ctx failed");
+ exit(1);
+ }
+ status = g_lock_lock(ctx,
+ string_term_tdb_data(lockname),
+ G_LOCK_READ,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "child g_lock_lock failed %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+ close(ready_pipe[1]);
+ nread = sys_read(exit_pipe[0], &c, sizeof(c));
+ if (nread != 0) {
+ fprintf(stderr, "sys_read returned %zu (%s)\n",
+ nread, strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ }
+
+ close(ready_pipe[1]);
+
+ nread = sys_read(ready_pipe[0], &c, sizeof(c));
+ if (nread != 0) {
+ fprintf(stderr, "sys_read returned %zu (%s)\n",
+ nread, strerror(errno));
+ return false;
+ }
+
+ close(exit_pipe[1]);
+
+ for (i=0; i<nprocs; i++) {
+ int child_status;
+ ret = waitpid(-1, &child_status, 0);
+ if (ret == -1) {
+ perror("waitpid failed");
+ return false;
+ }
+ }
+
+ for (i=0; i<nprocs; i++) {
+ struct lock5_parser_state state;
+
+ status = g_lock_dump(ctx, string_term_tdb_data(lockname),
+ lock5_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_dump returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (state.num_locks != (nprocs - i)) {
+ fprintf(stderr, "nlocks=%zu, expected %zu\n",
+ state.num_locks, (nprocs-i));
+ return false;
+ }
+
+ status = g_lock_lock(ctx, string_term_tdb_data(lockname),
+ G_LOCK_READ,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_unlock failed %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+struct lock6_parser_state {
+ size_t num_locks;
+};
+
+static void lock6_parser(struct server_id exclusive,
+ size_t num_shared,
+ const struct server_id *shared,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct lock6_parser_state *state = private_data;
+ state->num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
+}
+
+/*
+ * Test cleanup with contention and stale locks
+ */
+
+bool run_g_lock6(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ TDB_DATA lockname = string_term_tdb_data("lock6");
+ pid_t child;
+ int exit_pipe[2], ready_pipe[2];
+ NTSTATUS status;
+ size_t i, nprocs;
+ int ret;
+ bool ok;
+ ssize_t nread;
+ char c;
+
+ if ((pipe(exit_pipe) != 0) || (pipe(ready_pipe) != 0)) {
+ perror("pipe failed");
+ return false;
+ }
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ fprintf(stderr, "get_g_lock_ctx failed");
+ return false;
+ }
+
+ /*
+ * Wipe all stale locks -- in clustered mode there's no
+ * CLEAR_IF_FIRST
+ */
+ status = g_lock_lock(ctx, lockname, G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ status = g_lock_unlock(ctx, lockname);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_unlock failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ nprocs = 2;
+ for (i=0; i<nprocs; i++) {
+
+ child = fork();
+
+ if (child == -1) {
+ perror("fork failed");
+ return false;
+ }
+
+ if (child == 0) {
+ TALLOC_FREE(ctx);
+
+ status = reinit_after_fork(msg, ev, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "reinit_after_fork failed: %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+
+ close(ready_pipe[0]);
+ close(exit_pipe[1]);
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ fprintf(stderr, "get_g_lock_ctx failed");
+ exit(1);
+ }
+ status = g_lock_lock(ctx,
+ lockname,
+ G_LOCK_READ,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "child g_lock_lock failed %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+ if (i == 0) {
+ exit(0);
+ }
+ close(ready_pipe[1]);
+ nread = sys_read(exit_pipe[0], &c, sizeof(c));
+ if (nread != 0) {
+ fprintf(stderr, "sys_read returned %zu (%s)\n",
+ nread, strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ }
+
+ close(ready_pipe[1]);
+
+ nread = sys_read(ready_pipe[0], &c, sizeof(c));
+ if (nread != 0) {
+ fprintf(stderr, "sys_read returned %zd (%s)\n",
+ nread, strerror(errno));
+ return false;
+ }
+
+ {
+ int child_status;
+ ret = waitpid(-1, &child_status, 0);
+ if (ret == -1) {
+ perror("waitpid failed");
+ return false;
+ }
+ }
+
+ {
+ struct lock6_parser_state state;
+
+ status = g_lock_dump(ctx, lockname, lock6_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_dump returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (state.num_locks != nprocs) {
+ fprintf(stderr, "nlocks=%zu, expected %zu\n",
+ state.num_locks, nprocs);
+ return false;
+ }
+
+ status = g_lock_lock(ctx,
+ lockname,
+ G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ fprintf(stderr, "g_lock_lock should have failed with %s - %s\n",
+ nt_errstr(NT_STATUS_IO_TIMEOUT),
+ nt_errstr(status));
+ return false;
+ }
+
+ status = g_lock_lock(ctx, lockname, G_LOCK_READ,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ close(exit_pipe[1]);
+
+ {
+ int child_status;
+ ret = waitpid(-1, &child_status, 0);
+ if (ret == -1) {
+ perror("waitpid failed");
+ return false;
+ }
+ }
+
+ status = g_lock_lock(ctx, lockname, G_LOCK_UPGRADE,
+ (struct timeval) { .tv_sec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Test upgrade deadlock
+ */
+
+bool run_g_lock7(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ const char *lockname = "lock7";
+ TDB_DATA key = string_term_tdb_data(lockname);
+ pid_t child;
+ int ready_pipe[2];
+ int down_pipe[2];
+ ssize_t n;
+ NTSTATUS status;
+ bool ret = false;
+ bool ok = true;
+
+ if ((pipe(ready_pipe) != 0) || (pipe(down_pipe) != 0)) {
+ perror("pipe failed");
+ return false;
+ }
+
+ child = fork();
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (child == -1) {
+ perror("fork failed");
+ return false;
+ }
+
+ if (child == 0) {
+ struct tevent_req *req = NULL;
+
+ close(ready_pipe[0]);
+ ready_pipe[0] = -1;
+ close(down_pipe[1]);
+ down_pipe[1] = -1;
+
+ status = reinit_after_fork(msg, ev, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "reinit_after_fork failed: %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+
+ printf("%d: locking READ\n", (int)getpid());
+
+ status = g_lock_lock(
+ ctx,
+ key,
+ G_LOCK_READ,
+ (struct timeval) { .tv_usec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_lock(READ) failed: %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+
+ ok = true;
+
+ n = sys_write(ready_pipe[1], &ok, sizeof(ok));
+ if (n != sizeof(ok)) {
+ fprintf(stderr,
+ "sys_write failed: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ n = sys_read(down_pipe[0], &ok, sizeof(ok));
+ if (n != sizeof(ok)) {
+ fprintf(stderr,
+ "sys_read failed: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ printf("%d: starting UPGRADE\n", (int)getpid());
+
+ req = g_lock_lock_send(
+ msg,
+ ev,
+ ctx,
+ key,
+ G_LOCK_UPGRADE,
+ NULL, NULL);
+ if (req == NULL) {
+ fprintf(stderr, "g_lock_lock_send(UPGRADE) failed\n");
+ exit(1);
+ }
+
+ n = sys_write(ready_pipe[1], &ok, sizeof(ok));
+ if (n != sizeof(ok)) {
+ fprintf(stderr,
+ "sys_write failed: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ close(ready_pipe[1]);
+ close(down_pipe[0]);
+
+ if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
+ perror("read failed");
+ return false;
+ }
+ if (!ok) {
+ fprintf(stderr, "child returned error\n");
+ return false;
+ }
+
+ status = g_lock_lock(
+ ctx,
+ key,
+ G_LOCK_READ,
+ (struct timeval) { .tv_usec = 1 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_lock(READ) failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ n = sys_write(down_pipe[1], &ok, sizeof(ok));
+ if (n != sizeof(ok)) {
+ fprintf(stderr,
+ "sys_write failed: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
+ perror("read failed");
+ goto fail;
+ }
+
+ status = g_lock_lock(
+ ctx,
+ key,
+ G_LOCK_UPGRADE,
+ (struct timeval) { .tv_sec = 10 },
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_POSSIBLE_DEADLOCK)) {
+ fprintf(stderr,
+ "g_lock_lock returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+bool run_g_lock8(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ struct tevent_req *req = NULL;
+ TDB_DATA lockname = string_term_tdb_data("lock8");
+ NTSTATUS status;
+ bool ok;
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ fprintf(stderr, "get_g_lock_ctx failed");
+ return false;
+ }
+
+ req = g_lock_watch_data_send(
+ ev, ev, ctx, lockname, (struct server_id) { .pid = 0 });
+ if (req == NULL) {
+ fprintf(stderr, "get_g_lock_ctx failed");
+ return false;
+ }
+
+ status = g_lock_lock(
+ ctx,
+ lockname,
+ G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 999 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = g_lock_write_data(
+ ctx, lockname, lockname.dptr, lockname.dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_write_data failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = g_lock_write_data(ctx, lockname, NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_write_data failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = g_lock_unlock(ctx, lockname);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "g_lock_unlock failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ fprintf(stderr, "tevent_req_poll_ntstatus failed\n");
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "tevent_req_poll_ntstatus failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+extern int torture_numops;
+extern int torture_nprocs;
+
+static struct timeval tp1, tp2;
+
+static void start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
+ (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
+}
+
+/*
+ * g_lock ping_pong
+ */
+
+bool run_g_lock_ping_pong(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *ctx = NULL;
+ fstring name;
+ NTSTATUS status;
+ int i = 0;
+ bool ret = false;
+ bool ok;
+ unsigned count = 0;
+
+ torture_nprocs = MAX(2, torture_nprocs);
+
+ ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ start_timer();
+
+ snprintf(name, sizeof(name), "ping_pong_%d", i);
+
+ status = g_lock_lock(ctx, string_term_tdb_data(name), G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 60 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ for (i=0; i<torture_numops; i++) {
+
+ name[10] = '0' + ((i+1) % torture_nprocs);
+
+ status = g_lock_lock(ctx, string_term_tdb_data(name),
+ G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 60 },
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_lock failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ name[10] = '0' + ((i) % torture_nprocs);
+
+ status = g_lock_unlock(ctx, string_term_tdb_data(name));
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "g_lock_unlock failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ count++;
+
+ if (end_timer() > 1.0) {
+ printf("%8u locks/sec\r",
+ (unsigned)(2*count/end_timer()));
+ fflush(stdout);
+ start_timer();
+ count=0;
+ }
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
diff --git a/source3/torture/test_hidenewfiles.c b/source3/torture/test_hidenewfiles.c
new file mode 100644
index 0000000..6d6811c
--- /dev/null
+++ b/source3/torture/test_hidenewfiles.c
@@ -0,0 +1,234 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test "hide new files timeout"
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libcli/security/security.h"
+
+static NTSTATUS servertime(
+ struct cli_state *cli, const char *fname, struct timeval *tv)
+{
+ struct smb_create_returns cr;
+ NTSTATUS status;
+ uint16_t fnum;
+
+ status = cli_ntcreate(
+ cli,
+ fname,
+ 0,
+ FILE_GENERIC_WRITE|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0,
+ FILE_CREATE,
+ FILE_DELETE_ON_CLOSE,
+ 0,
+ &fnum,
+ &cr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_ntcreate failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_close failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ nttime_to_timeval(tv, cr.creation_time);
+
+ return NT_STATUS_OK;
+}
+
+struct have_file_state {
+ bool found;
+ const char *fname;
+};
+
+static NTSTATUS have_file_fn(struct file_info *f,
+ const char *mask,
+ void *private_data)
+{
+ struct have_file_state *state = private_data;
+ state->found |= strequal(f->name, state->fname);
+ return NT_STATUS_OK;
+}
+
+static bool have_file(struct cli_state *cli, const char *fname)
+{
+ struct have_file_state state = { .fname = fname };
+ NTSTATUS status;
+
+ status = cli_list(
+ cli,
+ "*",
+ FILE_ATTRIBUTE_DIRECTORY|
+ FILE_ATTRIBUTE_SYSTEM|
+ FILE_ATTRIBUTE_HIDDEN,
+ have_file_fn,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_list failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return state.found;
+}
+
+bool run_hidenewfiles(int dummy)
+{
+ const char *tsname = "timestamp.txt";
+ const char *fname = "new_hidden.txt";
+ struct cli_state *cli;
+ struct smb_create_returns cr;
+ struct timeval create_time;
+ uint16_t fnum;
+ NTSTATUS status;
+ bool ret = false;
+ bool gotit = false;
+ bool ok;
+
+ /* what is configured in smb.conf */
+ unsigned hideunreadable_seconds = 5;
+
+ ok = torture_open_connection_flags(&cli, 0, 0);
+ if (!ok) {
+ return false;
+ }
+
+ cli_unlink(cli, tsname, FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(
+ cli,
+ fname,
+ 0,
+ FILE_GENERIC_WRITE|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ &cr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_ntcreate failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ nttime_to_timeval(&create_time, cr.last_write_time);
+
+ while (!gotit) {
+ struct timeval now;
+ double age;
+
+ gotit = have_file(cli, fname);
+
+ status = servertime(cli, tsname, &now);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("servertime failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ age = timeval_elapsed2(&create_time, &now);
+
+ if ((age < hideunreadable_seconds) && gotit) {
+ d_printf("Found file at age of %f\n", age);
+ goto fail;
+ }
+ if ((age > (hideunreadable_seconds*10)) && !gotit) {
+ d_printf("Did not find file after %f seconds\n", age);
+ goto fail;
+ }
+ if (gotit) {
+ break;
+ }
+
+ smb_msleep(1000);
+ }
+
+ ret = true;
+fail:
+ cli_nt_delete_on_close(cli, fnum, true);
+ cli_close(cli, fnum);
+
+ return ret;
+}
+
+bool run_hidenewfiles_showdirs(int dummy)
+{
+ const char *dname = "dir";
+ const char *fname = "dir/x.txt";
+ struct cli_state *cli;
+ struct smb_create_returns cr;
+ struct timeval create_time;
+ uint16_t fnum = UINT16_MAX;
+ NTSTATUS status;
+ bool ret = false;
+ bool gotit = false;
+ bool ok;
+
+ ok = torture_open_connection_flags(&cli, 0, 0);
+ if (!ok) {
+ return false;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, dname);
+
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("mkdir(%s) failed: %s\n", dname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_ntcreate(
+ cli,
+ fname,
+ 0,
+ FILE_GENERIC_WRITE|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ &cr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_ntcreate failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+ nttime_to_timeval(&create_time, cr.last_write_time);
+
+ gotit = have_file(cli, dname);
+ if (!gotit) {
+ d_printf("%s was hidden\n", dname);
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ if (fnum != UINT16_MAX) {
+ cli_nt_delete_on_close(cli, fnum, true);
+ cli_close(cli, fnum);
+ }
+ cli_rmdir(cli, dname);
+
+ return ret;
+}
diff --git a/source3/torture/test_idmap_cache.c b/source3/torture/test_idmap_cache.c
new file mode 100644
index 0000000..b9cba3b
--- /dev/null
+++ b/source3/torture/test_idmap_cache.c
@@ -0,0 +1,122 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test dbwrap_watch API
+ * Copyright (C) Volker Lendecke 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "lib/idmap_cache.h"
+#include "librpc/gen_ndr/idmap.h"
+#include "libcli/security/dom_sid.h"
+
+bool run_local_idmap_cache1(int dummy)
+{
+ struct dom_sid sid, found_sid;
+ struct unixid xid, found_xid;
+ bool ret = false;
+ bool expired = false;
+
+ xid = (struct unixid) { .id = 1234, .type = ID_TYPE_UID };
+ dom_sid_parse("S-1-5-21-2864185242-3846410404-2398417794-1235", &sid);
+ idmap_cache_set_sid2unixid(&sid, &xid);
+
+ ret = idmap_cache_find_sid2unixid(&sid, &found_xid, &expired);
+ if (!ret) {
+ fprintf(stderr, "idmap_cache_find_sid2unixid failed\n");
+ goto done;
+ }
+ if (expired) {
+ fprintf(stderr,
+ "idmap_cache_find_sid2unixid returned an expired "
+ "value\n");
+ goto done;
+ }
+ if ((xid.type != found_xid.type) || (xid.id != found_xid.id)) {
+ fprintf(stderr,
+ "idmap_cache_find_sid2unixid returned wrong "
+ "values\n");
+ goto done;
+ }
+
+ ret = idmap_cache_find_xid2sid(&xid, &found_sid, &expired);
+ if (!ret) {
+ fprintf(stderr, "idmap_cache_find_xid2sid failed\n");
+ goto done;
+ }
+ if (expired) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid returned an expired "
+ "value\n");
+ goto done;
+ }
+ if (!dom_sid_equal(&sid, &found_sid)) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid returned wrong sid\n");
+ goto done;
+ }
+
+ xid.type = ID_TYPE_GID;
+
+ ret = idmap_cache_find_xid2sid(&xid, &found_sid, &expired);
+ if (ret) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid found a GID where it "
+ "should not\n");
+ goto done;
+ }
+
+ idmap_cache_del_sid(&sid);
+
+ xid.type = ID_TYPE_UID;
+ ret = idmap_cache_find_xid2sid(&xid, &found_sid, &expired);
+ if (ret) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid found a UID where it "
+ "should not\n");
+ goto done;
+ }
+
+ /*
+ * Test that negative mappings can also be cached
+ */
+ sid = (struct dom_sid) {0};
+ xid = (struct unixid) { .id = 1234, .type = ID_TYPE_UID };
+ idmap_cache_set_sid2unixid(&sid, &xid);
+
+ ret = idmap_cache_find_xid2sid(&xid, &found_sid, &expired);
+ if (!ret) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid failed to find "
+ "negative mapping\n");
+ goto done;
+ }
+ if (expired) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid returned an expired "
+ "value\n");
+ goto done;
+ }
+ if (!dom_sid_equal(&sid, &found_sid)) {
+ fprintf(stderr,
+ "idmap_cache_find_xid2sid returned wrong sid\n");
+ goto done;
+ }
+
+ ret = true;
+done:
+ return ret;
+}
diff --git a/source3/torture/test_idmap_tdb_common.c b/source3/torture/test_idmap_tdb_common.c
new file mode 100644
index 0000000..825ee3e
--- /dev/null
+++ b/source3/torture/test_idmap_tdb_common.c
@@ -0,0 +1,1047 @@
+/*
+ Unix SMB/CIFS implementation.
+ IDMAP TDB common code tester
+
+ Copyright (C) Christian Ambach 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/proto.h"
+#include "idmap.h"
+#include "winbindd/idmap_rw.h"
+#include "winbindd/idmap_tdb_common.h"
+#include "winbindd/winbindd.h"
+#include "winbindd/winbindd_proto.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../libcli/security/dom_sid.h"
+
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+#define LOW_ID 100
+#define HIGH_ID 199
+
+#define DOM_SID1 "S-1-5-21-1234-5678-9012"
+#define DOM_SID2 "S-1-5-21-0123-5678-9012"
+#define DOM_SID3 "S-1-5-21-0012-5678-9012"
+#define DOM_SID4 "S-1-5-21-0001-5678-9012"
+#define DOM_SID5 "S-1-5-21-2345-5678-9012"
+#define DOM_SID6 "S-1-5-21-3456-5678-9012"
+
+/* overwrite some winbind internal functions */
+struct winbindd_domain *find_domain_from_name(const char *domain_name)
+{
+ return NULL;
+}
+
+bool get_global_winbindd_state_offline(void) {
+ return false;
+}
+
+bool winbindd_use_idmap_cache(void) {
+ return false;
+}
+
+static bool open_db(struct idmap_tdb_common_context *ctx)
+{
+ NTSTATUS status;
+ char *db_path;
+
+ if(ctx->db) {
+ /* already open */
+ return true;
+ }
+
+ db_path = talloc_asprintf(talloc_tos(), "%s/idmap_test.tdb",
+ lp_private_dir());
+ if(!db_path) {
+ DEBUG(0, ("Out of memory!\n"));
+ return false;
+ }
+
+ ctx->db = db_open(ctx, db_path, 0, TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0600,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+
+ if(!ctx->db) {
+ DEBUG(0, ("Failed to open database: %s\n", strerror(errno)));
+ return false;
+ }
+
+ if(dbwrap_transaction_start(ctx->db) != 0) {
+ DEBUG(0, ("Failed to start transaction!\n"));
+ return false;
+ }
+
+ status = dbwrap_store_uint32_bystring(ctx->db, ctx->hwmkey_uid,
+ LOW_ID);
+ if(!NT_STATUS_IS_OK(status)) {
+ dbwrap_transaction_cancel(ctx->db);
+ return false;
+ }
+
+ status = dbwrap_store_uint32_bystring(ctx->db, ctx->hwmkey_gid,
+ LOW_ID);
+ if(!NT_STATUS_IS_OK(status)) {
+ dbwrap_transaction_cancel(ctx->db);
+ return false;
+ }
+
+ if(dbwrap_transaction_commit(ctx->db) != 0) {
+ DEBUG(0, ("Failed to commit transaction!\n"));
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS idmap_test_tdb_db_init(struct idmap_domain *dom)
+{
+ struct idmap_tdb_common_context *ret;
+
+ DBG_DEBUG("called for domain '%s'\n", dom->name);
+
+ ret = talloc_zero(dom, struct idmap_tdb_common_context);
+ if (ret == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret->rw_ops = talloc_zero(ret, struct idmap_rw_ops);
+ if (ret->rw_ops == NULL) {
+ TALLOC_FREE(ret);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret->max_id = HIGH_ID;
+ ret->hwmkey_uid = HWM_USER;
+ ret->hwmkey_gid = HWM_GROUP;
+
+ ret->rw_ops->get_new_id = idmap_tdb_common_get_new_id;
+ ret->rw_ops->set_mapping = idmap_tdb_common_set_mapping;
+
+ if (!open_db(ret)) {
+ TALLOC_FREE(ret);
+ return NT_STATUS_INTERNAL_ERROR;
+ };
+
+ dom->private_data = ret;
+
+ return NT_STATUS_OK;
+}
+
+static struct idmap_domain *createdomain(TALLOC_CTX *memctx)
+{
+ struct idmap_domain *dom;
+ struct idmap_methods *m;
+
+ dom = talloc_zero(memctx, struct idmap_domain);
+ dom->name = "*";
+ dom->low_id = LOW_ID;
+ dom->high_id = HIGH_ID;
+ dom->read_only = false;
+ m = talloc_zero(dom, struct idmap_methods);
+ *m = (struct idmap_methods) {
+ .init = idmap_test_tdb_db_init,
+ .sids_to_unixids = idmap_tdb_common_sids_to_unixids,
+ .unixids_to_sids = idmap_tdb_common_unixids_to_sids,
+ .allocate_id = idmap_tdb_common_get_new_id,
+ };
+ dom->methods = m;
+
+ return dom;
+}
+
+static bool test_getnewid1(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct unixid id;
+
+ id.type = ID_TYPE_UID;
+
+ status = idmap_tdb_common_get_new_id(dom, &id);
+
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_getnewid1: Could not allocate id!\n"));
+ return false;
+ }
+
+ if(id.id == 0) {
+ DEBUG(0, ("test_getnewid1: Allocate returned "
+ "empty id!\n"));
+ return false;
+ }
+
+ if(id.id > HIGH_ID || id.id < LOW_ID) {
+ DEBUG(0, ("test_getnewid1: Allocate returned "
+ "out of range id!\n"));
+ return false;
+ }
+
+ DEBUG(0, ("test_getnewid1: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_getnewid2(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct unixid id;
+ int i, left;
+
+ id.type = ID_TYPE_UID;
+
+ status = idmap_tdb_common_get_new_id(dom, &id);
+
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_getnewid2: Could not allocate id!\n"));
+ return false;
+ }
+
+ if(id.id == 0) {
+ DEBUG(0, ("test_getnewid2: Allocate returned "
+ "empty id!\n"));
+ return false;
+ }
+
+ if(id.id > HIGH_ID || id.id < LOW_ID) {
+ DEBUG(0, ("test_getnewid2: Allocate returned "
+ "out of range id!\n"));
+ return false;
+ }
+
+ /* how many ids are left? */
+
+ left = HIGH_ID - id.id;
+
+ /* consume them all */
+ for(i = 0; i<left; i++) {
+
+ status = idmap_tdb_common_get_new_id(dom, &id);
+
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_getnewid2: Allocate returned "
+ "error %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ if(id.id > HIGH_ID) {
+ DEBUG(0, ("test_getnewid2: Allocate returned "
+ "out of range id (%d)!\n", id.id));
+ return false;
+ }
+ }
+
+ /* one more must fail */
+ status = idmap_tdb_common_get_new_id(dom, &id);
+
+ if(NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_getnewid2: Could allocate id (%d) from "
+ "depleted pool!\n", id.id));
+ return false;
+ }
+
+ DEBUG(0, ("test_getnewid2: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_setmap1(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map map;
+
+ ZERO_STRUCT(map);
+
+ /* test for correct return code with invalid data */
+
+ status = idmap_tdb_common_set_mapping(dom, NULL);
+ if(!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ DEBUG(0, ("test_setmap1: bad parameter handling!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_set_mapping(dom, &map);
+ if(!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ DEBUG(0, ("test_setmap1: bad parameter handling!\n"));
+ return false;
+ }
+
+ map.sid = dom_sid_parse_talloc(memctx, DOM_SID1 "-100");
+
+ map.xid.type = ID_TYPE_NOT_SPECIFIED;
+ map.xid.id = 4711;
+
+ status = idmap_tdb_common_set_mapping(dom, &map);
+ if(!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ DEBUG(0, ("test_setmap1: bad parameter handling!\n"));
+ return false;
+ }
+
+ /* now the good ones */
+ map.xid.type = ID_TYPE_UID;
+ map.xid.id = 0;
+
+ status = idmap_tdb_common_get_new_id(dom, &(map.xid));
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_setmap1: get_new_uid failed!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_set_mapping(dom, &map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_setmap1: setting UID mapping failed!\n"));
+ return false;
+ }
+
+ /* try to set the same mapping again as group (must fail) */
+
+ map.xid.type = ID_TYPE_GID;
+ status = idmap_tdb_common_set_mapping(dom, &map);
+ if(NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_setmap1: could create map for "
+ "group and user!\n"));
+ return false;
+ }
+
+ /* now a group with a different SID*/
+ map.xid.id = 0;
+
+ map.sid = dom_sid_parse_talloc(memctx, DOM_SID1 "-101");
+
+ status = idmap_tdb_common_get_new_id(dom, &(map.xid));
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_setmap1: get_new_gid failed!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_set_mapping(dom, &map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_setmap1: setting GID mapping failed!\n"));
+ return false;
+ }
+ DEBUG(0, ("test_setmap1: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_sid2unixid1(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status1, status2, status3;
+ struct id_map map;
+
+ /* check for correct dealing with bad parameters */
+ status1 = idmap_tdb_common_sid_to_unixid(NULL, &map);
+ status2 = idmap_tdb_common_sid_to_unixid(dom, NULL);
+ status3 = idmap_tdb_common_sid_to_unixid(NULL, NULL);
+
+ if(!NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status1) ||
+ !NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status2) ||
+ !NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status3)) {
+ DEBUG(0, ("test_setmap1: bad parameter handling!\n"));
+ return false;
+ }
+
+ DEBUG(0, ("test_unixid2sid1: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_sid2unixid2(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map uid_map, gid_map, test_map;
+ bool doagain = true;
+
+ ZERO_STRUCT(uid_map);
+ ZERO_STRUCT(gid_map);
+
+ /* create two mappings for a UID and GID */
+
+again:
+
+ uid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID2 "-1000");
+ uid_map.xid.type = ID_TYPE_UID;
+
+ gid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID2 "-1001");
+ gid_map.xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_new_mapping(dom, &uid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sid2unixid2: could not create uid map!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_new_mapping(dom, &gid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sid2unixid2: could not create gid map!\n"));
+ return false;
+ }
+
+ /* now read them back */
+ ZERO_STRUCT(test_map);
+ test_map.sid = uid_map.sid;
+
+ status = idmap_tdb_common_sid_to_unixid(dom, &test_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sid2unixid2: sid2unixid failed for uid!\n"));
+ return false;
+ }
+
+ if(test_map.xid.id!=uid_map.xid.id) {
+ DEBUG(0, ("test_sid2unixid2: sid2unixid returned wrong uid!\n"));
+ return false;
+ }
+
+ test_map.sid = gid_map.sid;
+
+ status = idmap_tdb_common_sid_to_unixid(dom, &test_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sid2unixid2: sid2unixid failed for gid!\n"));
+ return false;
+ }
+
+ if(test_map.xid.id!=gid_map.xid.id) {
+ DEBUG(0, ("test_sid2unixid2: sid2unixid returned wrong gid!\n"));
+ return false;
+ }
+
+ /*
+ * Go through the same tests again once to see if trying to recreate
+ * a mapping that was already created will work or not
+ */
+ if(doagain) {
+ doagain = false;
+ goto again;
+ }
+
+ DEBUG(0, ("test_sid2unixid2: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_sids2unixids1(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map uid_map, gid_map, **test_maps;
+
+ ZERO_STRUCT(uid_map);
+ ZERO_STRUCT(gid_map);
+
+ /* create two mappings for a UID and GID */
+
+ uid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID4 "-1000");
+ uid_map.xid.type = ID_TYPE_UID;
+
+ gid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID4 "-1001");
+ gid_map.xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_new_mapping(dom, &uid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sids2unixids1: could not create uid map!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_new_mapping(dom, &gid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sids2unixids1: could not create gid map!\n"));
+ return false;
+ }
+
+ /* now read them back */
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ sid_copy(test_maps[0]->sid, uid_map.sid);
+ sid_copy(test_maps[1]->sid, gid_map.sid);
+
+ status = idmap_tdb_common_sids_to_unixids(dom, test_maps);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sids2sunixids1: sids2unixids failed!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ if(test_maps[0]->xid.id!=uid_map.xid.id ||
+ test_maps[1]->xid.id!=gid_map.xid.id ) {
+ DEBUG(0, ("test_sids2unixids1: sid2unixid returned wrong xid!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ DEBUG(0, ("test_sids2unixids1: PASSED!\n"));
+
+ talloc_free(test_maps);
+
+ return true;
+}
+
+static bool test_sids2unixids2(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map **test_maps;
+ struct unixid save;
+
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ /* ask for two new mappings for a UID and GID */
+ test_maps[0]->sid = dom_sid_parse_talloc(test_maps, DOM_SID4 "-1003");
+ test_maps[0]->xid.type = ID_TYPE_UID;
+ test_maps[1]->sid = dom_sid_parse_talloc(test_maps, DOM_SID4 "-1004");
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_sids_to_unixids(dom, test_maps);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sids2sunixids2: sids2unixids "
+ "failed (%s)!\n", nt_errstr(status)));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ if(test_maps[0]->xid.id == 0 || test_maps[1]->xid.id == 0) {
+ DEBUG(0, ("test_sids2sunixids2: sids2unixids "
+ "returned zero ids!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ save = test_maps[1]->xid;
+
+ /* ask for a known and a new mapping at the same time */
+ talloc_free(test_maps);
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ test_maps[0]->sid = dom_sid_parse_talloc(test_maps, DOM_SID4 "-1004");
+ test_maps[0]->xid.type = ID_TYPE_GID;
+ test_maps[1]->sid = dom_sid_parse_talloc(test_maps, DOM_SID4 "-1005");
+ test_maps[1]->xid.type = ID_TYPE_UID;
+
+ status = idmap_tdb_common_sids_to_unixids(dom, test_maps);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sids2sunixids2: sids2unixids (2) "
+ "failed (%s)!\n", nt_errstr(status)));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ if(test_maps[0]->xid.type != save.type ||
+ test_maps[0]->xid.id != save.id) {
+ DEBUG(0, ("test_sids2sunixids2: second lookup returned "
+ "different value!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ if(test_maps[1]->xid.id == 0) {
+ DEBUG(0, ("test_sids2sunixids2: sids2unixids "
+ "returned zero id for mixed mapping request!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ DEBUG(0, ("test_sids2unixids2: PASSED!\n"));
+
+ talloc_free(test_maps);
+
+ return true;
+}
+
+static bool test_sids2unixids3(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map **test_maps;
+ bool retval = true;
+
+ /*
+ * check the mapping states:
+ * NONE_MAPPED, SOME_UNMAPPED, OK (all mapped)
+ *
+ * use the ids created by test_sids2unixids1
+ * need to make dom read-only
+ */
+
+ dom->read_only = true;
+
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ /* NONE_MAPPED first */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->sid = dom_sid_parse_talloc(test_maps,
+ "S-1-5-21-1-2-3-4");
+ test_maps[0]->xid.type = ID_TYPE_UID;
+
+ test_maps[1]->sid = dom_sid_parse_talloc(test_maps,
+ "S-1-5-21-1-2-3-5");
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_sids_to_unixids(dom, test_maps);
+ if(!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ DEBUG(0, ("test_sids2unixids3: incorrect status "
+ "(%s), expected NT_STATUS_NONE_MAPPED!\n",
+ nt_errstr(status)));
+ retval = false;
+ goto out;
+ }
+
+ /* SOME_UNMAPPED */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->sid = dom_sid_parse_talloc(test_maps,
+ DOM_SID4 "-1000");
+ test_maps[0]->xid.type = ID_TYPE_UID;
+ test_maps[1]->sid = dom_sid_parse_talloc(test_maps,
+ "S-1-5-21-1-2-3-5");
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_sids_to_unixids(dom, test_maps);
+ if(!NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ DEBUG(0, ("test_sids2unixids3: incorrect status "
+ "(%s), expected STATUS_SOME_UNMAPPED!\n",
+ nt_errstr(status)));
+ retval = false;
+ goto out;
+ }
+
+ /* OK */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->sid = dom_sid_parse_talloc(test_maps,
+ DOM_SID4 "-1001");
+ test_maps[1]->sid = dom_sid_parse_talloc(test_maps,
+ DOM_SID4 "-1000");
+
+ status = idmap_tdb_common_sids_to_unixids(dom, test_maps);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_sids2unixids3: incorrect status "
+ "(%s), expected NT_STATUS_OK!\n",
+ nt_errstr(status)));
+ retval = false;
+ goto out;
+ }
+
+ DEBUG(0, ("test_sids2unixids3: PASSED!\n"));
+
+out:
+ talloc_free(test_maps);
+ dom->read_only = false;
+ return retval;
+}
+
+static bool test_unixid2sid1(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status1, status2, status3;
+ struct id_map map;
+
+ /* check for correct dealing with bad parameters */
+ status1 = idmap_tdb_common_unixid_to_sid(NULL, &map);
+ status2 = idmap_tdb_common_unixid_to_sid(dom, NULL);
+ status3 = idmap_tdb_common_unixid_to_sid(NULL, NULL);
+
+ if(!NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status1) ||
+ !NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status2) ||
+ !NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status3)) {
+ DEBUG(0, ("test_setmap1: bad parameter handling!\n"));
+ return false;
+ }
+
+ DEBUG(0, ("test_unixid2sid1: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_unixid2sid2(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map *map;
+ bool retval = true;
+
+ /* ask for mapping that is outside of the range */
+ map = talloc(memctx, struct id_map);
+ map->sid = talloc(map, struct dom_sid);
+
+ map->xid.type = ID_TYPE_UID;
+ map->xid.id = HIGH_ID + 1;
+
+ status = idmap_tdb_common_unixid_to_sid(dom, map);
+ if(NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixid2sid2: unixid2sid returned "
+ "out-of-range result\n"));
+ retval = false;
+ goto out;
+ }
+
+ DEBUG(0, ("test_unixid2sid2: PASSED!\n"));
+out:
+ talloc_free(map);
+ return retval;
+
+}
+
+static bool test_unixid2sid3(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map uid_map, gid_map, test_map;
+ struct dom_sid testsid;
+
+ ZERO_STRUCT(uid_map);
+ ZERO_STRUCT(gid_map);
+
+ /* create two mappings for a UID and GID */
+ uid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID3 "-1000");
+ uid_map.xid.type = ID_TYPE_UID;
+
+ gid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID3 "-1001");
+ gid_map.xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_new_mapping(dom, &uid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixid2sid3: could not create uid map!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_new_mapping(dom, &gid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixid2sid3: could not create gid map!\n"));
+ return false;
+ }
+
+ /* now read them back */
+ ZERO_STRUCT(test_map);
+ test_map.xid.id = uid_map.xid.id;
+ test_map.xid.type = ID_TYPE_UID;
+ test_map.sid = &testsid;
+
+ status = idmap_tdb_common_unixid_to_sid(dom, &test_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixid2sid3: unixid2sid failed for uid!\n"));
+ return false;
+ }
+
+ if(test_map.xid.type!=uid_map.xid.type) {
+ DEBUG(0, ("test_unixid2sid3: unixid2sid returned wrong type!\n"));
+ return false;
+ }
+
+ if(!dom_sid_equal(test_map.sid, uid_map.sid)) {
+ DEBUG(0, ("test_unixid2sid3: unixid2sid returned wrong SID!\n"));
+ return false;
+ }
+
+ ZERO_STRUCT(test_map);
+ test_map.xid.id = gid_map.xid.id;
+ test_map.xid.type = ID_TYPE_GID;
+ test_map.sid = &testsid;
+
+ status = idmap_tdb_common_unixid_to_sid(dom, &test_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixid2sid3: unixid2sid failed for gid!\n"));
+ return false;
+ }
+
+ if(test_map.xid.type!=gid_map.xid.type) {
+ DEBUG(0, ("test_unixid2sid3: unixid2sid returned wrong type!\n"));
+ return false;
+ }
+
+ if(!dom_sid_equal(test_map.sid,gid_map.sid)) {
+ DEBUG(0, ("test_unixid2sid3: unixid2sid returned wrong SID!\n"));
+ return false;
+ }
+
+ DEBUG(0, ("test_unixid2sid3: PASSED!\n"));
+
+ return true;
+}
+
+static bool test_unixids2sids1(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map uid_map, gid_map, **test_maps;
+
+ ZERO_STRUCT(uid_map);
+ ZERO_STRUCT(gid_map);
+
+ /* create two mappings for a UID and GID */
+
+ uid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID5 "-1000");
+ uid_map.xid.type = ID_TYPE_UID;
+
+ gid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID5 "-1001");
+ gid_map.xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_new_mapping(dom, &uid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids1: could not create uid map!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_new_mapping(dom, &gid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids1: could not create gid map!\n"));
+ return false;
+ }
+
+ /* now read them back */
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->xid.id = uid_map.xid.id;
+ test_maps[0]->xid.type = ID_TYPE_UID;
+ test_maps[1]->xid.id = gid_map.xid.id;
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_unixids_to_sids(dom, test_maps);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids1: unixids2sids failed!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ if(!dom_sid_equal(test_maps[0]->sid, uid_map.sid) ||
+ !dom_sid_equal(test_maps[1]->sid, gid_map.sid) ) {
+ DEBUG(0, ("test_unixids2sids1: unixids2sids returned wrong sid!\n"));
+ talloc_free(test_maps);
+ return false;
+ }
+
+ DEBUG(0, ("test_unixids2sids1: PASSED!\n"));
+
+ talloc_free(test_maps);
+
+ return true;
+}
+
+static bool test_unixids2sids2(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map **test_maps;
+ bool retval = true;
+
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ /* ask for two unknown mappings for a UID and GID */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->xid.id = HIGH_ID - 1;
+ test_maps[0]->xid.type = ID_TYPE_UID;
+ test_maps[1]->xid.id = HIGH_ID - 1;
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_unixids_to_sids(dom, test_maps);
+ if(NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids2: unixids2sids succeeded "
+ "unexpectedly!\n"));
+ retval = false;
+ goto out;
+ }
+
+ DEBUG(0, ("test_unixids2sids2: PASSED!\n"));
+
+out:
+ talloc_free(test_maps);
+
+ return retval;;
+}
+
+static bool test_unixids2sids3(TALLOC_CTX *memctx, struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ struct id_map uid_map, gid_map, **test_maps;
+ bool retval = true;
+
+ ZERO_STRUCT(uid_map);
+ ZERO_STRUCT(gid_map);
+
+ /* create two mappings for a UID and GID */
+ uid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID6 "-1000");
+ uid_map.xid.type = ID_TYPE_UID;
+
+ gid_map.sid = dom_sid_parse_talloc(memctx, DOM_SID6 "-1001");
+ gid_map.xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_new_mapping(dom, &uid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids3: could not create uid map!\n"));
+ return false;
+ }
+
+ status = idmap_tdb_common_new_mapping(dom, &gid_map);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids3: could not create gid map!\n"));
+ return false;
+ }
+
+ /*
+ * check the mapping states:
+ * NONE_MAPPED, SOME_UNMAPPED, OK (all mapped)
+ */
+ test_maps = talloc_zero_array(memctx, struct id_map*, 3);
+
+ test_maps[0] = talloc(test_maps, struct id_map);
+ test_maps[1] = talloc(test_maps, struct id_map);
+ test_maps[2] = NULL;
+
+ /* NONE_MAPPED first */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+
+ test_maps[0]->xid.id = HIGH_ID - 1;
+ test_maps[0]->xid.type = ID_TYPE_UID;
+
+ test_maps[1]->xid.id = HIGH_ID - 1;
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_unixids_to_sids(dom, test_maps);
+ if(!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ DEBUG(0, ("test_unixids2sids3: incorrect status "
+ "(%s), expected NT_STATUS_NONE_MAPPED!\n",
+ nt_errstr(status)));
+ retval = false;
+ goto out;
+ }
+
+ /* SOME_UNMAPPED */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->xid = uid_map.xid;
+ test_maps[1]->xid.id = HIGH_ID - 1;
+ test_maps[1]->xid.type = ID_TYPE_GID;
+
+ status = idmap_tdb_common_unixids_to_sids(dom, test_maps);
+ if(!NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ DEBUG(0, ("test_unixids2sids3: incorrect status "
+ "(%s), expected STATUS_SOME_UNMAPPED!\n",
+ nt_errstr(status)));
+ retval = false;
+ goto out;
+ }
+
+ /* OK */
+ test_maps[0]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[1]->sid = talloc(test_maps, struct dom_sid);
+ test_maps[0]->xid = uid_map.xid;
+ test_maps[1]->xid = gid_map.xid;
+
+ status = idmap_tdb_common_unixids_to_sids(dom, test_maps);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("test_unixids2sids3: incorrect status "
+ "(%s), expected NT_STATUS_OK!\n",
+ nt_errstr(status)));
+ retval = false;
+ goto out;
+ }
+
+ DEBUG(0, ("test_unixids2sids3: PASSED!\n"));
+
+out:
+ talloc_free(test_maps);
+ return retval;
+}
+
+#define CHECKRESULT(r) if(!r) {TALLOC_FREE(stack); return r;}
+
+bool run_idmap_tdb_common_test(int dummy)
+{
+ bool result;
+ struct idmap_domain *dom;
+ TALLOC_CTX *stack = talloc_stackframe();
+ TALLOC_CTX *memctx = talloc_new(stack);
+ NTSTATUS status;
+
+ dom = createdomain(memctx);
+ if (dom == NULL) {
+ TALLOC_FREE(stack);
+ return false;
+ }
+
+ status = dom->methods->init(dom);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(stack);
+ return false;
+ }
+
+ /* test a single allocation from pool (no mapping) */
+ result = test_getnewid1(memctx, dom);
+ CHECKRESULT(result);
+
+ /* test idmap_tdb_common_set_mapping */
+ result = test_setmap1(memctx, dom);
+ CHECKRESULT(result);
+
+ /* test idmap_tdb_common_sid_to_unixid */
+ result = test_sid2unixid1(memctx, dom);
+ CHECKRESULT(result);
+ result = test_sid2unixid2(memctx, dom);
+ CHECKRESULT(result);
+
+ /* test idmap_tdb_common_sids_to_unixids */
+ result = test_sids2unixids1(memctx, dom);
+ CHECKRESULT(result);
+ result = test_sids2unixids2(memctx, dom);
+ CHECKRESULT(result);
+ result = test_sids2unixids3(memctx, dom);
+ CHECKRESULT(result);
+
+ /* test idmap_tdb_common_unixid_to_sid */
+ result = test_unixid2sid1(memctx, dom);
+ CHECKRESULT(result);
+ result = test_unixid2sid2(memctx, dom);
+ CHECKRESULT(result);
+ result = test_unixid2sid3(memctx, dom);
+ CHECKRESULT(result);
+
+ /* test idmap_tdb_common_unixids_to_sids */
+ result = test_unixids2sids1(memctx, dom);
+ CHECKRESULT(result);
+ result = test_unixids2sids2(memctx, dom);
+ CHECKRESULT(result);
+ result = test_unixids2sids3(memctx, dom);
+ CHECKRESULT(result);
+
+ /* test filling up the range */
+ result = test_getnewid2(memctx, dom);
+ CHECKRESULT(result);
+
+ talloc_free(stack);
+
+ return true;
+}
diff --git a/source3/torture/test_matching.c b/source3/torture/test_matching.c
new file mode 100644
index 0000000..647b758
--- /dev/null
+++ b/source3/torture/test_matching.c
@@ -0,0 +1,276 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility tests
+ Copyright (C) Stefan Metzmacher 2021
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util_matching.h"
+#include "proto.h"
+
+bool run_str_match_mswild(int dummy)
+{
+ const char *namelist = "/abc*.txt/xyz*.dat/a0123456789Z/";
+ name_compare_entry *name_entries = NULL;
+ struct samba_path_matching *pmcs = NULL;
+ struct samba_path_matching *pmci = NULL;
+ const struct str_match_mswild_name {
+ const char *name;
+ ssize_t case_sensitive_idx;
+ ssize_t case_insensitive_idx;
+ } names[] = {{
+ .name = "/dir/abc123.txt",
+ .case_sensitive_idx = 0,
+ .case_insensitive_idx = 0,
+ },{
+ .name = "/dir/AbC123.TxT",
+ .case_sensitive_idx = -1,
+ .case_insensitive_idx = 0,
+ },{
+ .name = "/dir/xyz123.dat",
+ .case_sensitive_idx = 1,
+ .case_insensitive_idx = 1,
+ },{
+ .name = "/dir/XyZ123.DaT",
+ .case_sensitive_idx = -1,
+ .case_insensitive_idx = 1,
+ },{
+ .name = "/dir/aaa123.jpg",
+ .case_sensitive_idx = -1,
+ .case_insensitive_idx = -1,
+ },{
+ .name = "/dir/a0123456789Z",
+ .case_sensitive_idx = 2,
+ .case_insensitive_idx = 2,
+ },{
+ .name = "/dir/A0123456789z",
+ .case_sensitive_idx = -1,
+ .case_insensitive_idx = 2,
+ }};
+ NTSTATUS status;
+ size_t i;
+ bool ret = true;
+
+ d_fprintf(stderr, "namelist: %s\n", namelist);
+
+ set_namearray(&name_entries, namelist);
+ SMB_ASSERT(name_entries != NULL);
+
+ status = samba_path_matching_mswild_create(talloc_tos(),
+ true, /* case_sensitive */
+ namelist,
+ &pmcs);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+ status = samba_path_matching_mswild_create(talloc_tos(),
+ false, /* case_sensitive */
+ namelist,
+ &pmci);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ const struct str_match_mswild_name *n = &names[i];
+ bool case_sensitive_match;
+ bool case_insensitive_match;
+ ssize_t cs_match_idx = -1;
+ ssize_t ci_match_idx = -1;
+ ssize_t replace_start = -1;
+ ssize_t replace_end = -1;
+ bool ok = true;
+
+ case_sensitive_match = is_in_path(n->name,
+ name_entries,
+ true);
+ if (n->case_sensitive_idx != -1) {
+ ok &= case_sensitive_match;
+ } else {
+ ok &= !case_sensitive_match;
+ }
+ status = samba_path_matching_check_last_component(pmcs,
+ n->name,
+ &cs_match_idx,
+ &replace_start,
+ &replace_end);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+ SMB_ASSERT(replace_start == -1);
+ SMB_ASSERT(replace_end == -1);
+ if (n->case_sensitive_idx != cs_match_idx) {
+ ok = false;
+ }
+ case_insensitive_match = is_in_path(n->name,
+ name_entries,
+ false);
+ if (n->case_insensitive_idx != -1) {
+ ok &= case_insensitive_match;
+ } else {
+ ok &= !case_insensitive_match;
+ }
+ status = samba_path_matching_check_last_component(pmci,
+ n->name,
+ &ci_match_idx,
+ &replace_start,
+ &replace_end);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+ SMB_ASSERT(replace_start == -1);
+ SMB_ASSERT(replace_end == -1);
+ if (n->case_insensitive_idx != ci_match_idx) {
+ ok = false;
+ }
+
+ d_fprintf(stderr, "name[%s] "
+ "case_sensitive[TIDX=%zd;MATCH=%u;MIDX=%zd] "
+ "case_insensitive[TIDX=%zd;MATCH=%u;MIDX=%zd] "
+ "%s\n",
+ n->name,
+ n->case_sensitive_idx,
+ case_sensitive_match,
+ cs_match_idx,
+ n->case_insensitive_idx,
+ case_insensitive_match,
+ ci_match_idx,
+ ok ? "OK" : "FAIL");
+
+ ret &= ok;
+ }
+
+ return ret;
+}
+
+bool run_str_match_regex_sub1(int dummy)
+{
+ const char *invalidlist1 = "/Re7599Ex[0-9].*\\.txt/";
+ const char *invalidlist2 = "/Re7599Ex\\([0-9]\\).*\\.\\(txt\\)/";
+ const char *invalidlist3 = "/Re7599Ex\\([0-9]).*\\.txt/";
+ const char *invalidlist4 = "/Re7599Ex[0-9.*\\.txt/";
+ const char *namelist = "/Re7599Ex\\([0-9]\\).*\\.txt/test\\(.*\\).txt/^test\\([0-9]*\\).dat/";
+ struct samba_path_matching *pm = NULL;
+ const struct str_match_regex_sub1 {
+ const char *name;
+ ssize_t match_idx;
+ ssize_t sub_start;
+ ssize_t sub_end;
+ } names[] = {{
+ .name = "/dir/Re7599Ex567.txt",
+ .match_idx = 0,
+ .sub_start = 13,
+ .sub_end = 14,
+ },{
+ .name = "/dir/rE7599eX567.txt",
+ .match_idx = -1,
+ .sub_start = -1,
+ .sub_end = -1,
+ },{
+ .name = "/dir/Re7599Ex.txt",
+ .match_idx = -1,
+ .sub_start = -1,
+ .sub_end = -1,
+ },{
+ .name = "/dir/testabc123.txt",
+ .match_idx = 1,
+ .sub_start = 9,
+ .sub_end = 15,
+ },{
+ .name = "/dir/testabc123.tXt",
+ .match_idx = -1,
+ .sub_start = -1,
+ .sub_end = -1,
+ },{
+ .name = "/dir/test123.dat",
+ .match_idx = 2,
+ .sub_start = 9,
+ .sub_end = 12,
+ },{
+ .name = "/dir/tEst123.dat",
+ .match_idx = -1,
+ .sub_start = -1,
+ .sub_end = -1,
+ }};
+ NTSTATUS status;
+ size_t i;
+ bool ret = true;
+
+ d_fprintf(stderr, "invalidlist1: %s\n", invalidlist1);
+ status = samba_path_matching_regex_sub1_create(talloc_tos(),
+ invalidlist1,
+ &pm);
+ SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER));
+ d_fprintf(stderr, "invalidlist2: %s\n", invalidlist2);
+ status = samba_path_matching_regex_sub1_create(talloc_tos(),
+ invalidlist2,
+ &pm);
+ SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER));
+ d_fprintf(stderr, "invalidlist3: %s\n", invalidlist3);
+ status = samba_path_matching_regex_sub1_create(talloc_tos(),
+ invalidlist3,
+ &pm);
+ SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER));
+ d_fprintf(stderr, "invalidlist4: %s\n", invalidlist4);
+ status = samba_path_matching_regex_sub1_create(talloc_tos(),
+ invalidlist4,
+ &pm);
+ SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER));
+
+ d_fprintf(stderr, "namelist: %s\n", namelist);
+ status = samba_path_matching_regex_sub1_create(talloc_tos(),
+ namelist,
+ &pm);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ const struct str_match_regex_sub1 *n = &names[i];
+ ssize_t match_idx = -1;
+ ssize_t replace_start = -1;
+ ssize_t replace_end = -1;
+ bool ok = true;
+
+ status = samba_path_matching_check_last_component(pm,
+ n->name,
+ &match_idx,
+ &replace_start,
+ &replace_end);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+ if (match_idx == -1) {
+ SMB_ASSERT(replace_start == -1);
+ SMB_ASSERT(replace_end == -1);
+ }
+ if (n->match_idx != match_idx) {
+ ok = false;
+ }
+ if (n->sub_start != replace_start) {
+ ok = false;
+ }
+ if (n->sub_end != replace_end) {
+ ok = false;
+ }
+
+ d_fprintf(stderr, "name[%s] "
+ "T[IDX=%zd;START=%zd;END=%zd] "
+ "M[[IDX=%zd;START=%zd;END=%zd] "
+ "%s\n",
+ n->name,
+ n->match_idx,
+ n->sub_start,
+ n->sub_end,
+ match_idx,
+ replace_start,
+ replace_end,
+ ok ? "OK" : "FAIL");
+
+ ret &= ok;
+ }
+
+ return ret;
+}
diff --git a/source3/torture/test_messaging_fd_passing.c b/source3/torture/test_messaging_fd_passing.c
new file mode 100644
index 0000000..5030b66
--- /dev/null
+++ b/source3/torture/test_messaging_fd_passing.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test for fd passing with messaging
+
+ Copyright (C) Michael Adam 2014
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "lib/util/tevent_unix.h"
+#include "messages.h"
+
+/**
+ * test fdpass1:
+ *
+ * Try to pass an fd to the sending process - fails.
+ */
+bool run_messaging_fdpass1(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ bool retval = false;
+ int pipe_fds[2];
+ int pass_fds[1] = { 0 };
+ int ret;
+ NTSTATUS status;
+ struct server_id dst;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ goto fail;
+ }
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ goto fail;
+ }
+
+ dst = messaging_server_id(msg_ctx);
+
+ ret = pipe(pipe_fds);
+ if (ret != 0) {
+ perror("pipe failed");
+ goto fail;
+ }
+
+ pass_fds[0] = pipe_fds[0];
+
+ status = messaging_send_iov(msg_ctx, dst, MSG_PING, NULL, 0,
+ pass_fds, 1);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ fprintf(stderr,
+ "messaging_send_iov gave: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ retval = true;
+
+fail:
+ TALLOC_FREE(frame);
+ return retval;
+}
+
+/**
+ * test fdpass2:
+ *
+ * - parent: create a child
+ * - parent: create a two pipes in the parent: up and down
+ * - parent: pass the up pipe's reading end and the down pipe's writing
+ * end to the child and close them
+ * - parent: write a number into the up pipe's writing end
+ * - child: read number from the passed reading fd (up)
+ * - child: write the read number to the passed writing fd (down)
+ * - parent: read number from the down pipe's reading end and compare with
+ * original number
+ */
+
+#define MSG_TORTURE_FDPASS2 0xF002
+
+static bool fdpass2_filter(struct messaging_rec *rec, void *private_data)
+{
+ if (rec->msg_type != MSG_TORTURE_FDPASS2) {
+ return false;
+ }
+
+ if (rec->num_fds != 2) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool fdpass2_child(int ready_fd)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool retval = false;
+ uint8_t c = 1;
+ struct tevent_req *subreq;
+ int ret;
+ ssize_t bytes;
+ int up_fd, down_fd;
+ struct messaging_rec *rec;
+ bool ok;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ fprintf(stderr, "child: tevent_context_init failed\n");
+ goto done;
+ }
+
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "child: messaging_init failed\n");
+ goto done;
+ }
+
+ /* Tell the parent we are ready to receive messages. */
+ bytes = write(ready_fd, &c, 1);
+ if (bytes != 1) {
+ perror("child: failed to write to ready_fd");
+ goto done;
+ }
+
+ subreq = messaging_filtered_read_send(frame, /* TALLOC_CTX */
+ ev, msg_ctx,
+ fdpass2_filter, NULL);
+ if (subreq == NULL) {
+ fprintf(stderr, "child: messaging_filtered_read_send failed\n");
+ goto done;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ fprintf(stderr, "child: tevent_req_poll failed\n");
+ goto done;
+ }
+
+ ret = messaging_filtered_read_recv(subreq, frame, &rec);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ fprintf(stderr, "child: messaging_filtered_read_recv failed\n");
+ goto done;
+ }
+
+ SMB_ASSERT(rec->num_fds == 2);
+
+ /* Tell the parent we are done. */
+ bytes = write(ready_fd, &c, 1);
+ if (bytes != 1) {
+ perror("child: failed to write to ready_fd");
+ goto done;
+ }
+
+ up_fd = rec->fds[0];
+ down_fd = rec->fds[1];
+
+ bytes = read(up_fd, &c, 1);
+ if (bytes != 1) {
+ perror("child: read from up_fd failed");
+ goto done;
+ }
+
+ bytes = write(down_fd, &c, 1);
+ if (bytes != 1) {
+ perror("child: write to down_fd failed");
+ }
+
+ printf("child: done\n");
+
+ retval = true;
+
+done:
+ TALLOC_FREE(frame);
+ return retval;
+}
+
+struct child_done_state {
+ int fd;
+ bool done;
+};
+
+static void child_done_cb(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct child_done_state *state =
+ (struct child_done_state *)private_data;
+ char c = 0;
+ ssize_t bytes;
+
+ bytes = read(state->fd, &c, 1);
+ if (bytes != 1) {
+ perror("parent: read from ready_fd failed");
+ }
+
+ state->done = true;
+}
+
+static bool fdpass2_parent(pid_t child_pid, int ready_fd, size_t payload_size)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ bool retval = false;
+ int up_pipe[2];
+ int down_pipe[2];
+ int pass_fds[2] = { 0 };
+ int ret;
+ NTSTATUS status;
+ struct server_id dst;
+ TALLOC_CTX *frame = talloc_stackframe();
+ uint8_t c1 = 1, c2, c;
+ ssize_t bytes;
+ struct iovec iov;
+ DATA_BLOB blob;
+ struct tevent_fd *child_done_fde;
+ struct child_done_state child_state;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ fprintf(stderr, "parent: tevent_context_init failed\n");
+ goto done;
+ }
+
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "parent: messaging_init failed\n");
+ goto done;
+ }
+
+ /* wait util the child is ready to receive messages */
+ bytes = read(ready_fd, &c, 1);
+ if (bytes != 1) {
+ perror("parent: read from ready_fd failed");
+ goto done;
+ }
+
+ ret = pipe(up_pipe);
+ if (ret != 0) {
+ perror("parent: pipe failed for up_pipe");
+ goto done;
+ }
+
+ ret = pipe(down_pipe);
+ if (ret != 0) {
+ perror("parent: pipe failed for down_pipe");
+ goto done;
+ }
+
+ child_state.fd = ready_fd;
+ child_state.done = false;
+
+ child_done_fde = tevent_add_fd(ev, ev, ready_fd, TEVENT_FD_READ,
+ child_done_cb, &child_state);
+ if (child_done_fde == NULL) {
+ fprintf(stderr,
+ "parent: failed tevent_add_fd for child done\n");
+ goto done;
+ }
+
+ pass_fds[0] = up_pipe[0];
+ pass_fds[1] = down_pipe[1];
+
+ dst = messaging_server_id(msg_ctx);
+ dst.pid = child_pid;
+
+ /*
+ * Send a certain payload with the fds, to test to test
+ * that fd-passing works when we have fragmentation and
+ * re-assembly of the datagrams.
+ *
+ * Fragmentation/queuing is triggered by a certain payload
+ * size. Payloads below that size use the fast path.
+ */
+ blob = data_blob_talloc_zero(frame, payload_size);
+ iov.iov_base = blob.data;
+ iov.iov_len = blob.length;
+
+ status = messaging_send_iov(msg_ctx, dst, MSG_TORTURE_FDPASS2, &iov, 1,
+ pass_fds, 2);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "parent: messaging_send_iov failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ printf("parent: waiting for child to confirm\n");
+
+ while (!child_state.done) {
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ fprintf(stderr, "parent: tevent_loop_once failed\n");
+ goto done;
+ }
+ }
+
+ printf("parent: child confirmed\n");
+
+ close(up_pipe[0]);
+ close(down_pipe[1]);
+
+ bytes = write(up_pipe[1], &c1, 1);
+ if (bytes != 1) {
+ perror("parent: write to up pipe failed");
+ goto done;
+ }
+
+ bytes = read(down_pipe[0], &c2, 1);
+ if (bytes != 1) {
+ perror("parent: read from down pipe failed");
+ goto done;
+ }
+
+ if (c1 != c2) {
+ fprintf(stderr, "parent: c1[%d] != c2[%d]\n", c1, c2);
+ goto done;
+ }
+
+ ret = waitpid(child_pid, NULL, 0);
+ if (ret == -1) {
+ perror("parent: waitpid failed");
+ goto done;
+ }
+
+ retval = true;
+
+done:
+ TALLOC_FREE(frame);
+ return retval;
+}
+
+static bool run_messaging_fdpass2_int(int dummy, size_t payload_size)
+{
+ bool retval = false;
+ pid_t child_pid;
+ int ready_pipe[2];
+ int ret;
+
+ ret = pipe(ready_pipe);
+ if (ret != 0) {
+ perror("parent: pipe failed for ready_pipe");
+ return retval;
+ }
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork failed");
+ } else if (child_pid == 0) {
+ close(ready_pipe[0]);
+ retval = fdpass2_child(ready_pipe[1]);
+ } else {
+ close(ready_pipe[1]);
+ retval = fdpass2_parent(child_pid, ready_pipe[0], payload_size);
+ }
+
+ return retval;
+}
+
+bool run_messaging_fdpass2(int dummy)
+{
+ return run_messaging_fdpass2_int(dummy, 1000*1000);
+}
+
+/**
+ * Variant of the FDPASS2 test that tests the non-queuing fast path
+ * with a small payload.
+ */
+bool run_messaging_fdpass2a(int dummy)
+{
+ return run_messaging_fdpass2_int(dummy, 1);
+}
+
+/**
+ * Variant of the FDPASS2 test that tests the non-queuing fast path
+ * without a payload.
+ */
+bool run_messaging_fdpass2b(int dummy)
+{
+ return run_messaging_fdpass2_int(dummy, 0);
+}
diff --git a/source3/torture/test_messaging_read.c b/source3/torture/test_messaging_read.c
new file mode 100644
index 0000000..555f084
--- /dev/null
+++ b/source3/torture/test_messaging_read.c
@@ -0,0 +1,706 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test for a messaging_read bug
+ Copyright (C) Volker Lendecke 2014
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "lib/util/tevent_unix.h"
+#include "messages.h"
+
+struct msg_count_state {
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ uint32_t msg_type;
+ unsigned *count;
+};
+
+static void msg_count_done(struct tevent_req *subreq);
+
+static struct tevent_req *msg_count_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ uint32_t msg_type,
+ unsigned *count)
+{
+ struct tevent_req *req, *subreq;
+ struct msg_count_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct msg_count_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->msg_ctx = msg_ctx;
+ state->msg_type = msg_type;
+ state->count = count;
+
+ subreq = messaging_read_send(state, state->ev, state->msg_ctx,
+ state->msg_type);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, msg_count_done, req);
+ return req;
+}
+
+static void msg_count_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct msg_count_state *state = tevent_req_data(
+ req, struct msg_count_state);
+ int ret;
+
+ ret = messaging_read_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ *state->count += 1;
+
+ subreq = messaging_read_send(state, state->ev, state->msg_ctx,
+ state->msg_type);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, msg_count_done, req);
+}
+
+bool run_messaging_read1(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ struct tevent_req *req1 = NULL;
+ unsigned count1 = 0;
+ struct tevent_req *req2 = NULL;
+ unsigned count2 = 0;
+ NTSTATUS status;
+ bool retval = false;
+ int i;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ goto fail;
+ }
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ goto fail;
+ }
+
+ req1 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count1);
+ if (req1 == NULL) {
+ fprintf(stderr, "msg_count_send failed\n");
+ goto fail;
+ }
+ req2 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count2);
+ if (req1 == NULL) {
+ fprintf(stderr, "msg_count_send failed\n");
+ goto fail;
+ }
+ status = messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
+ MSG_SMB_NOTIFY, NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "messaging_send_buf failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ for (i=0; i<2; i++) {
+ if (tevent_loop_once(ev) != 0) {
+ fprintf(stderr, "tevent_loop_once failed\n");
+ goto fail;
+ }
+ }
+
+ printf("%u/%u\n", count1, count2);
+
+ if ((count1 != 1) || (count2 != 0)) {
+ fprintf(stderr, "Got %u/%u msgs, expected 1/0\n",
+ count1, count2);
+ goto fail;
+ }
+
+ retval = true;
+fail:
+ TALLOC_FREE(req1);
+ TALLOC_FREE(req2);
+ TALLOC_FREE(msg_ctx);
+ TALLOC_FREE(ev);
+ return retval;
+}
+
+struct msg_free_state {
+ struct tevent_req **to_free;
+};
+
+static void msg_free_done(struct tevent_req *subreq);
+
+static struct tevent_req *msg_free_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ uint32_t msg_type,
+ struct tevent_req **to_free)
+{
+ struct tevent_req *req, *subreq;
+ struct msg_free_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct msg_free_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->to_free = to_free;
+
+ subreq = messaging_read_send(state, ev, msg_ctx, msg_type);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, msg_free_done, req);
+ return req;
+}
+
+static void msg_free_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct msg_free_state *state = tevent_req_data(
+ req, struct msg_free_state);
+ int ret;
+
+ ret = messaging_read_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ TALLOC_FREE(*state->to_free);
+ tevent_req_done(req);
+}
+
+bool run_messaging_read2(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ struct tevent_req *req1 = NULL;
+ struct tevent_req *req2 = NULL;
+ unsigned count = 0;
+ NTSTATUS status;
+ bool retval = false;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ goto fail;
+ }
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ goto fail;
+ }
+
+ req1 = msg_free_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &req2);
+ if (req1 == NULL) {
+ fprintf(stderr, "msg_count_send failed\n");
+ goto fail;
+ }
+ req2 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count);
+ if (req1 == NULL) {
+ fprintf(stderr, "msg_count_send failed\n");
+ goto fail;
+ }
+ status = messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
+ MSG_SMB_NOTIFY, NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "messaging_send_buf failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (!tevent_req_poll(req1, ev) != 0) {
+ fprintf(stderr, "tevent_req_poll failed\n");
+ goto fail;
+ }
+
+ if (count != 0) {
+ fprintf(stderr, "Got %u msgs, expected none\n", count);
+ goto fail;
+ }
+
+ retval = true;
+fail:
+ TALLOC_FREE(req1);
+ TALLOC_FREE(msg_ctx);
+ TALLOC_FREE(ev);
+ return retval;
+}
+
+struct msg_pingpong_state {
+ struct messaging_context *msg_ctx;
+};
+
+static void msg_pingpong_done(struct tevent_req *subreq);
+
+static struct tevent_req *msg_pingpong_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct server_id dst)
+{
+ struct tevent_req *req, *subreq;
+ struct msg_pingpong_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct msg_pingpong_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(10, 0))) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->msg_ctx = messaging_init(state, ev);
+ if (tevent_req_nomem(state->msg_ctx, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = messaging_send_buf(state->msg_ctx, dst, MSG_PING, NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("messaging_send_buf failed: %s\n", nt_errstr(status));
+ tevent_req_error(req, map_errno_from_nt_status(status));
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = messaging_read_send(state, ev, state->msg_ctx, MSG_PONG);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, msg_pingpong_done, req);
+ return req;
+}
+
+static void msg_pingpong_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+
+ ret = messaging_read_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static int msg_pingpong_recv(struct tevent_req *req)
+{
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+ return 0;
+}
+
+static int msg_pingpong(struct server_id dst)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ int ret = ENOMEM;
+
+ ev = tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = msg_pingpong_send(ev, ev, dst);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ ret = errno;
+ goto fail;
+ }
+ ret = msg_pingpong_recv(req);
+fail:
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+static void ping_responder_exit(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ bool *done = private_data;
+
+ printf("Child: received write on exit-pipe\n");
+
+ *done = true;
+}
+
+static void ping_responder(int ready_pipe, int exit_pipe)
+{
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ struct tevent_fd *exit_handler;
+ char c = 0;
+ bool done = false;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ fprintf(stderr, "child tevent_context_init failed\n");
+ exit(1);
+ }
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "child messaging_init failed\n");
+ exit(1);
+ }
+ exit_handler = tevent_add_fd(ev, ev, exit_pipe, TEVENT_FD_READ,
+ ping_responder_exit, &done);
+ if (exit_handler == NULL) {
+ fprintf(stderr, "child tevent_add_fd failed\n");
+ exit(1);
+ }
+
+ if (write(ready_pipe, &c, 1) != 1) {
+ fprintf(stderr, "child messaging_init failed\n");
+ exit(1);
+ }
+
+ while (!done) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ fprintf(stderr, "child tevent_loop_once failed\n");
+ exit(1);
+ }
+ }
+
+ printf("Child: done, exiting...\n");
+
+ TALLOC_FREE(msg_ctx);
+ TALLOC_FREE(ev);
+}
+
+bool run_messaging_read3(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ bool retval = false;
+ pid_t child;
+ int ready_pipe[2];
+ int exit_pipe[2];
+ int i, ret;
+ char c;
+ struct server_id dst;
+ ssize_t written;
+
+ if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
+ perror("pipe failed");
+ return false;
+ }
+
+ child = fork();
+ if (child == -1) {
+ perror("fork failed");
+ return false;
+ }
+
+ if (child == 0) {
+ close(ready_pipe[0]);
+ close(exit_pipe[1]);
+ ping_responder(ready_pipe[1], exit_pipe[0]);
+ exit(0);
+ }
+ close(ready_pipe[1]);
+ close(exit_pipe[0]);
+
+ if (read(ready_pipe[0], &c, 1) != 1) {
+ perror("read failed");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ goto fail;
+ }
+
+ dst = (struct server_id){ .pid = child, .vnn = NONCLUSTER_VNN, };
+
+ for (i=0; i<100; i++) {
+ ret = msg_pingpong(dst);
+ if (ret != 0){
+ fprintf(stderr, "msg_pingpong failed\n");
+ goto fail;
+ }
+ }
+
+ printf("Parent: telling child to exit\n");
+
+ written = write(exit_pipe[1], &c, 1);
+ if (written != 1) {
+ perror("write to exit_pipe failed");
+ goto fail;
+ }
+
+ ret = waitpid(child, NULL, 0);
+ if (ret == -1) {
+ perror("waitpid failed");
+ goto fail;
+ }
+
+ printf("Parent: child exited. Done\n");
+
+ retval = true;
+fail:
+ TALLOC_FREE(msg_ctx);
+ TALLOC_FREE(ev);
+ return retval;
+}
+
+/**
+ * read4:
+ *
+ * test transferring a big payload.
+ */
+
+#define MSG_TORTURE_READ4 0xF104
+
+static bool read4_child(int ready_fd)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool retval = false;
+ uint8_t c = 1;
+ struct tevent_req *subreq;
+ int ret;
+ ssize_t bytes;
+ struct messaging_rec *rec;
+ bool ok;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ fprintf(stderr, "child: tevent_context_init failed\n");
+ goto done;
+ }
+
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "child: messaging_init failed\n");
+ goto done;
+ }
+
+ printf("child: telling parent we're ready to receive messages\n");
+
+ /* Tell the parent we are ready to receive messages. */
+ bytes = write(ready_fd, &c, 1);
+ if (bytes != 1) {
+ perror("child: failed to write to ready_fd");
+ goto done;
+ }
+
+ printf("child: waiting for messages\n");
+
+ subreq = messaging_read_send(frame, /* TALLOC_CTX */
+ ev, msg_ctx,
+ MSG_TORTURE_READ4);
+ if (subreq == NULL) {
+ fprintf(stderr, "child: messaging_read_send failed\n");
+ goto done;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ fprintf(stderr, "child: tevent_req_poll failed\n");
+ goto done;
+ }
+
+ printf("child: receiving message\n");
+
+ ret = messaging_read_recv(subreq, frame, &rec);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ fprintf(stderr, "child: messaging_read_recv failed\n");
+ goto done;
+ }
+
+ printf("child: received message\n");
+
+ /* Tell the parent we are done. */
+ bytes = write(ready_fd, &c, 1);
+ if (bytes != 1) {
+ perror("child: failed to write to ready_fd");
+ goto done;
+ }
+
+ printf("child: done\n");
+
+ retval = true;
+
+done:
+ TALLOC_FREE(frame);
+ return retval;
+}
+
+struct child_done_state {
+ int fd;
+ bool done;
+};
+
+static void child_done_cb(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct child_done_state *state =
+ (struct child_done_state *)private_data;
+ char c = 0;
+ ssize_t bytes;
+
+ bytes = read(state->fd, &c, 1);
+ if (bytes != 1) {
+ perror("parent: read from ready_fd failed");
+ }
+
+ state->done = true;
+}
+
+static bool read4_parent(pid_t child_pid, int ready_fd)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ bool retval = false;
+ int ret;
+ NTSTATUS status;
+ struct server_id dst;
+ TALLOC_CTX *frame = talloc_stackframe();
+ uint8_t c;
+ ssize_t bytes;
+ struct iovec iov;
+ DATA_BLOB blob;
+ struct tevent_fd *child_done_fde;
+ struct child_done_state child_state;
+
+ /* wait until the child is ready to receive messages */
+ bytes = read(ready_fd, &c, 1);
+ if (bytes != 1) {
+ perror("parent: read from ready_fd failed");
+ goto done;
+ }
+
+ printf("parent: child is ready to receive messages\n");
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ fprintf(stderr, "parent: tevent_context_init failed\n");
+ goto done;
+ }
+
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "parent: messaging_init failed\n");
+ goto done;
+ }
+
+ child_state.fd = ready_fd;
+ child_state.done = false;
+
+ child_done_fde = tevent_add_fd(ev, ev, ready_fd, TEVENT_FD_READ,
+ child_done_cb, &child_state);
+ if (child_done_fde == NULL) {
+ fprintf(stderr,
+ "parent: failed tevent_add_fd for child done\n");
+ goto done;
+ }
+
+ /*
+ * Send a 1M payload with the message.
+ */
+ blob = data_blob_talloc_zero(frame, 1000*1000);
+ iov.iov_base = blob.data;
+ iov.iov_len = blob.length;
+
+ dst = messaging_server_id(msg_ctx);
+ dst.pid = child_pid;
+
+ printf("parent: sending message to child\n");
+
+ status = messaging_send_iov(msg_ctx, dst, MSG_TORTURE_READ4, &iov, 1,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "parent: messaging_send_iov failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ printf("parent: waiting for child to confirm\n");
+
+ while (!child_state.done) {
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ fprintf(stderr, "parent: tevent_loop_once failed\n");
+ goto done;
+ }
+ }
+
+ printf("parent: child confirmed\n");
+
+ ret = waitpid(child_pid, NULL, 0);
+ if (ret == -1) {
+ perror("parent: waitpid failed");
+ goto done;
+ }
+
+ printf("parent: done\n");
+
+ retval = true;
+
+done:
+ TALLOC_FREE(frame);
+ return retval;
+}
+
+bool run_messaging_read4(int dummy)
+{
+ bool retval = false;
+ pid_t child_pid;
+ int ready_pipe[2];
+ int ret;
+
+ ret = pipe(ready_pipe);
+ if (ret != 0) {
+ perror("parent: pipe failed for ready_pipe");
+ return retval;
+ }
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork failed");
+ } else if (child_pid == 0) {
+ close(ready_pipe[0]);
+ retval = read4_child(ready_pipe[1]);
+ } else {
+ close(ready_pipe[1]);
+ retval = read4_parent(child_pid, ready_pipe[0]);
+ }
+
+ return retval;
+}
diff --git a/source3/torture/test_messaging_send_all.c b/source3/torture/test_messaging_send_all.c
new file mode 100644
index 0000000..8512fa4
--- /dev/null
+++ b/source3/torture/test_messaging_send_all.c
@@ -0,0 +1,279 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test for a messaging_send_all bug
+ * Copyright (C) Volker Lendecke 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "lib/util/tevent_unix.h"
+#include "messages.h"
+#include "lib/async_req/async_sock.h"
+#include "lib/util/sys_rw.h"
+
+static pid_t fork_responder(struct messaging_context *msg_ctx,
+ int exit_pipe[2])
+{
+ struct tevent_context *ev = messaging_tevent_context(msg_ctx);
+ struct tevent_req *req;
+ pid_t child_pid;
+ int ready_pipe[2];
+ char c = 0;
+ bool ok;
+ int ret, err;
+ NTSTATUS status;
+ ssize_t nwritten;
+
+ ret = pipe(ready_pipe);
+ if (ret == -1) {
+ perror("pipe failed");
+ return -1;
+ }
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork failed");
+ close(ready_pipe[0]);
+ close(ready_pipe[1]);
+ return -1;
+ }
+
+ if (child_pid != 0) {
+ ssize_t nread;
+ close(ready_pipe[1]);
+ nread = read(ready_pipe[0], &c, 1);
+ close(ready_pipe[0]);
+ if (nread != 1) {
+ perror("read failed");
+ return -1;
+ }
+ return child_pid;
+ }
+
+ close(ready_pipe[0]);
+ close(exit_pipe[1]);
+
+ status = messaging_reinit(msg_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "messaging_reinit failed: %s\n",
+ nt_errstr(status));
+ close(ready_pipe[1]);
+ exit(1);
+ }
+
+ nwritten = sys_write(ready_pipe[1], &c, 1);
+ if (nwritten != 1) {
+ fprintf(stderr, "write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ close(ready_pipe[1]);
+
+ req = wait_for_read_send(ev, ev, exit_pipe[0], false);
+ if (req == NULL) {
+ fprintf(stderr, "wait_for_read_send failed\n");
+ exit(1);
+ }
+
+ ok = tevent_req_poll_unix(req, ev, &err);
+ if (!ok) {
+ fprintf(stderr, "tevent_req_poll_unix failed: %s\n",
+ strerror(err));
+ exit(1);
+ }
+
+ exit(0);
+}
+
+struct messaging_send_all_state {
+ struct tevent_context *ev;
+ struct messaging_context *msg;
+ pid_t *senders;
+ size_t num_received;
+};
+
+static void collect_pong_received(struct tevent_req *subreq);
+
+static struct tevent_req *collect_pong_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ const pid_t *senders,
+ size_t num_senders)
+{
+ struct tevent_req *req, *subreq;
+ struct messaging_send_all_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct messaging_send_all_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->senders = talloc_memdup(
+ state, senders, num_senders * sizeof(pid_t));
+ if (tevent_req_nomem(state->senders, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->ev = ev;
+ state->msg = msg;
+
+ subreq = messaging_read_send(state, state->ev, state->msg, MSG_PONG);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, collect_pong_received, req);
+ return req;
+}
+
+static void collect_pong_received(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct messaging_send_all_state *state = tevent_req_data(
+ req, struct messaging_send_all_state);
+ size_t num_senders = talloc_array_length(state->senders);
+ size_t i;
+ struct messaging_rec *rec;
+ int ret;
+
+ ret = messaging_read_recv(subreq, state, &rec);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ /*
+ * We need to make sure we don't receive our own broadcast!
+ */
+
+ if (rec->src.pid == (uint64_t)getpid()) {
+ fprintf(stderr, "Received my own broadcast!\n");
+ tevent_req_error(req, EMULTIHOP);
+ return;
+ }
+
+ for (i=0; i<num_senders; i++) {
+ if (state->senders[i] == (pid_t)rec->src.pid) {
+ printf("got message from %"PRIu64"\n", rec->src.pid);
+ state->senders[i] = 0;
+ state->num_received += 1;
+ break;
+ }
+ }
+
+ if (state->num_received == num_senders) {
+ printf("done\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = messaging_read_send(state, state->ev, state->msg, MSG_PONG);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, collect_pong_received, req);
+}
+
+static int collect_pong_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+extern int torture_nprocs;
+
+bool run_messaging_send_all(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ int exit_pipe[2];
+ pid_t children[MAX(5, torture_nprocs)];
+ struct tevent_req *req;
+ size_t i;
+ bool ok;
+ int ret, err;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ return false;
+ }
+ msg_ctx = messaging_init(ev, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ return false;
+ }
+ ret = pipe(exit_pipe);
+ if (ret != 0) {
+ perror("parent: pipe failed for exit_pipe");
+ return false;
+ }
+
+ for (i=0; i<ARRAY_SIZE(children); i++) {
+ children[i] = fork_responder(msg_ctx, exit_pipe);
+ if (children[i] == -1) {
+ fprintf(stderr, "fork_responder(%zu) failed\n", i);
+ return false;
+ }
+ }
+
+ req = collect_pong_send(ev, ev, msg_ctx, children,
+ ARRAY_SIZE(children));
+ if (req == NULL) {
+ perror("collect_pong failed");
+ return false;
+ }
+
+ ok = tevent_req_set_endtime(req, ev,
+ tevent_timeval_current_ofs(10, 0));
+ if (!ok) {
+ perror("tevent_req_set_endtime failed");
+ return false;
+ }
+
+ messaging_send_all(msg_ctx, MSG_PING, NULL, 0);
+
+ ok = tevent_req_poll_unix(req, ev, &err);
+ if (!ok) {
+ perror("tevent_req_poll_unix failed");
+ return false;
+ }
+
+ ret = collect_pong_recv(req);
+ TALLOC_FREE(req);
+
+ if (ret != 0) {
+ fprintf(stderr, "collect_pong_send returned %s\n",
+ strerror(ret));
+ return false;
+ }
+
+ close(exit_pipe[1]);
+
+ for (i=0; i<ARRAY_SIZE(children); i++) {
+ pid_t child;
+ int status;
+
+ do {
+ child = waitpid(children[i], &status, 0);
+ } while ((child == -1) && (errno == EINTR));
+
+ if (child != children[i]) {
+ printf("waitpid(%d) failed\n", children[i]);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/source3/torture/test_namemap_cache.c b/source3/torture/test_namemap_cache.c
new file mode 100644
index 0000000..b30fe87
--- /dev/null
+++ b/source3/torture/test_namemap_cache.c
@@ -0,0 +1,270 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * namemap_cache.c
+ * Copyright (C) Volker Lendecke 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "lib/namemap_cache.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/gencache.h"
+
+static const struct dom_sid domsid = {
+ 1, 4, {0,0,0,0,0,5}, {21, 123, 456, 789}
+};
+
+static void namemap_cache1_fn1(const char *domain,
+ const char *name,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ bool *p_ok = private_data;
+ bool ok;
+
+ ok = strequal(domain, "nt authority");
+ ok &= strequal(name, "network");
+ ok &= (type == SID_NAME_WKN_GRP);
+
+ *p_ok = ok;
+}
+
+static void namemap_cache1_fn2(const struct dom_sid *sid,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ bool *p_ok = private_data;
+ bool ok;
+
+ ok = dom_sid_equal(sid, &global_sid_Network);
+ ok &= (type == SID_NAME_WKN_GRP);
+
+ *p_ok = ok;
+}
+
+static void namemap_cache1_fn3(const char *domain,
+ const char *name,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ bool *p_ok = private_data;
+ bool ok;
+
+ ok = strequal(domain, "");
+ ok &= strequal(name, "everyone");
+ ok &= (type == SID_NAME_WKN_GRP);
+
+ *p_ok = ok;
+}
+
+static void namemap_cache1_fn4(const struct dom_sid *sid,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ bool *p_ok = private_data;
+ bool ok;
+
+ ok = dom_sid_equal(sid, &global_sid_World);
+ ok &= (type == SID_NAME_WKN_GRP);
+
+ *p_ok = ok;
+}
+
+static void namemap_cache1_fn5(const char *domain,
+ const char *name,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ bool *p_ok = private_data;
+ bool ok;
+
+ ok = strequal(domain, "samba-dom");
+ ok &= strequal(name, "");
+ ok &= (type == SID_NAME_DOMAIN);
+
+ *p_ok = ok;
+}
+
+static void namemap_cache1_fn6(const struct dom_sid *sid,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ bool *p_ok = private_data;
+ bool ok;
+
+ ok = dom_sid_equal(sid, &domsid);
+ ok &= (type == SID_NAME_DOMAIN);
+
+ *p_ok = ok;
+}
+
+bool run_local_namemap_cache1(int dummy)
+{
+ bool found;
+ bool ok;
+
+ ok = gencache_set("SID2NAME/S-1-5-2", "invalid", time(NULL)+60);
+ if (!ok) {
+ fprintf(stderr, "gencache_set failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_sid(&global_sid_Network, namemap_cache1_fn1,
+ &found);
+ if (ok) {
+ fprintf(stderr, "namemap_cache_find_sid parsed valid value\n");
+ return false;
+ }
+
+ ok = namemap_cache_set_sid2name(&global_sid_Network,
+ "NT Authority", "Network",
+ SID_NAME_WKN_GRP,
+ time(NULL) + 60);
+ if (!ok) {
+ fprintf(stderr, "namemap_cache_set_sid2name failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_sid(&global_sid_Network, namemap_cache1_fn1,
+ &found);
+ if (!ok) {
+ fprintf(stderr, "namecache_find_sid failed\n");
+ return false;
+ }
+ if (!found) {
+ fprintf(stderr, "wrong values found\n");
+ return false;
+ }
+
+ ok = namemap_cache_set_name2sid("NT Authority", "Network",
+ &global_sid_Network,
+ SID_NAME_WKN_GRP,
+ time(NULL) + 60);
+ if (!ok) {
+ fprintf(stderr, "namemap_cache_set_name2sid failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_name("nt authority", "network",
+ namemap_cache1_fn2, &found);
+ if (!ok) {
+ fprintf(stderr, "namecache_find_name failed\n");
+ return false;
+ }
+ if (!found) {
+ fprintf(stderr, "wrong values found\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_name("foo", "bar", namemap_cache1_fn2, &found);
+ if (ok) {
+ fprintf(stderr,
+ "namemap_cache_find_name succeeded unexpectedly\n");
+ return false;
+ }
+
+ /*
+ * Test "" domain name
+ */
+
+ ok = namemap_cache_set_sid2name(&global_sid_World, "", "Everyone",
+ SID_NAME_WKN_GRP,
+ time(NULL) + 60);
+ if (!ok) {
+ fprintf(stderr, "namemap_cache_set_sid2name failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_sid(&global_sid_World, namemap_cache1_fn3,
+ &found);
+ if (!ok) {
+ fprintf(stderr, "namecache_find_sid failed\n");
+ return false;
+ }
+ if (!found) {
+ fprintf(stderr, "wrong values found\n");
+ return false;
+ }
+
+ ok = namemap_cache_set_name2sid("", "Everyone",
+ &global_sid_World, SID_NAME_WKN_GRP,
+ time(NULL) + 60);
+ if (!ok) {
+ fprintf(stderr, "namemap_cache_set failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_name("", "everyone",
+ namemap_cache1_fn4, &found);
+ if (!ok) {
+ fprintf(stderr, "namecache_find_name failed\n");
+ return false;
+ }
+ if (!found) {
+ fprintf(stderr, "wrong values found\n");
+ return false;
+ }
+
+ /*
+ * Test domain only
+ */
+
+ ok = namemap_cache_set_sid2name(&domsid, "SAMBA-DOM", "",
+ SID_NAME_DOMAIN,
+ time(NULL) + 60);
+ if (!ok) {
+ fprintf(stderr, "namemap_cache_set failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_sid(&domsid, namemap_cache1_fn5,
+ &found);
+ if (!ok) {
+ fprintf(stderr, "namecache_find_sid failed\n");
+ return false;
+ }
+ if (!found) {
+ fprintf(stderr, "wrong values found\n");
+ return false;
+ }
+
+ ok = namemap_cache_set_name2sid("SAMBA-DOM", "",
+ &domsid, SID_NAME_DOMAIN,
+ time(NULL) + 60);
+ if (!ok) {
+ fprintf(stderr, "namemap_cache_set failed\n");
+ return false;
+ }
+
+ ok = namemap_cache_find_name("samba-dom", "",
+ namemap_cache1_fn6, &found);
+ if (!ok) {
+ fprintf(stderr, "namecache_find_name failed\n");
+ return false;
+ }
+ if (!found) {
+ fprintf(stderr, "wrong values found\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/torture/test_notify.c b/source3/torture/test_notify.c
new file mode 100644
index 0000000..b265845
--- /dev/null
+++ b/source3/torture/test_notify.c
@@ -0,0 +1,731 @@
+/*
+ Unix SMB/CIFS implementation.
+ Scalability test for notifies
+ Copyright (C) Volker Lendecke 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/security/security.h"
+#include "lib/tevent_barrier.h"
+
+extern int torture_nprocs, torture_numops;
+
+struct wait_for_one_notify_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t dnum;
+ uint32_t filter;
+ bool recursive;
+ unsigned *num_notifies;
+};
+
+static void wait_for_one_notify_opened(struct tevent_req *subreq);
+static void wait_for_one_notify_chkpath_done(struct tevent_req *subreq);
+static void wait_for_one_notify_done(struct tevent_req *subreq);
+static void wait_for_one_notify_closed(struct tevent_req *subreq);
+
+static struct tevent_req *wait_for_one_notify_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *path,
+ uint32_t filter,
+ bool recursive,
+ unsigned *num_notifies)
+{
+ struct tevent_req *req, *subreq;
+ struct wait_for_one_notify_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wait_for_one_notify_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->filter = filter;
+ state->recursive = recursive;
+ state->num_notifies = num_notifies;
+
+ subreq = cli_ntcreate_send(
+ state, state->ev, state->cli, path, 0,
+ MAXIMUM_ALLOWED_ACCESS,
+ 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, FILE_DIRECTORY_FILE,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wait_for_one_notify_opened, req);
+ return req;
+}
+
+static void wait_for_one_notify_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wait_for_one_notify_state *state = tevent_req_data(
+ req, struct wait_for_one_notify_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->dnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_notify_send(state, state->ev, state->cli, state->dnum,
+ 0xffff, state->filter, state->recursive);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wait_for_one_notify_done, req);
+
+ /*
+ * To make sure the notify received at the server, we do another no-op
+ * that is replied to.
+ */
+ subreq = cli_chkpath_send(state, state->ev, state->cli, "\\");
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wait_for_one_notify_chkpath_done, req);
+}
+
+static void wait_for_one_notify_chkpath_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wait_for_one_notify_state *state = tevent_req_data(
+ req, struct wait_for_one_notify_state);
+ NTSTATUS status;
+
+ status = cli_chkpath_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ *state->num_notifies += 1;
+}
+
+static void wait_for_one_notify_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wait_for_one_notify_state *state = tevent_req_data(
+ req, struct wait_for_one_notify_state);
+ uint32_t num_changes;
+ struct notify_change *changes;
+ NTSTATUS status;
+
+ status = cli_notify_recv(subreq, state, &num_changes, &changes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_close_send(state, state->ev, state->cli, state->dnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wait_for_one_notify_closed, req);
+}
+
+static void wait_for_one_notify_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wait_for_one_notify_state *state = tevent_req_data(
+ req, struct wait_for_one_notify_state);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ *state->num_notifies -= 1;
+ tevent_req_done(req);
+}
+
+static NTSTATUS wait_for_one_notify_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void notify_bench2_done(struct tevent_req *req);
+
+bool run_notify_bench2(int dummy)
+{
+ struct cli_state *cli;
+ struct cli_state **clis;
+ struct tevent_context *ev;
+ unsigned num_notifies = 0;
+ NTSTATUS status;
+ int i;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ printf("starting notify bench 2 test\n");
+
+ cli_rmdir(cli, "\\notify.dir\\subdir");
+ cli_rmdir(cli, "\\notify.dir");
+
+ status = cli_mkdir(cli, "\\notify.dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mkdir failed : %s\n", nt_errstr(status));
+ return false;
+ }
+
+ clis = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
+ if (clis == NULL) {
+ printf("talloc failed\n");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("tevent_context_create failed\n");
+ return false;
+ }
+
+ for (i=0; i<torture_nprocs; i++) {
+ int j;
+ if (!torture_open_connection(&clis[i], i)) {
+ return false;
+ }
+
+ for (j=0; j<torture_numops; j++) {
+ struct tevent_req *req;
+ req = wait_for_one_notify_send(
+ talloc_tos(), ev, clis[i], "\\notify.dir",
+ FILE_NOTIFY_CHANGE_ALL, true,
+ &num_notifies);
+ if (req == NULL) {
+ printf("wait_for_one_notify_send failed\n");
+ return false;
+ }
+ tevent_req_set_callback(req, notify_bench2_done, NULL);
+ }
+ }
+
+ while (num_notifies < (unsigned)(torture_nprocs * torture_numops)) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ printf("tevent_loop_once failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ }
+
+ cli_mkdir(cli, "\\notify.dir\\subdir");
+
+ while (num_notifies > 0) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ printf("tevent_loop_once failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void notify_bench2_done(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ status = wait_for_one_notify_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("wait_for_one_notify returned %s\n",
+ nt_errstr(status));
+ }
+}
+
+/*
+ * This test creates a subdirectory. It then waits on a barrier before the
+ * notify is sent. Then it creates the notify. It then waits for another
+ * barrier, so that all of the notifies have gone through. It then creates
+ * another subdirectory, which will trigger notifications to be sent. When the
+ * notifies have been received, it waits once more before everything is
+ * cleaned up.
+ */
+
+struct notify_bench3_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *dir;
+ uint16_t dnum;
+ const char *subdir_path;
+ uint16_t subdir_dnum;
+ int wait_timeout;
+ struct tevent_barrier *small;
+ struct tevent_barrier *large;
+};
+
+static void notify_bench3_mkdir1_done(struct tevent_req *subreq);
+static void notify_bench3_before_notify(struct tevent_req *subreq);
+static void notify_bench3_chkpath_done(struct tevent_req *subreq);
+static void notify_bench3_before_mkdir2(struct tevent_req *subreq);
+static void notify_bench3_notify_done(struct tevent_req *subreq);
+static void notify_bench3_notifies_done(struct tevent_req *subreq);
+static void notify_bench3_mksubdir_done(struct tevent_req *subreq);
+static void notify_bench3_before_close_subdir(struct tevent_req *subreq);
+static void notify_bench3_close_subdir_done(struct tevent_req *subreq);
+static void notify_bench3_deleted_subdir(struct tevent_req *subreq);
+static void notify_bench3_deleted_subdirs(struct tevent_req *subreq);
+static void notify_bench3_del_on_close_set(struct tevent_req *subreq);
+static void notify_bench3_closed(struct tevent_req *subreq);
+
+static struct tevent_req *notify_bench3_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *dir, const char *subdir_path,
+ struct tevent_barrier *small, struct tevent_barrier *large)
+{
+ struct tevent_req *req, *subreq;
+ struct notify_bench3_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct notify_bench3_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->dir = dir;
+ state->subdir_path = subdir_path;
+ state->small = small;
+ state->large = large;
+
+ subreq = cli_ntcreate_send(
+ state, state->ev, state->cli, state->dir, 0,
+ MAXIMUM_ALLOWED_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN_IF, FILE_DIRECTORY_FILE,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, notify_bench3_mkdir1_done, req);
+ return req;
+}
+
+static void notify_bench3_mkdir1_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->dnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = tevent_barrier_wait_send(state, state->ev, state->small);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_before_notify, req);
+}
+
+static void notify_bench3_before_notify(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ int ret;
+
+ ret = tevent_barrier_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+ subreq = cli_notify_send(state, state->ev, state->cli, state->dnum,
+ 0xffff, FILE_NOTIFY_CHANGE_ALL, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_notify_done, req);
+
+ /*
+ * To make sure the notify received at the server, we do another no-op
+ * that is replied to.
+ */
+ subreq = cli_chkpath_send(state, state->ev, state->cli, "\\");
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_chkpath_done, req);
+}
+
+static void notify_bench3_notify_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ uint32_t num_changes;
+ struct notify_change *changes;
+ NTSTATUS status;
+
+ status = cli_notify_recv(subreq, state, &num_changes, &changes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = tevent_barrier_wait_send(state, state->ev, state->large);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_notifies_done, req);
+}
+
+static void notify_bench3_notifies_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+
+ ret = tevent_barrier_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+}
+
+static void notify_bench3_chkpath_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ NTSTATUS status;
+
+ status = cli_chkpath_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (state->subdir_path == NULL) {
+ return;
+ }
+ subreq = tevent_barrier_wait_send(state, state->ev, state->small);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_before_mkdir2, req);
+}
+
+static void notify_bench3_before_mkdir2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ int ret;
+
+ ret = tevent_barrier_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+ subreq = cli_ntcreate_send(
+ state, state->ev, state->cli, state->subdir_path, 0,
+ MAXIMUM_ALLOWED_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_mksubdir_done, req);
+}
+
+static void notify_bench3_mksubdir_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->subdir_dnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = tevent_barrier_wait_send(state, state->ev, state->large);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_before_close_subdir,
+ req);
+}
+
+static void notify_bench3_before_close_subdir(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ int ret;
+
+ ret = tevent_barrier_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+ subreq = cli_close_send(state,
+ state->ev,
+ state->cli,
+ state->subdir_dnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_close_subdir_done, req);
+}
+
+static void notify_bench3_close_subdir_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_rmdir_send(state, state->ev, state->cli,
+ state->subdir_path);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_deleted_subdir, req);
+}
+
+static void notify_bench3_deleted_subdir(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ NTSTATUS status;
+
+ status = cli_rmdir_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = tevent_barrier_wait_send(state, state->ev, state->small);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_deleted_subdirs, req);
+}
+
+static void notify_bench3_deleted_subdirs(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ int ret;
+
+ ret = tevent_barrier_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+ subreq = cli_nt_delete_on_close_send(state, state->ev, state->cli,
+ state->dnum, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_del_on_close_set, req);
+}
+
+static void notify_bench3_del_on_close_set(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_bench3_state *state = tevent_req_data(
+ req, struct notify_bench3_state);
+ NTSTATUS status;
+
+ status = cli_nt_delete_on_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_close_send(state, state->ev, state->cli, state->dnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_bench3_closed, req);
+}
+
+static void notify_bench3_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS notify_bench3_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void notify_bench3_done(struct tevent_req *req)
+{
+ unsigned *num_done = (unsigned *)tevent_req_callback_data_void(req);
+ NTSTATUS status;
+
+ status = notify_bench3_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("notify_bench3 returned %s\n", nt_errstr(status));
+ }
+ *num_done += 1;
+}
+
+static void notify_bench3_barrier_cb(void *private_data)
+{
+ struct timeval *ts = (struct timeval *)private_data;
+ struct timeval now;
+
+ GetTimeOfDay(&now);
+ printf("barrier triggered: %f\n", timeval_elapsed2(ts, &now));
+ GetTimeOfDay(ts);
+}
+
+bool run_notify_bench3(int dummy)
+{
+ struct cli_state **clis;
+ struct tevent_context *ev;
+ struct tevent_barrier *small;
+ struct tevent_barrier *large;
+ int i;
+ unsigned num_done = 0;
+ struct timeval ts, now;
+
+ clis = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
+ if (clis == NULL) {
+ printf("talloc failed\n");
+ return false;
+ }
+
+ GetTimeOfDay(&ts);
+
+ small = tevent_barrier_init(
+ talloc_tos(), torture_nprocs * torture_numops,
+ notify_bench3_barrier_cb, &ts);
+ if (small == NULL) {
+ return false;
+ }
+
+ large = tevent_barrier_init(
+ talloc_tos(), 2 * torture_nprocs * torture_numops,
+ notify_bench3_barrier_cb, &ts);
+ if (large == NULL) {
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("tevent_context_create failed\n");
+ return false;
+ }
+
+ for (i=0; i<torture_nprocs; i++) {
+ if (!torture_open_connection(&clis[i], i)) {
+ return false;
+ }
+ }
+
+ for (i=0; i<torture_nprocs; i++) {
+ int j;
+ for (j=0; j<torture_numops; j++) {
+ int idx = i * torture_numops + j;
+ struct tevent_req *req;
+ char *dirname, *subdirname;
+
+ dirname = talloc_asprintf(
+ talloc_tos(), "\\dir%.8d", idx);
+ if (dirname == NULL) {
+ return false;
+ }
+ subdirname = talloc_asprintf(
+ talloc_tos(), "\\dir%.8d\\subdir",
+ (idx + torture_numops + 1) %
+ (torture_nprocs * torture_numops));
+ if (subdirname == NULL) {
+ return false;
+ }
+
+ req = notify_bench3_send(
+ talloc_tos(), ev, clis[i], dirname,
+ subdirname, small, large);
+ if (req == NULL) {
+ return false;
+ }
+ tevent_req_set_callback(req, notify_bench3_done,
+ &num_done);
+ }
+ }
+
+ while (num_done < (unsigned)(torture_nprocs * torture_numops)) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ printf("tevent_loop_once failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ }
+
+ GetTimeOfDay(&now);
+ printf("turndow: %f\n", timeval_elapsed2(&ts, &now));
+ TALLOC_FREE(small);
+ TALLOC_FREE(large);
+ return true;
+}
diff --git a/source3/torture/test_notify_online.c b/source3/torture/test_notify_online.c
new file mode 100644
index 0000000..d8a5d37
--- /dev/null
+++ b/source3/torture/test_notify_online.c
@@ -0,0 +1,293 @@
+/*
+ Unix SMB/CIFS implementation.
+ Make sure that for offline files pread and pwrite trigger a notify
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libcli/security/security.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libsmb/libsmb.h"
+
+extern char *test_filename;
+
+struct notify_online_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t dnum;
+ const char *fname;
+ uint16_t fnum;
+ bool got_notify;
+};
+
+static void notify_online_opened_dir(struct tevent_req *subreq);
+static void notify_online_notify_callback(struct tevent_req *subreq);
+static void notify_online_opened_file(struct tevent_req *subreq);
+static void notify_online_sent_read(struct tevent_req *subreq);
+static void notify_online_sent_closefile(struct tevent_req *subreq);
+static void notify_online_waited(struct tevent_req *subreq);
+static void notify_online_sent_closedir(struct tevent_req *subreq);
+
+static struct tevent_req *notify_online_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, const char *dname, const char *fname)
+{
+ struct tevent_req *req, *subreq;
+ struct notify_online_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct notify_online_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fname = fname;
+
+ subreq = cli_ntcreate_send(
+ state, ev, cli, dname, EXTENDED_RESPONSE_REQUIRED,
+ SEC_FILE_READ_DATA, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN, 0, SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, notify_online_opened_dir, req);
+ return req;
+}
+
+static void notify_online_opened_dir(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->dnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_notify_send(state, state->ev, state->cli, state->dnum,
+ 128, FILE_NOTIFY_CHANGE_ATTRIBUTES, false);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_online_notify_callback, req);
+
+ subreq = cli_ntcreate_send(
+ state, state->ev, state->cli, state->fname, 0,
+ GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, FILE_NON_DIRECTORY_FILE,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_online_opened_file, req);
+}
+
+static void notify_online_notify_callback(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+ NTSTATUS status;
+ uint32_t num_changes;
+ struct notify_change *changes;
+
+ status = cli_notify_recv(subreq, state, &num_changes, &changes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if ((num_changes == 1)
+ && (changes[0].action == NOTIFY_ACTION_MODIFIED)
+ && (strcmp(changes[0].name, state->fname) == 0)) {
+ state->got_notify = true;
+ }
+ tevent_req_done(req);
+}
+
+static void notify_online_opened_file(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_read_andx_send(
+ state, state->ev, state->cli, state->fnum, 0, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_online_sent_read, req);
+}
+
+static void notify_online_sent_read(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+ NTSTATUS status;
+ ssize_t received;
+ uint8_t *buf;
+
+ status = cli_read_andx_recv(subreq, &received, &buf);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_online_sent_closefile, req);
+}
+
+static void notify_online_sent_closefile(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = tevent_wakeup_send(
+ state, state->ev, timeval_current_ofs(10, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_online_waited, req);
+}
+
+static void notify_online_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+
+ tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ subreq = cli_close_send(state, state->ev, state->cli, state->dnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, notify_online_sent_closedir, req);
+}
+
+static void notify_online_sent_closedir(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+}
+
+static NTSTATUS notify_online_recv(struct tevent_req *req, bool *got_notify)
+{
+ struct notify_online_state *state = tevent_req_data(
+ req, struct notify_online_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *got_notify = state->got_notify;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS notify_online(struct cli_state *cli,
+ const char *dirname, const char *filename,
+ bool *got_notify)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = notify_online_send(frame, ev, cli, dirname, filename);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = notify_online_recv(req, got_notify);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+bool run_notify_online(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+ char *p;
+ const char *dir;
+ const char *file;
+ bool got_notify = false;
+
+ printf("Starting NOTIFY_ONLINE\n");
+
+ if (test_filename == NULL) {
+ fprintf(stderr, "<-f filename> missing\n");
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ p = strrchr(test_filename, '/');
+ if (p != NULL) {
+ dir = SMB_STRNDUP(test_filename, p-test_filename);
+ file = SMB_STRDUP(p+1);
+ } else {
+ dir = "";
+ file = test_filename;
+ }
+
+ status = notify_online(cli, dir, file, &got_notify);
+ d_printf("notify_online returned %s (%d)\n", nt_errstr(status),
+ (int)got_notify);
+ torture_close_connection(cli);
+ return NT_STATUS_IS_OK(status) && got_notify;
+}
diff --git a/source3/torture/test_nttrans_create.c b/source3/torture/test_nttrans_create.c
new file mode 100644
index 0000000..5e7ce7e
--- /dev/null
+++ b/source3/torture/test_nttrans_create.c
@@ -0,0 +1,108 @@
+/*
+ Unix SMB/CIFS implementation.
+ Basic test for share secdescs vs nttrans_create
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/secdesc.h"
+#include "libcli/security/security.h"
+
+bool run_nttrans_create(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status, status2;
+ bool ret = false;
+ struct security_ace ace;
+ struct security_acl acl;
+ struct security_descriptor *sd;
+ const char *fname = "transtest";
+ uint16_t fnum, fnum2;
+ struct dom_sid owner;
+
+ printf("Starting NTTRANS_CREATE\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ printf("torture_open_connection failed\n");
+ goto fail;
+ }
+
+ ZERO_STRUCT(ace);
+ ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace.access_mask = SEC_RIGHTS_FILE_ALL & ~SEC_STD_WRITE_DAC;
+ sid_copy(&ace.trustee, &global_sid_World);
+
+ acl.revision = SECURITY_ACL_REVISION_NT4;
+ acl.size = 0;
+ acl.num_aces = 1;
+ acl.aces = &ace;
+
+ dom_sid_parse("S-1-22-1-1000", &owner);
+
+ sd = make_sec_desc(talloc_tos(),
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE|
+ SEC_DESC_DACL_PRESENT|SEC_DESC_OWNER_DEFAULTED|
+ SEC_DESC_GROUP_DEFAULTED,
+ NULL, NULL, NULL, &acl, NULL);
+ if (sd == NULL) {
+ d_fprintf(stderr, "make_sec_desc failed\n");
+ goto fail;
+ }
+
+ status = cli_nttrans_create(
+ cli, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS|
+ READ_CONTROL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE| FILE_SHARE_DELETE,
+ FILE_CREATE, 0, 0, sd, NULL, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_nttrans_create returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ cli_query_secdesc(cli, fnum, talloc_tos(), NULL);
+
+ status2 = cli_ntcreate(cli, fname, 0, WRITE_DAC_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+
+ status = cli_nt_delete_on_close(cli, fnum, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_nt_delete_on_close returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (!NT_STATUS_EQUAL(status2, NT_STATUS_ACCESS_DENIED)) {
+ d_fprintf(stderr, "cli_ntcreate returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ return ret;
+}
diff --git a/source3/torture/test_nttrans_fsctl.c b/source3/torture/test_nttrans_fsctl.c
new file mode 100644
index 0000000..aea80c5
--- /dev/null
+++ b/source3/torture/test_nttrans_fsctl.c
@@ -0,0 +1,288 @@
+/*
+ Unix SMB/CIFS implementation.
+ Basic test for NTTRANS FSCTL requests (copied from NTTRANS CREATE)
+ Copyright (C) Richard Sharpe 2011
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libcli/security/security.h"
+
+bool run_nttrans_fsctl(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool ret = false;
+ const char *fname = "fsctltest";
+ uint16_t fnum;
+ uint16_t setup[4];
+ uint8_t *object_data = NULL;
+ uint8_t *ranges = NULL;
+ uint8_t range_data[16];
+ uint8_t *param_data = NULL;
+ uint8_t data[1] = { 0x1 };
+ uint32_t rdata_size;
+ uint32_t rparam_size;
+
+ printf("Starting NTTRANS_FSCTL\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ printf("torture_open_connection failed\n");
+ goto fail;
+ }
+
+ status = cli_nttrans_create(
+ cli, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS|
+ READ_CONTROL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE| FILE_SHARE_DELETE,
+ FILE_CREATE, 0, 0, NULL, NULL, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_nttrans_create returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli, fnum, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_nt_delete_on_close returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* Fill in for FSCTL_SET_SPARSE and call cli_trans ... */
+ SIVAL(setup, 0, FSCTL_SET_SPARSE); /* returns value */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ data, 1, 1, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ NULL, 0, NULL); /* rdata, ... */
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_SET_SPARSE returned %s instead of NT_STATUS_OK\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ printf("FSCTL_SET_SPARSE returned correct status \n");
+
+ /* Fill in for FSCTL_CREATE_OR_GET_OBJECT_ID and call cli_trans ... */
+ SIVAL(setup, 0, FSCTL_CREATE_OR_GET_OBJECT_ID); /* returns value */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ NULL, 0, 64, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ &param_data, 0, &rparam_size, /* rparam, min_rparam, num_rparam */
+ &object_data, 0, &rdata_size); /* rdata, ... */
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_CREATE_OR_GET_OBJECT_ID returned %s instead of NT_STATUS_OK\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(object_data);
+ TALLOC_FREE(param_data);
+
+ printf("FSCTL_CREATE_OR_GET_OBJECT_ID returned correct status \n");
+
+ /* Fill in for FSCTL_GET_REPARSE_POINT and call cli_trans ... */
+ SIVAL(setup, 0, FSCTL_GET_REPARSE_POINT); /* returns NOT A REPARSE POINT */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ NULL, 0, 0, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ NULL, 0, NULL); /* rdata, ... */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_REPARSE_POINT)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_GET_REPARSE_POINT returned %s instead of NT_STATUS_NOT_A_REPARSE_POINT\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ printf("FSCTL_GET_REPARSE_POINT returned correct status \n");
+
+ /* Fill in for FSCTL_SET_REPARSE_POINT and call cli_trans ... */
+ SIVAL(setup, 0, FSCTL_SET_REPARSE_POINT); /* returns INVALID_BUFFER_SIZE */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ NULL, 0, 0, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ NULL, 0, NULL); /* rdata, ... */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_BUFFER_SIZE)) {
+ d_fprintf(stderr,
+ "cli_trans of FSCTL_SET_REPARSE_POINT returned %s "
+ "instead of NT_STATUS_INVALID_BUFFER_SIZE\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ printf("FSCTL_SET_REPARSE_POINT returned correct status \n");
+
+ /*
+ * Fill in for FSCTL_GET_SHADOW_COPY_DATA and call cli_trans ... what
+ * we do is send an invalid data length to provoke an INVALID PARAMETER
+ * response.
+ */
+ SIVAL(setup, 0, FSCTL_GET_SHADOW_COPY_DATA); /* Should return IVN VAL */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ memset(range_data, 0, sizeof(range_data)); /* 0 and 0 */
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ NULL, 0, 8, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ &ranges, 0, &rdata_size); /* rdata, ... */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_QUERY_ALLOCATED_RANGES returned %s instead of NT_STATUS_INVALID_PARAMETER\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(ranges);
+
+ printf("FSCTL_GET_SHADOW_COPY_DATA returned correct status \n");
+ /*
+ * Fill in for FSCTL_FIND_FILES_BY and call cli_trans ... here we are
+ * only probing for its existence by provoking an INVALID PARAM
+ * response with a short and invalid SID in range_data
+ */
+ SIVAL(setup, 0, FSCTL_FIND_FILES_BY_SID); /* Should return 16 bytes */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ memset(range_data, 0, sizeof(range_data)); /* 0 and 0 */
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ range_data, 4, 16, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ &ranges, 0, &rdata_size); /* rdata, ... */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_QUERY_ALLOCATED_RANGES returned %s instead of NT_STATUS_INVALID_PARAMETER\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ printf("FSCTL_FIND_FILES_BY_SID returned correct status \n");
+
+ /* Fill in for FSCTL_QUERY_ALLOCATED_RANGES and call cli_trans ... */
+ SIVAL(setup, 0, FSCTL_QUERY_ALLOCATED_RANGES); /* Should return 16 bytes */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ memset(range_data, 0, sizeof(range_data)); /* 0 and 0 */
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ range_data, 16, 16, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ &ranges, 0, &rdata_size); /* rdata, ... */
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_QUERY_ALLOCATED_RANGES returned %s instead of NT_STATUS_OK\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(ranges);
+
+ printf("FSCTL_QUERY_ALLOCATED_RANGES returned correct status \n");
+
+ /* Fill in for FSCTL_IS_VOLUME_DIRTY and call cli_trans ... */
+ SIVAL(setup, 0, FSCTL_IS_VOLUME_DIRTY); /* Should return INVAL PARAM */
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 0x1); /* It is an fsctl */
+ SCVAL(setup, 7, 0x0);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, fnum,
+ NT_TRANSACT_IOCTL, 0,
+ setup, 4, 4,
+ NULL, 0, 0, /* param, param_num, max_param */
+ NULL, 0, 0, /* data, data_len, max_data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup, min_rsetup, num_rsetup */
+ NULL, 0, NULL, /* rparam, min_rparam, num_rparam */
+ NULL, 0, NULL); /* rdata, ... */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ d_fprintf(stderr, "cli_trans of FSCTL_IS_VOLUME_DIRTY returned %s instead of NT_STATUS_INVALID_PARAMETER\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ printf("FSCTL_IS_VOLUME_DIRTY returned correct status \n");
+
+ ret = true;
+fail:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ return ret;
+}
diff --git a/source3/torture/test_oplock_cancel.c b/source3/torture/test_oplock_cancel.c
new file mode 100644
index 0000000..86ce5b7
--- /dev/null
+++ b/source3/torture/test_oplock_cancel.c
@@ -0,0 +1,168 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test cleanup behaviour
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "locking/proto.h"
+#include "torture/proto.h"
+#include "system/filesys.h"
+#include "system/select.h"
+#include "libsmb/libsmb.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/security/security.h"
+#include "lib/util/tevent_ntstatus.h"
+
+struct create_cancel_state {
+ uint8_t dummy;
+};
+
+static void create_cancel_done(struct tevent_req *subreq);
+
+static struct tevent_req *create_cancel_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, const char *fname)
+{
+ struct tevent_req *req, *subreq;
+ struct create_cancel_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct create_cancel_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_ntcreate_send(
+ mem_ctx, ev, cli, fname, 0, FILE_GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN_IF, 0, SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_cancel(subreq)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, create_cancel_done, req);
+ return req;
+}
+
+static void create_cancel_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) {
+ if (NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS create_cancel_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static NTSTATUS create_cancel(struct cli_state *cli, const char *fname)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = create_cancel_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = create_cancel_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+bool run_oplock_cancel(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "oplock-cancel";
+ uint16_t fnum1;
+ NTSTATUS status;
+ /*
+ * Currently this test seems to work only
+ * with SMB2/3 and only against Samba.
+ *
+ * TODO: we should change our server
+ * to ignore cancel for SMB2 Create
+ * and behave like Windows.
+ */
+ int flags = CLI_FULL_CONNECTION_DISABLE_SMB1;
+
+ if (!torture_open_connection_flags(&cli1, 0, flags)) {
+ return false;
+ }
+ cli1->use_oplocks = true;
+
+ if (!torture_open_connection_flags(&cli2, 0, flags)) {
+ return false;
+ }
+ cli2->use_oplocks = true;
+
+ status = cli_ntcreate(
+ cli1, fname, 0, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, 0, 0,
+ &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_ntcreate failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = create_cancel(cli2, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("create_cancel failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ cli_close(cli1, fnum1);
+
+ TALLOC_FREE(cli1);
+
+ /*
+ * Give cli1's smbd time to inform cli2's smbd
+ */
+ smb_msleep(5000);
+
+ status = cli_unlink(cli2, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_unlink failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/torture/test_posix.c b/source3/torture/test_posix.c
new file mode 100644
index 0000000..ca40336
--- /dev/null
+++ b/source3/torture/test_posix.c
@@ -0,0 +1,1972 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Ralph Boehme 2020
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "libsmb/proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "util_sd.h"
+#include "trans2.h"
+
+extern struct cli_credentials *torture_creds;
+extern fstring host, workgroup, share, password, username, myname;
+
+struct posix_test_entry {
+ const char *name;
+ const char *target;
+ const char *expected;
+ uint32_t attr_win;
+ uint32_t attr_lin;
+ uint64_t returned_size;
+ bool ok;
+};
+
+enum client_flavour { WINDOWS, POSIX };
+
+struct posix_test_state {
+ enum client_flavour flavour;
+ struct posix_test_entry *entries;
+};
+
+static NTSTATUS posix_ls_fn(struct file_info *finfo,
+ const char *name,
+ void *_state)
+{
+ struct posix_test_state *state =
+ (struct posix_test_state *)_state;
+ struct posix_test_entry *e = state->entries;
+
+ for (; e->name != NULL; e++) {
+ uint32_t attr;
+ if (!strequal(finfo->name, e->expected)) {
+ continue;
+ }
+ if (state->flavour == WINDOWS) {
+ attr = e->attr_win;
+ } else {
+ attr = e->attr_lin;
+ }
+ if (attr != finfo->attr) {
+ break;
+ }
+ e->ok = true;
+ e->returned_size = finfo->size;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void posix_test_entries_reset(struct posix_test_state *state)
+{
+ struct posix_test_entry *e = state->entries;
+
+ for (; e->name != NULL; e++) {
+ e->ok = false;
+ e->returned_size = 0;
+ }
+}
+
+static bool posix_test_entry_check(struct posix_test_state *state,
+ const char *name,
+ bool expected,
+ uint64_t expected_size)
+{
+ struct posix_test_entry *e = state->entries;
+ bool result = false;
+
+ for (; e->name != NULL; e++) {
+ if (strequal(name, e->name)) {
+ result = e->ok;
+ break;
+ }
+ }
+ if (e->name == NULL) {
+ printf("test failed, unknown name: %s\n", name);
+ return false;
+ }
+
+ if (expected == result) {
+ return true;
+ }
+
+ printf("test failed, %s: %s\n",
+ expected ? "missing" : "unexpected",
+ name);
+
+ return false;
+}
+
+/*
+ Test non-POSIX vs POSIX ls * of symlinks
+ */
+bool run_posix_ls_wildcard_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ struct cli_state *cli_win = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = file,
+ .target = NULL,
+ .expected = file,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_ARCHIVE,
+ }, {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = entries,
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-LS-WILDCARD test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!torture_open_connection(&cli_win, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+ torture_conn_set_sockopt(cli_win);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ if (entries[i].target == NULL) {
+ continue;
+ }
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ printf("Doing Windows ls *\n");
+ state->flavour = WINDOWS;
+
+ status = cli_list(cli_win, "*", 0, posix_ls_fn, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) {
+ goto out;
+ }
+
+ posix_test_entries_reset(state);
+
+ printf("Doing POSIX ls *\n");
+ state->flavour = POSIX;
+
+ status = cli_list(cli_unix, "*", 0, posix_ls_fn, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-LS-WILDCARD test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+ if (!torture_close_connection(cli_win)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test non-POSIX vs POSIX ls single of symlinks
+ */
+bool run_posix_ls_single_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ struct cli_state *cli_win = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = file,
+ .target = NULL,
+ .expected = file,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_ARCHIVE,
+ }, {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = &entries[0],
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-LS-SINGLE test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!torture_init_connection(&cli_win)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ status = smbXcli_negprot(cli_win->conn,
+ cli_win->timeout,
+ lp_client_min_protocol(),
+ lp_client_max_protocol(),
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli_win, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_sesssetup returned %s\n", nt_errstr(status));
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ status = cli_tree_connect(cli_win, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ TALLOC_FREE(frame);
+ return false;
+ }
+ torture_conn_set_sockopt(cli_unix);
+ torture_conn_set_sockopt(cli_win);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ if (entries[i].target == NULL) {
+ continue;
+ }
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ printf("Doing Windows ls single\n");
+ state->flavour = WINDOWS;
+
+ cli_list(cli_win, file, 0, posix_ls_fn, state);
+ cli_list(cli_win, symlnk_dangling, 0, posix_ls_fn, state);
+ cli_list(cli_win, symlnk_outside_share, 0, posix_ls_fn, state);
+ cli_list(cli_win, symlnk_in_share, 0, posix_ls_fn, state);
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) {
+ goto out;
+ }
+
+ posix_test_entries_reset(state);
+
+ printf("Doing POSIX ls single\n");
+ state->flavour = POSIX;
+
+ cli_list(cli_unix, file, 0, posix_ls_fn, state);
+ cli_list(cli_unix, symlnk_dangling, 0, posix_ls_fn, state);
+ cli_list(cli_unix, symlnk_outside_share, 0, posix_ls_fn, state);
+ cli_list(cli_unix, symlnk_in_share, 0, posix_ls_fn, state);
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-LS-SINGLE test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+ if (!torture_close_connection(cli_win)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test POSIX readlink of symlinks
+ */
+bool run_posix_readlink_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = &entries[0],
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-READLINK test\n");
+ state->flavour = POSIX;
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ char *target = NULL;
+
+ status = cli_readlink(
+ cli_unix,
+ entries[i].name,
+ talloc_tos(),
+ &target,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX readlink on %s failed (%s)\n",
+ entries[i].name, nt_errstr(status));
+ goto out;
+ }
+ if (strequal(target, entries[i].target)) {
+ entries[i].ok = true;
+ entries[i].returned_size = strlen(target);
+ }
+ }
+
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-READLINK test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test POSIX stat of symlinks
+ */
+bool run_posix_stat_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = &entries[0],
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-STAT test\n");
+ state->flavour = POSIX;
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ SMB_STRUCT_STAT sbuf;
+
+ status = cli_posix_stat(cli_unix,
+ entries[i].name,
+ &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX stat on %s failed (%s)\n",
+ entries[i].name, nt_errstr(status));
+ continue;
+ }
+ entries[i].ok = true;
+ entries[i].returned_size = sbuf.st_ex_size;
+ }
+
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-STAT test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test Creating files and directories directly
+ under a symlink.
+ */
+bool run_posix_symlink_parent_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *parent_dir = "target_dir";
+ const char *parent_symlink = "symlink_to_target_dir";
+ const char *fname_real = "target_dir/file";
+ const char *dname_real = "target_dir/dir";
+ const char *fname_link = "symlink_to_target_dir/file";
+ const char *dname_link = "symlink_to_target_dir/dir";
+ const char *sname_link = "symlink_to_target_dir/symlink";
+ const char *hname_link = "symlink_to_target_dir/hardlink";
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-PARENT test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_rmdir(cli_unix, dname_real);
+ cli_posix_unlink(cli_unix, fname_link);
+ cli_posix_rmdir(cli_unix, dname_link);
+ cli_posix_unlink(cli_unix, sname_link);
+ cli_posix_unlink(cli_unix, hname_link);
+ cli_posix_unlink(cli_unix, parent_symlink);
+ cli_posix_rmdir(cli_unix, parent_dir);
+
+ /* Create parent_dir. */
+ status = cli_posix_mkdir(cli_unix, parent_dir, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed error %s\n",
+ parent_dir,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Create symlink to parent_dir. */
+ status = cli_posix_symlink(cli_unix,
+ parent_dir,
+ parent_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ parent_symlink,
+ parent_dir,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Try and create a directory under the symlink. */
+ status = cli_posix_mkdir(cli_unix, dname_link, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed error %s\n",
+ dname_link,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Try and create a file under the symlink. */
+ status = cli_posix_open(cli_unix,
+ fname_link,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Try and create a symlink to the file under the symlink. */
+ status = cli_posix_symlink(cli_unix,
+ fname_link,
+ sname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ sname_link,
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Try and create a hardlink to the file under the symlink. */
+ status = cli_posix_hardlink(cli_unix,
+ fname_link,
+ hname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_hardlink of %s -> %s failed error %s\n",
+ hname_link,
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can delete the symlink via the parent symlink */
+ status = cli_posix_unlink(cli_unix, sname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ sname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can delete the hardlink via the parent symlink */
+ status = cli_posix_unlink(cli_unix, hname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ hname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can delete the directory via the parent symlink */
+ status = cli_posix_rmdir(cli_unix, dname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_rmdir of %s failed error %s\n",
+ dname_link,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Ensure we can delete the file via the parent symlink */
+ status = cli_posix_unlink(cli_unix, fname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("POSIX-SYMLINK-PARENT test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_rmdir(cli_unix, dname_real);
+ cli_posix_unlink(cli_unix, fname_link);
+ cli_posix_rmdir(cli_unix, dname_link);
+ cli_posix_unlink(cli_unix, sname_link);
+ cli_posix_unlink(cli_unix, hname_link);
+ cli_posix_unlink(cli_unix, parent_symlink);
+ cli_posix_rmdir(cli_unix, parent_dir);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Ensure we get an error when doing chmod on a symlink,
+ whether it is pointing to a real object or dangling.
+ */
+bool run_posix_symlink_chmod_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_real";
+ const char *fname_real_symlink = "file_real_symlink";
+ const char *nonexist = "nonexist";
+ const char *nonexist_symlink = "dangling_symlink";
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-CHMOD test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* We should not be able to chmod symlinks that point to something. */
+ status = cli_posix_chmod(cli_unix, fname_real_symlink, 0777);
+
+ /* This should fail with something other than server crashed. */
+ if (NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_chmod of %s succeeded (should have failed)\n",
+ fname_real_symlink);
+ goto out;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) {
+ /* Oops. Server crashed. */
+ printf("cli_posix_chmod of %s failed error %s\n",
+ fname_real_symlink,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Any other failure is ok. */
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* We should not be able to chmod symlinks that point to nothing. */
+ status = cli_posix_chmod(cli_unix, nonexist_symlink, 0777);
+
+ /* This should fail with something other than server crashed. */
+ if (NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_chmod of %s succeeded (should have failed)\n",
+ nonexist_symlink);
+ goto out;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) {
+ /* Oops. Server crashed. */
+ printf("cli_posix_chmod of %s failed error %s\n",
+ nonexist_symlink,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Any other failure is ok. */
+ printf("POSIX-SYMLINK-CHMOD test passed (expected failure was %s)\n",
+ nt_errstr(status));
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Ensure we get an ACL containing OI|IO ACE entries
+ after we add a default POSIX ACL to a directory.
+ This will only ever be an SMB1 test as it depends
+ on POSIX ACL semantics.
+ */
+bool run_posix_dir_default_acl_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *dname = "dir_with_default_acl";
+ bool correct = false;
+ SMB_STRUCT_STAT sbuf;
+ size_t acl_size = 0;
+ char *aclbuf = NULL;
+ size_t num_file_acls = 0;
+ size_t num_dir_acls = 0;
+ size_t expected_buflen;
+ uint8_t def_acl[SMB_POSIX_ACL_HEADER_SIZE +
+ 5*SMB_POSIX_ACL_ENTRY_SIZE] = {0};
+ uint8_t *p = NULL;
+ uint32_t i = 0;
+ struct security_descriptor *sd = NULL;
+ bool got_inherit = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-DIR-DEFAULT-ACL test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, dname);
+ cli_posix_rmdir(cli_unix, dname);
+
+ status = cli_posix_mkdir(cli_unix, dname, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed error %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Do a posix stat to get the owner. */
+ status = cli_posix_stat(cli_unix, dname, &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_stat of %s failed %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Get the ACL on the directory. */
+ status = cli_posix_getacl(cli_unix, dname, frame, &acl_size, &aclbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_getacl on %s failed %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (acl_size < 6 || SVAL(aclbuf,0) != SMB_POSIX_ACL_VERSION) {
+ printf("%s, unknown POSIX acl version %u.\n",
+ dname,
+ (unsigned int)CVAL(aclbuf,0) );
+ goto out;
+ }
+
+ num_file_acls = SVAL(aclbuf,2);
+ num_dir_acls = SVAL(aclbuf,4);
+
+ /*
+ * No overflow check, num_*_acls comes from a 16-bit value,
+ * and we expect expected_buflen (size_t) to be of at least 32
+ * bit.
+ */
+ expected_buflen = SMB_POSIX_ACL_HEADER_SIZE +
+ SMB_POSIX_ACL_ENTRY_SIZE*(num_file_acls+num_dir_acls);
+
+ if (acl_size != expected_buflen) {
+ printf("%s, incorrect POSIX acl buffer size "
+ "(should be %zu, was %zu).\n",
+ dname,
+ expected_buflen,
+ acl_size);
+ goto out;
+ }
+
+ if (num_dir_acls != 0) {
+ printf("%s, POSIX default acl already exists"
+ "(should be 0, was %zu).\n",
+ dname,
+ num_dir_acls);
+ goto out;
+ }
+
+ /*
+ * Get the Windows ACL on the directory.
+ * Make sure there are no inheritable entries.
+ */
+ status = cli_ntcreate(cli_unix,
+ dname,
+ 0,
+ SEC_STD_READ_CONTROL,
+ 0,
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE,
+ 0x0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open directory %s: %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_query_security_descriptor(cli_unix,
+ fnum,
+ SECINFO_DACL,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to get security descriptor on directory %s: %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ if (ace->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ printf("security descriptor on directory %s already "
+ "contains inheritance flags\n",
+ dname);
+ sec_desc_print(NULL, stdout, sd, true);
+ goto out;
+ }
+ }
+
+ TALLOC_FREE(sd);
+
+ /* Construct a new default ACL. */
+ SSVAL(def_acl,0,SMB_POSIX_ACL_VERSION);
+ SSVAL(def_acl,2,SMB_POSIX_IGNORE_ACE_ENTRIES);
+ SSVAL(def_acl,4,5); /* num_dir_acls. */
+
+ p = def_acl + SMB_POSIX_ACL_HEADER_SIZE;
+
+ /* USER_OBJ. */
+ SCVAL(p,0,SMB_POSIX_ACL_USER_OBJ); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* GROUP_OBJ. */
+ SCVAL(p,0,SMB_POSIX_ACL_GROUP_OBJ); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* OTHER. */
+ SCVAL(p,0,SMB_POSIX_ACL_OTHER); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* Explicit user. */
+ SCVAL(p,0,SMB_POSIX_ACL_USER); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ SIVAL(p,2,sbuf.st_ex_uid);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* MASK. */
+ SCVAL(p,0,SMB_POSIX_ACL_MASK); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* Set the POSIX default ACL. */
+ status = cli_posix_setacl(cli_unix, dname, def_acl, sizeof(def_acl));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_setacl on %s failed %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * Get the Windows ACL on the directory again.
+ * Now there should be inheritable entries.
+ */
+
+ status = cli_query_security_descriptor(cli_unix,
+ fnum,
+ SECINFO_DACL,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed (2) to get security descriptor "
+ "on directory %s: %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ if (ace->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ got_inherit = true;
+ break;
+ }
+ }
+
+ if (!got_inherit) {
+ printf("security descriptor on directory %s does not "
+ "contain inheritance flags\n",
+ dname);
+ sec_desc_print(NULL, stdout, sd, true);
+ goto out;
+ }
+
+ cli_close(cli_unix, fnum);
+ fnum = (uint16_t)-1;
+ printf("POSIX-DIR-DEFAULT-ACL test passed\n");
+ correct = true;
+
+out:
+
+ TALLOC_FREE(sd);
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, dname);
+ cli_posix_rmdir(cli_unix, dname);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Ensure we can rename a symlink whether it is
+ pointing to a real object or dangling.
+ */
+bool run_posix_symlink_rename_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_real";
+ const char *fname_real_symlink = "file_real_symlink";
+ const char *fname_real_symlink_newname = "rename_file_real_symlink";
+ const char *nonexist = "nonexist";
+ const char *nonexist_symlink = "dangling_symlink";
+ const char *nonexist_symlink_newname = "dangling_symlink_rename";
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-RENAME test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, fname_real_symlink_newname);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+ cli_posix_unlink(cli_unix, nonexist_symlink_newname);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can rename the symlink to the real file. */
+ status = cli_rename(cli_unix,
+ fname_real_symlink,
+ fname_real_symlink_newname,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_rename of %s -> %s failed %s\n",
+ fname_real_symlink,
+ fname_real_symlink_newname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can rename the dangling symlink. */
+ status = cli_rename(cli_unix,
+ nonexist_symlink,
+ nonexist_symlink_newname,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_rename of %s -> %s failed %s\n",
+ nonexist_symlink,
+ nonexist_symlink_newname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("POSIX-SYMLINK-RENAME test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, fname_real_symlink_newname);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+ cli_posix_unlink(cli_unix, nonexist_symlink_newname);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/* List of info levels to try with a POSIX symlink path. */
+
+static struct {
+ uint32_t level;
+ const char *name;
+} posix_smb1_qpath_array[] = {
+ { SMB_INFO_STANDARD, "SMB_INFO_STANDARD"},
+ { SMB_INFO_QUERY_EA_SIZE, "SMB_INFO_QUERY_EA_SIZE"},
+ { SMB_INFO_IS_NAME_VALID, "SMB_INFO_IS_NAME_VALID"},
+ { SMB_INFO_QUERY_EAS_FROM_LIST, "SMB_INFO_QUERY_EAS_FROM_LIST"},
+ { SMB_INFO_QUERY_ALL_EAS, "SMB_INFO_QUERY_ALL_EAS"},
+ { SMB_FILE_BASIC_INFORMATION, "SMB_FILE_BASIC_INFORMATION"},
+ { SMB_FILE_STANDARD_INFORMATION, "SMB_FILE_STANDARD_INFORMATION"},
+ { SMB_FILE_EA_INFORMATION, "SMB_FILE_EA_INFORMATION"},
+ { SMB_FILE_ALTERNATE_NAME_INFORMATION,"SMB_FILE_ALTERNATE_NAME_INFORMATION"},
+ { SMB_QUERY_FILE_NAME_INFO, "SMB_QUERY_FILE_NAME_INFO"},
+ { SMB_FILE_NORMALIZED_NAME_INFORMATION,"SMB_FILE_NORMALIZED_NAME_INFORMATION"},
+ { SMB_FILE_ALLOCATION_INFORMATION, "SMB_FILE_ALLOCATION_INFORMATION"},
+ { SMB_FILE_END_OF_FILE_INFORMATION, "SMB_FILE_END_OF_FILE_INFORMATION"},
+ { SMB_FILE_ALL_INFORMATION, "SMB_FILE_ALL_INFORMATION"},
+ { SMB_FILE_INTERNAL_INFORMATION, "SMB_FILE_INTERNAL_INFORMATION"},
+ { SMB_FILE_ACCESS_INFORMATION, "SMB_FILE_ACCESS_INFORMATION"},
+ { SMB_FILE_NAME_INFORMATION, "SMB_FILE_NAME_INFORMATION"},
+ { SMB_FILE_DISPOSITION_INFORMATION, "SMB_FILE_DISPOSITION_INFORMATION"},
+ { SMB_FILE_POSITION_INFORMATION, "SMB_FILE_POSITION_INFORMATION"},
+ { SMB_FILE_MODE_INFORMATION, "SMB_FILE_MODE_INFORMATION"},
+ { SMB_FILE_ALIGNMENT_INFORMATION, "SMB_FILE_ALIGNMENT_INFORMATION"},
+ { SMB_FILE_STREAM_INFORMATION, "SMB_FILE_STREAM_INFORMATION"},
+ { SMB_FILE_COMPRESSION_INFORMATION, "SMB_FILE_COMPRESSION_INFORMATION"},
+ { SMB_FILE_NETWORK_OPEN_INFORMATION, "SMB_FILE_NETWORK_OPEN_INFORMATION"},
+ { SMB_FILE_ATTRIBUTE_TAG_INFORMATION, "SMB_FILE_ATTRIBUTE_TAG_INFORMATION"},
+ { SMB_QUERY_FILE_UNIX_BASIC, "SMB_QUERY_FILE_UNIX_BASIC"},
+ { SMB_QUERY_FILE_UNIX_INFO2, "SMB_QUERY_FILE_UNIX_INFO2"},
+ { SMB_QUERY_FILE_UNIX_LINK, "SMB_QUERY_FILE_UNIX_LINK"},
+ { SMB_QUERY_POSIX_ACL, "SMB_QUERY_POSIX_ACL"},
+ { SMB_QUERY_POSIX_LOCK, "SMB_QUERY_POSIX_LOCK"},
+};
+
+static NTSTATUS do_qpath(TALLOC_CTX *ctx,
+ struct cli_state *cli_unix,
+ const char *fname,
+ size_t i)
+{
+ NTSTATUS status;
+
+ if (posix_smb1_qpath_array[i].level ==
+ SMB_INFO_QUERY_EAS_FROM_LIST) {
+ uint16_t setup;
+ uint8_t *param;
+ uint8_t data[8];
+ uint8_t *rparam = NULL;
+ uint8_t *rdata = NULL;
+ uint32_t rbytes = 0;
+
+ /* Set up an EA list with 'a' as the single name. */
+ SIVAL(data,0, 8);
+ SCVAL(data,4, 2); /* namelen. */
+ SCVAL(data,5, 'a');
+ SCVAL(data,6, '\0'); /* name. */
+ SCVAL(data,7, '\0'); /* padding. */
+
+ SSVAL(&setup, 0, TRANSACT2_QPATHINFO);
+
+ param = talloc_zero_array(ctx, uint8_t, 6);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(param, 0, SMB_INFO_QUERY_EAS_FROM_LIST);
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli_unix->conn),
+ fname,
+ strlen(fname)+1,
+ NULL);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_trans(ctx,
+ cli_unix,
+ SMBtrans2,
+ NULL,
+ -1,
+ 0,
+ 0,
+ &setup, 1, 0,
+ param, talloc_get_size(param), talloc_get_size(param),
+ data, 8, 0,
+ NULL,
+ NULL, 0, NULL,
+ &rparam, 0, &rbytes,
+ &rdata, 0, &rbytes);
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+ } else {
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata = 0;
+
+ status = cli_qpathinfo(ctx,
+ cli_unix,
+ fname,
+ posix_smb1_qpath_array[i].level,
+ 0, /* min_rdata */
+ 65534, /* max_rdata */
+ &rdata,
+ &num_rdata);
+ TALLOC_FREE(rdata);
+ }
+ /*
+ * We don't care what came back, so long as the
+ * server didn't crash.
+ */
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_CONNECTION_DISCONNECTED)) {
+ printf("cli_qpathinfo of %s failed error "
+ "NT_STATUS_CONNECTION_DISCONNECTED\n",
+ fname);
+ return status;
+ }
+
+ printf("cli_qpathinfo info %x (%s) of %s got %s "
+ "(this is not an error)\n",
+ (unsigned int)posix_smb1_qpath_array[i].level,
+ posix_smb1_qpath_array[i].name,
+ fname,
+ nt_errstr(status));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Ensure we can call SMB1 getpathinfo in a symlink,
+ pointing to a real object or dangling. We mostly
+ expect errors, but the server must not crash.
+ */
+bool run_posix_symlink_getpathinfo_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_getpath_real";
+ const char *fname_real_symlink = "file_real_getpath_symlink";
+ const char *nonexist = "nonexist_getpath";
+ const char *nonexist_symlink = "dangling_getpath_symlink";
+ bool correct = false;
+ size_t i;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-GETPATHINFO test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(posix_smb1_qpath_array); i++) {
+ status = do_qpath(frame,
+ cli_unix,
+ fname_real_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = do_qpath(frame,
+ cli_unix,
+ nonexist_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ printf("POSIX-SYMLINK-GETPATHINFO test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/* List of info levels to try with a POSIX symlink path. */
+
+static struct {
+ uint32_t level;
+ const char *name;
+ uint32_t data_len;
+} posix_smb1_setpath_array[] = {
+ { SMB_SET_FILE_UNIX_BASIC, "SMB_SET_FILE_UNIX_BASIC", 100},
+ { SMB_SET_FILE_UNIX_INFO2, "SMB_SET_FILE_UNIX_INFO2", 116},
+ { SMB_SET_FILE_UNIX_LINK, "SMB_SET_FILE_UNIX_LINK", 8},
+ { SMB_SET_FILE_UNIX_HLINK, "SMB_SET_FILE_UNIX_HLINK", 8},
+ { SMB_SET_POSIX_ACL, "SMB_SET_POSIX_ACL", 6},
+ { SMB_SET_POSIX_LOCK, "SMB_SET_POSIX_LOCK", 24},
+ { SMB_INFO_STANDARD, "SMB_INFO_STANDARD", 12},
+ { SMB_INFO_SET_EA, "SMB_INFO_SET_EA", 10},
+ { SMB_FILE_BASIC_INFORMATION, "SMB_FILE_BASIC_INFORMATION", 36},
+ { SMB_SET_FILE_ALLOCATION_INFO, "SMB_SET_FILE_ALLOCATION_INFO", 8},
+ { SMB_SET_FILE_END_OF_FILE_INFO,"SMB_SET_FILE_END_OF_FILE_INFO",8},
+ { SMB_SET_FILE_DISPOSITION_INFO,"SMB_SET_FILE_DISPOSITION_INFO",1},
+ { SMB_FILE_POSITION_INFORMATION,"SMB_FILE_POSITION_INFORMATION",8},
+ { SMB_FILE_FULL_EA_INFORMATION, "SMB_FILE_FULL_EA_INFORMATION",10},
+ { SMB_FILE_MODE_INFORMATION, "SMB_FILE_MODE_INFORMATION", 4},
+ { SMB_FILE_SHORT_NAME_INFORMATION,"SMB_FILE_SHORT_NAME_INFORMATION",12},
+ { SMB_FILE_RENAME_INFORMATION,"SMB_FILE_RENAME_INFORMATION", 20},
+ { SMB_FILE_LINK_INFORMATION, "SMB_FILE_LINK_INFORMATION", 20},
+};
+
+static NTSTATUS do_setpath(TALLOC_CTX *ctx,
+ struct cli_state *cli_unix,
+ const char *fname,
+ size_t i)
+{
+ NTSTATUS status;
+ uint8_t *data = NULL;
+
+ data = talloc_zero_array(ctx,
+ uint8_t,
+ posix_smb1_setpath_array[i].data_len);
+ if (data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_setpathinfo(cli_unix,
+ posix_smb1_setpath_array[i].level,
+ fname,
+ data,
+ posix_smb1_setpath_array[i].data_len);
+ TALLOC_FREE(data);
+
+ /*
+ * We don't care what came back, so long as the
+ * server didn't crash.
+ */
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_CONNECTION_DISCONNECTED)) {
+ printf("cli_setpathinfo info %x (%s) of %s failed"
+ "error NT_STATUS_CONNECTION_DISCONNECTED\n",
+ (unsigned int)posix_smb1_setpath_array[i].level,
+ posix_smb1_setpath_array[i].name,
+ fname);
+ return status;
+ }
+
+ printf("cli_setpathinfo info %x (%s) of %s got %s "
+ "(this is not an error)\n",
+ (unsigned int)posix_smb1_setpath_array[i].level,
+ posix_smb1_setpath_array[i].name,
+ fname,
+ nt_errstr(status));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Ensure we can call SMB1 setpathinfo in a symlink,
+ pointing to a real object or dangling. We mostly
+ expect errors, but the server must not crash.
+ */
+bool run_posix_symlink_setpathinfo_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_setpath_real";
+ const char *fname_real_symlink = "file_real_setpath_symlink";
+ const char *nonexist = "nonexist_setpath";
+ const char *nonexist_symlink = "dangling_setpath_symlink";
+ bool correct = false;
+ size_t i;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-SETPATHINFO test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(posix_smb1_setpath_array); i++) {
+ status = do_setpath(frame,
+ cli_unix,
+ fname_real_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = do_setpath(frame,
+ cli_unix,
+ nonexist_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ printf("POSIX-SYMLINK-SETPATHINFO test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
diff --git a/source3/torture/test_posix_append.c b/source3/torture/test_posix_append.c
new file mode 100644
index 0000000..3abd448
--- /dev/null
+++ b/source3/torture/test_posix_append.c
@@ -0,0 +1,100 @@
+/*
+ Unix SMB/CIFS implementation.
+ reproducer for bug 6898
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+
+/*
+ * Make sure that GENERIC_WRITE does not trigger append. See
+ * https://bugzilla.samba.org/show_bug.cgi?id=6898
+ */
+
+bool run_posix_append(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "append";
+ NTSTATUS status;
+ uint16_t fnum;
+ off_t size;
+ uint8_t c = '\0';
+ bool ret = false;
+
+ printf("Starting POSIX_APPEND\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("torture_setup_unix_extensions failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_ntcreate(
+ cli, fname, 0,
+ GENERIC_WRITE_ACCESS|GENERIC_READ_ACCESS|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_POSIX_SEMANTICS,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF,
+ FILE_NON_DIRECTORY_FILE|FILE_DELETE_ON_CLOSE,
+ 0, &fnum, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Write two bytes at offset 0. With bug 6898 we would end up
+ * with a file of 2 byte length.
+ */
+
+ status = cli_writeall(cli, fnum, 0, &c, 0, sizeof(c), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_write failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+ status = cli_writeall(cli, fnum, 0, &c, 0, sizeof(c), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_write failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_qfileinfo_basic(
+ cli, fnum, NULL, &size, NULL, NULL, NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_qfileinfo_basic failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ if (size != sizeof(c)) {
+ printf("BUG: Writing with O_APPEND!!\n");
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ torture_close_connection(cli);
+ return ret;
+}
diff --git a/source3/torture/test_pthreadpool_tevent.c b/source3/torture/test_pthreadpool_tevent.c
new file mode 100644
index 0000000..c90a394
--- /dev/null
+++ b/source3/torture/test_pthreadpool_tevent.c
@@ -0,0 +1,82 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Test pthreadpool_tevent
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/select.h"
+#include "proto.h"
+#include "lib/pthreadpool/pthreadpool_tevent.h"
+
+static void job_fn(void *private_data);
+
+bool run_pthreadpool_tevent(int dummy)
+{
+ struct tevent_context *ev;
+ struct pthreadpool_tevent *pool;
+ struct tevent_req *req;
+ int ret, val;
+ bool ok;
+
+ ev = tevent_context_init_byname(NULL, "poll");
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init failed\n");
+ return false;
+ }
+
+ ret = pthreadpool_tevent_init(ev, 100, &pool);
+ if (ret != 0) {
+ fprintf(stderr, "pthreadpool_tevent_init failed: %s\n",
+ strerror(ret));
+ return false;
+ }
+
+ val = -1;
+
+ req = pthreadpool_tevent_job_send(ev, ev, pool, job_fn, &val);
+ if (req == NULL) {
+ fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(req, ev);
+ if (!ok) {
+ fprintf(stderr, "tevent_req_poll failed\n");
+ return false;
+ }
+
+ ret = pthreadpool_tevent_job_recv(req);
+ if (ret != 0) {
+ fprintf(stderr, "pthreadpool_tevent_job failed: %s\n",
+ strerror(ret));
+ return false;
+ }
+
+ printf("%d\n", val);
+
+ TALLOC_FREE(pool);
+ TALLOC_FREE(ev);
+ return true;
+}
+
+static void job_fn(void *private_data)
+{
+ int *pret = private_data;
+ *pret = 4711;
+
+ poll(NULL, 0, 100);
+}
diff --git a/source3/torture/test_readdir_timestamp.c b/source3/torture/test_readdir_timestamp.c
new file mode 100644
index 0000000..0eba415
--- /dev/null
+++ b/source3/torture/test_readdir_timestamp.c
@@ -0,0 +1,533 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/smb_strtox.h"
+
+extern int torture_nprocs;
+extern int torture_numops;
+
+struct create_ts_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ unsigned timestamp_idx;
+ uint16_t fnum;
+};
+
+static void create_ts_opened(struct tevent_req *subreq);
+static void create_ts_setinfo_done(struct tevent_req *subreq);
+static void create_ts_waited(struct tevent_req *subreq);
+static void create_ts_written(struct tevent_req *subreq);
+static void create_ts_doc_done(struct tevent_req *subreq);
+
+static struct tevent_req *create_ts_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ unsigned timestamp_idx)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct create_ts_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct create_ts_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->timestamp_idx = timestamp_idx;
+
+ subreq = cli_ntcreate_send(
+ state,
+ ev,
+ cli,
+ fname,
+ 0, /* CreatFlags */
+ SEC_FILE_WRITE_ATTRIBUTE|
+ SEC_FILE_WRITE_DATA|
+ SEC_STD_DELETE, /* DesiredAccess */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
+ FILE_OPEN_IF, /* CreateDisposition */
+ FILE_NON_DIRECTORY_FILE, /* CreateOptions */
+ 0, /* Impersonation */
+ 0); /* SecurityFlags */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, create_ts_opened, req);
+ return req;
+}
+
+static void create_ts_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct create_ts_state *state = tevent_req_data(
+ req, struct create_ts_state);
+ struct smb_create_returns cr;
+ struct timespec mtime;
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->fnum, &cr);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ mtime = nt_time_to_unix_timespec(cr.last_write_time);
+
+ mtime.tv_sec &= ~(0xFFFFULL);
+ mtime.tv_sec |= (state->timestamp_idx & 0xFFFF);
+
+ subreq = cli_setfileinfo_ext_send(
+ state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
+ mtime,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
+ UINT32_MAX); /* attr */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, create_ts_setinfo_done, req);
+}
+
+static void create_ts_setinfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct create_ts_state *state = tevent_req_data(
+ req, struct create_ts_state);
+ NTSTATUS status;
+
+ status = cli_setfileinfo_ext_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = tevent_wakeup_send(
+ state, state->ev, timeval_current_ofs_msec(100));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, create_ts_waited, req);
+}
+
+static void create_ts_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct create_ts_state *state = tevent_req_data(
+ req, struct create_ts_state);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(subreq);
+ return;
+ }
+
+ subreq = cli_write_send(
+ state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0,
+ (uint8_t *)&state->fnum,
+ 0,
+ sizeof(state->fnum));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, create_ts_written, req);
+}
+
+static void create_ts_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct create_ts_state *state = tevent_req_data(
+ req, struct create_ts_state);
+ size_t written;
+ NTSTATUS status;
+
+ status = cli_write_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(subreq, status)) {
+ return;
+ }
+
+ subreq = cli_nt_delete_on_close_send(
+ state, state->ev, state->cli, state->fnum, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, create_ts_doc_done, req);
+}
+
+static void create_ts_doc_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_nt_delete_on_close_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS create_ts_recv(struct tevent_req *req, uint16_t *fnum)
+{
+ struct create_ts_state *state = tevent_req_data(
+ req, struct create_ts_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *fnum = state->fnum;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct create_ts_files_state {
+ size_t num_files;
+ size_t num_received;
+ uint16_t *fnums;
+};
+
+static void create_ts_files_done(struct tevent_req *subreq);
+
+static struct tevent_req *create_ts_files_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *prefix,
+ size_t idx,
+ size_t num_files)
+{
+ struct tevent_req *req = NULL;
+ struct create_ts_files_state *state = NULL;
+ size_t i;
+
+ req = tevent_req_create(mem_ctx, &state, struct create_ts_files_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->num_files = num_files;
+
+ state->fnums = talloc_array(state, uint16_t, num_files);
+ if (tevent_req_nomem(state->fnums, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_files; i++) {
+ struct tevent_req *subreq = NULL;
+ const char *fname = NULL;
+
+ fname = talloc_asprintf(state, "%s%zu_%zu", prefix, idx, i);
+ if (tevent_req_nomem(fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = create_ts_send(state, ev, cli, fname, i);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ talloc_steal(subreq, fname);
+
+ tevent_req_set_callback(subreq, create_ts_files_done, req);
+ }
+ return req;
+}
+
+static void create_ts_files_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct create_ts_files_state *state = tevent_req_data(
+ req, struct create_ts_files_state);
+ NTSTATUS status;
+
+ status = create_ts_recv(subreq, &state->fnums[state->num_received]);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->num_received += 1;
+ if (state->num_received == state->num_files) {
+ tevent_req_done(req);
+ }
+}
+
+static NTSTATUS create_ts_files_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t **fnums)
+{
+ struct create_ts_files_state *state = tevent_req_data(
+ req, struct create_ts_files_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *fnums = talloc_move(mem_ctx, &state->fnums);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct create_files_state {
+ size_t num_reqs;
+ size_t num_received;
+ struct tevent_req **reqs;
+ uint16_t **fnums;
+};
+
+static void create_files_done(struct tevent_req *subreq);
+
+static struct tevent_req *create_files_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state **cli,
+ size_t num_cli,
+ const char *prefix,
+ size_t num_files)
+{
+ struct tevent_req *req = NULL;
+ struct create_files_state *state = NULL;
+ size_t i;
+
+ req = tevent_req_create(mem_ctx, &state, struct create_files_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->num_reqs = num_cli;
+
+ state->reqs = talloc_array(state, struct tevent_req *, num_cli);
+ if (tevent_req_nomem(state->reqs, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->fnums = talloc_array(state, uint16_t *, num_cli);
+ if (tevent_req_nomem(state->fnums, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_cli; i++) {
+ state->reqs[i] = create_ts_files_send(
+ state, ev, cli[i], prefix, i, num_files);
+ if (tevent_req_nomem(state->reqs[i], req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->reqs[i], create_files_done, req);
+ }
+ return req;
+}
+
+static void create_files_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct create_files_state *state = tevent_req_data(
+ req, struct create_files_state);
+ uint16_t *fnums = NULL;
+ NTSTATUS status;
+ size_t i;
+
+ status = create_ts_files_recv(subreq, state->fnums, &fnums);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ for (i=0; i<state->num_reqs; i++) {
+ if (state->reqs[i] == subreq) {
+ break;
+ }
+ }
+ if (i == state->num_reqs) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ TALLOC_FREE(subreq);
+ state->reqs[i] = NULL;
+ state->fnums[i] = fnums;
+
+ state->num_received += 1;
+
+ if (state->num_reqs == state->num_received) {
+ tevent_req_done(req);
+ }
+}
+
+static NTSTATUS create_files_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t ***fnums)
+{
+ struct create_files_state *state = tevent_req_data(
+ req, struct create_files_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *fnums = talloc_move(mem_ctx, &state->fnums);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct list_cb_state {
+ size_t found;
+ bool ok;
+};
+
+static NTSTATUS list_cb(
+ struct file_info *f,
+ const char *mask,
+ void *private_data)
+{
+ struct list_cb_state *state = private_data;
+ char *underbar = NULL;
+ unsigned long long int name_idx;
+ int err;
+
+ underbar = strchr(f->name, '_');
+ if (underbar == NULL) {
+ /* alien filename, . or ..? */
+ return NT_STATUS_OK;
+ }
+
+ name_idx = smb_strtoull(underbar+1, NULL, 10, &err, SMB_STR_STANDARD);
+ if (err != 0) {
+ /* non-numeric? */
+ return NT_STATUS_OK;
+ }
+
+ if ((name_idx & 0xFFFF) != (f->mtime_ts.tv_sec & 0xFFFF)) {
+ d_printf("idx=%llu, nsec=%ld\n",
+ name_idx,
+ f->mtime_ts.tv_nsec);
+ state->ok = false;
+ }
+ state->found += 1;
+
+ return NT_STATUS_OK;
+}
+
+bool run_readdir_timestamp(int dummy)
+{
+ struct cli_state **cli = NULL;
+ int i;
+ bool ret = false;
+ bool ok;
+ const char prefix[] = "readdir_ts/";
+ struct list_cb_state state = { .ok = true };
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ uint16_t **fnums = NULL;
+ NTSTATUS status;
+ size_t expected;
+
+ cli = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
+ if (cli == NULL) {
+ d_printf("talloc_array failed\n");
+ goto fail;
+ }
+
+ for (i=0; i<torture_nprocs; i++) {
+ ok = torture_open_connection_flags(&cli[i], i, 0);
+ if (!ok) {
+ d_printf("torture_open_connection_flags(%d) failed\n",
+ i);
+ goto fail;
+ }
+ }
+
+ status = cli_mkdir(cli[0], "readdir_ts");
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_mkdir failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(cli);
+ if (ev == NULL) {
+ d_printf("samba_tevent_context_init() failed\n");
+ goto fail;
+ }
+
+ req = create_files_send(
+ cli, ev, cli, torture_nprocs, prefix, torture_numops);
+ if (req == NULL) {
+ d_printf("create_files_send() failed\n");
+ goto fail;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_printf("tevent_req_poll_ntstatus failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = create_files_recv(req, talloc_tos(), &fnums);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("create_files_recv failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_list(cli[0],
+ "readdir_ts\\*",
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN,
+ list_cb,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_list failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ expected = torture_nprocs * torture_numops;
+ if (state.found != expected) {
+ d_printf("Expected %zu, got %zu files\n",
+ expected,
+ state.found);
+ goto fail;
+ }
+ if (!state.ok) {
+ d_printf("timestamp mismatch\n");
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(cli);
+ return ret;
+}
diff --git a/source3/torture/test_rpc_scale.c b/source3/torture/test_rpc_scale.c
new file mode 100644
index 0000000..6ef26f3
--- /dev/null
+++ b/source3/torture/test_rpc_scale.c
@@ -0,0 +1,301 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "source3/include/includes.h"
+#include "source3/torture/proto.h"
+#include "source3/libsmb/libsmb.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "source3/rpc_client/rpc_client.h"
+#include "source3/rpc_client/cli_pipe.h"
+#include "libcli/smb/smbXcli_base.h"
+
+extern int torture_nprocs;
+extern int torture_numops;
+
+struct rpc_scale_one_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ size_t num_iterations;
+ struct rpc_pipe_client *rpccli;
+ DATA_BLOB buffer;
+ uint32_t needed;
+ uint32_t num_printers;
+ union spoolss_PrinterInfo *printers;
+};
+
+static void rpc_scale_one_opened(struct tevent_req *subreq);
+static void rpc_scale_one_bound(struct tevent_req *subreq);
+static void rpc_scale_one_listed(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_scale_one_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ size_t num_iterations)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct rpc_scale_one_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_scale_one_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->num_iterations = num_iterations;
+
+ subreq = rpc_pipe_open_np_send(
+ state, ev, cli, &ndr_table_spoolss);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_scale_one_opened, req);
+ return req;
+}
+
+static void rpc_scale_one_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_scale_one_state *state = tevent_req_data(
+ req, struct rpc_scale_one_state);
+ struct pipe_auth_data *auth = NULL;
+ NTSTATUS status;
+
+ status = rpc_pipe_open_np_recv(subreq, state, &state->rpccli);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = rpccli_anon_bind_data(state, &auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = rpc_pipe_bind_send(state, state->ev, state->rpccli, auth);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_scale_one_bound, req);
+}
+
+static void rpc_scale_one_bound(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_scale_one_state *state = tevent_req_data(
+ req, struct rpc_scale_one_state);
+ char *server = NULL;
+ NTSTATUS status;
+
+ status = rpc_pipe_bind_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ server = talloc_asprintf(
+ state,
+ "\\%s\n",
+ smbXcli_conn_remote_name(state->cli->conn));
+ if (tevent_req_nomem(server, req)) {
+ return;
+ }
+ state->buffer = data_blob_talloc(state, NULL, 4096);
+ if (tevent_req_nomem(state->buffer.data, req)) {
+ return;
+ }
+
+ subreq = dcerpc_spoolss_EnumPrinters_send(
+ state,
+ state->ev,
+ state->rpccli->binding_handle,
+ PRINTER_ENUM_LOCAL,
+ server,
+ 1, /* level */
+ &state->buffer,
+ state->buffer.length,
+ &state->num_printers,
+ &state->printers,
+ &state->needed);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_scale_one_listed, req);
+}
+
+static void rpc_scale_one_listed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_scale_one_state *state = tevent_req_data(
+ req, struct rpc_scale_one_state);
+ NTSTATUS status;
+ WERROR result;
+
+ status = dcerpc_spoolss_EnumPrinters_recv(subreq, state, &result);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ status = werror_to_ntstatus(result);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ /*
+ * This will trigger a sync close. Making that async will be a
+ * lot of effort, and even with this being sync this test is
+ * nasty enough.
+ */
+ TALLOC_FREE(state->rpccli);
+
+ state->num_iterations -= 1;
+
+ if (state->num_iterations == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = rpc_pipe_open_np_send(
+ state, state->ev, state->cli, &ndr_table_spoolss);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_scale_one_opened, req);
+}
+
+static NTSTATUS rpc_scale_one_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct rpc_scale_state {
+ size_t num_reqs;
+ size_t done;
+};
+
+static void rpc_scale_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_scale_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state **clis)
+{
+ struct tevent_req *req = NULL;
+ struct rpc_scale_state *state = NULL;
+ size_t i, num_clis = talloc_array_length(clis);
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_scale_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->num_reqs = num_clis;
+
+ for (i=0; i<num_clis; i++) {
+ struct tevent_req *subreq = rpc_scale_one_send(
+ state, ev, clis[i], torture_numops);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_scale_done, req);
+ }
+ return req;
+}
+
+static void rpc_scale_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_scale_state *state = tevent_req_data(
+ req, struct rpc_scale_state);
+ NTSTATUS status;
+
+ status = rpc_scale_one_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->done += 1;
+
+ if (state->done == state->num_reqs) {
+ tevent_req_done(req);
+ }
+}
+
+static NTSTATUS rpc_scale_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+bool run_rpc_scale(int dummy)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct cli_state **clis = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ bool ok, result = false;
+ NTSTATUS status;
+ int i;
+
+ clis = talloc_zero_array(
+ talloc_tos(), struct cli_state *, torture_nprocs);
+ if (clis == NULL) {
+ fprintf(stderr, "talloc failed\n");
+ goto fail;
+ }
+
+ for (i=0; i<torture_nprocs; i++) {
+ ok = torture_open_connection_flags(&clis[i], i, 0);
+ if (!ok) {
+ fprintf(stderr, "could not open connection %d\n", i);
+ goto fail;
+ }
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = rpc_scale_send(talloc_tos(), ev, clis);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ fprintf(stderr,
+ "rpc_scale_send failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = rpc_scale_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "rpc_scale failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ result = true;
+fail:
+ TALLOC_FREE(frame);
+ return result;
+}
diff --git a/source3/torture/test_smb1_dfs.c b/source3/torture/test_smb1_dfs.c
new file mode 100644
index 0000000..4cd75c9
--- /dev/null
+++ b/source3/torture/test_smb1_dfs.c
@@ -0,0 +1,4284 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB1 DFS tests.
+ Copyright (C) Jeremy Allison 2022.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "client.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/security/security.h"
+#include "libsmb/proto.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth_generic.h"
+#include "../librpc/ndr/libndr.h"
+#include "libsmb/clirap.h"
+#include "async_smb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "lib/util/time_basic.h"
+
+extern fstring host, workgroup, share, password, username, myname;
+extern struct cli_credentials *torture_creds;
+
+/*
+ * Open an SMB1 file readonly and return the create time.
+ */
+static NTSTATUS get_smb1_crtime(struct cli_state *cli,
+ const char *pathname,
+ struct timespec *pcrtime)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0;
+ struct timespec crtime = {0};
+
+ /*
+ * Open the file.
+ */
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ pathname,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Get the create time. Note - we can use
+ * a higher-level cli_XXX function here
+ * for SMB1 as cli_qfileinfo_basic()
+ * doesn't use any pathnames, only fnums
+ * so it isn't affected by DFS pathnames.
+ */
+ status = cli_qfileinfo_basic(cli,
+ fnum,
+ NULL, /* attr */
+ NULL, /* size */
+ &crtime, /* create_time */
+ NULL, /* access_time */
+ NULL, /* write_time */
+ NULL, /* change_time */
+ NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ *pcrtime = crtime;
+ }
+
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return status;
+}
+
+/*
+ * Check a crtime matches a given SMB1 path.
+ */
+static bool smb1_crtime_matches(struct cli_state *cli,
+ const char *match_pathname,
+ struct timespec crtime_tomatch,
+ const char *test_pathname)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+ bool equal = false;
+
+ status = get_smb1_crtime(cli,
+ test_pathname,
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s: Failed to get crtime "
+ "for %s, (%s)\n",
+ __func__,
+ test_pathname,
+ nt_errstr(status));
+ return false;
+ }
+ equal = (timespec_compare(&test_crtime, &crtime_tomatch) == 0);
+ if (!equal) {
+ struct timeval_buf test_buf;
+ struct timeval_buf tomatch_buf;
+ printf("%s: crtime mismatch "
+ "%s:crtime_tomatch=%s, %s:test_crtime = %s\n",
+ __func__,
+ match_pathname,
+ timespec_string_buf(&crtime_tomatch,
+ true,
+ &tomatch_buf),
+ test_pathname,
+ timespec_string_buf(&test_crtime,
+ true,
+ &test_buf));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Delete an SMB1 file on a DFS share.
+ */
+static NTSTATUS smb1_dfs_delete(struct cli_state *cli,
+ const char *pathname)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0;
+
+ /*
+ * Open the file.
+ */
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ pathname,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Set delete on close. Note - we can use
+ * a higher-level cli_XXX function here
+ * for SMB1 as cli_nt_delete_on_close()
+ * doesn't use any pathnames, only fnums
+ * so it isn't affected by DFS pathnames.
+ */
+ /*
+ */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+}
+
+static void smb1_mv_done(struct tevent_req *subreq);
+
+struct smb1_mv_state {
+ uint16_t vwv[1];
+};
+
+static struct tevent_req *smb1_mv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ uint8_t *bytes = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_mv_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_mv_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(state->vwv,
+ 0,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ src_dfs_name,
+ strlen(src_dfs_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes = talloc_realloc(state,
+ bytes,
+ uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ target_name,
+ strlen(target_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state,
+ ev,
+ cli,
+ SMBmv,
+ 0, /* additional_flags */
+ 0, /* additional_flags2 */
+ 1,
+ state->vwv,
+ talloc_get_size(bytes),
+ bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_mv_done, req);
+ return req;
+}
+
+static void smb1_mv_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(subreq,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq,
+ status);
+}
+
+static NTSTATUS smb1_mv_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename an SMB1 file on a DFS share. SMBmv version.
+ */
+static NTSTATUS smb1_mv(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_mv_send(frame,
+ ev,
+ cli,
+ src_dfs_name,
+ target_name);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_mv_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool test_smb1_mv(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_timespec = { 0 };
+ NTSTATUS status;
+
+ status = smb1_mv(cli,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMBmv of %s -> %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_timespec);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Put it back. */
+ status = smb1_mv(cli,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMBmv of %s -> %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did put it back. */
+ status = get_smb1_crtime(cli,
+ src_dfs_name,
+ &test_timespec);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ status = smb1_mv(cli,
+ src_dfs_name,
+ "renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMBmv of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ status = smb1_mv(cli,
+ src_dfs_name,
+ "BAD\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMBmv of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static void smb1_setpathinfo_done(struct tevent_req *subreq);
+
+struct smb1_setpathinfo_state {
+ uint16_t setup;
+ uint8_t *param;
+ uint8_t *data;
+};
+
+static struct tevent_req *smb1_setpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t info_level)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_setpathinfo_state *state = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ bool ok = false;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_setpathinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETPATHINFO);
+
+ state->param = talloc_zero_array(state, uint8_t, 6);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+ PUSH_LE_U16(state->param, 0, info_level);
+
+ state->param = trans2_bytes_push_str(state->param,
+ smbXcli_conn_use_unicode(cli->conn),
+ src_dfs_name,
+ strlen(src_dfs_name)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ok = push_ucs2_talloc(state,
+ &converted_str,
+ target_name,
+ &converted_size_bytes);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * W2K8 insists the dest name is not null
+ * terminated. Remove the last 2 zero bytes
+ * and reduce the name length.
+ */
+
+ if (converted_size_bytes < 2) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ converted_size_bytes -= 2;
+
+ state->data = talloc_zero_array(state,
+ uint8_t,
+ 12 + converted_size_bytes);
+ if (tevent_req_nomem(state->data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SIVAL(state->data, 8, converted_size_bytes);
+ memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev,/* event ctx. */
+ cli,/* cli_state. */
+ 0,/* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL,/* pipe name. */
+ -1,/* fid. */
+ 0,/* function. */
+ 0,/* flags. */
+ &state->setup,/* setup. */
+ 1,/* num setup uint16_t words. */
+ 0,/* max returned setup. */
+ state->param,/* param. */
+ talloc_get_size(state->param),/* num param. */
+ 2,/* max returned param. */
+ state->data,/* data. */
+ talloc_get_size(state->data),/* num data. */
+ 0);/* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_setpathinfo_done, req);
+ return req;
+}
+
+static void smb1_setpathinfo_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(subreq,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq,
+ status);
+}
+
+static NTSTATUS smb1_setpathinfo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename or hardlink an SMB1 file on a DFS share. SMB1 setpathinfo
+ * (pathnames only) version.
+ */
+static NTSTATUS smb1_setpathinfo(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t info_level)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_setpathinfo_send(frame,
+ ev,
+ cli,
+ src_dfs_name,
+ target_name,
+ info_level);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_setpathinfo_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS smb1_setpathinfo_rename(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_setpathinfo(cli,
+ src_dfs_name,
+ target_name,
+ SMB_FILE_RENAME_INFORMATION);
+}
+
+static bool test_smb1_setpathinfo_rename(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+ const char *putback_path = NULL;
+
+ /*
+ * On Windows, setpathinfo rename where the target contains
+ * any directory separator returns STATUS_NOT_SUPPORTED.
+ *
+ * MS-SMB behavior note: <133> Section 3.3.5.10.6:
+ *
+ * "If the file name pointed to by the FileName parameter of the
+ * FILE_RENAME_INFORMATION structure contains a separator character,
+ * then the request fails with STATUS_NOT_SUPPORTED."
+ */
+ status = smb1_setpathinfo_rename(cli,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("%s:%d SMB1 setpathinfo rename of %s -> %s should get "
+ "NT_STATUS_NOT_SUPPORTED got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ status = smb1_setpathinfo_rename(cli,
+ src_dfs_name,
+ "renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * To put it back we need to reverse the DFS-ness of src
+ * and destination paths.
+ */
+ putback_path = strrchr(src_dfs_name, '\\');
+ if (putback_path == NULL) {
+ printf("%s:%d non DFS path %s passed. Internal error\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name);
+ return false;
+ }
+ /* Walk past the last '\\' */
+ putback_path++;
+
+ /* Put it back. */
+ status = smb1_setpathinfo_rename(cli,
+ "BAD\\BAD\\renamed_file",
+ putback_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ putback_path,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ src_dfs_name,
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS smb1_setpathinfo_hardlink(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_setpathinfo(cli,
+ src_dfs_name,
+ target_name,
+ SMB_FILE_LINK_INFORMATION);
+}
+
+static bool test_smb1_setpathinfo_hardlink(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ NTSTATUS status;
+
+ /*
+ * On Windows, setpathinfo rename where the target contains
+ * any directory separator returns STATUS_NOT_SUPPORTED.
+ *
+ * MS-SMB behavior note: <133> Section 3.3.5.10.6:
+ *
+ * "If the file name pointed to by the FileName parameter of the
+ * FILE_RENAME_INFORMATION structure contains a separator character,
+ * then the request fails with STATUS_NOT_SUPPORTED."
+ *
+ * setpathinfo info level SMB_FILE_LINK_INFORMATION
+ * seems to do the same, but this could be an artifact
+ * of the Windows version tested (Win2K8). I will
+ * revisit this when I'm able to test against
+ * a later Windows version with a DFS server.
+ */
+ status = smb1_setpathinfo_hardlink(cli,
+ src_dfs_name,
+ "BAD\\BAD\\hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
+ "NT_STATUS_NOT_SUPPORTED got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ /*
+ * At least on Windows 2008 this also fails with
+ * NT_STATUS_NOT_SUPPORTED, leading me to believe
+ * setting hardlinks is only supported via NTrename
+ * in SMB1.
+ */
+ status = smb1_setpathinfo_hardlink(cli,
+ src_dfs_name,
+ "hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
+ "NT_STATUS_NOT_SUPPORTED got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static void smb1_ntrename_done(struct tevent_req *subreq);
+
+struct smb1_ntrename_state {
+ uint16_t vwv[4];
+};
+
+static struct tevent_req *smb1_ntrename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t rename_flag)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_ntrename_state *state = NULL;
+ uint8_t *bytes = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_ntrename_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(state->vwv,
+ 0,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY);
+ PUSH_LE_U16(state->vwv, 2, rename_flag);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ src_dfs_name,
+ strlen(src_dfs_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = talloc_realloc(state,
+ bytes,
+ uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ target_name,
+ strlen(target_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state,
+ ev,
+ cli,
+ SMBntrename,
+ 0, /* additional_flags */
+ 0, /* additional_flags2 */
+ 4,
+ state->vwv,
+ talloc_get_size(bytes),
+ bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_ntrename_done, req);
+ return req;
+}
+
+static void smb1_ntrename_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(subreq,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS smb1_ntrename_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename or hardlink an SMB1 file on a DFS share. SMB1 ntrename version.
+ * (pathnames only).
+ */
+static NTSTATUS smb1_ntrename(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t rename_flag)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_ntrename_send(frame,
+ ev,
+ cli,
+ src_dfs_name,
+ target_name,
+ rename_flag);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_ntrename_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+/*
+ * Rename an SMB1 file on a DFS share. SMB1 ntrename version.
+ */
+static NTSTATUS smb1_ntrename_rename(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_ntrename(cli,
+ src_dfs_name,
+ target_name,
+ RENAME_FLAG_RENAME);
+}
+
+
+static bool test_smb1_ntrename_rename(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+
+ /* Try with a non-DFS name. */
+ status = smb1_ntrename_rename(cli,
+ src_dfs_name,
+ "renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMB1 ntrename rename of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb1_ntrename_rename(cli,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 ntrename rename of %s -> %s should "
+ "succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Put it back. */
+ status = smb1_ntrename_rename(cli,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 ntrename rename of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ src_dfs_name,
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Hard link an SMB1 file on a DFS share. SMB1 ntrename version.
+ */
+static NTSTATUS smb1_ntrename_hardlink(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_ntrename(cli,
+ src_dfs_name,
+ target_name,
+ RENAME_FLAG_HARD_LINK);
+}
+
+static bool test_smb1_ntrename_hardlink(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Try with a non-DFS name. */
+ status = smb1_ntrename_hardlink(cli,
+ src_dfs_name,
+ "hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMB1 ntrename of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb1_ntrename_hardlink(cli,
+ src_dfs_name,
+ "BAD\\BAD\\hlink");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 ntrename hardlink of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we did hardlink. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\hlink",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ retval = smb1_crtime_matches(cli,
+ "BAD\\BAD\\hlink",
+ test_crtime,
+ src_dfs_name);
+ if (!retval) {
+ printf("%s:%d smb1_crtime_matches failed for "
+ "%s %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink");
+ goto out;
+ }
+
+ out:
+
+ /* Remove the hardlink to clean up. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+static void smb1_setfileinfo_done(struct tevent_req *subreq);
+
+struct smb1_setfileinfo_state {
+ uint16_t setup;
+ uint8_t param[6];
+ uint8_t *data;
+};
+
+static struct tevent_req *smb1_setfileinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name,
+ uint16_t info_level)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_setfileinfo_state *state = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ bool ok = false;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_setfileinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETPATHINFO);
+
+ PUSH_LE_U16(state->param, 0, fnum);
+ PUSH_LE_U16(state->param, 2, info_level);
+
+ ok = push_ucs2_talloc(state,
+ &converted_str,
+ target_name,
+ &converted_size_bytes);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * W2K8 insists the dest name is not null
+ * terminated. Remove the last 2 zero bytes
+ * and reduce the name length.
+ */
+
+ if (converted_size_bytes < 2) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ converted_size_bytes -= 2;
+
+ state->data = talloc_zero_array(state,
+ uint8_t,
+ 12 + converted_size_bytes);
+ if (tevent_req_nomem(state->data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SIVAL(state->data, 8, converted_size_bytes);
+ memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev,/* event ctx. */
+ cli,/* cli_state. */
+ 0,/* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL,/* pipe name. */
+ -1,/* fid. */
+ 0,/* function. */
+ 0,/* flags. */
+ &state->setup,/* setup. */
+ 1,/* num setup uint16_t words. */
+ 0,/* max returned setup. */
+ state->param,/* param. */
+ 6,/* num param. */
+ 2,/* max returned param. */
+ state->data,/* data. */
+ talloc_get_size(state->data),/* num data. */
+ 0);/* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_setfileinfo_done, req);
+ return req;
+}
+
+static void smb1_setfileinfo_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(subreq,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq,
+ status);
+}
+
+static NTSTATUS smb1_setfileinfo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename or hardlink an SMB1 file on a DFS share.
+ * setfileinfo (file handle + target pathname) version.
+ */
+static NTSTATUS smb1_setfileinfo(struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name,
+ uint16_t info_level)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_setfileinfo_send(frame,
+ ev,
+ cli,
+ fnum,
+ target_name,
+ info_level);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_setfileinfo_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS smb1_setfileinfo_rename(struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name)
+{
+ return smb1_setfileinfo(cli,
+ fnum,
+ target_name,
+ SMB_FILE_RENAME_INFORMATION);
+}
+
+/*
+ * On Windows, rename using a file handle as source
+ * is not supported.
+ */
+
+static bool test_smb1_setfileinfo_rename(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ bool retval = false;
+
+ /* First open the source file. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ src_dfs_name,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to open %s, %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * On Windows rename given a file handle returns
+ * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
+ */
+
+ status = smb1_setfileinfo_rename(cli,
+ fnum,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
+ status = smb1_setfileinfo_rename(cli,
+ fnum,
+ "renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ retval = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ return retval;
+}
+
+
+static NTSTATUS smb1_setfileinfo_hardlink(struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name)
+{
+ return smb1_setfileinfo(cli,
+ fnum,
+ target_name,
+ SMB_FILE_LINK_INFORMATION);
+}
+
+/*
+ * On Windows, hardlink using a file handle as source
+ * is not supported.
+ */
+
+static bool test_smb1_setfileinfo_hardlink(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ bool retval = false;
+
+ /* First open the source file. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ src_dfs_name,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_RIGHTS_FILE_READ, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to open %s, %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * On Windows hardlink given a file handle returns
+ * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
+ */
+
+ status = smb1_setfileinfo_hardlink(cli,
+ fnum,
+ "BAD\\BAD\\hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
+ status = smb1_setfileinfo_hardlink(cli,
+ fnum,
+ "hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ retval = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+/*
+ * According to:
+
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/dc9978d7-6299-4c5a-a22d-a039cdc716ea
+ *
+ * (Characters " \ / [ ] : | < > + = ; , * ?,
+ * and control characters in range 0x00 through
+ * 0x1F, inclusive, are illegal in a share name)
+ *
+ * But Windows server only checks in DFS sharenames ':'. All other
+ * share names are allowed.
+ */
+
+static bool test_smb1_dfs_sharenames(struct cli_state *cli,
+ const char *dfs_root_share_name,
+ struct timespec root_crtime)
+{
+ char test_path[20];
+ const char *test_str = "/[]:|<>+=;,*?";
+ const char *p;
+ unsigned int i;
+ bool crtime_matched = false;
+
+ /* Setup template pathname. */
+ memcpy(test_path, "\\SERVER\\X", 10);
+
+ /* Test invalid control characters. */
+ for (i = 1; i < 0x20; i++) {
+ test_path[8] = i;
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ test_path);
+ if (!crtime_matched) {
+ return false;
+ }
+ }
+
+ /* Test explicit invalid characters. */
+ for (p = test_str; *p != '\0'; p++) {
+ test_path[8] = *p;
+ if (*p == ':') {
+ /*
+ * Only ':' is treated as an INVALID sharename
+ * for a DFS SERVER\\SHARE path.
+ */
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status = get_smb1_crtime(cli,
+ test_path,
+ &test_crtime);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ printf("%s:%d Open of %s should get "
+ "NT_STATUS_OBJECT_NAME_INVALID, got %s\n",
+ __FILE__,
+ __LINE__,
+ test_path,
+ nt_errstr(status));
+ return false;
+ }
+ } else {
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ test_path);
+ if (!crtime_matched) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * "Raw" test of SMB1 paths to a DFS share.
+ * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb1_dfs_paths(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ char *dfs_root_share_name = NULL;
+ struct timespec root_crtime = { 0 };
+ struct timespec test_crtime = { 0 };
+ bool crtime_matched = false;
+ bool retval = false;
+ bool ok = false;
+ bool equal = false;
+ unsigned int i;
+ uint16_t fnum = (uint16_t)-1;
+
+ printf("Starting SMB1-DFS-PATHS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ /* Start with an empty share. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\BAD");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+
+ /*
+ * Create the "official" DFS share root name.
+ */
+ dfs_root_share_name = talloc_asprintf(talloc_tos(),
+ "\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_root_share_name == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /* Get the share root crtime. */
+ status = get_smb1_crtime(cli,
+ dfs_root_share_name,
+ &root_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime for share root %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ dfs_root_share_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Test the Windows algorithm for parsing DFS names.
+ */
+ /*
+ * A single "SERVER" element should open and match the share root.
+ */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ smbXcli_conn_remote_name(cli->conn));
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+
+ /* An "" (empty) server name should open and match the share root. */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "");
+ return false;
+ }
+
+ /*
+ * For SMB1 the server just strips off any number of leading '\\'
+ * characters. Show this is the case.
+ */
+ for (i = 0; i < 10; i++) {
+ char leading_backslash_name[20];
+ leading_backslash_name[i] = '\\';
+ memcpy(&leading_backslash_name[i+1],
+ "SERVER",
+ strlen("SERVER")+1);
+
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ leading_backslash_name);
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ leading_backslash_name);
+ return false;
+ }
+ }
+
+ /* A "BAD" server name should open and match the share root. */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "BAD");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD");
+ return false;
+ }
+ /*
+ * A "BAD\\BAD" server and share name should open
+ * and match the share root.
+ */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "BAD\\BAD");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD");
+ return false;
+ }
+ /*
+ * Trying to open "BAD\\BAD\\BAD" should get
+ * NT_STATUS_OBJECT_NAME_NOT_FOUND.
+ */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\BAD",
+ &test_crtime);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\BAD",
+ nt_errstr(status));
+ return false;
+ }
+ /*
+ * Trying to open "BAD\\BAD\\BAD\\BAD" should get
+ * NT_STATUS_OBJECT_PATH_NOT_FOUND.
+ */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\BAD\\BAD",
+ &test_crtime);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\BAD\\BAD",
+ nt_errstr(status));
+ return false;
+ }
+ /*
+ * Test for invalid pathname characters in the servername.
+ * They are ignored, and it still opens the share root.
+ */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "::::");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "::::");
+ return false;
+ }
+
+ /*
+ * Test for invalid pathname characters in the sharename.
+ * Invalid sharename characters should still be flagged as
+ * NT_STATUS_OBJECT_NAME_INVALID. It turns out only ':'
+ * is considered an invalid sharename character.
+ */
+ ok = test_smb1_dfs_sharenames(cli,
+ dfs_root_share_name,
+ root_crtime);
+ if (!ok) {
+ return false;
+ }
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ "BAD\\BAD\\file",
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ fnum = (uint16_t)-1;
+
+ /*
+ * Trying to open "BAD\\BAD\\file" should now get
+ * a valid crtime.
+ */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\file",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Open of %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * This crtime must be different from the root_crtime.
+ * This checks we're actually correctly reading crtimes
+ * from the filesystem.
+ */
+ equal = (timespec_compare(&test_crtime, &root_crtime) == 0);
+ if (equal) {
+ printf("%s:%d Error. crtime of %s must differ from "
+ "root_crtime\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file");
+ goto err;
+ }
+
+ /*
+ * Test different SMB1 renames
+ * and hard links.
+ */
+
+ /* SMBmv only does rename. */
+ ok = test_smb1_mv(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setpathinfo_rename(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setpathinfo_hardlink(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setfileinfo_rename(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setfileinfo_hardlink(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ntrename_rename(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ntrename_hardlink(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ /* Delete anything we made. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\BAD");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+/*
+ * SMB1 Findfirst. This is a minimal implementation
+ * that expects all filename returns in one packet.
+ * We're only using this to test the search DFS pathname
+ * parsing.
+ */
+
+/****************************************************************************
+ Calculate a safe next_entry_offset.
+****************************************************************************/
+
+static size_t calc_next_entry_offset(const uint8_t *base,
+ const uint8_t *pdata_end)
+{
+ size_t next_entry_offset = (size_t)PULL_LE_U32(base,0);
+
+ if (next_entry_offset == 0 ||
+ base + next_entry_offset < base ||
+ base + next_entry_offset > pdata_end) {
+ next_entry_offset = pdata_end - base;
+ }
+ return next_entry_offset;
+}
+
+static size_t get_filename(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const uint8_t *base_ptr,
+ uint16_t recv_flags2,
+ const uint8_t *p,
+ const uint8_t *pdata_end,
+ struct file_info *finfo)
+{
+ size_t ret = 0;
+ const uint8_t *base = p;
+ size_t namelen = 0;
+ size_t slen = 0;
+
+ ZERO_STRUCTP(finfo);
+
+ if (pdata_end - base < 94) {
+ return pdata_end - base;
+ }
+ p += 4; /* next entry offset */
+ p += 4; /* fileindex */
+ /* Offset zero is "create time", not "change time". */
+ p += 8;
+ finfo->atime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->mtime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->ctime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->size = PULL_LE_U64(p, 0);
+ p += 8;
+ p += 8; /* alloc size */
+ finfo->attr = PULL_LE_U32(p, 0);
+ p += 4;
+ namelen = PULL_LE_U32(p, 0);
+ p += 4;
+ p += 4; /* EA size */
+ slen = PULL_LE_U8(p, 0);
+ if (slen > 24) {
+ /* Bad short name length. */
+ return pdata_end - base;
+ }
+ p += 2;
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->short_name,
+ p,
+ slen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += 24; /* short name */
+ if (p + namelen < p || p + namelen > pdata_end) {
+ return pdata_end - base;
+ }
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->name,
+ p,
+ namelen,
+ 0);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ return calc_next_entry_offset(base, pdata_end);
+}
+
+/* Single shot SMB1 TRANS2 FindFirst. */
+
+static NTSTATUS smb1_findfirst(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ const char *search_name,
+ struct file_info **names,
+ size_t *num_names)
+{
+ NTSTATUS status;
+ uint16_t setup[1];
+ uint8_t *param = NULL;
+ uint16_t recv_flags2 = 0;
+ uint8_t *rparam = NULL;
+ uint32_t num_rparam = 0;
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata = 0;
+ uint16_t num_names_returned = 0;
+ struct file_info *finfo = NULL;
+ uint8_t *p2 = NULL;
+ uint8_t *data_end = NULL;
+ uint16_t i = 0;
+
+ PUSH_LE_U16(&setup[0], 0, TRANSACT2_FINDFIRST);
+
+ param = talloc_array(mem_ctx, uint8_t, 12);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ PUSH_LE_U16(param, 0, FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ PUSH_LE_U16(param, 2, 1366); /* max_matches */
+ PUSH_LE_U16(param, 4, FLAG_TRANS2_FIND_CLOSE_IF_END);
+ PUSH_LE_U16(param, 6, SMB_FIND_FILE_BOTH_DIRECTORY_INFO); /* info_level */
+
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ search_name,
+ strlen(search_name)+1,
+ NULL);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * A one shot SMB1 findfirst will be enough to
+ * return ".", "..", and "file".
+ */
+ status = cli_trans(mem_ctx,
+ cli,
+ SMBtrans2, /* cmd */
+ NULL, /* pipe_name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ &setup[0],
+ 1, /* num_setup uint16_t words */
+ 0, /* max returned setup */
+ param,
+ talloc_get_size(param), /* num_param */
+ 10, /* max returned param */
+ NULL, /* data */
+ 0, /* num_data */
+ SMB_BUFFER_SIZE_MAX, /* max returned data */
+ /* Return values from here on.. */
+ &recv_flags2, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min returned rsetup */
+ NULL, /* num_rsetup */
+ &rparam,
+ 6, /* min returned rparam */
+ &num_rparam, /* number of returned rparam */
+ &rdata,
+ 0, /* min returned rdata */
+ &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ num_names_returned = PULL_LE_U16(rparam, 2);
+
+ finfo = talloc_array(mem_ctx, struct file_info, num_names_returned);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p2 = rdata;
+ data_end = rdata + num_rdata;
+
+ for (i = 0; i < num_names_returned; i++) {
+ if (p2 >= data_end) {
+ break;
+ }
+ if (i == num_names_returned - 1) {
+ /* Last entry - fixup the last offset length. */
+ PUSH_LE_U32(p2, 0, PTR_DIFF((rdata + num_rdata), p2));
+ }
+
+ p2 += get_filename(mem_ctx,
+ cli,
+ rdata,
+ recv_flags2,
+ p2,
+ data_end,
+ &finfo[i]);
+
+ if (finfo->name == NULL) {
+ printf("%s:%d Unable to parse name from listing "
+ "of %s, position %u\n",
+ __FILE__,
+ __LINE__,
+ search_name,
+ (unsigned int)i);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ *num_names = i;
+ *names = finfo;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Test a specific SMB1 findfirst path to see if it
+ * matches a given file array.
+ */
+static bool test_smb1_findfirst_path(struct cli_state *cli,
+ const char *search_path,
+ struct file_info *root_finfo,
+ size_t num_root_finfo)
+{
+ size_t i = 0;
+ size_t num_finfo = 0;
+ struct file_info *finfo = NULL;
+ NTSTATUS status;
+
+ status = smb1_findfirst(talloc_tos(),
+ cli,
+ search_path,
+ &finfo,
+ &num_finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1findfirst on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ search_path,
+ nt_errstr(status));
+ return false;
+ }
+
+ if (num_finfo != num_root_finfo) {
+ printf("%s:%d On %s, num_finfo = %zu, num_root_finfo = %zu\n",
+ __FILE__,
+ __LINE__,
+ search_path,
+ num_finfo,
+ num_root_finfo);
+ return false;
+ }
+ for (i = 0; i < num_finfo; i++) {
+ bool match = strequal_m(finfo[i].name,
+ root_finfo[i].name);
+ if (!match) {
+ printf("%s:%d Mismatch. For %s, at position %zu, "
+ "finfo[i].name = %s, "
+ "root_finfo[i].name = %s\n",
+ __FILE__,
+ __LINE__,
+ search_path,
+ i,
+ finfo[i].name,
+ root_finfo[i].name);
+ return false;
+ }
+ }
+ TALLOC_FREE(finfo);
+ return true;
+}
+
+/*
+ * "Raw" test of doing a SMB1 findfirst to a DFS share.
+ * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb1_dfs_search_paths(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ struct file_info *root_finfo = NULL;
+ size_t num_root_finfo = 0;
+ bool retval = false;
+ bool ok = false;
+ uint16_t fnum = (uint16_t)-1;
+
+ printf("Starting SMB1-DFS-SEARCH-PATHS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+
+ /* Create a test file to search for. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ "BAD\\BAD\\file",
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ fnum = (uint16_t)-1;
+
+ /* Get the list of files in the share. */
+ status = smb1_findfirst(talloc_tos(),
+ cli,
+ "SERVER\\SHARE\\*",
+ &root_finfo,
+ &num_root_finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1findfirst on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "SERVER\\SHARE\\*",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Try different search names. They should
+ * all match the root directory list.
+ */
+ ok = test_smb1_findfirst_path(cli,
+ "\\SERVER\\SHARE\\*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_findfirst_path(cli,
+ "*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+ ok = test_smb1_findfirst_path(cli,
+ "\\*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+ ok = test_smb1_findfirst_path(cli,
+ "\\SERVER\\*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+ retval = true;
+
+ err:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ /* Delete anything we made. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+ return retval;
+}
+
+static bool smb1_create_testfile(struct cli_state *cli,
+ const char *path)
+{
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+
+ /* Create a test file. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ path,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return true;
+}
+
+static NTSTATUS smb1_unlink(struct cli_state *cli,
+ const char *path)
+{
+ uint16_t vwv[1];
+ uint8_t *bytes = NULL;
+
+ PUSH_LE_U16(vwv, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return cli_smb(talloc_tos(),
+ cli,
+ SMBunlink, /* command. */
+ 0, /* additional_flags. */
+ 1, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+}
+
+static bool test_smb1_unlink(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\file");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\file");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\file");
+ goto err;
+ }
+
+ status = smb1_unlink(cli, "file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1unlink of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "file",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_unlink(cli, "\\BAD\\file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1unlink of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_unlink(cli, "\\BAD\\BAD\\file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1unlink on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\file");
+ return retval;
+}
+
+static NTSTATUS smb1_mkdir(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *bytes = NULL;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return cli_smb(talloc_tos(),
+ cli,
+ SMBmkdir, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+}
+
+static bool test_smb1_mkdir(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+
+ status = smb1_mkdir(cli, "dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1mkdir of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_mkdir(cli, "\\BAD\\dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1mkdir of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_mkdir(cli, "\\BAD\\BAD\\dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1mkdir on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+ return retval;
+}
+
+static NTSTATUS smb1_rmdir(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *bytes = NULL;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return cli_smb(talloc_tos(),
+ cli,
+ SMBrmdir, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+}
+
+static bool test_smb1_rmdir(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+
+ status = smb1_mkdir(cli, "\\BAD\\BAD\\dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1rmdir on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = smb1_rmdir(cli, "dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1rmdir of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_rmdir(cli, "\\BAD\\dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1rmdir of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_rmdir(cli, "\\BAD\\BAD\\dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1rmdir on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+ return retval;
+}
+
+static NTSTATUS smb1_ntcreatex(struct cli_state *cli,
+ const char *path)
+{
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ path,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return NT_STATUS_OK;
+}
+
+static bool test_smb1_ntcreatex(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ntcreateXfile");
+
+ status = smb1_ntcreatex(cli, "ntcreateXfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1ntcreateX of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "ntcreateXfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ntcreatex(cli, "\\BAD\\ntcreateXfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1ntcreateX of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\ntcreateXfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ntcreatex(cli, "\\BAD\\BAD\\ntcreateXfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1ntcreateX on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\ntcreateXfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ntcreateXfile");
+ return retval;
+}
+
+static NTSTATUS smb1_nttrans_create(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *param = NULL;
+ size_t converted_len = 0;
+ uint8_t *rparam = NULL;
+ uint32_t num_rparam = 0;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+
+ param = talloc_zero_array(talloc_tos(), uint8_t, 53);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path),
+ &converted_len);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ PUSH_LE_U32(param, 8, SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE); /* DesiredAccess */
+ PUSH_LE_U32(param, 20, FILE_ATTRIBUTE_NORMAL);
+ PUSH_LE_U32(param, 24, FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE); /* ShareAccess */
+ PUSH_LE_U32(param, 28, FILE_CREATE);
+ PUSH_LE_U32(param, 44, converted_len);
+ PUSH_LE_U32(param, 48, 0x02); /* ImpersonationLevel */
+
+ status = cli_trans(talloc_tos(),
+ cli,
+ SMBnttrans, /* trans cmd */
+ NULL, /* pipe_name */
+ 0, /* fid */
+ NT_TRANSACT_CREATE, /* function */
+ 0, /* flags */
+ NULL, /* setup */
+ 0, /* num_setup */
+ 0, /* max_setup */
+ param, /* param */
+ talloc_get_size(param), /* num_param */
+ 128, /* max_param */
+ NULL, /* data */
+ 0, /* num_data */
+ 0, /* max_data */
+ NULL, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min_rsetup */
+ NULL, /* num_rsetup */
+ &rparam, /* rparam */
+ 69, /* min_rparam */
+ &num_rparam, /* num_rparam */
+ NULL, /* rdata */
+ 0, /* min_rdata */
+ NULL); /* num_rdata */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ fnum = PULL_LE_U16(param, 2);
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return NT_STATUS_OK;
+}
+
+static bool test_smb1_nttrans_create(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\nttransfile");
+
+ status = smb1_nttrans_create(cli, "nttransfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1trans NT_TRANSACT_CREATE of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "nttransfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_nttrans_create(cli, "\\BAD\\nttransfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1trans NT_TRANSACT_CREATE of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\nttransfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_nttrans_create(cli, "\\BAD\\BAD\\nttransfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1trans NT_TRANSACT_CREATE on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\nttransfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\nttransfile");
+ return retval;
+}
+
+struct smb1_openx_state {
+ const char *fname;
+ uint16_t vwv[15];
+ uint16_t fnum;
+ struct iovec bytes;
+};
+
+static void smb1_openx_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb1_openx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *path)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ uint16_t accessmode = 0;
+ struct smb1_openx_state *state = NULL;
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct smb1_openx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ accessmode = (DENY_NONE<<4);
+ accessmode |= DOS_OPEN_RDONLY;
+
+ PUSH_LE_U8(state->vwv + 0, 0, 0xFF);
+ PUSH_LE_U16(state->vwv + 3, 0, accessmode);
+ PUSH_LE_U16(state->vwv + 4, 0,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY);
+ PUSH_LE_U16(state->vwv + 8,
+ 0,
+ OPENX_FILE_CREATE_IF_NOT_EXIST| OPENX_FILE_EXISTS_FAIL);
+
+ bytes = talloc_array(state, uint8_t, 0);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->bytes.iov_base = (void *)bytes;
+ state->bytes.iov_len = talloc_get_size(bytes);
+ subreq = cli_smb_req_create(state,
+ ev,
+ cli,
+ SMBopenX, /* cmd */
+ 0, /* additional_flags */
+ 0, /* additional_flags2 */
+ 15, /* num_vwv */
+ state->vwv, /* vwv */
+ 1, /* iovcount */
+ &state->bytes); /* iovec */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_openx_done, req);
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void smb1_openx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smb1_openx_state *state = tevent_req_data(
+ req, struct smb1_openx_state);
+ uint8_t wct = 0;
+ uint16_t *vwv = NULL;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq,
+ state,
+ NULL, /* pinbuf */
+ 3, /* min_wct */
+ &wct, /* wct */
+ &vwv, /* vwv */
+ NULL, /* num_rbytes */
+ NULL); /* rbytes */
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->fnum = PULL_LE_U16(vwv+2, 0);
+ tevent_req_done(req);
+}
+
+static NTSTATUS smb1_openx_recv(struct tevent_req *req, uint16_t *pfnum)
+{
+ struct smb1_openx_state *state = tevent_req_data(
+ req, struct smb1_openx_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb1_openx(struct cli_state *cli, const char *path)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = smb1_openx_send(frame,
+ ev,
+ cli,
+ path);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_openx_recv(req, &fnum);
+ fail:
+
+ /* Close "file" handle. */
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool test_smb1_openx(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openxfile");
+
+ status = smb1_openx(cli, "openxfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1openx of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "openxfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_openx(cli, "\\BAD\\openxfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1openx of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\openxfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_openx(cli, "\\BAD\\BAD\\openxfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1openx on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openxfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openxfile");
+ return retval;
+}
+
+static NTSTATUS smb1_open(struct cli_state *cli,
+ const char *path,
+ uint16_t *pfnum)
+{
+ uint16_t vwv[2] = { 0, 0};
+ uint8_t *bytes = NULL;
+ uint16_t accessmode = 0;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ NTSTATUS status;
+
+ accessmode = (DENY_NONE<<4);
+ accessmode |= DOS_OPEN_RDONLY;
+
+ PUSH_LE_U16(vwv + 0, 0, accessmode);
+ PUSH_LE_U16(vwv + 1, 0, FILE_ATTRIBUTE_NORMAL);
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBopen, /* command. */
+ 0, /* additional_flags. */
+ 2, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 7, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pfnum = PULL_LE_U16(return_words, 0);
+ return status;
+}
+
+static bool test_smb1_open(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ bool equal = false;
+ uint16_t fnum = (uint16_t)-1;
+ struct timespec testfile_crtime = { 0 };
+ struct timespec open_crtime = { 0 };
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\openfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile");
+ goto err;
+ }
+
+ /* Get the test file crtime number. */
+ status = get_smb1_crtime(cli,
+ "\\BAD\\BAD\\openfile",
+ &testfile_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = smb1_open(cli, "openfile", &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1open of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_open(cli, "\\BAD\\openfile", &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1open of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_open(cli, "\\BAD\\BAD\\openfile", &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to open test file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = cli_qfileinfo_basic(cli,
+ fnum,
+ NULL, /* attr */
+ NULL, /* size */
+ &open_crtime, /* create_time */
+ NULL, /* access_time */
+ NULL, /* write_time */
+ NULL, /* change_time */
+ NULL); /* ino */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to get crtime of test file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ equal = (timespec_compare(&testfile_crtime, &open_crtime) == 0);
+ if (!equal) {
+ printf("%s:%d crtime mismatch of test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile");
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ /* Close "openfile" handle. */
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openfile");
+ return retval;
+}
+
+static NTSTATUS smb1_create(struct cli_state *cli,
+ const char *path,
+ uint16_t smb1_operation,
+ uint16_t *pfnum)
+{
+ uint16_t vwv[3] = { 0, 0, 0};
+ uint8_t *bytes = NULL;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ NTSTATUS status;
+
+ PUSH_LE_U16(vwv + 0, 0, FILE_ATTRIBUTE_NORMAL);
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ smb1_operation, /* command. */
+ 0, /* additional_flags. */
+ 3, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 1, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pfnum = PULL_LE_U16(return_words, 0);
+ return status;
+}
+
+static bool test_smb1_create(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ uint16_t fnum = (uint16_t)-1;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\createfile");
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\mknewfile");
+
+ status = smb1_create(cli, "createfile", SMBcreate, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1create of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "createfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\createfile", SMBcreate, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1open of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\BAD\\createfile", SMBcreate, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to create file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\createfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+
+ fnum = (uint16_t)-1;
+
+ /* Now do the same with SMBmknew */
+ status = smb1_create(cli, "mknewfile", SMBmknew, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1mknew of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "mknewfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\mknewfile", SMBmknew, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1mknew of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\mknewfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\BAD\\mknewfile", SMBmknew, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to create file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\mknewfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+
+ fnum = (uint16_t)-1;
+
+ retval = true;
+
+ err:
+
+ /* Close "openfile" handle. */
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\createfile");
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\mknewfile");
+ return retval;
+}
+
+static NTSTATUS smb1_getatr(struct cli_state *cli,
+ const char *path,
+ uint16_t *pattr)
+{
+ uint8_t *bytes = NULL;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ NTSTATUS status;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBgetatr, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 10, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pattr = PULL_LE_U16(return_words, 0);
+ return status;
+}
+
+static bool test_smb1_getatr(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ uint16_t attrs = 0;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\getatrfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\getatrfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\getatrfile");
+ goto err;
+ }
+
+ /*
+ * We expect this to succeed, but get attributes of
+ * the root directory.
+ */
+ status = smb1_getatr(cli, "getatrfile", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "getatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d error expected SMB1getatr of file %s "
+ "to return directory attributes. Got 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "getatrfile",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /*
+ * We expect this to succeed, but get attributes of
+ * the root directory.
+ */
+ status = smb1_getatr(cli, "\\BAD\\getatrfile", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\getatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d error expected SMB1getatr of file %s "
+ "to return directory attributes. Got 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\getatrfile",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /*
+ * We expect this to succeed, and get attributes of
+ * the testfile.
+ */
+ status = smb1_getatr(cli, "\\BAD\\BAD\\getatrfile", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\getatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
+ printf("%s:%d error expected SMB1getatr of file %s "
+ "to return non-directory attributes. Got 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\getatrfile",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\getatrfile");
+ return retval;
+}
+
+static NTSTATUS smb1_setatr(struct cli_state *cli,
+ const char *path,
+ uint16_t attr)
+{
+ uint16_t vwv[8] = { 0 };
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+
+ PUSH_LE_U16(vwv, 0, attr);
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBsetatr, /* command. */
+ 0, /* additional_flags. */
+ 8, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return status;
+}
+
+static bool test_smb1_setatr(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ uint16_t file_attrs = 0;
+ uint16_t orig_file_attrs = 0;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\setatrfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\setatrfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile");
+ goto err;
+ }
+ /* Get it's original attributes. */
+ status = smb1_getatr(cli, "\\BAD\\BAD\\setatrfile", &orig_file_attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ if (orig_file_attrs & FILE_ATTRIBUTE_SYSTEM) {
+ printf("%s:%d orig_file_attrs of %s already has SYSTEM. "
+ "Test cannot proceed.\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile");
+ goto err;
+ }
+
+ /*
+ * Seems we can't set attrs on the root of a share,
+ * even as Administrator.
+ */
+ status = smb1_setatr(cli, "setatrfile", FILE_ATTRIBUTE_SYSTEM);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1setatr of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Seems we can't set attrs on the root of a share,
+ * even as Administrator.
+ */
+ status = smb1_setatr(cli, "\\BAD\\setatrfile", FILE_ATTRIBUTE_SYSTEM);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1setatr of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = smb1_setatr(cli,
+ "\\BAD\\BAD\\setatrfile",
+ FILE_ATTRIBUTE_SYSTEM);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1setatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_getatr(cli, "\\BAD\\BAD\\setatrfile", &file_attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ if (file_attrs != FILE_ATTRIBUTE_SYSTEM) {
+ printf("%s:%d Failed to set SYSTEM attr on %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile");
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\setatrfile");
+ return retval;
+}
+
+static NTSTATUS smb1_chkpath(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBcheckpath, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return status;
+}
+
+static bool test_smb1_chkpath(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\chkpathfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\chkpathfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\chkpathfile");
+ goto err;
+ }
+ /*
+ * Should succeed - "chkpathfile" maps to
+ * directory "".
+ */
+ status = smb1_chkpath(cli, "chkpathfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1chkpath of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "chkpathfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Should succeed - "\\BAD\\chkpathfile" maps to
+ * directory "".
+ */
+ status = smb1_chkpath(cli, "\\BAD\\chkpathfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1chkpath of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\chkpathfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Should fail - "\\BAD\\BAD\\chkpathfile" maps to the
+ * "\\BAD\\BAD\\chkpathfile", not a directory.
+ */
+ status = smb1_chkpath(cli, "\\BAD\\BAD\\chkpathfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ printf("%s:%d SMB1chkpath of %s should get "
+ "NT_STATUS_NOT_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\chkpathfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\chkpathfile");
+ return retval;
+}
+
+/*
+ * Test BUG: https://bugzilla.samba.org/show_bug.cgi?id=15419
+ */
+
+static bool test_smb1_chkpath_bad(struct cli_state *cli)
+{
+ NTSTATUS status;
+
+ status = smb1_chkpath(cli, "\\x//\\/");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1chkpath of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\x//\\/",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static NTSTATUS smb1_ctemp(struct cli_state *cli,
+ const char *path,
+ char **tmp_path)
+{
+ uint16_t vwv[3] = { 0 };
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ uint32_t return_bytecount = 0;
+ uint8_t *return_bytes = NULL;
+ size_t sret = 0;
+ uint16_t fnum = (uint16_t)-1;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBctemp, /* command. */
+ 0, /* additional_flags. */
+ 3, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 1, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ &return_bytecount, /* return byte count. */
+ &return_bytes); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (return_wcount != 1) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ fnum = PULL_LE_U16(return_words, 0);
+
+ /* Delete the file by fnum. */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ fnum = (uint16_t)-1;
+
+ if (return_bytecount < 2) {
+ return NT_STATUS_DATA_ERROR;
+ }
+
+ sret = pull_string_talloc(talloc_tos(),
+ NULL,
+ 0,
+ tmp_path,
+ return_bytes,
+ return_bytecount,
+ STR_ASCII);
+ if (sret == 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return status;
+}
+
+static bool test_smb1_ctemp(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ char *retpath = NULL;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ctemp_dir");
+
+ status = smb1_mkdir(cli, "\\BAD\\BAD\\ctemp_dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to create %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Windows returns NT_STATUS_FILE_IS_A_DIRECTORY
+ * for all SMBctemp calls on a DFS share, no
+ * matter what we put in the pathname.
+ */
+
+ /*
+ * When we fix smbd we'll need to detect running
+ * in smbtorture3 against smbd here and modify
+ * the expected behavior. Windows is simply
+ * broken here.
+ */
+ status = smb1_ctemp(cli, "ctemp_dir", &retpath);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1ctemp of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ctemp(cli, "\\BAD\\ctemp_dir", &retpath);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1ctemp of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ctemp(cli, "\\BAD\\BAD\\ctemp_dir", &retpath);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1ctemp of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ctemp_dir");
+ return retval;
+}
+
+static NTSTATUS smb1_qpathinfo(struct cli_state *cli,
+ const char *fname,
+ uint32_t *pattrs)
+{
+ NTSTATUS status;
+ uint8_t *param = NULL;
+ uint16_t setup[1] = { 0 };
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata = 0;
+
+ PUSH_LE_U16(setup, 0, TRANSACT2_QPATHINFO);
+
+ param = talloc_zero_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ PUSH_LE_U16(param, 0, SMB_QUERY_FILE_BASIC_INFO);
+
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname,
+ strlen(fname)+1,
+ NULL);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_trans(talloc_tos(),
+ cli,
+ SMBtrans2, /* cmd */
+ NULL, /* pipe_name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ &setup[0],
+ 1, /* num_setup uint16_t words */
+ 0, /* max returned setup */
+ param,
+ talloc_get_size(param), /* num_param */
+ 2, /* max returned param */
+ NULL, /* data */
+ 0, /* num_data */
+ SMB_BUFFER_SIZE_MAX, /* max returned data */
+ /* Return values from here on.. */
+ NULL, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min returned rsetup */
+ NULL, /* num_rsetup */
+ NULL,
+ 0, /* min returned rparam */
+ NULL, /* number of returned rparam */
+ &rdata,
+ 36, /* min returned rdata */
+ &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pattrs = PULL_LE_U32(rdata, 32);
+ return NT_STATUS_OK;
+}
+
+static bool test_smb1_qpathinfo(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ uint32_t attrs;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\qpathinfo_file");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\qpathinfo_file");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\qpathinfo_file");
+ goto err;
+ }
+
+ /* Should get root dir attrs. */
+ status = smb1_qpathinfo(cli, "qpathinfo_file", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1_qpathinfo failed %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "qpathinfo_file",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d expected FILE_ATTRIBUTE_DIRECTORY on %s "
+ "got attribute 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "qpathinfo_file",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /* Should get root dir attrs. */
+ status = smb1_qpathinfo(cli, "\\BAD\\qpathinfo_file", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1_qpathinfo failed %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\qpathinfo_file",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d expected FILE_ATTRIBUTE_DIRECTORY on %s "
+ "got attribute 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\qpathinfo_file",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /* Should get file attrs. */
+ status = smb1_qpathinfo(cli, "\\BAD\\BAD\\qpathinfo_file", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1_qpathinfo failed %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\qpathinfo_file",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ printf("%s:%d expected not FILE_ATTRIBUTE_DIRECTORY on %s "
+ "got attribute 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\qpathinfo_file",
+ (unsigned int)attrs);
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\qpathinfo_file");
+ return retval;
+}
+
+/*
+ * "Raw" test of different SMB1 operations to a DFS share.
+ * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb1_dfs_operations(int dummy)
+{
+ struct cli_state *cli = NULL;
+ bool dfs_supported = false;
+ bool retval = false;
+ bool ok = false;
+
+ printf("Starting SMB1-DFS-OPS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ ok = test_smb1_unlink(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_mkdir(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_rmdir(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ntcreatex(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_nttrans_create(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_openx(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_open(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_create(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_getatr(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setatr(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_chkpath(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ctemp(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_qpathinfo(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ /* Delete anything we made. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\file");
+ return retval;
+}
+
+/*
+ * Test BUG: https://bugzilla.samba.org/show_bug.cgi?id=15419
+ */
+
+bool run_smb1_dfs_check_badpath(int dummy)
+{
+ struct cli_state *cli = NULL;
+ bool dfs_supported = false;
+
+ printf("Starting SMB1-DFS-CHECK-BADPATH\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ return test_smb1_chkpath_bad(cli);
+}
diff --git a/source3/torture/test_smb2.c b/source3/torture/test_smb2.c
new file mode 100644
index 0000000..3b2e1e4
--- /dev/null
+++ b/source3/torture/test_smb2.c
@@ -0,0 +1,5471 @@
+/*
+ Unix SMB/CIFS implementation.
+ Initial test for the smb2 client lib
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "client.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/security/security.h"
+#include "libsmb/proto.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth_generic.h"
+#include "../librpc/ndr/libndr.h"
+#include "libsmb/clirap.h"
+#include "libsmb/cli_smb2_fnum.h"
+
+extern fstring host, workgroup, share, password, username, myname;
+extern struct cli_credentials *torture_creds;
+
+bool run_smb2_basic(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+ uint64_t fid_persistent, fid_volatile;
+ const char *hello = "Hello, world\n";
+ uint8_t *result;
+ uint32_t nread;
+ uint8_t *dir_data;
+ uint32_t dir_data_length;
+ uint32_t saved_tid = 0;
+ struct smbXcli_tcon *saved_tcon = NULL;
+ char *saved_share = NULL;
+ uint64_t saved_uid = 0;
+
+ printf("Starting SMB2-BASIC\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB2_02,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "smb2-basic.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (nread != strlen(hello)) {
+ printf("smb2cli_read returned %d bytes, expected %d\n",
+ (int)nread, (int)strlen(hello));
+ return false;
+ }
+
+ if (memcmp(hello, result, nread) != 0) {
+ printf("smb2cli_read returned '%s', expected '%s'\n",
+ result, hello);
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ saved_tid = smb2cli_tcon_current_id(cli->smb2.tcon);
+ cli_state_save_tcon_share(cli, &saved_tcon, &saved_share);
+ cli->smb2.tcon = smbXcli_tcon_create(cli);
+ smb2cli_tcon_set_values(cli->smb2.tcon,
+ NULL, /* session */
+ saved_tid,
+ 0, /* type */
+ 0, /* flags */
+ 0, /* capabilities */
+ 0 /* maximal_access */);
+ status = smb2cli_tdis(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon);
+ cli_state_restore_tcon_share(cli, saved_tcon, saved_share);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_tdis returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_tdis(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) {
+ printf("2nd smb2cli_tdis returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ saved_uid = smb2cli_session_current_id(cli->smb2.session);
+ status = smb2cli_logoff(cli->conn, cli->timeout, cli->smb2.session);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_logoff returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ cli->smb2.session = smbXcli_session_create(cli, cli->conn);
+ if (cli->smb2.session == NULL) {
+ printf("smbXcli_session_create() returned NULL\n");
+ return false;
+ }
+
+ smb2cli_session_set_id_and_flags(cli->smb2.session, saved_uid, 0);
+
+ status = smb2cli_logoff(cli->conn, cli->timeout, cli->smb2.session);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
+ printf("2nd smb2cli_logoff returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_negprot(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+ enum protocol_types protocol;
+ const char *name = NULL;
+
+ printf("Starting SMB2-NEGPROT\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_CORE,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ protocol = smbXcli_conn_protocol(cli->conn);
+ name = smb_protocol_types_string(protocol);
+
+ if (protocol >= PROTOCOL_SMB2_02) {
+ printf("Server supports %s\n", name);
+ } else {
+ printf("Server DOES NOT support SMB2, only %s\n", name);
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ protocol,
+ protocol,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_ABORTED)) {
+ printf("2nd smbXcli_negprot should disconnect - returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (smbXcli_conn_is_connected(cli->conn)) {
+ printf("2nd smbXcli_negprot should disconnect "
+ "- still connected\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_anonymous(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ struct cli_credentials *anon_creds = NULL;
+ bool guest = false;
+
+ printf("Starting SMB2-ANONYMOUS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ anon_creds = cli_credentials_init_anon(talloc_tos());
+ if (anon_creds == NULL) {
+ printf("cli_credentials_init_anon failed\n");
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, anon_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ guest = smbXcli_session_is_guest(cli->smb2.session);
+ if (guest) {
+ printf("anonymous session should not have guest authentication\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_session_reconnect(int dummy)
+{
+ struct cli_state *cli1;
+ struct cli_state *cli2;
+ NTSTATUS status;
+ bool ok;
+ uint64_t fid_persistent, fid_volatile;
+ struct tevent_context *ev;
+ struct tevent_req *subreq;
+ DATA_BLOB in_blob = data_blob_null;
+ DATA_BLOB out_blob;
+ DATA_BLOB session_key;
+ struct auth_generic_state *auth_generic_state;
+ struct iovec *recv_iov;
+ const char *hello = "Hello, world\n";
+ uint8_t *result;
+ uint32_t nread;
+
+ printf("Starting SMB2-SESSION-RECONNECT\n");
+
+ if (!torture_init_connection(&cli1)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli1->conn,
+ cli1->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli1, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli1, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli1->conn,
+ cli1->timeout,
+ cli1->smb2.session,
+ cli1->smb2.tcon,
+ "session-reconnect.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create on cli1 %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (nread != strlen(hello)) {
+ printf("smb2cli_read returned %d bytes, expected %d\n",
+ (int)nread, (int)strlen(hello));
+ return false;
+ }
+
+ if (memcmp(hello, result, nread) != 0) {
+ printf("smb2cli_read returned '%s', expected '%s'\n",
+ result, hello);
+ return false;
+ }
+
+ /* prepare second session */
+
+ if (!torture_init_connection(&cli2)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli2->conn,
+ cli2->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_prepare(talloc_tos(), &auth_generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_prepare returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ gensec_want_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+
+ status = auth_generic_set_creds(auth_generic_state, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_start returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("samba_tevent_context_init() returned NULL\n");
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), data_blob_null, &in_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("gensec_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ cli2->smb2.session = smbXcli_session_create(cli2, cli2->conn);
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ 0x0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ /* in_previous_session_id: */
+ smb2cli_session_current_id(cli1->smb2.session),
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ NULL, &out_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), out_blob, &in_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ 0x0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ /* in_previous_session_id: */
+ smb2cli_session_current_id(cli1->smb2.session),
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ &recv_iov, &out_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_session_key(auth_generic_state->gensec_security, talloc_tos(),
+ &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("gensec_session_key returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* check file operation on the old client */
+
+ status = smb2cli_flush(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli1, share, "?????", NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * checking file operations without signing.
+ * on w2k8r2 at least, flush, read and write also work the same way,
+ * while create gives ACCESS_DENIED without signing
+ */
+ status = smb2cli_flush(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli2->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli2->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli2->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ cli2->smb2.tcon,
+ "session-reconnect.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) {
+ printf("smb2cli_create on cli2 %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* now grab the session key and try with signing */
+
+ status = smb2cli_session_set_session_key(cli2->smb2.session,
+ session_key,
+ recv_iov);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_set_session_key %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* the tid seems to be irrelevant at this stage */
+
+ status = smb2cli_flush(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ cli1->smb2.tcon,
+ "session-reconnect.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED))
+ {
+ printf("smb2cli_create on cli2 %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* now do a new tcon and test file calls again */
+
+ status = cli_tree_connect(cli2, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ cli2->smb2.tcon,
+ "session-reconnect.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create on cli2 %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli2->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli2->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli2->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (nread != strlen(hello)) {
+ printf("smb2cli_read returned %d bytes, expected %d\n",
+ (int)nread, (int)strlen(hello));
+ return false;
+ }
+
+ if (memcmp(hello, result, nread) != 0) {
+ printf("smb2cli_read returned '%s', expected '%s'\n",
+ result, hello);
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_tcon_dependence(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+ uint64_t fid_persistent, fid_volatile;
+ const char *hello = "Hello, world\n";
+ uint8_t *result;
+ uint32_t nread;
+ struct smbXcli_tcon *tcon2;
+ uint32_t tcon2_id;
+
+ printf("Starting SMB2-TCON-DEPENDENCE\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "tcon_depedence.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create on cli %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (nread != strlen(hello)) {
+ printf("smb2cli_read returned %d bytes, expected %d\n",
+ (int)nread, (int)strlen(hello));
+ return false;
+ }
+
+ if (memcmp(hello, result, nread) != 0) {
+ printf("smb2cli_read returned '%s', expected '%s'\n",
+ result, hello);
+ return false;
+ }
+
+ /* check behaviour with wrong tid... */
+
+ tcon2 = smbXcli_tcon_create(cli);
+ tcon2_id = smb2cli_tcon_current_id(cli->smb2.tcon);
+ tcon2_id++;
+ smb2cli_tcon_set_values(tcon2,
+ NULL, /* session */
+ tcon2_id,
+ 0, /* type */
+ 0, /* flags */
+ 0, /* capabilities */
+ 0 /* maximal_access */);
+
+ status = smb2cli_read(cli->conn, cli->timeout, cli->smb2.session,
+ tcon2, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ talloc_free(tcon2);
+
+ return true;
+}
+
+bool run_smb2_multi_channel(int dummy)
+{
+ struct cli_state *cli1;
+ struct cli_state *cli2;
+ struct cli_state *cli3;
+ NTSTATUS status;
+ bool ok;
+ uint64_t fid_persistent, fid_volatile;
+ struct tevent_context *ev;
+ struct tevent_req *subreq;
+ DATA_BLOB in_blob = data_blob_null;
+ DATA_BLOB out_blob;
+ DATA_BLOB channel_session_key;
+ struct auth_generic_state *auth_generic_state;
+ struct iovec *recv_iov;
+ const char *hello = "Hello, world\n";
+ uint8_t *result;
+ uint32_t nread;
+ struct GUID saved_guid = cli_state_client_guid;
+
+ printf("Starting SMB2-MULTI-CHANNEL\n");
+
+ cli_state_client_guid = GUID_random();
+
+ if (!torture_init_connection(&cli1)) {
+ return false;
+ }
+
+ if (!torture_init_connection(&cli2)) {
+ return false;
+ }
+
+ if (!torture_init_connection(&cli3)) {
+ return false;
+ }
+
+ cli_state_client_guid = saved_guid;
+
+ status = smbXcli_negprot(cli1->conn,
+ cli1->timeout,
+ PROTOCOL_SMB3_00,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smbXcli_negprot(cli2->conn,
+ cli2->timeout,
+ PROTOCOL_SMB3_00,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smbXcli_negprot(cli3->conn,
+ cli3->timeout,
+ PROTOCOL_SMB3_00,
+ PROTOCOL_LATEST,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli1, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_sesssetup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli1, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_session_create_channel(cli2,
+ cli1->smb2.session,
+ cli2->conn,
+ &cli2->smb2.session);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_create_channel returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_prepare(talloc_tos(), &auth_generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_prepare returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ gensec_want_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+
+ status = auth_generic_set_creds(auth_generic_state, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_start returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("samba_tevent_context_init() returned NULL\n");
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), data_blob_null, &in_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("gensec_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ 0x01, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ NULL, &out_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), out_blob, &in_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ 0x01, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ &recv_iov, &out_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_session_key(auth_generic_state->gensec_security, talloc_tos(),
+ &channel_session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("gensec_session_key returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_session_set_channel_key(cli2->smb2.session,
+ channel_session_key,
+ recv_iov);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_set_channel_key %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_session_create_channel(cli3,
+ cli1->smb2.session,
+ cli3->conn,
+ &cli3->smb2.session);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_create_channel returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_prepare(talloc_tos(), &auth_generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_prepare returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ gensec_want_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+
+ status = auth_generic_set_creds(auth_generic_state, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_start returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), data_blob_null, &in_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("gensec_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli3->conn,
+ cli3->timeout,
+ cli3->smb2.session,
+ 0x01, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ NULL, &out_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), out_blob, &in_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli3->conn,
+ cli3->timeout,
+ cli3->smb2.session,
+ 0x01, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ &recv_iov, &out_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_session_key(auth_generic_state->gensec_security, talloc_tos(),
+ &channel_session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("gensec_session_key returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_session_set_channel_key(cli3->smb2.session,
+ channel_session_key,
+ recv_iov);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_set_channel_key %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ cli1->smb2.tcon,
+ "multi-channel.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create on cli2 %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_write(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, strlen(hello), 0, fid_persistent,
+ fid_volatile, 0, 0, (const uint8_t *)hello, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_write returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli3->conn, cli3->timeout, cli3->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_read(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, 0x10000, 0, fid_persistent,
+ fid_volatile, 2, 0,
+ talloc_tos(), &result, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_read returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (nread != strlen(hello)) {
+ printf("smb2cli_read returned %d bytes, expected %d\n",
+ (int)nread, (int)strlen(hello));
+ return false;
+ }
+
+ if (memcmp(hello, result, nread) != 0) {
+ printf("smb2cli_read returned '%s', expected '%s'\n",
+ result, hello);
+ return false;
+ }
+
+ status = auth_generic_client_prepare(talloc_tos(), &auth_generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_prepare returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ gensec_want_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+
+ status = auth_generic_set_creds(auth_generic_state, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_start returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), data_blob_null, &in_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("gensec_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli3->conn,
+ cli3->timeout,
+ cli3->smb2.session,
+ 0x0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ NULL, &out_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), out_blob, &in_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli3->conn, cli3->timeout, cli3->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli1->conn,
+ cli1->timeout,
+ cli1->smb2.session,
+ cli1->smb2.tcon,
+ "multi-channel-invalid.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_create %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ cli1->smb2.tcon,
+ "multi-channel-invalid.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_create %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli3->conn,
+ cli3->timeout,
+ cli3->smb2.session,
+ cli1->smb2.tcon,
+ "multi-channel-invalid.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_create %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli2->conn,
+ cli2->timeout,
+ cli2->smb2.session,
+ 0x0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ &recv_iov, &out_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli3->conn, cli3->timeout, cli3->smb2.session,
+ cli1->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli3->conn, cli3->timeout, cli3->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli2->conn, cli2->timeout, cli2->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli1->conn, cli1->timeout, cli1->smb2.session,
+ cli1->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_session_reauth(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+ bool ok;
+ uint64_t fid_persistent, fid_volatile;
+ uint64_t dir_persistent, dir_volatile;
+ uint8_t *dir_data;
+ uint32_t dir_data_length;
+ struct tevent_context *ev;
+ struct tevent_req *subreq;
+ DATA_BLOB in_blob = data_blob_null;
+ DATA_BLOB out_blob;
+ DATA_BLOB in_input_buffer;
+ DATA_BLOB out_output_buffer;
+ uint8_t in_file_info_class;
+ struct auth_generic_state *auth_generic_state;
+ struct iovec *recv_iov;
+ uint32_t saved_tid;
+ struct smbXcli_tcon *saved_tcon;
+
+ printf("Starting SMB2-SESSION_REAUTH\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ /*
+ * PROTOCOL_SMB2_22 has a bug in win8pre0
+ * it behaves like PROTOCOL_SMB2_02
+ * and returns NT_STATUS_REQUEST_NOT_ACCEPTED,
+ * while it allows it on PROTOCOL_SMB2_10.
+ */
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_10,
+ PROTOCOL_SMB2_10,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_sesssetup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "session-reauth.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &dir_persistent,
+ &dir_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0x3, 0, dir_persistent, dir_volatile,
+ "session-reauth.txt", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_prepare(talloc_tos(), &auth_generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_prepare returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ gensec_want_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+
+ status = auth_generic_set_creds(auth_generic_state, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_set_creds returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_client_start returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("samba_tevent_context_init() returned NULL\n");
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), data_blob_null, &in_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("gensec_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ 0x0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ NULL, &out_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), out_blob, &in_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("auth_generic_update returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0x3, 0, dir_persistent, dir_volatile,
+ "session-reauth.txt", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * query_info seems to be a path based operation on Windows...
+ */
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_SECURITY,
+ 0, /* in_file_info_class */
+ 1024, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ SECINFO_OWNER, /* in_additional_info */
+ 0, /* in_flags */
+ fid_persistent,
+ fid_volatile,
+ talloc_tos(),
+ &out_output_buffer);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_query_info (security) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ in_file_info_class = SMB_FILE_POSITION_INFORMATION - 1000;
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE,
+ in_file_info_class,
+ 1024, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ fid_persistent,
+ fid_volatile,
+ talloc_tos(),
+ &out_output_buffer);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_query_info (position) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ in_input_buffer = data_blob_talloc(talloc_tos(), NULL, 8);
+ SBVAL(in_input_buffer.data, 0, 512);
+
+ in_file_info_class = SMB_FILE_POSITION_INFORMATION - 1000;
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE,
+ in_file_info_class,
+ &in_input_buffer,
+ 0, /* in_additional_info */
+ fid_persistent,
+ fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_set_info (position) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "session-reauth-invalid.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_create %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &dir_persistent,
+ &dir_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("smb2cli_create returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ saved_tid = smb2cli_tcon_current_id(cli->smb2.tcon);
+ saved_tcon = cli->smb2.tcon;
+ cli->smb2.tcon = smbXcli_tcon_create(cli);
+ smb2cli_tcon_set_values(cli->smb2.tcon,
+ NULL, /* session */
+ saved_tid,
+ 0, /* type */
+ 0, /* flags */
+ 0, /* capabilities */
+ 0 /* maximal_access */);
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+ talloc_free(cli->smb2.tcon);
+ cli->smb2.tcon = saved_tcon;
+
+ subreq = smb2cli_session_setup_send(talloc_tos(), ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ 0x0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &in_blob); /* in_security_buffer */
+ if (subreq == NULL) {
+ printf("smb2cli_session_setup_send() returned NULL\n");
+ return false;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ printf("tevent_req_poll() returned false\n");
+ return false;
+ }
+
+ status = smb2cli_session_setup_recv(subreq, talloc_tos(),
+ &recv_iov, &out_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_session_setup_recv returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_SECURITY,
+ 0, /* in_file_info_class */
+ 1024, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ SECINFO_OWNER, /* in_additional_info */
+ 0, /* in_flags */
+ fid_persistent,
+ fid_volatile,
+ talloc_tos(),
+ &out_output_buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_info (security) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ in_file_info_class = SMB_FILE_POSITION_INFORMATION - 1000;
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE,
+ in_file_info_class,
+ 1024, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ fid_persistent,
+ fid_volatile,
+ talloc_tos(),
+ &out_output_buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_info (position) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ in_input_buffer = data_blob_talloc(talloc_tos(), NULL, 8);
+ SBVAL(in_input_buffer.data, 0, 512);
+
+ in_file_info_class = SMB_FILE_POSITION_INFORMATION - 1000;
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE,
+ in_file_info_class,
+ &in_input_buffer,
+ 0, /* in_additional_info */
+ fid_persistent,
+ fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_set_info (position) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ in_file_info_class = SMB_FILE_POSITION_INFORMATION - 1000;
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE,
+ in_file_info_class,
+ 1024, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ fid_persistent,
+ fid_volatile,
+ talloc_tos(),
+ &out_output_buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_info (position) returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "session-reauth.txt",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_ALL | SEC_FILE_ALL, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DELETE_ON_CLOSE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0x3, 0, dir_persistent, dir_volatile,
+ "session-reauth.txt", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, dir_persistent, dir_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ saved_tid = smb2cli_tcon_current_id(cli->smb2.tcon);
+ saved_tcon = cli->smb2.tcon;
+ cli->smb2.tcon = smbXcli_tcon_create(cli);
+ smb2cli_tcon_set_values(cli->smb2.tcon,
+ NULL, /* session */
+ saved_tid,
+ 0, /* type */
+ 0, /* flags */
+ 0, /* capabilities */
+ 0 /* maximal_access */);
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+ talloc_free(cli->smb2.tcon);
+ cli->smb2.tcon = saved_tcon;
+
+ return true;
+}
+
+static NTSTATUS check_size(struct cli_state *cli,
+ uint16_t fnum,
+ const char *fname,
+ size_t size)
+{
+ off_t size_read = 0;
+
+ NTSTATUS status = cli_qfileinfo_basic(cli,
+ fnum,
+ NULL,
+ &size_read,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_qfileinfo_basic of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ return status;
+ }
+
+ if (size != size_read) {
+ printf("size (%u) != size_read(%u) for %s\n",
+ (unsigned int)size,
+ (unsigned int)size_read,
+ fname);
+ /* Use EOF to mean bad size. */
+ return NT_STATUS_END_OF_FILE;
+ }
+ return NT_STATUS_OK;
+}
+
+/* Ensure cli_ftruncate() works for SMB2. */
+
+bool run_smb2_ftruncate(int dummy)
+{
+ struct cli_state *cli = NULL;
+ const char *fname = "smb2_ftruncate.txt";
+ uint16_t fnum = (uint16_t)-1;
+ bool correct = false;
+ size_t buflen = 1024*1024;
+ uint8_t *buf = NULL;
+ unsigned int i;
+ NTSTATUS status;
+
+ printf("Starting SMB2-FTRUNCATE\n");
+
+ if (!torture_init_connection(&cli)) {
+ goto fail;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB2_02,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ cli_setatr(cli, fname, 0, 0);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ buf = talloc_zero_array(cli, uint8_t, buflen);
+ if (buf == NULL) {
+ goto fail;
+ }
+
+ /* Write 1MB. */
+ status = cli_writeall(cli,
+ fnum,
+ 0,
+ buf,
+ 0,
+ buflen,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write of %u to %s failed (%s)\n",
+ (unsigned int)buflen,
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = check_size(cli, fnum, fname, buflen);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Now ftruncate. */
+ for ( i = 0; i < 10; i++) {
+ status = cli_ftruncate(cli, fnum, i*1024);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ftruncate %u of %s failed (%s)\n",
+ (unsigned int)i*1024,
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+ status = check_size(cli, fnum, fname, i*1024);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+
+ correct = true;
+
+ fail:
+
+ if (cli == NULL) {
+ return false;
+ }
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ }
+ cli_setatr(cli, fname, 0, 0);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+ return correct;
+}
+
+/* Ensure SMB2 flush on directories behaves correctly. */
+
+static bool test_dir_fsync(struct cli_state *cli, const char *path)
+{
+ NTSTATUS status;
+ uint64_t fid_persistent, fid_volatile;
+ uint8_t *dir_data = NULL;
+ uint32_t dir_data_length = 0;
+
+ /* Open directory - no write abilities. */
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (readonly) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory no write access. Flush should fail. */
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("smb2cli_flush on a read-only directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory write-attributes only. Flush should still fail. */
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_WRITE_ATTRIBUTE|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (write attr) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("smb2cli_flush on a write-attributes directory "
+ "returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory with SEC_DIR_ADD_FILE access. Flush should now succeed. */
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_ADD_FILE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (write FILE access) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush on a directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory with SEC_DIR_ADD_FILE access. Flush should now succeed. */
+
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_ADD_SUBDIR, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (write DIR access) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush on a directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+
+ return true;
+}
+
+bool run_smb2_dir_fsync(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool bret = false;
+ const char *dname = "fsync_test_dir";
+
+ printf("Starting SMB2-DIR-FSYNC\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB2_02,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ (void)cli_rmdir(cli, dname);
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_mkdir(%s) returned %s\n",
+ dname,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Test on a subdirectory. */
+ bret = test_dir_fsync(cli, dname);
+ if (bret == false) {
+ (void)cli_rmdir(cli, dname);
+ return false;
+ }
+ (void)cli_rmdir(cli, dname);
+
+ /* Test on the root handle of a share. */
+ bret = test_dir_fsync(cli, "");
+ if (bret == false) {
+ return false;
+ }
+ return true;
+}
+
+bool run_smb2_path_slash(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ uint64_t fid_persistent;
+ uint64_t fid_volatile;
+ const char *dname_noslash = "smb2_dir_slash";
+ const char *dname_backslash = "smb2_dir_slash\\";
+ const char *dname_slash = "smb2_dir_slash/";
+ const char *fname_noslash = "smb2_file_slash";
+ const char *fname_backslash = "smb2_file_slash\\";
+ const char *fname_slash = "smb2_file_slash/";
+
+ printf("Starting SMB2-PATH-SLASH\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB2_02,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ (void)cli_unlink(cli, dname_noslash, 0);
+ (void)cli_rmdir(cli, dname_noslash);
+ (void)cli_unlink(cli, fname_noslash, 0);
+ (void)cli_rmdir(cli, fname_noslash);
+
+ /* Try to create a directory with the backslash name. */
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ dname_backslash,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ FILE_READ_DATA|FILE_READ_ATTRIBUTES, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ /* directory ending in '\\' should be success. */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' returned %s - "
+ "should be NT_STATUS_OK\n",
+ dname_backslash,
+ nt_errstr(status));
+ return false;
+ }
+ status = smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ fid_persistent,
+ fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ (void)cli_rmdir(cli, dname_noslash);
+
+ /* Try to create a directory with the slash name. */
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ dname_slash,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ FILE_READ_DATA|FILE_READ_ATTRIBUTES, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ /* directory ending in '/' is an error. */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ printf("smb2cli_create '%s' returned %s - "
+ "should be NT_STATUS_OBJECT_NAME_INVALID\n",
+ dname_slash,
+ nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ (void)smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ fid_persistent,
+ fid_volatile);
+ }
+ (void)cli_rmdir(cli, dname_noslash);
+ return false;
+ }
+
+ (void)cli_rmdir(cli, dname_noslash);
+
+ /* Try to create a file with the backslash name. */
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ fname_backslash,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ FILE_READ_DATA|FILE_READ_ATTRIBUTES, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_NON_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ /* file ending in '\\' should be error. */
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ printf("smb2cli_create '%s' returned %s - "
+ "should be NT_STATUS_OBJECT_NAME_INVALID\n",
+ fname_backslash,
+ nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ (void)smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ fid_persistent,
+ fid_volatile);
+ }
+ (void)cli_unlink(cli, fname_noslash, 0);
+ return false;
+ }
+
+ (void)cli_unlink(cli, fname_noslash, 0);
+
+ /* Try to create a file with the slash name. */
+ status = smb2cli_create(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ fname_slash,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ FILE_READ_DATA|FILE_READ_ATTRIBUTES, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ FILE_NON_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ /* file ending in '/' should be error. */
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ printf("smb2cli_create '%s' returned %s - "
+ "should be NT_STATUS_OBJECT_NAME_INVALID\n",
+ fname_slash,
+ nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ (void)smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ fid_persistent,
+ fid_volatile);
+ }
+ (void)cli_unlink(cli, fname_noslash, 0);
+ return false;
+ }
+
+ (void)cli_unlink(cli, fname_noslash, 0);
+ return true;
+}
+
+/*
+ * NB. This can only work against a server where
+ * the connecting user has been granted SeSecurityPrivilege.
+ *
+ * 1). Create a test file.
+ * 2). Open with SEC_FLAG_SYSTEM_SECURITY *only*. ACCESS_DENIED -
+ * NB. SMB2-only behavior.
+ * 3). Open with SEC_FLAG_SYSTEM_SECURITY|FILE_WRITE_ATTRIBUTES.
+ * 4). Write SACL. Should fail with ACCESS_DENIED (seems to need WRITE_DAC).
+ * 5). Close (3).
+ * 6). Open with SEC_FLAG_SYSTEM_SECURITY|SEC_STD_WRITE_DAC.
+ * 7). Write SACL. Success.
+ * 8). Close (4).
+ * 9). Open with SEC_FLAG_SYSTEM_SECURITY|READ_ATTRIBUTES.
+ * 10). Read SACL. Success.
+ * 11). Read DACL. Should fail with ACCESS_DENIED (no READ_CONTROL).
+ * 12). Close (9).
+ */
+
+bool run_smb2_sacl(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ struct security_descriptor *sd_dacl = NULL;
+ struct security_descriptor *sd_sacl = NULL;
+ const char *fname = "sacl_test_file";
+ uint16_t fnum = (uint16_t)-1;
+
+ printf("Starting SMB2-SACL\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ (void)cli_unlink(cli, fname, 0);
+
+ /* First create a file. */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ cli_close(cli, fnum);
+ fnum = (uint16_t)-1;
+
+ /*
+ * Now try to open with *only* SEC_FLAG_SYSTEM_SECURITY.
+ * This should fail with NT_STATUS_ACCESS_DENIED - but
+ * only against an SMB2 server. SMB1 allows this as tested
+ * in SMB1-SYSTEM-SECURITY.
+ */
+
+ status = cli_smb2_create_fnum(cli,
+ fname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ SEC_FLAG_SYSTEM_SECURITY, /* desired access */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_NON_DIRECTORY_FILE, /* create_options, */
+ NULL, /* in_cblobs. */
+ &fnum, /* fnum */
+ NULL, /* smb_create_returns */
+ talloc_tos(), /* mem_ctx */
+ NULL); /* out_cblobs */
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PRIVILEGE_NOT_HELD)) {
+ printf("SMB2-SACL-TEST can only work with a user "
+ "who has been granted SeSecurityPrivilege.\n"
+ "This is the "
+ "\"Manage auditing and security log\""
+ "privilege setting on Windows\n");
+ goto fail;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("open file %s with SEC_FLAG_SYSTEM_SECURITY only: "
+ "got %s - should fail with ACCESS_DENIED\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Open with SEC_FLAG_SYSTEM_SECURITY|FILE_WRITE_ATTRIBUTES.
+ */
+
+ status = cli_smb2_create_fnum(cli,
+ fname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ SEC_FLAG_SYSTEM_SECURITY|
+ FILE_WRITE_ATTRIBUTES, /* desired access */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_NON_DIRECTORY_FILE, /* create_options, */
+ NULL, /* in_cblobs. */
+ &fnum, /* fnum */
+ NULL, /* smb_create_returns */
+ talloc_tos(), /* mem_ctx */
+ NULL); /* out_cblobs */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s with (SEC_FLAG_SYSTEM_SECURITY|"
+ "FILE_WRITE_ATTRIBUTES) failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* Create an SD with a SACL. */
+ sd_sacl = security_descriptor_sacl_create(talloc_tos(),
+ 0,
+ NULL, /* owner. */
+ NULL, /* group. */
+ /* first ACE. */
+ SID_WORLD,
+ SEC_ACE_TYPE_SYSTEM_AUDIT,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_FAILED_ACCESS,
+ NULL);
+
+ if (sd_sacl == NULL) {
+ printf("Out of memory creating SACL\n");
+ goto fail;
+ }
+
+ /*
+ * Write the SACL SD. This should fail
+ * even though we have SEC_FLAG_SYSTEM_SECURITY,
+ * as it seems to also need WRITE_DAC access.
+ */
+ status = cli_set_security_descriptor(cli,
+ fnum,
+ SECINFO_DACL|SECINFO_SACL,
+ sd_sacl);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Writing SACL on file %s got (%s) "
+ "should have failed with ACCESS_DENIED.\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* And close. */
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+
+ /*
+ * Open with SEC_FLAG_SYSTEM_SECURITY|SEC_STD_WRITE_DAC.
+ */
+
+ status = cli_smb2_create_fnum(cli,
+ fname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ SEC_FLAG_SYSTEM_SECURITY|
+ SEC_STD_WRITE_DAC, /* desired access */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_NON_DIRECTORY_FILE, /* create_options, */
+ NULL, /* in_cblobs. */
+ &fnum, /* fnum */
+ NULL, /* smb_create_returns */
+ talloc_tos(), /* mem_ctx */
+ NULL); /* out_cblobs */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s with (SEC_FLAG_SYSTEM_SECURITY|"
+ "FILE_WRITE_ATTRIBUTES) failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Write the SACL SD. This should now succeed
+ * as we have both SEC_FLAG_SYSTEM_SECURITY
+ * and WRITE_DAC access.
+ */
+ status = cli_set_security_descriptor(cli,
+ fnum,
+ SECINFO_DACL|SECINFO_SACL,
+ sd_sacl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_set_security_descriptor SACL "
+ "on file %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* And close. */
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+
+ /* We're done with the sacl we made. */
+ TALLOC_FREE(sd_sacl);
+
+ /*
+ * Now try to open with SEC_FLAG_SYSTEM_SECURITY|READ_ATTRIBUTES.
+ * This gives us access to the SACL.
+ */
+
+ status = cli_smb2_create_fnum(cli,
+ fname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ SEC_FLAG_SYSTEM_SECURITY|
+ FILE_READ_ATTRIBUTES, /* desired access */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_NON_DIRECTORY_FILE, /* create_options, */
+ NULL, /* in_cblobs. */
+ &fnum, /* fnum */
+ NULL, /* smb_create_returns */
+ talloc_tos(), /* mem_ctx */
+ NULL); /* out_cblobs */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s with (SEC_FLAG_SYSTEM_SECURITY|"
+ "FILE_READ_ATTRIBUTES) failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* Try and read the SACL - should succeed. */
+ status = cli_query_security_descriptor(
+ cli, fnum, SECINFO_SACL, talloc_tos(), &sd_sacl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Read SACL from file %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(sd_sacl);
+
+ /*
+ * Try and read the DACL - should fail as we have
+ * no READ_DAC access.
+ */
+ status = cli_query_security_descriptor(
+ cli, fnum, SECINFO_DACL, talloc_tos(), &sd_sacl);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Reading DACL on file %s got (%s) "
+ "should have failed with ACCESS_DENIED.\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (fnum != (uint16_t)-1) {
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ TALLOC_FREE(sd_dacl);
+ TALLOC_FREE(sd_sacl);
+
+ (void)cli_unlink(cli, fname, 0);
+ return true;
+
+ fail:
+
+ TALLOC_FREE(sd_dacl);
+ TALLOC_FREE(sd_sacl);
+
+ if (fnum != (uint16_t)-1) {
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ (void)cli_unlink(cli, fname, 0);
+ return false;
+}
+
+bool run_smb2_quota1(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ SMB_NTQUOTA_STRUCT qt = {0};
+
+ printf("Starting SMB2-QUOTA1\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_smb2_create_fnum(
+ cli,
+ "\\",
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ SEC_GENERIC_READ, /* desired access */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* in_cblobs. */
+ &fnum, /* fnum */
+ NULL, /* smb_create_returns */
+ NULL, /* mem_ctx */
+ NULL); /* out_cblobs */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_smb2_create_fnum failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_smb2_get_user_quota(cli, fnum, &qt);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("cli_smb2_get_user_quota returned %s, expected "
+ "NT_STATUS_INVALID_HANDLE\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_stream_acl(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname = "stream_acl_test_file";
+ const char *sname = "stream_acl_test_file:streamname";
+ struct security_descriptor *sd_dacl = NULL;
+ bool ret = false;
+
+ printf("SMB2 stream acl\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure file doesn't exist. */
+ (void)cli_unlink(cli, fname, 0);
+
+ /* Create the file. */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* Close the handle. */
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+
+ /* Create the stream. */
+ status = cli_ntcreate(cli,
+ sname,
+ 0,
+ FILE_READ_DATA|
+ SEC_STD_READ_CONTROL|
+ SEC_STD_WRITE_DAC,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s failed (%s)\n",
+ sname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* Close the handle. */
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+
+ /*
+ * Open the stream - for Samba this ensures
+ * we prove we have a pathref fsp.
+ */
+ status = cli_ntcreate(cli,
+ sname,
+ 0,
+ FILE_READ_DATA|
+ SEC_STD_READ_CONTROL|
+ SEC_STD_WRITE_DAC,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s failed (%s)\n",
+ sname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* Read the security descriptor off the stream handle. */
+ status = cli_query_security_descriptor(cli,
+ fnum,
+ SECINFO_DACL,
+ talloc_tos(),
+ &sd_dacl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Reading DACL on stream %s got (%s)\n",
+ sname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (sd_dacl == NULL || sd_dacl->dacl == NULL ||
+ sd_dacl->dacl->num_aces < 1) {
+ printf("Invalid DACL returned on stream %s "
+ "(this should not happen)\n",
+ sname);
+ goto fail;
+ }
+
+ /*
+ * Ensure it allows FILE_READ_DATA in the first ace.
+ * It always should.
+ */
+ if ((sd_dacl->dacl->aces[0].access_mask & FILE_READ_DATA) == 0) {
+ printf("DACL->ace[0] returned on stream %s "
+ "doesn't have read access (should not happen)\n",
+ sname);
+ goto fail;
+ }
+
+ /* Remove FILE_READ_DATA from the first ace and set. */
+ sd_dacl->dacl->aces[0].access_mask &= ~FILE_READ_DATA;
+
+ status = cli_set_security_descriptor(cli,
+ fnum,
+ SECINFO_DACL,
+ sd_dacl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Setting DACL on stream %s got (%s)\n",
+ sname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(sd_dacl);
+
+ /* Read again and check it changed. */
+ status = cli_query_security_descriptor(cli,
+ fnum,
+ SECINFO_DACL,
+ talloc_tos(),
+ &sd_dacl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Reading DACL on stream %s got (%s)\n",
+ sname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (sd_dacl == NULL || sd_dacl->dacl == NULL ||
+ sd_dacl->dacl->num_aces < 1) {
+ printf("Invalid DACL (1) returned on stream %s "
+ "(this should not happen)\n",
+ sname);
+ goto fail;
+ }
+
+ /* FILE_READ_DATA should be gone from the first ace. */
+ if ((sd_dacl->dacl->aces[0].access_mask & FILE_READ_DATA) != 0) {
+ printf("DACL on stream %s did not change\n",
+ sname);
+ goto fail;
+ }
+
+ ret = true;
+
+ fail:
+
+ if (fnum != (uint16_t)-1) {
+ cli_smb2_close_fnum(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ (void)cli_unlink(cli, fname, 0);
+ return ret;
+}
+
+static NTSTATUS list_fn(struct file_info *finfo,
+ const char *name,
+ void *state)
+{
+ bool *matched = (bool *)state;
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ *matched = true;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Must be run against a share with "smbd async dosmode = yes".
+ * Checks we can return DOS attriutes other than "N".
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14758
+ */
+
+bool run_list_dir_async_test(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ const char *dname = "ASYNC_DIR";
+ bool ret = false;
+ bool matched = false;
+
+ printf("SMB2 list dir async\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure directory doesn't exist. */
+ (void)cli_rmdir(cli, dname);
+
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_mkdir %s returned %s\n", dname, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_list(cli,
+ dname,
+ FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY,
+ list_fn,
+ &matched);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list %s returned %s\n", dname, nt_errstr(status));
+ goto fail;
+ }
+
+ if (!matched) {
+ printf("Failed to find %s\n", dname);
+ goto fail;
+ }
+
+ ret = true;
+
+ fail:
+
+ (void)cli_rmdir(cli, dname);
+ return ret;
+}
+
+/*
+ * Test delete a directory fails if a file is created
+ * in a directory after the delete on close is set.
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14892
+ */
+
+bool run_delete_on_close_non_empty(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ const char *dname = "DEL_ON_CLOSE_DIR";
+ const char *fname = "DEL_ON_CLOSE_DIR\\testfile";
+ uint16_t fnum = (uint16_t)-1;
+ uint16_t fnum1 = (uint16_t)-1;
+ bool ret = false;
+
+ printf("SMB2 delete on close nonempty\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure directory doesn't exist. */
+ (void)cli_unlink(cli,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ (void)cli_rmdir(cli, dname);
+
+ /* Create target directory. */
+ status = cli_ntcreate(cli,
+ dname,
+ 0,
+ DELETE_ACCESS|FILE_READ_DATA,
+ FILE_ATTRIBUTE_DIRECTORY,
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate for directory %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now set the delete on close bit. */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_cli_nt_delete_on_close set for directory "
+ "%s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Create file inside target directory. */
+ /*
+ * NB. On Windows this will return NT_STATUS_DELETE_PENDING. Only on
+ * Samba will this succeed by default (the option "check parent
+ * directory delete on close" configures behaviour), but we're using
+ * this to test a race condition.
+ */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum1,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate for file %s returned %s\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+ cli_close(cli, fnum1);
+ fnum1 = (uint16_t)-1;
+
+ /* Now the close should fail. */
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_DIRECTORY_NOT_EMPTY)) {
+ printf("cli_close for directory %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ ret = true;
+
+ out:
+
+ if (fnum1 != (uint16_t)-1) {
+ cli_close(cli, fnum1);
+ }
+ if (fnum != (uint16_t)-1) {
+ cli_nt_delete_on_close(cli, fnum, 0);
+ cli_close(cli, fnum);
+ }
+ (void)cli_unlink(cli,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ (void)cli_rmdir(cli, dname);
+ return ret;
+}
+
+static NTSTATUS check_empty_fn(struct file_info *finfo,
+ const char *mask,
+ void *private_data)
+{
+ unsigned int *pcount = (unsigned int *)private_data;
+
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ (*pcount)++;
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_DIRECTORY_NOT_EMPTY;
+}
+
+/*
+ * Test setting the delete on close bit on a directory
+ * containing an unwritable file fails or succeeds
+ * an a share set with "hide unwritable = yes"
+ * depending on the setting of "delete veto files".
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15023
+ *
+ * First version. With "delete veto files = yes"
+ * setting the delete on close should succeed.
+ */
+
+bool run_delete_on_close_nonwrite_delete_yes_test(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ const char *dname = "delete_veto_yes";
+ const char *list_dname = "delete_veto_yes\\*";
+ uint16_t fnum = (uint16_t)-1;
+ bool ret = false;
+ unsigned int list_count = 0;
+
+ printf("SMB2 delete on close nonwrite - delete veto yes\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure target directory is seen as empty. */
+ status = cli_list(cli,
+ list_dname,
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM,
+ check_empty_fn,
+ &list_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list of %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ return false;
+ }
+ if (list_count != 2) {
+ printf("cli_list of %s returned a count of %u\n",
+ dname,
+ list_count);
+ return false;
+ }
+
+ /* Open target directory. */
+ status = cli_ntcreate(cli,
+ dname,
+ 0,
+ DELETE_ACCESS|FILE_READ_DATA,
+ FILE_ATTRIBUTE_DIRECTORY,
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate for directory %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now set the delete on close bit. */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_cli_nt_delete_on_close set for directory "
+ "%s returned %s (should have succeeded)\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ ret = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ (void)cli_nt_delete_on_close(cli, fnum, 0);
+ (void)cli_close(cli, fnum);
+ }
+ return ret;
+}
+
+/*
+ * Test setting the delete on close bit on a directory
+ * containing an unwritable file fails or succeeds
+ * an a share set with "hide unwritable = yes"
+ * depending on the setting of "delete veto files".
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15023
+ *
+ * Second version. With "delete veto files = no"
+ * setting the delete on close should fail.
+ */
+
+bool run_delete_on_close_nonwrite_delete_no_test(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ const char *dname = "delete_veto_no";
+ const char *list_dname = "delete_veto_no\\*";
+ uint16_t fnum = (uint16_t)-1;
+ bool ret = false;
+ unsigned int list_count = 0;
+
+ printf("SMB2 delete on close nonwrite - delete veto yes\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure target directory is seen as empty. */
+ status = cli_list(cli,
+ list_dname,
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM,
+ check_empty_fn,
+ &list_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list of %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ return false;
+ }
+ if (list_count != 2) {
+ printf("cli_list of %s returned a count of %u\n",
+ dname,
+ list_count);
+ return false;
+ }
+
+ /* Open target directory. */
+ status = cli_ntcreate(cli,
+ dname,
+ 0,
+ DELETE_ACCESS|FILE_READ_DATA,
+ FILE_ATTRIBUTE_DIRECTORY,
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate for directory %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now set the delete on close bit. */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("cli_cli_nt_delete_on_close set for directory "
+ "%s returned NT_STATUS_OK "
+ "(should have failed)\n",
+ dname);
+ goto out;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_DIRECTORY_NOT_EMPTY)) {
+ printf("cli_cli_nt_delete_on_close set for directory "
+ "%s returned %s "
+ "(should have returned "
+ "NT_STATUS_DIRECTORY_NOT_EMPTY)\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ ret = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ (void)cli_nt_delete_on_close(cli, fnum, 0);
+ (void)cli_close(cli, fnum);
+ }
+ return ret;
+}
+
+/*
+ * Open an SMB2 file readonly and return the inode number.
+ */
+static NTSTATUS get_smb2_inode(struct cli_state *cli,
+ const char *pathname,
+ uint64_t *ino_ret)
+{
+ NTSTATUS status;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ DATA_BLOB outbuf = data_blob_null;
+ /*
+ * Open the file.
+ */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ pathname,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Get the inode.
+ */
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE,
+ (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
+ 1024, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ fid_persistent,
+ fid_volatile,
+ talloc_tos(),
+ &outbuf);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *ino_ret = PULL_LE_U64(outbuf.data, 0x40);
+ }
+
+ (void)smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ fid_persistent,
+ fid_volatile);
+ return status;
+}
+
+/*
+ * Check an inode matches a given SMB2 path.
+ */
+static bool smb2_inode_matches(struct cli_state *cli,
+ const char *match_pathname,
+ uint64_t ino_tomatch,
+ const char *test_pathname)
+{
+ uint64_t test_ino = 0;
+ NTSTATUS status;
+
+ status = get_smb2_inode(cli,
+ test_pathname,
+ &test_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s: Failed to get ino "
+ "number for %s, (%s)\n",
+ __func__,
+ test_pathname,
+ nt_errstr(status));
+ return false;
+ }
+ if (test_ino != ino_tomatch) {
+ printf("%s: Inode mismatch, ino_tomatch (%s) "
+ "ino=%"PRIu64" test (%s) "
+ "ino=%"PRIu64"\n",
+ __func__,
+ match_pathname,
+ ino_tomatch,
+ test_pathname,
+ test_ino);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Delete an SMB2 file on a DFS share.
+ */
+static NTSTATUS smb2_dfs_delete(struct cli_state *cli,
+ const char *pathname)
+{
+ NTSTATUS status;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ uint8_t data[1];
+ DATA_BLOB inbuf;
+
+ /*
+ * Open the file.
+ */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ pathname,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Set delete on close.
+ */
+ PUSH_LE_U8(&data[0], 0, 1);
+ inbuf.data = &data[0];
+ inbuf.length = 1;
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE, /* info_type. */
+ SMB_FILE_DISPOSITION_INFORMATION - 1000, /* info_class */
+ &inbuf,
+ 0, /* additional_info. */
+ fid_persistent,
+ fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ fid_persistent,
+ fid_volatile);
+ return status;
+}
+
+/*
+ * Rename or hardlink an SMB2 file on a DFS share.
+ */
+static NTSTATUS smb2_dfs_setinfo_name(struct cli_state *cli,
+ uint64_t fid_persistent,
+ uint64_t fid_volatile,
+ const char *newname,
+ bool do_rename)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ size_t inbuf_size;
+ uint8_t info_class = 0;
+ bool ok;
+
+ ok = push_ucs2_talloc(talloc_tos(),
+ &converted_str,
+ newname,
+ &converted_size_bytes);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ /*
+ * W2K8 insists the dest name is not null terminated. Remove
+ * the last 2 zero bytes and reduce the name length.
+ */
+ if (converted_size_bytes < 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ converted_size_bytes -= 2;
+ inbuf_size = 20 + converted_size_bytes;
+ if (inbuf_size < 20) {
+ /* Integer wrap check. */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * The Windows 10 SMB2 server has a minimum length
+ * for a SMB2_FILE_RENAME_INFORMATION buffer of
+ * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
+ * if the length is less.
+ */
+ inbuf_size = MAX(inbuf_size, 24);
+ inbuf = data_blob_talloc_zero(talloc_tos(), inbuf_size);
+ if (inbuf.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ PUSH_LE_U32(inbuf.data, 16, converted_size_bytes);
+ memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
+ TALLOC_FREE(converted_str);
+
+ if (do_rename == true) {
+ info_class = SMB_FILE_RENAME_INFORMATION - 1000;
+ } else {
+ /* Hardlink. */
+ info_class = SMB_FILE_LINK_INFORMATION - 1000;
+ }
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_0_INFO_FILE, /* info_type. */
+ info_class, /* info_class */
+ &inbuf,
+ 0, /* additional_info. */
+ fid_persistent,
+ fid_volatile);
+ return status;
+}
+
+static NTSTATUS smb2_dfs_rename(struct cli_state *cli,
+ uint64_t fid_persistent,
+ uint64_t fid_volatile,
+ const char *newname)
+{
+ return smb2_dfs_setinfo_name(cli,
+ fid_persistent,
+ fid_volatile,
+ newname,
+ true); /* do_rename */
+}
+
+static NTSTATUS smb2_dfs_hlink(struct cli_state *cli,
+ uint64_t fid_persistent,
+ uint64_t fid_volatile,
+ const char *newname)
+{
+ return smb2_dfs_setinfo_name(cli,
+ fid_persistent,
+ fid_volatile,
+ newname,
+ false); /* do_rename */
+}
+
+/*
+ * According to:
+
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/dc9978d7-6299-4c5a-a22d-a039cdc716ea
+ *
+ * (Characters " \ / [ ] : | < > + = ; , * ?,
+ * and control characters in range 0x00 through
+ * 0x1F, inclusive, are illegal in a share name)
+ *
+ * But Windows server only checks in DFS sharenames ':'. All other
+ * share names are allowed.
+ */
+
+static bool test_smb2_dfs_sharenames(struct cli_state *cli,
+ const char *dfs_root_share_name,
+ uint64_t root_ino)
+{
+ char test_path[9];
+ const char *test_str = "/[]:|<>+=;,*?";
+ const char *p;
+ unsigned int i;
+ bool ino_matched = false;
+
+ /* Setup template pathname. */
+ memcpy(test_path, "SERVER\\X", 9);
+
+ /* Test invalid control characters. */
+ for (i = 1; i < 0x20; i++) {
+ test_path[7] = i;
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ test_path);
+ if (!ino_matched) {
+ return false;
+ }
+ }
+
+ /* Test explicit invalid characters. */
+ for (p = test_str; *p != '\0'; p++) {
+ test_path[7] = *p;
+ if (*p == ':') {
+ /*
+ * Only ':' is treated as an INVALID sharename
+ * for a DFS SERVER\\SHARE path.
+ */
+ uint64_t test_ino = 0;
+ NTSTATUS status = get_smb2_inode(cli,
+ test_path,
+ &test_ino);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ printf("%s:%d Open of %s should get "
+ "NT_STATUS_OBJECT_NAME_INVALID, got %s\n",
+ __FILE__,
+ __LINE__,
+ test_path,
+ nt_errstr(status));
+ return false;
+ }
+ } else {
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ test_path);
+ if (!ino_matched) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * "Raw" test of SMB2 paths to a DFS share.
+ * We must use the lower level smb2cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb2_dfs_paths(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ char *dfs_root_share_name = NULL;
+ uint64_t root_ino = 0;
+ uint64_t test_ino = 0;
+ bool ino_matched = false;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ bool retval = false;
+ bool ok = false;
+
+ printf("Starting SMB2-DFS-PATHS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+ /*
+ * Create the "official" DFS share root name.
+ * No SMB2 paths can start with '\\'.
+ */
+ dfs_root_share_name = talloc_asprintf(talloc_tos(),
+ "%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_root_share_name == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /* Get the share root inode number. */
+ status = get_smb2_inode(cli,
+ dfs_root_share_name,
+ &root_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get ino number for share root %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ dfs_root_share_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Test the Windows algorithm for parsing DFS names.
+ */
+ /*
+ * A single "SERVER" element should open and match the share root.
+ */
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ smbXcli_conn_remote_name(cli->conn));
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+
+ /*
+ * An "" DFS empty server name should open and match the share root on
+ * Windows 2008. Windows 2022 returns NT_STATUS_INVALID_PARAMETER
+ * for a DFS empty server name.
+ */
+ status = get_smb2_inode(cli,
+ "",
+ &test_ino);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * Windows 2008 - open succeeded. Proceed to
+ * check ino number.
+ */
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ "");
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ "");
+ return false;
+ }
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ /*
+ * For Windows 2022 we expect to fail with
+ * NT_STATUS_INVALID_PARAMETER. Anything else is
+ * unexpected.
+ */
+ printf("%s:%d Unexpected error (%s) getting ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ nt_errstr(status),
+ "");
+ return false;
+ }
+ /* A "BAD" server name should open and match the share root. */
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ "BAD");
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD");
+ return false;
+ }
+ /*
+ * A "BAD\\BAD" server and share name should open
+ * and match the share root.
+ */
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ "BAD\\BAD");
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD");
+ return false;
+ }
+ /*
+ * Trying to open "BAD\\BAD\\BAD" should get
+ * NT_STATUS_OBJECT_NAME_NOT_FOUND.
+ */
+ status = get_smb2_inode(cli,
+ "BAD\\BAD\\BAD",
+ &test_ino);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\BAD",
+ nt_errstr(status));
+ return false;
+ }
+ /*
+ * Trying to open "BAD\\BAD\\BAD\\BAD" should get
+ * NT_STATUS_OBJECT_PATH_NOT_FOUND.
+ */
+ status = get_smb2_inode(cli,
+ "BAD\\BAD\\BAD\\BAD",
+ &test_ino);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\BAD\\BAD",
+ nt_errstr(status));
+ return false;
+ }
+ /*
+ * Test for invalid pathname characters in the servername.
+ * They are ignored, and it still opens the share root.
+ */
+ ino_matched = smb2_inode_matches(cli,
+ dfs_root_share_name,
+ root_ino,
+ "::::");
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ "::::");
+ return false;
+ }
+
+ /*
+ * Test for invalid pathname characters in the sharename.
+ * Invalid sharename characters should still be flagged as
+ * NT_STATUS_OBJECT_NAME_INVALID. It turns out only ':'
+ * is considered an invalid sharename character.
+ */
+ ok = test_smb2_dfs_sharenames(cli,
+ dfs_root_share_name,
+ root_ino);
+ if (!ok) {
+ return false;
+ }
+
+ /* Now create a file called "file". */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "BAD\\BAD\\file",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb2cli_create on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Trying to open "BAD\\BAD\\file" should now get
+ * a valid inode.
+ */
+ status = get_smb2_inode(cli,
+ "BAD\\BAD\\file",
+ &test_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Open of %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Now show that renames use relative,
+ * not full DFS paths.
+ */
+
+ /* Full DFS path should fail. */
+ status = smb2_dfs_rename(cli,
+ fid_persistent,
+ fid_volatile,
+ "ANY\\NAME\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("%s:%d Rename of %s -> %s should fail "
+ "with NT_STATUS_OBJECT_PATH_NOT_FOUND. Got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ "ANY\\NAME\\renamed_file",
+ nt_errstr(status));
+ goto err;
+ }
+ /* Relative DFS path should succeed. */
+ status = smb2_dfs_rename(cli,
+ fid_persistent,
+ fid_volatile,
+ "renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d: Rename of %s -> %s should succeed. "
+ "Got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ "renamed_file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Trying to open "BAD\\BAD\\renamed_file" should now get
+ * a valid inode.
+ */
+ status = get_smb2_inode(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d: Open of %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Now show that hard links use relative,
+ * not full DFS paths.
+ */
+
+ /* Full DFS path should fail. */
+ status = smb2_dfs_hlink(cli,
+ fid_persistent,
+ fid_volatile,
+ "ANY\\NAME\\hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("%s:%d Hlink of %s -> %s should fail "
+ "with NT_STATUS_OBJECT_PATH_NOT_FOUND. Got %s\n",
+ __FILE__,
+ __LINE__,
+ "ANY\\NAME\\renamed_file",
+ "ANY\\NAME\\hlink",
+ nt_errstr(status));
+ goto err;
+ }
+ /* Relative DFS path should succeed. */
+ status = smb2_dfs_hlink(cli,
+ fid_persistent,
+ fid_volatile,
+ "hlink");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d: Hlink of %s -> %s should succeed. "
+ "Got %s\n",
+ __FILE__,
+ __LINE__,
+ "ANY\\NAME\\renamed_file",
+ "hlink",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Trying to open "BAD\\BAD\\hlink" should now get
+ * a valid inode.
+ */
+ status = get_smb2_inode(cli,
+ "BAD\\BAD\\hlink",
+ &test_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Open of %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ if (fid_persistent != 0 || fid_volatile != 0) {
+ smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0, /* flags */
+ fid_persistent,
+ fid_volatile);
+ }
+ /* Delete anything we made. */
+ (void)smb2_dfs_delete(cli, "BAD\\BAD\\BAD");
+ (void)smb2_dfs_delete(cli, "BAD\\BAD\\file");
+ (void)smb2_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ (void)smb2_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+/*
+ * Add a test that sends DFS paths and sets the
+ * SMB2 flag FLAGS2_DFS_PATHNAMES, but to a non-DFS
+ * share. Windows passes this (it just treats the
+ * pathnames as non-DFS and ignores the FLAGS2_DFS_PATHNAMES
+ * bit).
+ */
+
+bool run_smb2_non_dfs_share(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ bool retval = false;
+ char *dfs_filename = NULL;
+
+ printf("Starting SMB2-DFS-NON-DFS-SHARE\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ /* Ensure this is *NOT* a DFS share. */
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
+ if (dfs_supported) {
+ printf("Share %s is a DFS share.\n",
+ cli->share);
+ return false;
+ }
+ /*
+ * Force the share to be DFS, as far as the client
+ * is concerned.
+ */
+ smb2cli_tcon_set_values(cli->smb2.tcon,
+ cli->smb2.session,
+ smb2cli_tcon_current_id(cli->smb2.tcon),
+ 0,
+ smb2cli_tcon_flags(cli->smb2.tcon),
+ smb2cli_tcon_capabilities(cli->smb2.tcon) |
+ SMB2_SHARE_CAP_DFS,
+ 0);
+
+ /* Come up with a "valid" SMB2 DFS name. */
+ dfs_filename = talloc_asprintf(talloc_tos(),
+ "%s\\%s\\file",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_filename == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /* Now try create dfs_filename. */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ dfs_filename,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs */
+ NULL); /* struct symlink_reparse_struct */
+ /*
+ * Should fail with NT_STATUS_OBJECT_PATH_NOT_FOUND, as
+ * even though we set the FLAGS2_DFS_PATHNAMES the server
+ * knows this isn't a DFS share and so treats BAD\\BAD as
+ * part of the filename.
+ */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("%s:%d create of %s should fail "
+ "with NT_STATUS_OBJECT_PATH_NOT_FOUND. Got %s\n",
+ __FILE__,
+ __LINE__,
+ dfs_filename,
+ nt_errstr(status));
+ goto err;
+ }
+ /*
+ * Prove we can still use non-DFS pathnames, even though
+ * we are setting the FLAGS2_DFS_PATHNAMES in the SMB2
+ * request.
+ */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "file",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb2cli_create on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "file",
+ nt_errstr(status));
+ return false;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb2_dfs_delete(cli, dfs_filename);
+ (void)smb2_dfs_delete(cli, "file");
+ return retval;
+}
+
+/*
+ * Add a test that sends a non-DFS path and does not set the
+ * SMB2 flag FLAGS2_DFS_PATHNAMES to a DFS
+ * share. Windows passes this (it just treats the
+ * pathnames as non-DFS).
+ */
+
+bool run_smb2_dfs_share_non_dfs_path(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ bool retval = false;
+ char *dfs_filename = NULL;
+ uint64_t root_ino = (uint64_t)-1;
+ bool ino_matched = false;
+
+ printf("Starting SMB2-DFS-SHARE-NON-DFS-PATH\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
+ if (!dfs_supported) {
+ printf("Share %s is not a DFS share.\n",
+ cli->share);
+ return false;
+ }
+ /* Come up with a "valid" SMB2 DFS name. */
+ dfs_filename = talloc_asprintf(talloc_tos(),
+ "%s\\%s\\file",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_filename == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /* Get the root of the share ino. */
+ status = get_smb2_inode(cli,
+ "SERVER\\SHARE",
+ &root_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d get_smb2_inode on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "SERVER\\SHARE",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /* Create a dfs_filename. */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ dfs_filename,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* psymlink */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb2cli_create on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ dfs_filename,
+ nt_errstr(status));
+ goto err;
+ }
+
+ /* Close the handle we just opened. */
+ smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0, /* flags */
+ fid_persistent,
+ fid_volatile);
+
+ fid_persistent = 0;
+ fid_volatile = 0;
+
+ /*
+ * Force the share to be non-DFS, as far as the client
+ * is concerned.
+ */
+ smb2cli_tcon_set_values(cli->smb2.tcon,
+ cli->smb2.session,
+ smb2cli_tcon_current_id(cli->smb2.tcon),
+ 0,
+ smb2cli_tcon_flags(cli->smb2.tcon),
+ smb2cli_tcon_capabilities(cli->smb2.tcon) &
+ ~SMB2_SHARE_CAP_DFS,
+ 0);
+
+ /*
+ * Prove we can still use non-DFS pathnames on a DFS
+ * share so long as we don't set the FLAGS2_DFS_PATHNAMES
+ * in the SMB2 request.
+ */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "file",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* psymlink */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb2cli_create on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Show that now we're using non-DFS pathnames
+ * on a DFS share, "" opens the root of the share.
+ */
+ ino_matched = smb2_inode_matches(cli,
+ "SERVER\\SHARE",
+ root_ino,
+ "");
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ "");
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ if (fid_volatile != 0) {
+ smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0, /* flags */
+ fid_persistent,
+ fid_volatile);
+ }
+ (void)smb2_dfs_delete(cli, "file");
+ (void)smb2_dfs_delete(cli, dfs_filename);
+ return retval;
+}
+
+/*
+ * "Raw" test of an SMB2 filename with one or more leading
+ * backslash characters to a DFS share.
+ *
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15277
+ *
+ * Once the server passes SMB2-DFS-PATHS we can
+ * fold this test into that one.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb2_dfs_filename_leading_backslash(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ char *dfs_filename_slash = NULL;
+ char *dfs_filename_slash_multi = NULL;
+ uint64_t file_ino = 0;
+ bool ino_matched = false;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ bool retval = false;
+
+ printf("Starting SMB2-DFS-FILENAME-LEADING-BACKSLASH\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ /*
+ * Create the filename with one leading backslash.
+ */
+ dfs_filename_slash = talloc_asprintf(talloc_tos(),
+ "\\%s\\%s\\file",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_filename_slash == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /*
+ * Create the filename with many leading backslashes.
+ */
+ dfs_filename_slash_multi = talloc_asprintf(talloc_tos(),
+ "\\\\\\\\%s\\%s\\file",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_filename_slash_multi == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /*
+ * Trying to open "\\server\\share\\file" should get
+ * NT_STATUS_OBJECT_NAME_NOT_FOUND.
+ */
+ status = get_smb2_inode(cli,
+ dfs_filename_slash,
+ &file_ino);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ dfs_filename_slash,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Now create a file called "\\server\\share\\file". */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ dfs_filename_slash,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb2cli_create on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ dfs_filename_slash,
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Trying to open "\\server\\share\\file" should now get
+ * a valid inode.
+ */
+ status = get_smb2_inode(cli,
+ dfs_filename_slash,
+ &file_ino);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Open of %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ dfs_filename_slash,
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Trying to open "\\\\\\server\\share\\file" should now get
+ * a valid inode that matches. MacOSX-style of DFS name test.
+ */
+ ino_matched = smb2_inode_matches(cli,
+ dfs_filename_slash,
+ file_ino,
+ dfs_filename_slash_multi);
+ if (!ino_matched) {
+ printf("%s:%d Failed to match ino number for %s\n",
+ __FILE__,
+ __LINE__,
+ dfs_filename_slash_multi);
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ if (fid_persistent != 0 || fid_volatile != 0) {
+ smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0, /* flags */
+ fid_persistent,
+ fid_volatile);
+ }
+ /* Delete anything we made. */
+ (void)smb2_dfs_delete(cli, dfs_filename_slash);
+ return retval;
+}
+
+/*
+ * Ensure a named pipe async read followed by a disconnect
+ * doesn't crash the server (server crash checked for in
+ * containing test script:
+ * source3/script/tests/test_smbtorture_nocrash_s3.sh)
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15423
+ */
+
+bool run_smb2_pipe_read_async_disconnect(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ bool retval = false;
+
+ printf("Starting SMB2-PIPE-READ-ASYNC-DISCONNECT\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect_creds(cli, "IPC$", "IPC", torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect to IPC$ returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Open the SAMR pipe. */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "SAMR",
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_FILE_READ_DATA|
+ SEC_FILE_WRITE_DATA, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* psymlink */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb2cli_create on SAMR returned %s\n",
+ __FILE__,
+ __LINE__,
+ nt_errstr(status));
+ goto err;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto err;
+ }
+
+ /* Start an async read. */
+ req = smb2cli_read_send(talloc_tos(),
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 16*1024,
+ 0, /* offset */
+ fid_persistent,
+ fid_volatile,
+ 0, /* minimum_count */
+ 0); /* remaining_bytes */
+ if (req == NULL) {
+ goto err;
+ }
+
+ /* Force disconnect. */
+ smbXcli_conn_disconnect(cli->conn, NT_STATUS_LOCAL_DISCONNECT);
+ fid_volatile = 0;
+ retval = true;
+
+ err:
+
+ if (fid_volatile != 0) {
+ smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0, /* flags */
+ fid_persistent,
+ fid_volatile);
+ }
+ return retval;
+}
+
+bool run_smb2_invalid_pipename(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ uint64_t fid_persistent = 0;
+ uint64_t fid_volatile = 0;
+ const char *unknown_pipe = "badpipe";
+ const char *invalid_pipe = "../../../../../../../../../badpipe";
+
+ printf("Starting SMB2-INVALID-PIPENAME\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ PROTOCOL_SMB2_02,
+ PROTOCOL_SMB3_11,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, "IPC$", "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Try and connect to an unknown pipename. */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ unknown_pipe,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_FILE_READ_DATA|
+ SEC_FILE_WRITE_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ /* We should get NT_STATUS_OBJECT_NAME_NOT_FOUND */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s:%d smb2cli_create on name %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ unknown_pipe,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try and connect to an invalid pipename containing unix separators. */
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ invalid_pipe,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_FILE_READ_DATA|
+ SEC_FILE_WRITE_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* desired_access, */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_CREATE, /* create_disposition, */
+ 0, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, /* struct smb_create_returns * */
+ talloc_tos(), /* mem_ctx. */
+ NULL, /* struct smb2_create_blobs * */
+ NULL); /* struct symlink_reparse_struct */
+ /*
+ * We should still get NT_STATUS_OBJECT_NAME_NOT_FOUND
+ * (tested against Windows 2022).
+ */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s:%d smb2cli_create on name %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ invalid_pipe,
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
diff --git a/source3/torture/test_smbsock_any_connect.c b/source3/torture/test_smbsock_any_connect.c
new file mode 100644
index 0000000..a964e0f
--- /dev/null
+++ b/source3/torture/test_smbsock_any_connect.c
@@ -0,0 +1,47 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test the smb_any_connect functionality
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+
+bool run_smb_any_connect(int dummy)
+{
+ int fd;
+ NTSTATUS status;
+ struct sockaddr_storage addrs[5];
+ size_t chosen_index;
+ uint16_t port;
+
+ interpret_string_addr(&addrs[0], "192.168.99.5", 0);
+ interpret_string_addr(&addrs[1], "192.168.99.6", 0);
+ interpret_string_addr(&addrs[2], "192.168.99.7", 0);
+ interpret_string_addr(&addrs[3], "192.168.99.8", 0);
+ interpret_string_addr(&addrs[4], "192.168.99.9", 0);
+
+ status = smbsock_any_connect(addrs, NULL, NULL, NULL, NULL,
+ ARRAY_SIZE(addrs), 0, 0,
+ &fd, &chosen_index, &port);
+
+ d_printf("smbsock_any_connect returned %s (fd %d)\n",
+ nt_errstr(status), NT_STATUS_IS_OK(status) ? fd : -1);
+ if (NT_STATUS_IS_OK(status)) {
+ close(fd);
+ }
+ return true;
+}
diff --git a/source3/torture/test_tdb_validate.c b/source3/torture/test_tdb_validate.c
new file mode 100644
index 0000000..4768512
--- /dev/null
+++ b/source3/torture/test_tdb_validate.c
@@ -0,0 +1,68 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "source3/include/includes.h"
+#include <tdb.h>
+#include "source3/torture/proto.h"
+#include "source3/lib/tdb_validate.h"
+
+static int validate_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA value,
+ void *private_data)
+{
+ struct tdb_validation_status *state = private_data;
+ state->success = false;
+ printf("validate_fn called\n");
+ return -1;
+}
+
+bool run_tdb_validate(int dummy)
+{
+ const char tdb_name[] = "tdb_validate.tdb";
+ bool result = false;
+ struct tdb_context *tdb = NULL;
+ char buf[] = "data";
+ TDB_DATA data = { .dptr = (uint8_t *)buf, .dsize = sizeof(buf), };
+ int ret;
+
+ unlink(tdb_name);
+
+ tdb = tdb_open(tdb_name, 0, 0, O_CREAT|O_EXCL|O_RDWR, 0600);
+ if (tdb == NULL) {
+ perror("Could not open tdb");
+ goto done;
+ }
+
+ ret = tdb_store(tdb, data, data, 0);
+ if (ret == -1) {
+ perror("tdb_store failed");
+ goto done;
+ }
+
+ ret = tdb_validate(tdb, validate_fn);
+ if (ret == 0) {
+ fprintf(stderr,
+ "tdb_validate succeeded where it should have "
+ "failed\n");
+ goto done;
+ }
+
+ result = true;
+done:
+ tdb_close(tdb);
+ unlink(tdb_name);
+ return result;
+}
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
new file mode 100644
index 0000000..86a6e92
--- /dev/null
+++ b/source3/torture/torture.c
@@ -0,0 +1,16512 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-1998
+ Copyright (C) Jeremy Allison 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/shmem.h"
+#include "libsmb/namequery.h"
+#include "wbc_async.h"
+#include "torture/proto.h"
+#include "libcli/security/security.h"
+#include "tldap.h"
+#include "tldap_util.h"
+#include "tldap_gensec_bind.h"
+#include "../librpc/gen_ndr/svcctl.h"
+#include "../lib/util/memcache.h"
+#include "nsswitch/winbind_client.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "async_smb.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "trans2.h"
+#include "libsmb/nmblib.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "util_tdb.h"
+#include "../libcli/smb/read_smb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/util/sys_rw_data.h"
+#include "lib/util/base64.h"
+#include "lib/util/time.h"
+#include "lib/gencache.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/asn1.h"
+#include "lib/param/param.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/string_wrappers.h"
+#include "source3/lib/substitute.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+extern char *optarg;
+extern int optind;
+
+fstring host, workgroup, share, password, username, myname;
+struct cli_credentials *torture_creds;
+static const char *sockops="TCP_NODELAY";
+int torture_nprocs=1;
+static int port_to_use=0;
+int torture_numops=100;
+int torture_blocksize=1024*1024;
+static int procnum; /* records process count number when forking */
+static struct cli_state *current_cli;
+static fstring randomfname;
+static bool use_oplocks;
+static bool use_level_II_oplocks;
+static const char *client_txt = "client_oplocks.txt";
+static bool disable_spnego;
+static bool use_kerberos;
+static bool force_dos_errors;
+static fstring multishare_conn_fname;
+static bool use_multishare_conn = False;
+static bool do_encrypt;
+static const char *local_path = NULL;
+static enum smb_signing_setting signing_state = SMB_SIGNING_DEFAULT;
+char *test_filename;
+
+bool torture_showall = False;
+
+static double create_procs(bool (*fn)(int), bool *result);
+
+/********************************************************************
+ Ensure a connection is encrypted.
+********************************************************************/
+
+static bool force_cli_encryption(struct cli_state *c,
+ const char *sharename)
+{
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+ NTSTATUS status;
+
+ if (!SERVER_HAS_UNIX_CIFS(c)) {
+ d_printf("Encryption required and "
+ "server that doesn't support "
+ "UNIX extensions - failing connect\n");
+ return false;
+ }
+
+ status = cli_unix_extensions_version(c, &major, &minor, &caplow,
+ &caphigh);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "can't get UNIX CIFS extensions "
+ "version from server: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
+ d_printf("Encryption required and "
+ "share %s doesn't support "
+ "encryption.\n", sharename);
+ return false;
+ }
+
+ status = cli_smb1_setup_encryption(c, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "setup failed with error %s.\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static struct cli_state *open_nbt_connection(void)
+{
+ struct cli_state *c;
+ NTSTATUS status;
+ int flags = 0;
+
+ if (disable_spnego) {
+ flags |= CLI_FULL_CONNECTION_DONT_SPNEGO;
+ }
+
+ if (use_oplocks) {
+ flags |= CLI_FULL_CONNECTION_OPLOCKS;
+ }
+
+ if (use_level_II_oplocks) {
+ flags |= CLI_FULL_CONNECTION_LEVEL_II_OPLOCKS;
+ }
+
+ if (force_dos_errors) {
+ flags |= CLI_FULL_CONNECTION_FORCE_DOS_ERRORS;
+ }
+
+ status = cli_connect_nb(host, NULL, port_to_use, 0x20, myname,
+ signing_state, flags, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect with %s. Error %s\n", host, nt_errstr(status) );
+ return NULL;
+ }
+
+ cli_set_timeout(c, 120000); /* set a really long timeout (2 minutes) */
+
+ return c;
+}
+
+/****************************************************************************
+ Send a corrupt session request. See rfc1002.txt 4.3 and 4.3.2.
+****************************************************************************/
+
+static bool cli_bad_session_request(int fd,
+ struct nmb_name *calling, struct nmb_name *called)
+{
+ TALLOC_CTX *frame;
+ uint8_t len_buf[4];
+ struct iovec iov[3];
+ ssize_t len;
+ uint8_t *inbuf;
+ int err;
+ bool ret = false;
+ uint8_t message_type;
+ uint8_t error;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+
+ frame = talloc_stackframe();
+
+ iov[0].iov_base = len_buf;
+ iov[0].iov_len = sizeof(len_buf);
+
+ /* put in the destination name */
+
+ iov[1].iov_base = name_mangle(talloc_tos(), called->name,
+ called->name_type);
+ if (iov[1].iov_base == NULL) {
+ goto fail;
+ }
+ iov[1].iov_len = name_len((unsigned char *)iov[1].iov_base,
+ talloc_get_size(iov[1].iov_base));
+
+ /* and my name */
+
+ iov[2].iov_base = name_mangle(talloc_tos(), calling->name,
+ calling->name_type);
+ if (iov[2].iov_base == NULL) {
+ goto fail;
+ }
+ iov[2].iov_len = name_len((unsigned char *)iov[2].iov_base,
+ talloc_get_size(iov[2].iov_base));
+
+ /* Deliberately corrupt the name len (first byte) */
+ *((uint8_t *)iov[2].iov_base) = 100;
+
+ /* send a session request (RFC 1002) */
+ /* setup the packet length
+ * Remove four bytes from the length count, since the length
+ * field in the NBT Session Service header counts the number
+ * of bytes which follow. The cli_send_smb() function knows
+ * about this and accounts for those four bytes.
+ * CRH.
+ */
+
+ _smb_setlen(len_buf, iov[1].iov_len + iov[2].iov_len);
+ SCVAL(len_buf,0,0x81);
+
+ len = write_data_iov(fd, iov, 3);
+ if (len == -1) {
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = read_smb_send(frame, ev, fd);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ goto fail;
+ }
+ len = read_smb_recv(req, talloc_tos(), &inbuf, &err);
+ if (len == -1) {
+ errno = err;
+ goto fail;
+ }
+ TALLOC_FREE(ev);
+
+ message_type = CVAL(inbuf, 0);
+ if (message_type != 0x83) {
+ d_fprintf(stderr, "Expected msg type 0x83, got 0x%2.2x\n",
+ message_type);
+ goto fail;
+ }
+
+ if (smb_len(inbuf) != 1) {
+ d_fprintf(stderr, "Expected smb_len 1, got %d\n",
+ (int)smb_len(inbuf));
+ goto fail;
+ }
+
+ error = CVAL(inbuf, 4);
+ if (error != 0x82) {
+ d_fprintf(stderr, "Expected error 0x82, got %d\n",
+ (int)error);
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/* Insert a NULL at the first separator of the given path and return a pointer
+ * to the remainder of the string.
+ */
+static char *
+terminate_path_at_separator(char * path)
+{
+ char * p;
+
+ if (!path) {
+ return NULL;
+ }
+
+ if ((p = strchr_m(path, '/'))) {
+ *p = '\0';
+ return p + 1;
+ }
+
+ if ((p = strchr_m(path, '\\'))) {
+ *p = '\0';
+ return p + 1;
+ }
+
+ /* No separator. */
+ return NULL;
+}
+
+/*
+ parse a //server/share type UNC name
+*/
+bool smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
+ char **hostname, char **sharename)
+{
+ char *p;
+
+ *hostname = *sharename = NULL;
+
+ if (strncmp(unc_name, "\\\\", 2) &&
+ strncmp(unc_name, "//", 2)) {
+ return False;
+ }
+
+ *hostname = talloc_strdup(mem_ctx, &unc_name[2]);
+ p = terminate_path_at_separator(*hostname);
+
+ if (p && *p) {
+ *sharename = talloc_strdup(mem_ctx, p);
+ terminate_path_at_separator(*sharename);
+ }
+
+ if (*hostname && *sharename) {
+ return True;
+ }
+
+ TALLOC_FREE(*hostname);
+ TALLOC_FREE(*sharename);
+ return False;
+}
+
+static bool torture_open_connection_share(struct cli_state **c,
+ const char *hostname,
+ const char *sharename,
+ int flags)
+{
+ NTSTATUS status;
+
+ status = cli_full_connection_creds(c,
+ myname,
+ hostname,
+ NULL, /* dest_ss */
+ port_to_use,
+ sharename,
+ "?????",
+ torture_creds,
+ flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to open share connection: //%s/%s port:%d - %s\n",
+ hostname, sharename, port_to_use, nt_errstr(status));
+ return False;
+ }
+
+ cli_set_timeout(*c, 120000); /* set a really long timeout (2 minutes) */
+
+ if (do_encrypt) {
+ return force_cli_encryption(*c,
+ sharename);
+ }
+ return True;
+}
+
+bool torture_open_connection_flags(struct cli_state **c, int conn_index, int flags)
+{
+ char **unc_list = NULL;
+ int num_unc_names = 0;
+ bool result;
+
+ if (use_multishare_conn==True) {
+ char *h, *s;
+ unc_list = file_lines_load(multishare_conn_fname, &num_unc_names, 0, NULL);
+ if (!unc_list || num_unc_names <= 0) {
+ printf("Failed to load unc names list from '%s'\n", multishare_conn_fname);
+ exit(1);
+ }
+
+ if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
+ NULL, &h, &s)) {
+ printf("Failed to parse UNC name %s\n",
+ unc_list[conn_index % num_unc_names]);
+ TALLOC_FREE(unc_list);
+ exit(1);
+ }
+
+ result = torture_open_connection_share(c, h, s, flags);
+
+ /* h, s were copied earlier */
+ TALLOC_FREE(unc_list);
+ return result;
+ }
+
+ return torture_open_connection_share(c, host, share, flags);
+}
+
+bool torture_open_connection(struct cli_state **c, int conn_index)
+{
+ int flags = CLI_FULL_CONNECTION_FORCE_SMB1;
+
+ if (use_oplocks) {
+ flags |= CLI_FULL_CONNECTION_OPLOCKS;
+ }
+ if (use_level_II_oplocks) {
+ flags |= CLI_FULL_CONNECTION_LEVEL_II_OPLOCKS;
+ }
+
+ return torture_open_connection_flags(c, conn_index, flags);
+}
+
+bool torture_init_connection(struct cli_state **pcli)
+{
+ struct cli_state *cli;
+
+ cli = open_nbt_connection();
+ if (cli == NULL) {
+ return false;
+ }
+
+ *pcli = cli;
+ return true;
+}
+
+bool torture_cli_session_setup2(struct cli_state *cli, uint16_t *new_vuid)
+{
+ uint16_t old_vuid = cli_state_get_uid(cli);
+ NTSTATUS status;
+ bool ret;
+
+ cli_state_set_uid(cli, 0);
+ status = cli_session_setup_creds(cli, torture_creds);
+ ret = NT_STATUS_IS_OK(status);
+ *new_vuid = cli_state_get_uid(cli);
+ cli_state_set_uid(cli, old_vuid);
+ return ret;
+}
+
+
+bool torture_close_connection(struct cli_state *c)
+{
+ bool ret = True;
+ NTSTATUS status;
+
+ status = cli_tdis(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("tdis failed (%s)\n", nt_errstr(status));
+ ret = False;
+ }
+
+ cli_shutdown(c);
+
+ return ret;
+}
+
+void torture_conn_set_sockopt(struct cli_state *cli)
+{
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+}
+
+static NTSTATUS torture_delete_fn(struct file_info *finfo,
+ const char *pattern,
+ void *state)
+{
+ NTSTATUS status;
+ char *filename = NULL;
+ char *dirname = NULL;
+ char *p = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct cli_state *cli = (struct cli_state *)state;
+
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ dirname = talloc_strdup(frame, pattern);
+ if (dirname == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ p = strrchr_m(dirname, '\\');
+ if (p != NULL) {
+ /* Remove the terminating '\' */
+ *p = '\0';
+ }
+ if (dirname[0] != '\0') {
+ filename = talloc_asprintf(frame,
+ "%s\\%s",
+ dirname,
+ finfo->name);
+ } else {
+ filename = talloc_asprintf(frame,
+ "%s",
+ finfo->name);
+ }
+ if (filename == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ char *subdirname = talloc_asprintf(frame,
+ "%s\\*",
+ filename);
+ if (subdirname == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_list(cli,
+ subdirname,
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM,
+ torture_delete_fn,
+ cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("torture_delete_fn: cli_list "
+ "of %s failed (%s)\n",
+ subdirname,
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+ status = cli_rmdir(cli, filename);
+ } else {
+ status = cli_unlink(cli,
+ filename,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ printf("torture_delete_fn: cli_rmdir"
+ " of %s failed (%s)\n",
+ filename,
+ nt_errstr(status));
+ } else {
+ printf("torture_delete_fn: cli_unlink"
+ " of %s failed (%s)\n",
+ filename,
+ nt_errstr(status));
+ }
+ }
+ TALLOC_FREE(frame);
+ return status;
+}
+
+void torture_deltree(struct cli_state *cli, const char *dname)
+{
+ char *mask = NULL;
+ NTSTATUS status;
+
+ /* It might be a file */
+ (void)cli_unlink(cli,
+ dname,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+
+ mask = talloc_asprintf(cli,
+ "%s\\*",
+ dname);
+ if (mask == NULL) {
+ printf("torture_deltree: talloc_asprintf failed\n");
+ return;
+ }
+
+ status = cli_list(cli,
+ mask,
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_HIDDEN|
+ FILE_ATTRIBUTE_SYSTEM,
+ torture_delete_fn,
+ cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("torture_deltree: cli_list of %s failed (%s)\n",
+ mask,
+ nt_errstr(status));
+ }
+ TALLOC_FREE(mask);
+ status = cli_rmdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("torture_deltree: cli_rmdir of %s failed (%s)\n",
+ dname,
+ nt_errstr(status));
+ }
+}
+
+/* check if the server produced the expected dos or nt error code */
+static bool check_both_error(int line, NTSTATUS status,
+ uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
+{
+ if (NT_STATUS_IS_DOS(status)) {
+ uint8_t cclass;
+ uint32_t num;
+
+ /* Check DOS error */
+ cclass = NT_STATUS_DOS_CLASS(status);
+ num = NT_STATUS_DOS_CODE(status);
+
+ if (eclass != cclass || ecode != num) {
+ printf("unexpected error code class=%d code=%d\n",
+ (int)cclass, (int)num);
+ printf(" expected %d/%d %s (line=%d)\n",
+ (int)eclass, (int)ecode, nt_errstr(nterr), line);
+ return false;
+ }
+ } else {
+ /* Check NT error */
+ if (!NT_STATUS_EQUAL(nterr, status)) {
+ printf("unexpected error code %s\n",
+ nt_errstr(status));
+ printf(" expected %s (line=%d)\n",
+ nt_errstr(nterr), line);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/* check if the server produced the expected error code */
+static bool check_error(int line, NTSTATUS status,
+ uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
+{
+ if (NT_STATUS_IS_DOS(status)) {
+ uint8_t cclass;
+ uint32_t num;
+
+ /* Check DOS error */
+
+ cclass = NT_STATUS_DOS_CLASS(status);
+ num = NT_STATUS_DOS_CODE(status);
+
+ if (eclass != cclass || ecode != num) {
+ printf("unexpected error code class=%d code=%d\n",
+ (int)cclass, (int)num);
+ printf(" expected %d/%d %s (line=%d)\n",
+ (int)eclass, (int)ecode, nt_errstr(nterr),
+ line);
+ return False;
+ }
+
+ } else {
+ /* Check NT error */
+
+ if (NT_STATUS_V(nterr) != NT_STATUS_V(status)) {
+ printf("unexpected error code %s\n",
+ nt_errstr(status));
+ printf(" expected %s (line=%d)\n", nt_errstr(nterr),
+ line);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+NTSTATUS cli_qpathinfo1(struct cli_state *cli,
+ const char *fname,
+ time_t *change_time,
+ time_t *access_time,
+ time_t *write_time,
+ off_t *size,
+ uint32_t *pattr)
+{
+ int timezone = smb1cli_conn_server_time_zone(cli->conn);
+ time_t (*date_fn)(const void *buf, int serverzone) = NULL;
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata;
+ NTSTATUS status;
+
+ status = cli_qpathinfo(talloc_tos(),
+ cli,
+ fname,
+ SMB_INFO_STANDARD,
+ 22,
+ CLI_BUFFER_SIZE,
+ &rdata,
+ &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (cli->win95) {
+ date_fn = make_unix_date;
+ } else {
+ date_fn = make_unix_date2;
+ }
+
+ if (change_time) {
+ *change_time = date_fn(rdata + 0, timezone);
+ }
+ if (access_time) {
+ *access_time = date_fn(rdata + 4, timezone);
+ }
+ if (write_time) {
+ *write_time = date_fn(rdata + 8, timezone);
+ }
+ if (size) {
+ *size = PULL_LE_U32(rdata, 12);
+ }
+ if (pattr) {
+ *pattr = PULL_LE_U16(rdata, l1_attrFile);
+ }
+ return NT_STATUS_OK;
+}
+
+static bool wait_lock(struct cli_state *c, int fnum, uint32_t offset, uint32_t len)
+{
+ NTSTATUS status;
+
+ status = cli_lock32(c, fnum, offset, len, -1, WRITE_LOCK);
+
+ while (!NT_STATUS_IS_OK(status)) {
+ if (!check_both_error(__LINE__, status, ERRDOS,
+ ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) {
+ return false;
+ }
+
+ status = cli_lock32(c, fnum, offset, len, -1, WRITE_LOCK);
+ }
+
+ return true;
+}
+
+
+static bool rw_torture(struct cli_state *c)
+{
+ const char *lockfname = "\\torture.lck";
+ fstring fname;
+ uint16_t fnum;
+ uint16_t fnum2;
+ pid_t pid2, pid = getpid();
+ int i, j;
+ char buf[1024];
+ bool correct = True;
+ size_t nread = 0;
+ NTSTATUS status;
+
+ memset(buf, '\0', sizeof(buf));
+
+ status = cli_openx(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = cli_openx(c, lockfname, O_RDWR, DENY_NONE, &fnum2);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n",
+ lockfname, nt_errstr(status));
+ return False;
+ }
+
+ for (i=0;i<torture_numops;i++) {
+ unsigned n = (unsigned)sys_random()%10;
+
+ if (i % 10 == 0) {
+ printf("%d\r", i); fflush(stdout);
+ }
+ slprintf(fname, sizeof(fstring) - 1, "\\torture.%u", n);
+
+ if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ return False;
+ }
+
+ status = cli_openx(c, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_ALL, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open failed (%s)\n", nt_errstr(status));
+ correct = False;
+ break;
+ }
+
+ status = cli_writeall(c, fnum, 0, (uint8_t *)&pid, 0,
+ sizeof(pid), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ for (j=0;j<50;j++) {
+ status = cli_writeall(c, fnum, 0, (uint8_t *)buf,
+ sizeof(pid)+(j*sizeof(buf)),
+ sizeof(buf), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n",
+ nt_errstr(status));
+ correct = False;
+ }
+ }
+
+ pid2 = 0;
+
+ status = cli_read(c, fnum, (char *)&pid2, 0, sizeof(pid),
+ &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("read failed (%s)\n", nt_errstr(status));
+ correct = false;
+ } else if (nread != sizeof(pid)) {
+ printf("read/write compare failed: "
+ "recv %ld req %ld\n", (unsigned long)nread,
+ (unsigned long)sizeof(pid));
+ correct = false;
+ }
+
+ if (pid2 != pid) {
+ printf("data corruption!\n");
+ correct = False;
+ }
+
+ status = cli_close(c, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_unlink(c, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_unlock(c, fnum2, n*sizeof(int), sizeof(int));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlock failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+ }
+
+ cli_close(c, fnum2);
+ cli_unlink(c, lockfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("%d\n", i);
+
+ return correct;
+}
+
+static bool run_torture(int dummy)
+{
+ struct cli_state *cli;
+ bool ret;
+
+ cli = current_cli;
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ ret = rw_torture(cli);
+
+ if (!torture_close_connection(cli)) {
+ ret = False;
+ }
+
+ return ret;
+}
+
+static bool rw_torture3(struct cli_state *c, char *lockfname)
+{
+ uint16_t fnum = (uint16_t)-1;
+ unsigned int i = 0;
+ char buf[131072];
+ char buf_rd[131072];
+ unsigned count;
+ unsigned countprev = 0;
+ size_t sent = 0;
+ bool correct = True;
+ NTSTATUS status = NT_STATUS_OK;
+
+ srandom(1);
+ for (i = 0; i < sizeof(buf); i += sizeof(uint32_t))
+ {
+ SIVAL(buf, i, sys_random());
+ }
+
+ if (procnum == 0)
+ {
+ status = cli_unlink(
+ c, lockfname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s) (normal, this file should "
+ "not exist)\n", nt_errstr(status));
+ }
+
+ status = cli_openx(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("first open read/write of %s failed (%s)\n",
+ lockfname, nt_errstr(status));
+ return False;
+ }
+ }
+ else
+ {
+ for (i = 0; i < 500 && fnum == (uint16_t)-1; i++)
+ {
+ status = cli_openx(c, lockfname, O_RDONLY,
+ DENY_NONE, &fnum);
+ if (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ smb_msleep(10);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("second open read-only of %s failed (%s)\n",
+ lockfname, nt_errstr(status));
+ return False;
+ }
+ }
+
+ i = 0;
+ for (count = 0; count < sizeof(buf); count += sent)
+ {
+ if (count >= countprev) {
+ printf("%d %8d\r", i, count);
+ fflush(stdout);
+ i++;
+ countprev += (sizeof(buf) / 20);
+ }
+
+ if (procnum == 0)
+ {
+ sent = ((unsigned)sys_random()%(20))+ 1;
+ if (sent > sizeof(buf) - count)
+ {
+ sent = sizeof(buf) - count;
+ }
+
+ status = cli_writeall(c, fnum, 0, (uint8_t *)buf+count,
+ count, sent, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n",
+ nt_errstr(status));
+ correct = False;
+ }
+ }
+ else
+ {
+ status = cli_read(c, fnum, buf_rd+count, count,
+ sizeof(buf)-count, &sent);
+ if(!NT_STATUS_IS_OK(status)) {
+ printf("read failed offset:%d size:%ld (%s)\n",
+ count, (unsigned long)sizeof(buf)-count,
+ nt_errstr(status));
+ correct = False;
+ sent = 0;
+ } else if (sent > 0) {
+ if (memcmp(buf_rd+count, buf+count, sent) != 0)
+ {
+ printf("read/write compare failed\n");
+ printf("offset: %d req %ld recvd %ld\n", count, (unsigned long)sizeof(buf)-count, (unsigned long)sent);
+ correct = False;
+ break;
+ }
+ }
+ }
+
+ }
+
+ status = cli_close(c, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ return correct;
+}
+
+static bool rw_torture2(struct cli_state *c1, struct cli_state *c2)
+{
+ const char *lockfname = "\\torture2.lck";
+ uint16_t fnum1;
+ uint16_t fnum2;
+ int i;
+ char buf[131072];
+ char buf_rd[131072];
+ bool correct = True;
+ size_t bytes_read;
+ NTSTATUS status;
+
+ status = cli_unlink(c1, lockfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s) (normal, this file should not exist)\n", nt_errstr(status));
+ }
+
+ status = cli_openx(c1, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("first open read/write of %s failed (%s)\n",
+ lockfname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(c2, lockfname, O_RDONLY, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("second open read-only of %s failed (%s)\n",
+ lockfname, nt_errstr(status));
+ cli_close(c1, fnum1);
+ return False;
+ }
+
+ for (i = 0; i < torture_numops; i++)
+ {
+ size_t buf_size = ((unsigned)sys_random()%(sizeof(buf)-1))+ 1;
+ if (i % 10 == 0) {
+ printf("%d\r", i); fflush(stdout);
+ }
+
+ generate_random_buffer((unsigned char *)buf, buf_size);
+
+ status = cli_writeall(c1, fnum1, 0, (uint8_t *)buf, 0,
+ buf_size, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n", nt_errstr(status));
+ correct = False;
+ break;
+ }
+
+ status = cli_read(c2, fnum2, buf_rd, 0, buf_size, &bytes_read);
+ if(!NT_STATUS_IS_OK(status)) {
+ printf("read failed (%s)\n", nt_errstr(status));
+ correct = false;
+ break;
+ } else if (bytes_read != buf_size) {
+ printf("read failed\n");
+ printf("read %ld, expected %ld\n",
+ (unsigned long)bytes_read,
+ (unsigned long)buf_size);
+ correct = False;
+ break;
+ }
+
+ if (memcmp(buf_rd, buf, buf_size) != 0)
+ {
+ printf("read/write compare failed\n");
+ correct = False;
+ break;
+ }
+ }
+
+ status = cli_close(c2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_close(c1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_unlink(c1, lockfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ return correct;
+}
+
+static bool run_readwritetest(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ bool test1, test2 = False;
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("starting readwritetest\n");
+
+ test1 = rw_torture2(cli1, cli2);
+ printf("Passed readwritetest v1: %s\n", BOOLSTR(test1));
+
+ if (test1) {
+ test2 = rw_torture2(cli1, cli1);
+ printf("Passed readwritetest v2: %s\n", BOOLSTR(test2));
+ }
+
+ if (!torture_close_connection(cli1)) {
+ test1 = False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ test2 = False;
+ }
+
+ return (test1 && test2);
+}
+
+static bool run_readwritemulti(int dummy)
+{
+ struct cli_state *cli;
+ bool test;
+
+ cli = current_cli;
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ printf("run_readwritemulti: fname %s\n", randomfname);
+ test = rw_torture3(cli, randomfname);
+
+ if (!torture_close_connection(cli)) {
+ test = False;
+ }
+
+ return test;
+}
+
+static bool run_readwritelarge_internal(void)
+{
+ static struct cli_state *cli1;
+ uint16_t fnum1;
+ const char *lockfname = "\\large.dat";
+ off_t fsize;
+ char buf[126*1024];
+ bool correct = True;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ memset(buf,'\0',sizeof(buf));
+
+ printf("starting readwritelarge_internal\n");
+
+ cli_unlink(cli1, lockfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open read/write of %s failed (%s)\n", lockfname, nt_errstr(status));
+ return False;
+ }
+
+ cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 0, sizeof(buf), NULL);
+
+ status = cli_qfileinfo_basic(cli1, fnum1, NULL, &fsize, NULL, NULL,
+ NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("qfileinfo failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ if (fsize == sizeof(buf))
+ printf("readwritelarge_internal test 1 succeeded (size = %lx)\n",
+ (unsigned long)fsize);
+ else {
+ printf("readwritelarge_internal test 1 failed (size = %lx)\n",
+ (unsigned long)fsize);
+ correct = False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_unlink(cli1, lockfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_openx(cli1, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open read/write of %s failed (%s)\n", lockfname, nt_errstr(status));
+ return False;
+ }
+
+ cli_smbwrite(cli1, fnum1, buf, 0, sizeof(buf), NULL);
+
+ status = cli_qfileinfo_basic(cli1, fnum1, NULL, &fsize, NULL, NULL,
+ NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("qfileinfo failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ if (fsize == sizeof(buf))
+ printf("readwritelarge_internal test 2 succeeded (size = %lx)\n",
+ (unsigned long)fsize);
+ else {
+ printf("readwritelarge_internal test 2 failed (size = %lx)\n",
+ (unsigned long)fsize);
+ correct = False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ return correct;
+}
+
+static bool run_readwritelarge(int dummy)
+{
+ return run_readwritelarge_internal();
+}
+
+static bool run_readwritelarge_signtest(int dummy)
+{
+ bool ret;
+ signing_state = SMB_SIGNING_REQUIRED;
+ ret = run_readwritelarge_internal();
+ signing_state = SMB_SIGNING_DEFAULT;
+ return ret;
+}
+
+int line_count = 0;
+int nbio_id;
+
+#define ival(s) strtol(s, NULL, 0)
+
+/* run a test that simulates an approximate netbench client load */
+static bool run_netbench(int client)
+{
+ struct cli_state *cli;
+ int i;
+ char line[1024];
+ char cname[20];
+ FILE *f;
+ const char *params[20];
+ bool correct = True;
+
+ cli = current_cli;
+
+ nbio_id = client;
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ nb_setup(cli);
+
+ slprintf(cname,sizeof(cname)-1, "client%d", client);
+
+ f = fopen(client_txt, "r");
+
+ if (!f) {
+ perror(client_txt);
+ return False;
+ }
+
+ while (fgets(line, sizeof(line)-1, f)) {
+ char *saveptr;
+ line_count++;
+
+ line[strlen(line)-1] = 0;
+
+ /* printf("[%d] %s\n", line_count, line); */
+
+ all_string_sub(line,"client1", cname, sizeof(line));
+
+ /* parse the command parameters */
+ params[0] = strtok_r(line, " ", &saveptr);
+ i = 0;
+ while (params[i]) params[++i] = strtok_r(NULL, " ", &saveptr);
+
+ params[i] = "";
+
+ if (i < 2) continue;
+
+ if (!strncmp(params[0],"SMB", 3)) {
+ printf("ERROR: You are using a dbench 1 load file\n");
+ exit(1);
+ }
+
+ if (!strcmp(params[0],"NTCreateX")) {
+ nb_createx(params[1], ival(params[2]), ival(params[3]),
+ ival(params[4]));
+ } else if (!strcmp(params[0],"Close")) {
+ nb_close(ival(params[1]));
+ } else if (!strcmp(params[0],"Rename")) {
+ nb_rename(params[1], params[2]);
+ } else if (!strcmp(params[0],"Unlink")) {
+ nb_unlink(params[1]);
+ } else if (!strcmp(params[0],"Deltree")) {
+ nb_deltree(params[1]);
+ } else if (!strcmp(params[0],"Rmdir")) {
+ nb_rmdir(params[1]);
+ } else if (!strcmp(params[0],"QUERY_PATH_INFORMATION")) {
+ nb_qpathinfo(params[1]);
+ } else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) {
+ nb_qfileinfo(ival(params[1]));
+ } else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) {
+ nb_qfsinfo(ival(params[1]));
+ } else if (!strcmp(params[0],"FIND_FIRST")) {
+ nb_findfirst(params[1]);
+ } else if (!strcmp(params[0],"WriteX")) {
+ nb_writex(ival(params[1]),
+ ival(params[2]), ival(params[3]), ival(params[4]));
+ } else if (!strcmp(params[0],"ReadX")) {
+ nb_readx(ival(params[1]),
+ ival(params[2]), ival(params[3]), ival(params[4]));
+ } else if (!strcmp(params[0],"Flush")) {
+ nb_flush(ival(params[1]));
+ } else {
+ printf("Unknown operation %s\n", params[0]);
+ exit(1);
+ }
+ }
+ fclose(f);
+
+ nb_cleanup();
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+
+/* run a test that simulates an approximate netbench client load */
+static bool run_nbench(int dummy)
+{
+ double t;
+ bool correct = True;
+
+ nbio_shmem(torture_nprocs);
+
+ nbio_id = -1;
+
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+ t = create_procs(run_netbench, &correct);
+ alarm(0);
+
+ printf("\nThroughput %g MB/sec\n",
+ 1.0e-6 * nbio_total() / t);
+ return correct;
+}
+
+
+/*
+ This test checks for two things:
+
+ 1) correct support for retaining locks over a close (ie. the server
+ must not use posix semantics)
+ 2) support for lock timeouts
+ */
+static bool run_locktest1(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt1.lck";
+ uint16_t fnum1, fnum2, fnum3;
+ time_t t1, t2;
+ unsigned lock_timeout;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("starting locktest1\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(cli2, fname, O_RDWR, DENY_NONE, &fnum3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open3 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_lock32(cli1, fnum1, 0, 4, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock1 failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_lock32(cli2, fnum3, 0, 4, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("lock2 succeeded! This is a locking bug\n");
+ return false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) {
+ return false;
+ }
+ }
+
+ lock_timeout = (1 + (random() % 20));
+ printf("Testing lock timeout with timeout=%u\n", lock_timeout);
+ t1 = time(NULL);
+ status = cli_lock32(cli2, fnum3, 0, 4, lock_timeout * 1000, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("lock3 succeeded! This is a locking bug\n");
+ return false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) {
+ return false;
+ }
+ }
+ t2 = time(NULL);
+
+ if (ABS(t2 - t1) < lock_timeout-1) {
+ printf("error: This server appears not to support timed lock requests\n");
+ }
+
+ printf("server slept for %u seconds for a %u second timeout\n",
+ (unsigned int)(t2-t1), lock_timeout);
+
+ status = cli_close(cli1, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_lock32(cli2, fnum3, 0, 4, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("lock4 succeeded! This is a locking bug\n");
+ return false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) {
+ return false;
+ }
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close3 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+
+ if (!torture_close_connection(cli1)) {
+ return False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ return False;
+ }
+
+ printf("Passed locktest1\n");
+ return True;
+}
+
+/*
+ this checks to see if a secondary tconx can use open files from an
+ earlier tconx
+ */
+static bool run_tcon_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\tcontest.tmp";
+ uint16_t fnum1;
+ uint32_t cnum1, cnum2, cnum3;
+ struct smbXcli_tcon *orig_tcon = NULL;
+ char *orig_share = NULL;
+ uint16_t vuid1, vuid2;
+ char buf[4];
+ bool ret = True;
+ NTSTATUS status;
+
+ memset(buf, '\0', sizeof(buf));
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ printf("starting tcontest\n");
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ cnum1 = cli_state_get_tid(cli);
+ vuid1 = cli_state_get_uid(cli);
+
+ status = cli_writeall(cli, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("initial write failed (%s)", nt_errstr(status));
+ return False;
+ }
+
+ cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
+
+ status = cli_tree_connect_creds(cli, share, "?????", torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s refused 2nd tree connect (%s)\n", host,
+ nt_errstr(status));
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+ cli_shutdown(cli);
+ return False;
+ }
+
+ cnum2 = cli_state_get_tid(cli);
+ cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */
+ vuid2 = cli_state_get_uid(cli) + 1;
+
+ /* try a write with the wrong tid */
+ cli_state_set_tid(cli, cnum2);
+
+ status = cli_writeall(cli, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("* server allows write with wrong TID\n");
+ ret = False;
+ } else {
+ printf("server fails write with wrong TID : %s\n",
+ nt_errstr(status));
+ }
+
+
+ /* try a write with an invalid tid */
+ cli_state_set_tid(cli, cnum3);
+
+ status = cli_writeall(cli, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("* server allows write with invalid TID\n");
+ ret = False;
+ } else {
+ printf("server fails write with invalid TID : %s\n",
+ nt_errstr(status));
+ }
+
+ /* try a write with an invalid vuid */
+ cli_state_set_uid(cli, vuid2);
+ cli_state_set_tid(cli, cnum1);
+
+ status = cli_writeall(cli, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("* server allows write with invalid VUID\n");
+ ret = False;
+ } else {
+ printf("server fails write with invalid VUID : %s\n",
+ nt_errstr(status));
+ }
+
+ cli_state_set_tid(cli, cnum1);
+ cli_state_set_uid(cli, vuid1);
+
+ status = cli_close(cli, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+ cli_shutdown(cli);
+ return False;
+ }
+
+ cli_state_set_tid(cli, cnum2);
+
+ status = cli_tdis(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("secondary tdis failed (%s)\n", nt_errstr(status));
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+ cli_shutdown(cli);
+ return False;
+ }
+
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+
+ cli_state_set_tid(cli, cnum1);
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return ret;
+}
+
+
+/*
+ checks for old style tcon support
+ */
+static bool run_tcon2_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16_t cnum, max_xmit;
+ char *service;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ printf("starting tcon2 test\n");
+
+ if (asprintf(&service, "\\\\%s\\%s", host, share) == -1) {
+ return false;
+ }
+
+ status = cli_raw_tcon(cli, service, password, "?????", &max_xmit, &cnum);
+
+ SAFE_FREE(service);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("tcon2 failed : %s\n", nt_errstr(status));
+ } else {
+ printf("tcon OK : max_xmit=%d cnum=%d\n",
+ (int)max_xmit, (int)cnum);
+ }
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ printf("Passed tcon2 test\n");
+ return True;
+}
+
+static bool tcon_devtest(struct cli_state *cli,
+ const char *myshare, const char *devtype,
+ const char *return_devtype,
+ NTSTATUS expected_error)
+{
+ NTSTATUS status;
+ bool ret;
+
+ status = cli_tree_connect_creds(cli, myshare, devtype, torture_creds);
+
+ if (NT_STATUS_IS_OK(expected_error)) {
+ if (NT_STATUS_IS_OK(status)) {
+ if (return_devtype != NULL &&
+ strequal(cli->dev, return_devtype)) {
+ ret = True;
+ } else {
+ printf("tconX to share %s with type %s "
+ "succeeded but returned the wrong "
+ "device type (got [%s] but should have got [%s])\n",
+ myshare, devtype, cli->dev, return_devtype);
+ ret = False;
+ }
+ } else {
+ printf("tconX to share %s with type %s "
+ "should have succeeded but failed\n",
+ myshare, devtype);
+ ret = False;
+ }
+ cli_tdis(cli);
+ } else {
+ if (NT_STATUS_IS_OK(status)) {
+ printf("tconx to share %s with type %s "
+ "should have failed but succeeded\n",
+ myshare, devtype);
+ ret = False;
+ } else {
+ if (NT_STATUS_EQUAL(status, expected_error)) {
+ ret = True;
+ } else {
+ printf("Returned unexpected error\n");
+ ret = False;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ checks for correct tconX support
+ */
+static bool run_tcon_devtype_test(int dummy)
+{
+ static struct cli_state *cli1 = NULL;
+ int flags = CLI_FULL_CONNECTION_FORCE_SMB1;
+ NTSTATUS status;
+ bool ret = True;
+
+ status = cli_full_connection_creds(&cli1,
+ myname,
+ host,
+ NULL, /* dest_ss */
+ port_to_use,
+ NULL, /* service */
+ NULL, /* service_type */
+ torture_creds,
+ flags);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("could not open connection\n");
+ return False;
+ }
+
+ if (!tcon_devtest(cli1, "IPC$", "A:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "?????", "IPC", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "LPT:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "IPC", "IPC", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "FOOBA", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "A:", "A:", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "?????", "A:", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "LPT:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "IPC", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "FOOBA", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ cli_shutdown(cli1);
+
+ if (ret)
+ printf("Passed tcondevtest\n");
+
+ return ret;
+}
+
+
+/*
+ This test checks that
+
+ 1) the server supports multiple locking contexts on the one SMB
+ connection, distinguished by PID.
+
+ 2) the server correctly fails overlapping locks made by the same PID (this
+ goes against POSIX behaviour, which is why it is tricky to implement)
+
+ 3) the server denies unlock requests by an incorrect client PID
+*/
+static bool run_locktest2(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\lockt2.lck";
+ uint16_t fnum1, fnum2, fnum3;
+ bool correct = True;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ printf("starting locktest2\n");
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ cli_setpid(cli, 1);
+
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(cli, fname, O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ cli_setpid(cli, 2);
+
+ status = cli_openx(cli, fname, O_RDWR, DENY_NONE, &fnum3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open3 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ cli_setpid(cli, 1);
+
+ status = cli_lock32(cli, fnum1, 0, 4, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock1 failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_lock32(cli, fnum1, 0, 4, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("WRITE lock1 succeeded! This is a locking bug\n");
+ correct = false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) {
+ return false;
+ }
+ }
+
+ status = cli_lock32(cli, fnum2, 0, 4, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("WRITE lock2 succeeded! This is a locking bug\n");
+ correct = false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) {
+ return false;
+ }
+ }
+
+ status = cli_lock32(cli, fnum2, 0, 4, 0, READ_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("READ lock2 succeeded! This is a locking bug\n");
+ correct = false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) {
+ return false;
+ }
+ }
+
+ status = cli_lock32(cli, fnum1, 100, 4, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock at 100 failed (%s)\n", nt_errstr(status));
+ }
+ cli_setpid(cli, 2);
+ if (NT_STATUS_IS_OK(cli_unlock(cli, fnum1, 100, 4))) {
+ printf("unlock at 100 succeeded! This is a locking bug\n");
+ correct = False;
+ }
+
+ status = cli_unlock(cli, fnum1, 0, 4);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("unlock1 succeeded! This is a locking bug\n");
+ correct = false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_RANGE_NOT_LOCKED)) {
+ return false;
+ }
+ }
+
+ status = cli_unlock(cli, fnum1, 0, 8);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("unlock2 succeeded! This is a locking bug\n");
+ correct = false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_RANGE_NOT_LOCKED)) {
+ return false;
+ }
+ }
+
+ status = cli_lock32(cli, fnum3, 0, 4, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("lock3 succeeded! This is a locking bug\n");
+ correct = false;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) {
+ return false;
+ }
+ }
+
+ cli_setpid(cli, 1);
+
+ status = cli_close(cli, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli, fnum3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close3 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("locktest2 finished\n");
+
+ return correct;
+}
+
+
+/*
+ This test checks that
+
+ 1) the server supports the full offset range in lock requests
+*/
+static bool run_locktest3(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt3.lck";
+ uint16_t fnum1, fnum2;
+ int i;
+ uint32_t offset;
+ bool correct = True;
+ NTSTATUS status;
+
+#define NEXT_OFFSET offset += (~(uint32_t)0) / torture_numops
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("starting locktest3\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(cli2, fname, O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ status = cli_lock32(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock1 %d failed (%s)\n",
+ i,
+ nt_errstr(status));
+ return False;
+ }
+
+ status = cli_lock32(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock2 %d failed (%s)\n",
+ i,
+ nt_errstr(status));
+ return False;
+ }
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ status = cli_lock32(cli1, fnum1, offset-2, 1, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("error: lock1 %d succeeded!\n", i);
+ return False;
+ }
+
+ status = cli_lock32(cli2, fnum2, offset-1, 1, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("error: lock2 %d succeeded!\n", i);
+ return False;
+ }
+
+ status = cli_lock32(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("error: lock3 %d succeeded!\n", i);
+ return False;
+ }
+
+ status = cli_lock32(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("error: lock4 %d succeeded!\n", i);
+ return False;
+ }
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ status = cli_unlock(cli1, fnum1, offset-1, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlock1 %d failed (%s)\n",
+ i,
+ nt_errstr(status));
+ return False;
+ }
+
+ status = cli_unlock(cli2, fnum2, offset-2, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlock2 %d failed (%s)\n",
+ i,
+ nt_errstr(status));
+ return False;
+ }
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished locktest3\n");
+
+ return correct;
+}
+
+static bool test_cli_read(struct cli_state *cli, uint16_t fnum,
+ char *buf, off_t offset, size_t size,
+ size_t *nread, size_t expect)
+{
+ NTSTATUS status;
+ size_t l_nread;
+
+ status = cli_read(cli, fnum, buf, offset, size, &l_nread);
+
+ if(!NT_STATUS_IS_OK(status)) {
+ return false;
+ } else if (l_nread != expect) {
+ return false;
+ }
+
+ if (nread) {
+ *nread = l_nread;
+ }
+
+ return true;
+}
+
+#define EXPECTED(ret, v) if ((ret) != (v)) { \
+ printf("** "); correct = False; \
+ }
+
+/*
+ looks at overlapping locks
+*/
+static bool run_locktest4(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt4.lck";
+ uint16_t fnum1, fnum2, f;
+ bool ret;
+ char buf[1000];
+ bool correct = True;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("starting locktest4\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ cli_openx(cli2, fname, O_RDWR, DENY_NONE, &fnum2);
+
+ memset(buf, 0, sizeof(buf));
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 0, sizeof(buf),
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to create file: %s\n", nt_errstr(status));
+ correct = False;
+ goto fail;
+ }
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 2, 4, 0, WRITE_LOCK));
+ EXPECTED(ret, False);
+ printf("the same process %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 10, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 12, 4, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("the same process %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 20, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli2, fnum2, 22, 4, 0, WRITE_LOCK));
+ EXPECTED(ret, False);
+ printf("a different connection %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 30, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli2, fnum2, 32, 4, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("a different connection %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1),
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 40, 4, 0, WRITE_LOCK))) &&
+ (cli_setpid(cli1, 2),
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 42, 4, 0, WRITE_LOCK)));
+ EXPECTED(ret, False);
+ printf("a different pid %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1),
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 50, 4, 0, READ_LOCK))) &&
+ (cli_setpid(cli1, 2),
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 52, 4, 0, READ_LOCK)));
+ EXPECTED(ret, True);
+ printf("a different pid %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 60, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 60, 4, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("the same process %s set the same read lock twice\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 70, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 70, 4, 0, WRITE_LOCK));
+ EXPECTED(ret, False);
+ printf("the same process %s set the same write lock twice\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 80, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 80, 4, 0, WRITE_LOCK));
+ EXPECTED(ret, False);
+ printf("the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 90, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 90, 4, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1),
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 100, 4, 0, WRITE_LOCK))) &&
+ (cli_setpid(cli1, 2),
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 100, 4, 0, READ_LOCK)));
+ EXPECTED(ret, False);
+ printf("a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 110, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 112, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 110, 6));
+ EXPECTED(ret, False);
+ printf("the same process %s coalesce read locks\n", ret?"can":"cannot");
+
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 120, 4, 0, WRITE_LOCK)) &&
+ test_cli_read(cli2, fnum2, buf, 120, 4, NULL, 4);
+ EXPECTED(ret, False);
+ printf("this server %s strict write locking\n", ret?"doesn't do":"does");
+
+ status = cli_lock32(cli1, fnum1, 130, 4, 0, READ_LOCK);
+ ret = NT_STATUS_IS_OK(status);
+ if (ret) {
+ status = cli_writeall(cli2, fnum2, 0, (uint8_t *)buf, 130, 4,
+ NULL);
+ ret = NT_STATUS_IS_OK(status);
+ }
+ EXPECTED(ret, False);
+ printf("this server %s strict read locking\n", ret?"doesn't do":"does");
+
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 140, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 140, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 140, 4)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 140, 4));
+ EXPECTED(ret, True);
+ printf("this server %s do recursive read locking\n", ret?"does":"doesn't");
+
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 150, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 150, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 150, 4)) &&
+ test_cli_read(cli2, fnum2, buf, 150, 4, NULL, 4) &&
+ !(NT_STATUS_IS_OK(cli_writeall(cli2, fnum2, 0, (uint8_t *)buf,
+ 150, 4, NULL))) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 150, 4));
+ EXPECTED(ret, True);
+ printf("this server %s do recursive lock overlays\n", ret?"does":"doesn't");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 160, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 160, 4)) &&
+ NT_STATUS_IS_OK(cli_writeall(cli2, fnum2, 0, (uint8_t *)buf,
+ 160, 4, NULL)) &&
+ test_cli_read(cli2, fnum2, buf, 160, 4, NULL, 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 170, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 170, 4)) &&
+ NT_STATUS_IS_OK(cli_writeall(cli2, fnum2, 0, (uint8_t *)buf,
+ 170, 4, NULL)) &&
+ test_cli_read(cli2, fnum2, buf, 170, 4, NULL, 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 190, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 190, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 190, 4)) &&
+ !NT_STATUS_IS_OK(cli_writeall(cli2, fnum2, 0, (uint8_t *)buf,
+ 190, 4, NULL)) &&
+ test_cli_read(cli2, fnum2, buf, 190, 4, NULL, 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove the first lock first\n", ret?"does":"doesn't");
+
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1);
+ cli_openx(cli1, fname, O_RDWR, DENY_NONE, &f);
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 0, 8, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, f, 0, 1, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_close(cli1, fnum1)) &&
+ NT_STATUS_IS_OK(cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 7, 1, 0, WRITE_LOCK));
+ cli_close(cli1, f);
+ cli_close(cli1, fnum1);
+ EXPECTED(ret, True);
+ printf("the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
+
+ fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ torture_close_connection(cli1);
+ torture_close_connection(cli2);
+
+ printf("finished locktest4\n");
+ return correct;
+}
+
+/*
+ looks at lock upgrade/downgrade.
+*/
+static bool run_locktest5(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt5.lck";
+ uint16_t fnum1, fnum2, fnum3;
+ bool ret;
+ char buf[1000];
+ bool correct = True;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("starting locktest5\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ cli_openx(cli2, fname, O_RDWR, DENY_NONE, &fnum2);
+ cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum3);
+
+ memset(buf, 0, sizeof(buf));
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 0, sizeof(buf),
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to create file: %s\n", nt_errstr(status));
+ correct = False;
+ goto fail;
+ }
+
+ /* Check for NT bug... */
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 0, 8, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum3, 0, 1, 0, READ_LOCK));
+ cli_close(cli1, fnum1);
+ cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1);
+ status = cli_lock32(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+ ret = NT_STATUS_IS_OK(status);
+ EXPECTED(ret, True);
+ printf("this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
+ cli_close(cli1, fnum1);
+ cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1);
+ cli_unlock(cli1, fnum3, 0, 1);
+
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 1, 1, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
+
+ status = cli_lock32(cli2, fnum2, 0, 4, 0, READ_LOCK);
+ ret = NT_STATUS_IS_OK(status);
+ EXPECTED(ret, False);
+
+ printf("a different process %s get a read lock on the first process lock stack\n", ret?"can":"cannot");
+
+ /* Unlock the process 2 lock. */
+ cli_unlock(cli2, fnum2, 0, 4);
+
+ status = cli_lock32(cli1, fnum3, 0, 4, 0, READ_LOCK);
+ ret = NT_STATUS_IS_OK(status);
+ EXPECTED(ret, False);
+
+ printf("the same process on a different fnum %s get a read lock\n", ret?"can":"cannot");
+
+ /* Unlock the process 1 fnum3 lock. */
+ cli_unlock(cli1, fnum3, 0, 4);
+
+ /* Stack 2 more locks here. */
+ ret = NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 0, 4, 0, READ_LOCK)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli1, fnum1, 0, 4, 0, READ_LOCK));
+
+ EXPECTED(ret, True);
+ printf("the same process %s stack read locks\n", ret?"can":"cannot");
+
+ /* Unlock the first process lock, then check this was the WRITE lock that was
+ removed. */
+
+ ret = NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 0, 4)) &&
+ NT_STATUS_IS_OK(cli_lock32(cli2, fnum2, 0, 4, 0, READ_LOCK));
+
+ EXPECTED(ret, True);
+ printf("the first unlock removes the %s lock\n", ret?"WRITE":"READ");
+
+ /* Unlock the process 2 lock. */
+ cli_unlock(cli2, fnum2, 0, 4);
+
+ /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */
+
+ ret = NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 1, 1)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 0, 4)) &&
+ NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 0, 4));
+
+ EXPECTED(ret, True);
+ printf("the same process %s unlock the stack of 4 locks\n", ret?"can":"cannot");
+
+ /* Ensure the next unlock fails. */
+ ret = NT_STATUS_IS_OK(cli_unlock(cli1, fnum1, 0, 4));
+ EXPECTED(ret, False);
+ printf("the same process %s count the lock stack\n", !ret?"can":"cannot");
+
+ /* Ensure connection 2 can get a write lock. */
+ status = cli_lock32(cli2, fnum2, 0, 4, 0, WRITE_LOCK);
+ ret = NT_STATUS_IS_OK(status);
+ EXPECTED(ret, True);
+
+ printf("a different process %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
+
+
+ fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished locktest5\n");
+
+ return correct;
+}
+
+/*
+ tries the unusual lockingX locktype bits
+*/
+static bool run_locktest6(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname[1] = { "\\lock6.txt" };
+ int i;
+ uint16_t fnum;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ printf("starting locktest6\n");
+
+ for (i=0;i<1;i++) {
+ printf("Testing %s\n", fname[i]);
+
+ cli_unlink(cli, fname[i], FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ cli_openx(cli, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
+ cli_close(cli, fnum);
+ printf("CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
+
+ cli_openx(cli, fname[i], O_RDWR, DENY_NONE, &fnum);
+ status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
+ cli_close(cli, fnum);
+ printf("CANCEL_LOCK gave %s\n", nt_errstr(status));
+
+ cli_unlink(cli, fname[i], FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ }
+
+ torture_close_connection(cli);
+
+ printf("finished locktest6\n");
+ return True;
+}
+
+static bool run_locktest7(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt7.lck";
+ uint16_t fnum1;
+ char buf[200];
+ bool correct = False;
+ size_t nread;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ printf("starting locktest7\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+
+ memset(buf, 0, sizeof(buf));
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 0, sizeof(buf),
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to create file: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ cli_setpid(cli1, 1);
+
+ status = cli_lock32(cli1, fnum1, 130, 4, 0, READ_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Unable to apply read lock on range 130:4, "
+ "error was %s\n", nt_errstr(status));
+ goto fail;
+ } else {
+ printf("pid1 successfully locked range 130:4 for READ\n");
+ }
+
+ status = cli_read(cli1, fnum1, buf, 130, 4, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid1 unable to read the range 130:4, error was %s\n",
+ nt_errstr(status));
+ goto fail;
+ } else if (nread != 4) {
+ printf("pid1 unable to read the range 130:4, "
+ "recv %ld req %d\n", (unsigned long)nread, 4);
+ goto fail;
+ } else {
+ printf("pid1 successfully read the range 130:4\n");
+ }
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid1 unable to write to the range 130:4, error was "
+ "%s\n", nt_errstr(status));
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid1 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 2);
+
+ status = cli_read(cli1, fnum1, buf, 130, 4, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid2 unable to read the range 130:4, error was %s\n",
+ nt_errstr(status));
+ goto fail;
+ } else if (nread != 4) {
+ printf("pid2 unable to read the range 130:4, "
+ "recv %ld req %d\n", (unsigned long)nread, 4);
+ goto fail;
+ } else {
+ printf("pid2 successfully read the range 130:4\n");
+ }
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid2 unable to write to the range 130:4, error was "
+ "%s\n", nt_errstr(status));
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 1);
+ cli_unlock(cli1, fnum1, 130, 4);
+
+ status = cli_lock32(cli1, fnum1, 130, 4, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Unable to apply write lock on range 130:4, error was %s\n", nt_errstr(status));
+ goto fail;
+ } else {
+ printf("pid1 successfully locked range 130:4 for WRITE\n");
+ }
+
+ status = cli_read(cli1, fnum1, buf, 130, 4, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid1 unable to read the range 130:4, error was %s\n",
+ nt_errstr(status));
+ goto fail;
+ } else if (nread != 4) {
+ printf("pid1 unable to read the range 130:4, "
+ "recv %ld req %d\n", (unsigned long)nread, 4);
+ goto fail;
+ } else {
+ printf("pid1 successfully read the range 130:4\n");
+ }
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid1 unable to write to the range 130:4, error was "
+ "%s\n", nt_errstr(status));
+ goto fail;
+ } else {
+ printf("pid1 successfully wrote to the range 130:4\n");
+ }
+
+ cli_setpid(cli1, 2);
+
+ status = cli_read(cli1, fnum1, buf, 130, 4, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid2 unable to read the range 130:4, error was "
+ "%s\n", nt_errstr(status));
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully read the range 130:4 (should be denied) recv %ld\n",
+ (unsigned long)nread);
+ goto fail;
+ }
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 130, 4, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pid2 unable to write to the range 130:4, error was "
+ "%s\n", nt_errstr(status));
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_unlock(cli1, fnum1, 130, 0);
+ correct = True;
+
+fail:
+ cli_close(cli1, fnum1);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ torture_close_connection(cli1);
+
+ printf("finished locktest7\n");
+ return correct;
+}
+
+/*
+ * This demonstrates a problem with our use of GPFS share modes: A file
+ * descriptor sitting in the pending close queue holding a GPFS share mode
+ * blocks opening a file another time. Happens with Word 2007 temp files.
+ * With "posix locking = yes" and "gpfs:sharemodes = yes" enabled, the third
+ * open is denied with NT_STATUS_SHARING_VIOLATION.
+ */
+
+static bool run_locktest8(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt8.lck";
+ uint16_t fnum1, fnum2;
+ char buf[200];
+ bool correct = False;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ printf("starting locktest8\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_WRITE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_openx returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_openx second time returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_lock32(cli1, fnum2, 1, 1, 0, READ_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Unable to apply read lock on range 1:1, error was "
+ "%s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_close(fnum1) %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_openx third time returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ correct = true;
+
+fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli1, fnum2);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ torture_close_connection(cli1);
+
+ printf("finished locktest8\n");
+ return correct;
+}
+
+/*
+ * This test is designed to be run in conjunction with
+ * external NFS or POSIX locks taken in the filesystem.
+ * It checks that the smbd server will block until the
+ * lock is released and then acquire it. JRA.
+ */
+
+static bool got_alarm;
+static struct cli_state *alarm_cli;
+
+static void alarm_handler(int dummy)
+{
+ got_alarm = True;
+}
+
+static void alarm_handler_parent(int dummy)
+{
+ smbXcli_conn_disconnect(alarm_cli->conn, NT_STATUS_LOCAL_DISCONNECT);
+}
+
+static void do_local_lock(const char *fname, int read_fd, int write_fd)
+{
+ int fd;
+ char c = '\0';
+ struct flock lock;
+ const char *local_pathname = NULL;
+ int ret;
+
+ local_pathname = talloc_asprintf(talloc_tos(),
+ "%s/%s", local_path, fname);
+ if (!local_pathname) {
+ printf("child: alloc fail\n");
+ exit(1);
+ }
+
+ unlink(local_pathname);
+ fd = open(local_pathname, O_RDWR|O_CREAT, 0666);
+ if (fd == -1) {
+ printf("child: open of %s failed %s.\n",
+ local_pathname, strerror(errno));
+ exit(1);
+ }
+
+ /* Now take a fcntl lock. */
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ ret = fcntl(fd,F_SETLK,&lock);
+ if (ret == -1) {
+ printf("child: failed to get lock 0:4 on file %s. Error %s\n",
+ local_pathname, strerror(errno));
+ exit(1);
+ } else {
+ printf("child: got lock 0:4 on file %s.\n",
+ local_pathname );
+ fflush(stdout);
+ }
+
+ CatchSignal(SIGALRM, alarm_handler);
+ alarm(5);
+ /* Signal the parent. */
+ if (write(write_fd, &c, 1) != 1) {
+ printf("child: start signal fail %s.\n",
+ strerror(errno));
+ exit(1);
+ }
+ alarm(0);
+
+ alarm(10);
+ /* Wait for the parent to be ready. */
+ if (read(read_fd, &c, 1) != 1) {
+ printf("child: reply signal fail %s.\n",
+ strerror(errno));
+ exit(1);
+ }
+ alarm(0);
+
+ sleep(5);
+ close(fd);
+ printf("child: released lock 0:4 on file %s.\n",
+ local_pathname );
+ fflush(stdout);
+ exit(0);
+}
+
+static bool _run_locktest9X(const char *fname, int timeout)
+{
+ struct cli_state *cli1;
+ char *fpath = talloc_asprintf(talloc_tos(), "\\%s", fname);
+ uint16_t fnum;
+ bool correct = False;
+ int pipe_in[2], pipe_out[2];
+ pid_t child_pid;
+ char c = '\0';
+ int ret;
+ struct timeval start;
+ double seconds;
+ NTSTATUS status;
+
+ printf("starting locktest9X: %s\n", fname);
+
+ if (local_path == NULL) {
+ d_fprintf(stderr, "locktest9X must be given a local path via -l <localpath>\n");
+ return false;
+ }
+
+ if (pipe(pipe_in) == -1 || pipe(pipe_out) == -1) {
+ return false;
+ }
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ return false;
+ }
+
+ if (child_pid == 0) {
+ /* Child. */
+ do_local_lock(fname, pipe_out[0], pipe_in[1]);
+ exit(0);
+ }
+
+ close(pipe_out[0]);
+ close(pipe_in[1]);
+ pipe_out[0] = -1;
+ pipe_in[1] = -1;
+
+ /* Parent. */
+ ret = read(pipe_in[0], &c, 1);
+ if (ret != 1) {
+ d_fprintf(stderr, "failed to read start signal from child. %s\n",
+ strerror(errno));
+ return false;
+ }
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ status = cli_openx(cli1, fpath, O_RDWR, DENY_NONE,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_openx returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure the child has the lock. */
+ status = cli_lock32(cli1, fnum, 0, 4, 0, WRITE_LOCK);
+ if (NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Got the lock on range 0:4 - this should not happen !\n");
+ goto fail;
+ } else {
+ d_printf("Child has the lock.\n");
+ }
+
+ /* Tell the child to wait 5 seconds then exit. */
+ ret = write(pipe_out[1], &c, 1);
+ if (ret != 1) {
+ d_fprintf(stderr, "failed to send exit signal to child. %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ /* Wait 20 seconds for the lock. */
+ alarm_cli = cli1;
+ CatchSignal(SIGALRM, alarm_handler_parent);
+ alarm(20);
+
+ start = timeval_current();
+
+ status = cli_lock32(cli1, fnum, 0, 4, timeout, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Unable to apply write lock on range 0:4, error was "
+ "%s\n", nt_errstr(status));
+ goto fail_nofd;
+ }
+ alarm(0);
+
+ seconds = timeval_elapsed(&start);
+
+ printf("Parent got the lock after %.2f seconds.\n",
+ seconds);
+
+ status = cli_close(cli1, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "cli_close(fnum1) %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ correct = true;
+
+fail:
+ cli_close(cli1, fnum);
+ torture_close_connection(cli1);
+
+fail_nofd:
+
+ printf("finished locktest9X: %s\n", fname);
+ return correct;
+}
+
+static bool run_locktest9a(int dummy)
+{
+ return _run_locktest9X("lock9a.dat", -1);
+}
+
+static bool run_locktest9b(int dummy)
+{
+ return _run_locktest9X("lock9b.dat", 10000);
+}
+
+struct locktest10_state {
+ bool ok;
+ bool done;
+};
+
+static void locktest10_lockingx_done(struct tevent_req *subreq);
+static void locktest10_read_andx_done(struct tevent_req *subreq);
+
+static bool run_locktest10(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct cli_state *cli1 = NULL;
+ struct cli_state *cli2 = NULL;
+ struct smb1_lock_element lck = { 0 };
+ struct tevent_req *reqs[2] = { NULL };
+ struct tevent_req *smbreqs[2] = { NULL };
+ const char fname[] = "\\lockt10.lck";
+ uint16_t fnum1, fnum2;
+ bool ret = false;
+ bool ok;
+ uint8_t data = 1;
+ struct locktest10_state state = { .ok = true };
+ NTSTATUS status;
+
+ printf("starting locktest10\n");
+
+ ev = samba_tevent_context_init(NULL);
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ goto done;
+ }
+
+ ok = torture_open_connection(&cli1, 0);
+ if (!ok) {
+ goto done;
+ }
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ ok = torture_open_connection(&cli2, 1);
+ if (!ok) {
+ goto done;
+ }
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ status = cli_openx(cli1, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_writeall(cli1, fnum1, 0, &data, 0, sizeof(data), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_writeall failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_openx(cli2, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_locktype(
+ cli2, fnum2, 0, 1, 0, LOCKING_ANDX_EXCLUSIVE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_locktype failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ lck = (struct smb1_lock_element) {
+ .pid = cli_getpid(cli1), .offset = 0, .length = 1,
+ };
+
+ reqs[0] = cli_lockingx_create(
+ ev, /* mem_ctx */
+ ev, /* tevent_context */
+ cli1, /* cli */
+ fnum1, /* fnum */
+ LOCKING_ANDX_EXCLUSIVE_LOCK, /* typeoflock */
+ 0, /* newoplocklevel */
+ 1, /* timeout */
+ 0, /* num_unlocks */
+ NULL, /* unlocks */
+ 1, /* num_locks */
+ &lck, /* locks */
+ &smbreqs[0]); /* psmbreq */
+ if (reqs[0] == NULL) {
+ d_fprintf(stderr, "cli_lockingx_create failed\n");
+ goto done;
+ }
+ tevent_req_set_callback(reqs[0], locktest10_lockingx_done, &state);
+
+ reqs[1] = cli_read_andx_create(
+ ev, /* mem_ctx */
+ ev, /* ev */
+ cli1, /* cli */
+ fnum1, /* fnum */
+ 0, /* offset */
+ 1, /* size */
+ &smbreqs[1]); /* psmbreq */
+ if (reqs[1] == NULL) {
+ d_fprintf(stderr, "cli_read_andx_create failed\n");
+ goto done;
+ }
+ tevent_req_set_callback(reqs[1], locktest10_read_andx_done, &state);
+
+ status = smb1cli_req_chain_submit(smbreqs, ARRAY_SIZE(smbreqs));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "smb1cli_req_chain_submit failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ while (!state.done) {
+ tevent_loop_once(ev);
+ }
+
+ torture_close_connection(cli1);
+
+ if (state.ok) {
+ ret = true;
+ }
+done:
+ return ret;
+}
+
+static void locktest10_lockingx_done(struct tevent_req *subreq)
+{
+ struct locktest10_state *state = tevent_req_callback_data_void(subreq);
+ NTSTATUS status;
+
+ status = cli_lockingx_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ d_printf("cli_lockingx returned %s\n", nt_errstr(status));
+ state->ok = false;
+ }
+}
+
+static void locktest10_read_andx_done(struct tevent_req *subreq)
+{
+ struct locktest10_state *state = tevent_req_callback_data_void(subreq);
+ ssize_t received = -1;
+ uint8_t *rcvbuf = NULL;
+ NTSTATUS status;
+
+ status = cli_read_andx_recv(subreq, &received, &rcvbuf);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_REQUEST_ABORTED)) {
+ d_printf("cli_read_andx returned %s\n", nt_errstr(status));
+ state->ok = false;
+ }
+
+ state->done = true;
+ TALLOC_FREE(subreq);
+}
+
+static bool run_locktest11(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt11.lck";
+ NTSTATUS status;
+ uint16_t fnum;
+ bool ret = false;
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Test that LOCKING_ANDX_CANCEL_LOCK without any locks
+ * returns NT_STATUS_OK
+ */
+
+ status = cli_lockingx(
+ cli1, /* cli */
+ fnum, /* fnum */
+ LOCKING_ANDX_CANCEL_LOCK, /* typeoflock */
+ 0, /* newoplocklevel */
+ 0, /* timeout */
+ 0, /* num_unlocks */
+ NULL, /* unlocks */
+ 0, /* num_locks */
+ NULL); /* locks */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_lockingX returned %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ cli_close(cli1, fnum);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ return ret;
+}
+
+struct deferred_close_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+};
+
+static void deferred_close_waited(struct tevent_req *subreq);
+static void deferred_close_done(struct tevent_req *subreq);
+
+static struct tevent_req *deferred_close_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int wait_secs,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct deferred_close_state *state = NULL;
+ struct timeval wakeup_time = timeval_current_ofs(wait_secs, 0);
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct deferred_close_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fnum = fnum;
+
+ subreq = tevent_wakeup_send(state, state->ev, wakeup_time);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, deferred_close_waited, req);
+ return req;
+}
+
+static void deferred_close_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct deferred_close_state *state = tevent_req_data(
+ req, struct deferred_close_state);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, deferred_close_done, req);
+}
+
+static void deferred_close_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_close_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS deferred_close_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct lockread_state {
+ struct smb1_lock_element lck;
+ struct tevent_req *reqs[2];
+ struct tevent_req *smbreqs[2];
+ NTSTATUS lock_status;
+ NTSTATUS read_status;
+ uint8_t *readbuf;
+};
+
+static void lockread_lockingx_done(struct tevent_req *subreq);
+static void lockread_read_andx_done(struct tevent_req *subreq);
+
+static struct tevent_req *lockread_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req = NULL;
+ struct lockread_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct lockread_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->lck = (struct smb1_lock_element) {
+ .pid = cli_getpid(cli), .offset = 0, .length = 1,
+ };
+
+ state->reqs[0] = cli_lockingx_create(
+ ev, /* mem_ctx */
+ ev, /* tevent_context */
+ cli, /* cli */
+ fnum, /* fnum */
+ LOCKING_ANDX_EXCLUSIVE_LOCK, /* typeoflock */
+ 0, /* newoplocklevel */
+ 10000, /* timeout */
+ 0, /* num_unlocks */
+ NULL, /* unlocks */
+ 1, /* num_locks */
+ &state->lck, /* locks */
+ &state->smbreqs[0]); /* psmbreq */
+ if (tevent_req_nomem(state->reqs[0], req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->reqs[0], lockread_lockingx_done, req);
+
+ state->reqs[1] = cli_read_andx_create(
+ ev, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ fnum, /* fnum */
+ 0, /* offset */
+ 1, /* size */
+ &state->smbreqs[1]); /* psmbreq */
+ if (tevent_req_nomem(state->reqs[1], req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->reqs[1], lockread_read_andx_done, req);
+
+ status = smb1cli_req_chain_submit(state->smbreqs, 2);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void lockread_lockingx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct lockread_state *state = tevent_req_data(
+ req, struct lockread_state);
+ state->lock_status = cli_lockingx_recv(subreq);
+ TALLOC_FREE(subreq);
+ d_fprintf(stderr,
+ "lockingx returned %s\n",
+ nt_errstr(state->lock_status));
+}
+
+static void lockread_read_andx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct lockread_state *state = tevent_req_data(
+ req, struct lockread_state);
+ ssize_t received = -1;
+ uint8_t *rcvbuf = NULL;
+
+ state->read_status = cli_read_andx_recv(subreq, &received, &rcvbuf);
+
+ d_fprintf(stderr,
+ "read returned %s\n",
+ nt_errstr(state->read_status));
+
+ if (!NT_STATUS_IS_OK(state->read_status)) {
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+ return;
+ }
+
+ if (received > 0) {
+ state->readbuf = talloc_memdup(state, rcvbuf, received);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nomem(state->readbuf, req)) {
+ return;
+ }
+ }
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+static NTSTATUS lockread_recv(
+ struct tevent_req *req,
+ NTSTATUS *lock_status,
+ NTSTATUS *read_status,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **read_buf)
+{
+ struct lockread_state *state = tevent_req_data(
+ req, struct lockread_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *lock_status = state->lock_status;
+ *read_status = state->read_status;
+ if (state->readbuf != NULL) {
+ *read_buf = talloc_move(mem_ctx, &state->readbuf);
+ } else {
+ *read_buf = NULL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct lock12_state {
+ uint8_t dummy;
+};
+
+static void lock12_closed(struct tevent_req *subreq);
+static void lock12_read(struct tevent_req *subreq);
+
+static struct tevent_req *lock12_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum1,
+ uint16_t fnum2)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct lock12_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct lock12_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = deferred_close_send(state, ev, 1, cli, fnum1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, lock12_closed, req);
+
+ subreq = lockread_send(state, ev, cli, fnum2);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, lock12_read, req);
+
+ return req;
+}
+
+static void lock12_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = deferred_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ DBG_DEBUG("close returned %s\n", nt_errstr(status));
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+}
+
+static void lock12_read(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct lock12_state *state = tevent_req_data(
+ req, struct lock12_state);
+ NTSTATUS status, lock_status, read_status;
+ uint8_t *buf = NULL;
+
+ status = lockread_recv(
+ subreq, &lock_status, &read_status, state, &buf);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status) ||
+ tevent_req_nterror(req, lock_status) ||
+ tevent_req_nterror(req, read_status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS lock12_recv(struct tevent_req *req)
+
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static bool run_locktest12(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ struct cli_state *cli = NULL;
+ const char fname[] = "\\lockt12.lck";
+ uint16_t fnum1, fnum2;
+ bool ret = false;
+ bool ok;
+ uint8_t data = 1;
+ NTSTATUS status;
+
+ printf("starting locktest12\n");
+
+ ev = samba_tevent_context_init(NULL);
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ goto done;
+ }
+
+ ok = torture_open_connection(&cli, 0);
+ if (!ok) {
+ goto done;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = cli_openx(cli, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_openx(cli, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_writeall(cli, fnum1, 0, &data, 0, sizeof(data), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_writeall failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_locktype(
+ cli, fnum1, 0, 1, 0, LOCKING_ANDX_EXCLUSIVE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_locktype failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ req = lock12_send(ev, ev, cli, fnum1, fnum2);
+ if (req == NULL) {
+ d_fprintf(stderr, "lock12_send failed\n");
+ goto done;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr, "tevent_req_poll_ntstatus failed\n");
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "tevent_req_poll_ntstatus returned %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = lock12_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "lock12 returned %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+done:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ return ret;
+}
+
+struct lock_ntcancel_state {
+ struct timeval start;
+ struct smb1_lock_element lck;
+ struct tevent_req *subreq;
+};
+
+static void lock_ntcancel_waited(struct tevent_req *subreq);
+static void lock_ntcancel_done(struct tevent_req *subreq);
+
+static struct tevent_req *lock_ntcancel_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct lock_ntcancel_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct lock_ntcancel_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->lck = (struct smb1_lock_element) {
+ .pid = cli_getpid(cli), .offset = 0, .length = 1,
+ };
+ state->start = timeval_current();
+
+ state->subreq = cli_lockingx_send(
+ state, /* mem_ctx */
+ ev, /* tevent_context */
+ cli, /* cli */
+ fnum, /* fnum */
+ LOCKING_ANDX_EXCLUSIVE_LOCK, /* typeoflock */
+ 0, /* newoplocklevel */
+ 10000, /* timeout */
+ 0, /* num_unlocks */
+ NULL, /* unlocks */
+ 1, /* num_locks */
+ &state->lck); /* locks */
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreq, lock_ntcancel_done, req);
+
+ subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(1, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, lock_ntcancel_waited, req);
+ return req;
+}
+
+static void lock_ntcancel_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct lock_ntcancel_state *state = tevent_req_data(
+ req, struct lock_ntcancel_state);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ ok = tevent_req_cancel(state->subreq);
+ if (!ok) {
+ d_fprintf(stderr, "Could not cancel subreq\n");
+ tevent_req_oom(req);
+ return;
+ }
+}
+
+static void lock_ntcancel_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct lock_ntcancel_state *state = tevent_req_data(
+ req, struct lock_ntcancel_state);
+ NTSTATUS status;
+ double elapsed;
+
+ status = cli_lockingx_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ d_printf("cli_lockingx returned %s\n", nt_errstr(status));
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ elapsed = timeval_elapsed(&state->start);
+
+ if (elapsed > 3) {
+ d_printf("cli_lockingx was too slow, cancel did not work\n");
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS lock_ntcancel_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_locktest13(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ struct cli_state *cli = NULL;
+ const char fname[] = "\\lockt13.lck";
+ uint16_t fnum1, fnum2;
+ bool ret = false;
+ bool ok;
+ uint8_t data = 1;
+ NTSTATUS status;
+
+ printf("starting locktest13\n");
+
+ ev = samba_tevent_context_init(NULL);
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ goto done;
+ }
+
+ ok = torture_open_connection(&cli, 0);
+ if (!ok) {
+ goto done;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = cli_openx(cli, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_openx(cli, fname, O_CREAT|O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_openx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_writeall(cli, fnum1, 0, &data, 0, sizeof(data), NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_writeall failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = cli_locktype(
+ cli, fnum1, 0, 1, 0, LOCKING_ANDX_EXCLUSIVE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_locktype failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ req = lock_ntcancel_send(ev, ev, cli, fnum2);
+ if (req == NULL) {
+ d_fprintf(stderr, "lock_ntcancel_send failed\n");
+ goto done;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr, "tevent_req_poll_ntstatus failed\n");
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "tevent_req_poll_ntstatus returned %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = lock_ntcancel_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "lock_ntcancel returned %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+done:
+ if (cli != NULL) {
+ torture_close_connection(cli);
+ }
+ return ret;
+}
+
+/*
+test whether fnums and tids open on one VC are available on another (a major
+security hole)
+*/
+static bool run_fdpasstest(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\fdpass.tst";
+ uint16_t fnum1;
+ char buf[1024];
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli1, 0) || !torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("starting fdpasstest\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_writeall(cli1, fnum1, 0, (const uint8_t *)"hello world\n", 0,
+ 13, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ cli_state_set_uid(cli2, cli_state_get_uid(cli1));
+ cli_state_set_tid(cli2, cli_state_get_tid(cli1));
+ cli_setpid(cli2, cli_getpid(cli1));
+
+ if (test_cli_read(cli2, fnum1, buf, 0, 13, NULL, 13)) {
+ printf("read succeeded! nasty security hole [%s]\n", buf);
+ return false;
+ }
+
+ cli_close(cli1, fnum1);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ torture_close_connection(cli1);
+ torture_close_connection(cli2);
+
+ printf("finished fdpasstest\n");
+ return True;
+}
+
+static bool run_fdsesstest(int dummy)
+{
+ struct cli_state *cli;
+ uint16_t new_vuid;
+ uint16_t saved_vuid;
+ uint32_t new_cnum;
+ uint32_t saved_cnum;
+ const char *fname = "\\fdsess.tst";
+ const char *fname1 = "\\fdsess1.tst";
+ uint16_t fnum1;
+ uint16_t fnum2;
+ char buf[1024];
+ bool ret = True;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0))
+ return False;
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ if (!torture_cli_session_setup2(cli, &new_vuid))
+ return False;
+
+ saved_cnum = cli_state_get_tid(cli);
+ if (!NT_STATUS_IS_OK(cli_tree_connect(cli, share, "?????", NULL)))
+ return False;
+ new_cnum = cli_state_get_tid(cli);
+ cli_state_set_tid(cli, saved_cnum);
+
+ printf("starting fdsesstest\n");
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_writeall(cli, fnum1, 0, (const uint8_t *)"hello world\n", 0, 13,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ saved_vuid = cli_state_get_uid(cli);
+ cli_state_set_uid(cli, new_vuid);
+
+ if (test_cli_read(cli, fnum1, buf, 0, 13, NULL, 13)) {
+ printf("read succeeded with different vuid! "
+ "nasty security hole [%s]\n", buf);
+ ret = false;
+ }
+ /* Try to open a file with different vuid, samba cnum. */
+ if (NT_STATUS_IS_OK(cli_openx(cli, fname1, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum2))) {
+ printf("create with different vuid, same cnum succeeded.\n");
+ cli_close(cli, fnum2);
+ cli_unlink(cli, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ } else {
+ printf("create with different vuid, same cnum failed.\n");
+ printf("This will cause problems with service clients.\n");
+ ret = False;
+ }
+
+ cli_state_set_uid(cli, saved_vuid);
+
+ /* Try with same vuid, different cnum. */
+ cli_state_set_tid(cli, new_cnum);
+
+ if (test_cli_read(cli, fnum1, buf, 0, 13, NULL, 13)) {
+ printf("read succeeded with different cnum![%s]\n", buf);
+ ret = false;
+ }
+
+ cli_state_set_tid(cli, saved_cnum);
+ cli_close(cli, fnum1);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ torture_close_connection(cli);
+
+ printf("finished fdsesstest\n");
+ return ret;
+}
+
+/*
+ This test checks that
+
+ 1) the server does not allow an unlink on a file that is open
+*/
+static bool run_unlinktest(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\unlink.tst";
+ uint16_t fnum;
+ bool correct = True;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ printf("starting unlink test\n");
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ cli_setpid(cli, 1);
+
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_unlink(cli, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("error: server allowed unlink on an open file\n");
+ correct = False;
+ } else {
+ correct = check_error(__LINE__, status, ERRDOS, ERRbadshare,
+ NT_STATUS_SHARING_VIOLATION);
+ }
+
+ cli_close(cli, fnum);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("unlink test finished\n");
+
+ return correct;
+}
+
+
+/*
+test how many open files this server supports on the one socket
+*/
+static bool run_maxfidtest(int dummy)
+{
+ struct cli_state *cli;
+ fstring fname;
+ uint16_t fnums[0x11000];
+ int i;
+ int retries=4;
+ bool correct = True;
+ NTSTATUS status;
+
+ cli = current_cli;
+
+ if (retries <= 0) {
+ printf("failed to connect\n");
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ for (i=0; i<0x11000; i++) {
+ slprintf(fname,sizeof(fname)-1,"\\maxfid.%d.%d", i,(int)getpid());
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE,
+ &fnums[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n",
+ fname, nt_errstr(status));
+ printf("maximum fnum is %d\n", i);
+ break;
+ }
+ printf("%6d\r", i);
+ }
+ printf("%6d\n", i);
+ i--;
+
+ printf("cleaning up\n");
+ for (;i>=0;i--) {
+ slprintf(fname,sizeof(fname)-1,"\\maxfid.%d.%d", i,(int)getpid());
+ cli_close(cli, fnums[i]);
+
+ status = cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink of %s failed (%s)\n",
+ fname, nt_errstr(status));
+ correct = False;
+ }
+ printf("%6d\r", i);
+ }
+ printf("%6d\n", 0);
+
+ printf("maxfid test finished\n");
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+ return correct;
+}
+
+/* generate a random buffer */
+static void rand_buf(char *buf, int len)
+{
+ while (len--) {
+ *buf = (char)sys_random();
+ buf++;
+ }
+}
+
+/* send smb negprot commands, not reading the response */
+static bool run_negprot_nowait(int dummy)
+{
+ struct tevent_context *ev;
+ int i;
+ struct cli_state *cli;
+ bool correct = True;
+
+ printf("starting negprot nowait test\n");
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ return false;
+ }
+
+ if (!(cli = open_nbt_connection())) {
+ TALLOC_FREE(ev);
+ return False;
+ }
+
+ for (i=0;i<50000;i++) {
+ struct tevent_req *req;
+
+ req = smbXcli_negprot_send(
+ ev,
+ ev,
+ cli->conn,
+ cli->timeout,
+ PROTOCOL_CORE,
+ PROTOCOL_NT1,
+ 0,
+ NULL);
+ if (req == NULL) {
+ TALLOC_FREE(ev);
+ return false;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ d_fprintf(stderr, "tevent_req_poll failed: %s\n",
+ strerror(errno));
+ TALLOC_FREE(ev);
+ return false;
+ }
+ TALLOC_FREE(req);
+ }
+
+ if (torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished negprot nowait test\n");
+
+ return correct;
+}
+
+/* send smb negprot commands, not reading the response */
+static bool run_bad_nbt_session(int dummy)
+{
+ struct nmb_name called, calling;
+ struct sockaddr_storage ss;
+ NTSTATUS status;
+ int fd;
+ bool ret;
+
+ printf("starting bad nbt session test\n");
+
+ make_nmb_name(&calling, myname, 0x0);
+ make_nmb_name(&called , host, 0x20);
+
+ if (!resolve_name(host, &ss, 0x20, true)) {
+ d_fprintf(stderr, "Could not resolve name %s\n", host);
+ return false;
+ }
+
+ status = open_socket_out(&ss, NBT_SMB_PORT, 10000, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "open_socket_out failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ ret = cli_bad_session_request(fd, &calling, &called);
+ close(fd);
+ if (!ret) {
+ d_fprintf(stderr, "open_socket_out failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ printf("finished bad nbt session test\n");
+ return true;
+}
+
+/* send random IPC commands */
+static bool run_randomipc(int dummy)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char param[1024];
+ int api, param_len, i;
+ struct cli_state *cli;
+ bool correct = True;
+ int count = 50000;
+
+ printf("starting random ipc test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ for (i=0;i<count;i++) {
+ api = sys_random() % 500;
+ param_len = (sys_random() % 64);
+
+ rand_buf(param, param_len);
+
+ SSVAL(param,0,api);
+
+ cli_api(cli,
+ param, param_len, 8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt);
+ if (i % 100 == 0) {
+ printf("%d/%d\r", i,count);
+ }
+ }
+ printf("%d/%d\n", i, count);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ printf("finished random ipc test\n");
+
+ return correct;
+}
+
+
+
+static void browse_callback(const char *sname, uint32_t stype,
+ const char *comment, void *state)
+{
+ printf("\t%20.20s %08x %s\n", sname, stype, comment);
+}
+
+
+
+/*
+ This test checks the browse list code
+
+*/
+static bool run_browsetest(int dummy)
+{
+ static struct cli_state *cli;
+ bool correct = True;
+
+ printf("starting browse test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ printf("domain list:\n");
+ cli_NetServerEnum(cli, cli->server_domain,
+ SV_TYPE_DOMAIN_ENUM,
+ browse_callback, NULL);
+
+ printf("machine list:\n");
+ cli_NetServerEnum(cli, cli->server_domain,
+ SV_TYPE_ALL,
+ browse_callback, NULL);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("browse test finished\n");
+
+ return correct;
+
+}
+
+static bool check_attributes(struct cli_state *cli,
+ const char *fname,
+ uint32_t expected_attrs)
+{
+ uint32_t attrs = 0;
+ NTSTATUS status = cli_getatr(cli,
+ fname,
+ &attrs,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_getatr failed with %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (attrs != expected_attrs) {
+ printf("Attributes incorrect 0x%x, should be 0x%x\n",
+ (unsigned int)attrs,
+ (unsigned int)expected_attrs);
+ return false;
+ }
+ return true;
+}
+
+/*
+ This checks how the getatr calls works
+*/
+static bool run_attrtest(int dummy)
+{
+ struct cli_state *cli;
+ uint16_t fnum;
+ time_t t, t2;
+ const char *fname = "\\attrib123456789.tst";
+ bool correct = True;
+ NTSTATUS status;
+
+ printf("starting attrib test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ /* Ensure we can't unlink with out-of-range (unknown) attribute. */
+ status = cli_unlink(cli, fname, 0x20000);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ correct = false;
+ goto out;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_openx(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum);
+ cli_close(cli, fnum);
+
+ status = cli_getatr(cli, fname, NULL, NULL, &t);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("getatr failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ if (labs(t - time(NULL)) > 60*60*24*10) {
+ printf("ERROR: SMBgetatr bug. time is %s",
+ ctime(&t));
+ t = time(NULL);
+ correct = True;
+ }
+
+ t2 = t-60*60*24; /* 1 day ago */
+
+ /* Ensure we can't set with out-of-range (unknown) attribute. */
+ status = cli_setatr(cli, fname, 0x20000, t2);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ correct = false;
+ goto out;
+ }
+
+ status = cli_setatr(cli, fname, 0, t2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("setatr failed (%s)\n", nt_errstr(status));
+ correct = True;
+ }
+
+ status = cli_getatr(cli, fname, NULL, NULL, &t);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("getatr failed (%s)\n", nt_errstr(status));
+ correct = True;
+ }
+
+ if (t != t2) {
+ printf("ERROR: getatr/setatr bug. times are\n%s",
+ ctime(&t));
+ printf("%s", ctime(&t2));
+ correct = True;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Check cli_setpathinfo_ext() */
+ /* Re-create the file. */
+ status = cli_openx(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to recreate %s (%s)\n",
+ fname, nt_errstr(status));
+ correct = false;
+ }
+ cli_close(cli, fnum);
+
+ status = cli_setpathinfo_ext(
+ cli,
+ fname,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* write */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_READONLY);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_setpathinfo_ext failed with %s\n",
+ nt_errstr(status));
+ correct = false;
+ }
+
+ /* Check attributes are correct. */
+ correct = check_attributes(cli,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_READONLY);
+ if (correct == false) {
+ goto out;
+ }
+
+ /* Setting to FILE_ATTRIBUTE_NORMAL should be ignored. */
+ status = cli_setpathinfo_ext(
+ cli,
+ fname,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* write */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
+ FILE_ATTRIBUTE_NORMAL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_setpathinfo_ext failed with %s\n",
+ nt_errstr(status));
+ correct = false;
+ }
+
+ /* Check attributes are correct. */
+ correct = check_attributes(cli,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_READONLY);
+ if (correct == false) {
+ goto out;
+ }
+
+ /* Setting to (uint16_t)-1 should also be ignored. */
+ status = cli_setpathinfo_ext(
+ cli,
+ fname,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* write */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
+ (uint32_t)-1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_setpathinfo_ext failed with %s\n",
+ nt_errstr(status));
+ correct = false;
+ }
+
+ /* Check attributes are correct. */
+ correct = check_attributes(cli,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_READONLY);
+ if (correct == false) {
+ goto out;
+ }
+
+ /* Setting to 0 should clear them all. */
+ status = cli_setpathinfo_ext(
+ cli,
+ fname,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* write */
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
+ 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_setpathinfo_ext failed with %s\n",
+ nt_errstr(status));
+ correct = false;
+ }
+
+ /* Check attributes are correct. */
+ correct = check_attributes(cli,
+ fname,
+ FILE_ATTRIBUTE_NORMAL);
+ if (correct == false) {
+ goto out;
+ }
+
+ out:
+
+ cli_unlink(cli,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN|
+ FILE_ATTRIBUTE_READONLY);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("attrib test finished\n");
+
+ return correct;
+}
+
+static NTSTATUS cli_qfilename(
+ struct cli_state *cli,
+ uint16_t fnum,
+ TALLOC_CTX *mem_ctx,
+ char **_name)
+{
+ uint16_t recv_flags2;
+ uint8_t *rdata;
+ uint32_t num_rdata;
+ NTSTATUS status;
+ char *name = NULL;
+ uint32_t namelen;
+
+ status = cli_qfileinfo(talloc_tos(), cli, fnum,
+ SMB_QUERY_FILE_NAME_INFO,
+ 4, CLI_BUFFER_SIZE, &recv_flags2,
+ &rdata, &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ namelen = IVAL(rdata, 0);
+ if (namelen > (num_rdata - 4)) {
+ TALLOC_FREE(rdata);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ pull_string_talloc(mem_ctx,
+ (const char *)rdata,
+ recv_flags2,
+ &name,
+ rdata + 4,
+ namelen,
+ STR_UNICODE);
+ if (name == NULL) {
+ status = map_nt_error_from_unix(errno);
+ TALLOC_FREE(rdata);
+ return status;
+ }
+
+ *_name = name;
+ TALLOC_FREE(rdata);
+ return NT_STATUS_OK;
+}
+
+/*
+ This checks a couple of trans2 calls
+*/
+static bool run_trans2test(int dummy)
+{
+ struct cli_state *cli;
+ uint16_t fnum;
+ off_t size;
+ time_t c_time, a_time, m_time;
+ struct timespec c_time_ts, a_time_ts, m_time_ts, w_time_ts, m_time2_ts;
+ const char *fname = "\\trans2.tst";
+ const char *dname = "\\trans2";
+ const char *fname2 = "\\trans2\\trans2.tst";
+ char *pname = NULL;
+ bool correct = True;
+ NTSTATUS status;
+ uint32_t fs_attr;
+ uint64_t ino;
+
+ printf("starting trans2 test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ /* Ensure ino is zero, SMB2 gets a real one. */
+ ino = 0;
+ } else {
+ /* Ensure ino is -1, SMB1 never gets a real one. */
+ ino = (uint64_t)-1;
+ }
+
+ status = cli_get_fs_attr_info(cli, &fs_attr);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: cli_get_fs_attr_info returned %s\n",
+ nt_errstr(status));
+ correct = false;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_openx(cli, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum);
+ status = cli_qfileinfo_basic(cli, fnum, NULL, &size, &c_time_ts,
+ &a_time_ts, &w_time_ts, &m_time_ts, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qfileinfo failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_qfilename(cli, fnum, talloc_tos(), &pname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qfilename failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+ else if (strcmp(pname, fname)) {
+ printf("qfilename gave different name? [%s] [%s]\n",
+ fname, pname);
+ correct = False;
+ }
+
+ cli_close(cli, fnum);
+
+ sleep(2);
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ status = cli_openx(cli, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+ cli_close(cli, fnum);
+
+ status = cli_qpathinfo1(cli, fname, &c_time, &a_time, &m_time, &size,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qpathinfo failed (%s)\n", nt_errstr(status));
+ correct = False;
+ } else {
+ time_t t = time(NULL);
+
+ if (c_time != m_time) {
+ printf("create time=%s", ctime(&c_time));
+ printf("modify time=%s", ctime(&m_time));
+ printf("This system appears to have sticky create times\n");
+ }
+ if ((labs(a_time - t) > 60) && (a_time % (60*60) == 0)) {
+ printf("access time=%s", ctime(&a_time));
+ printf("This system appears to set a midnight access time\n");
+ correct = False;
+ }
+
+ if (labs(m_time - t) > 60*60*24*7) {
+ printf("ERROR: totally incorrect times - maybe word reversed? mtime=%s", ctime(&m_time));
+ correct = False;
+ }
+ }
+
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_openx(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum);
+ cli_close(cli, fnum);
+ status = cli_qpathinfo2(cli,
+ fname,
+ &c_time_ts,
+ &a_time_ts,
+ &w_time_ts,
+ &m_time_ts,
+ &size,
+ NULL,
+ &ino,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", nt_errstr(status));
+ correct = False;
+ } else {
+ if (w_time_ts.tv_sec < 60*60*24*2) {
+ printf("write time=%s", ctime(&w_time_ts.tv_sec));
+ printf("This system appears to set a initial 0 write time\n");
+ correct = False;
+ }
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ /* SMB2 should always return an inode. */
+ if (ino == 0) {
+ printf("SMB2 bad inode (0)\n");
+ correct = false;
+ }
+ } else {
+ /* SMB1 must always return zero here. */
+ if (ino != 0) {
+ printf("SMB1 bad inode (!0)\n");
+ correct = false;
+ }
+ }
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+
+ /* check if the server updates the directory modification time
+ when creating a new file */
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: mkdir failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+ sleep(3);
+ status = cli_qpathinfo2(cli,
+ "\\trans2\\",
+ &c_time_ts,
+ &a_time_ts,
+ &w_time_ts,
+ &m_time_ts,
+ &size,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ cli_openx(cli, fname2,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum);
+ cli_writeall(cli, fnum, 0, (uint8_t *)&fnum, 0, sizeof(fnum), NULL);
+ cli_close(cli, fnum);
+ status = cli_qpathinfo2(cli,
+ "\\trans2\\",
+ &c_time_ts,
+ &a_time_ts,
+ &w_time_ts,
+ &m_time2_ts,
+ &size,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", nt_errstr(status));
+ correct = False;
+ } else {
+ if (memcmp(&m_time_ts, &m_time2_ts, sizeof(struct timespec))
+ == 0) {
+ printf("This system does not update directory modification times\n");
+ correct = False;
+ }
+ }
+ cli_unlink(cli, fname2, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, dname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("trans2 test finished\n");
+
+ return correct;
+}
+
+/*
+ This checks new W2K calls.
+*/
+
+static NTSTATUS new_trans(struct cli_state *pcli, int fnum, int level)
+{
+ uint8_t *buf = NULL;
+ uint32_t len;
+ NTSTATUS status;
+
+ status = cli_qfileinfo(talloc_tos(), pcli, fnum, level, 0,
+ CLI_BUFFER_SIZE, NULL, &buf, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: qfileinfo (%d) failed (%s)\n", level,
+ nt_errstr(status));
+ } else {
+ printf("qfileinfo: level %d, len = %u\n", level, len);
+ dump_data(0, (uint8_t *)buf, len);
+ printf("\n");
+ }
+ TALLOC_FREE(buf);
+ return status;
+}
+
+static bool run_w2ktest(int dummy)
+{
+ struct cli_state *cli;
+ uint16_t fnum;
+ const char *fname = "\\w2ktest\\w2k.tst";
+ int level;
+ bool correct = True;
+
+ printf("starting w2k test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ cli_openx(cli, fname,
+ O_RDWR | O_CREAT , DENY_NONE, &fnum);
+
+ for (level = 1004; level < 1040; level++) {
+ new_trans(cli, fnum, level);
+ }
+
+ cli_close(cli, fnum);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("w2k test finished\n");
+
+ return correct;
+}
+
+
+/*
+ this is a harness for some oplock tests
+ */
+static bool run_oplock1(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt1.lck";
+ uint16_t fnum1;
+ bool correct = True;
+ NTSTATUS status;
+
+ printf("starting oplock test 1\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ cli1->use_oplocks = True;
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ cli1->use_oplocks = False;
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ printf("finished oplock test 1\n");
+
+ return correct;
+}
+
+static bool run_oplock2(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt2.lck";
+ uint16_t fnum1, fnum2;
+ int saved_use_oplocks = use_oplocks;
+ char buf[4];
+ bool correct = True;
+ volatile bool *shared_correct;
+ size_t nread;
+ NTSTATUS status;
+
+ shared_correct = (volatile bool *)anonymous_shared_allocate(sizeof(bool));
+ *shared_correct = True;
+
+ use_level_II_oplocks = True;
+ use_oplocks = True;
+
+ printf("starting oplock test 2\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+ return False;
+ }
+
+ if (!torture_open_connection(&cli2, 1)) {
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ /* Don't need the globals any more. */
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+
+ if (fork() == 0) {
+ /* Child code */
+ status = cli_openx(cli2, fname, O_RDWR, DENY_NONE, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("second open of %s failed (%s)\n", fname, nt_errstr(status));
+ *shared_correct = False;
+ exit(0);
+ }
+
+ sleep(2);
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ *shared_correct = False;
+ }
+
+ exit(0);
+ }
+
+ sleep(2);
+
+ /* Ensure cli1 processes the break. Empty file should always return 0
+ * bytes. */
+ status = cli_read(cli1, fnum1, buf, 0, 4, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("read on fnum1 failed (%s)\n", nt_errstr(status));
+ correct = false;
+ } else if (nread != 0) {
+ printf("read on empty fnum1 failed. recv %ld expected %d\n",
+ (unsigned long)nread, 0);
+ correct = false;
+ }
+
+ /* Should now be at level II. */
+ /* Test if sending a write locks causes a break to none. */
+ status = cli_lock32(cli1, fnum1, 0, 4, 0, READ_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ sleep(2);
+
+ status = cli_lock32(cli1, fnum1, 0, 4, 0, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lock failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ sleep(2);
+
+ cli_read(cli1, fnum1, buf, 0, 4, NULL);
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ sleep(4);
+
+ status = cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ if (!*shared_correct) {
+ correct = False;
+ }
+
+ printf("finished oplock test 2\n");
+
+ return correct;
+}
+
+struct oplock4_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ bool *got_break;
+ uint16_t *fnum2;
+};
+
+static void oplock4_got_break(struct tevent_req *req);
+static void oplock4_got_open(struct tevent_req *req);
+
+static bool run_oplock4(int dummy)
+{
+ struct tevent_context *ev;
+ struct cli_state *cli1, *cli2;
+ struct tevent_req *oplock_req, *open_req;
+ const char *fname = "\\lockt4.lck";
+ const char *fname_ln = "\\lockt4_ln.lck";
+ uint16_t fnum1, fnum2;
+ int saved_use_oplocks = use_oplocks;
+ NTSTATUS status;
+ bool correct = true;
+
+ bool got_break;
+
+ struct oplock4_state *state;
+
+ printf("starting oplock test 4\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ use_level_II_oplocks = false;
+ use_oplocks = saved_use_oplocks;
+ return false;
+ }
+
+ if (!torture_open_connection(&cli2, 1)) {
+ use_level_II_oplocks = false;
+ use_oplocks = saved_use_oplocks;
+ return false;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname_ln, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ /* Create the file. */
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Now create a hardlink. */
+ status = cli_hardlink(cli1, fname, fname_ln);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("nt hardlink failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Prove that opening hardlinks cause deny modes to conflict. */
+ status = cli_openx(cli1, fname, O_RDWR, DENY_ALL, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_openx(cli1, fname_ln, O_RDWR, DENY_NONE, &fnum2);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("open of %s succeeded - should fail with sharing violation.\n",
+ fname_ln);
+ return false;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ printf("open of %s should fail with sharing violation. Got %s\n",
+ fname_ln, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ cli1->use_oplocks = true;
+ cli2->use_oplocks = true;
+
+ status = cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("tevent_context_init failed\n");
+ return false;
+ }
+
+ state = talloc(ev, struct oplock4_state);
+ if (state == NULL) {
+ printf("talloc failed\n");
+ return false;
+ }
+ state->ev = ev;
+ state->cli = cli1;
+ state->got_break = &got_break;
+ state->fnum2 = &fnum2;
+
+ oplock_req = cli_smb_oplock_break_waiter_send(
+ talloc_tos(), ev, cli1);
+ if (oplock_req == NULL) {
+ printf("cli_smb_oplock_break_waiter_send failed\n");
+ return false;
+ }
+ tevent_req_set_callback(oplock_req, oplock4_got_break, state);
+
+ open_req = cli_openx_send(
+ talloc_tos(), ev, cli2, fname_ln, O_RDWR, DENY_NONE);
+ if (open_req == NULL) {
+ printf("cli_openx_send failed\n");
+ return false;
+ }
+ tevent_req_set_callback(open_req, oplock4_got_open, state);
+
+ got_break = false;
+ fnum2 = 0xffff;
+
+ while (!got_break || fnum2 == 0xffff) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret == -1) {
+ printf("tevent_loop_once failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ correct = false;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ correct = false;
+ }
+
+ status = cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = false;
+ }
+
+ status = cli_unlink(cli1, fname_ln, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = false;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = false;
+ }
+
+ if (!got_break) {
+ correct = false;
+ }
+
+ printf("finished oplock test 4\n");
+
+ return correct;
+}
+
+static void oplock4_got_break(struct tevent_req *req)
+{
+ struct oplock4_state *state = tevent_req_callback_data(
+ req, struct oplock4_state);
+ uint16_t fnum;
+ uint8_t level;
+ NTSTATUS status;
+
+ status = cli_smb_oplock_break_waiter_recv(req, &fnum, &level);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_smb_oplock_break_waiter_recv returned %s\n",
+ nt_errstr(status));
+ return;
+ }
+ *state->got_break = true;
+
+ req = cli_oplock_ack_send(state, state->ev, state->cli, fnum,
+ NO_OPLOCK);
+ if (req == NULL) {
+ printf("cli_oplock_ack_send failed\n");
+ return;
+ }
+}
+
+static void oplock4_got_open(struct tevent_req *req)
+{
+ struct oplock4_state *state = tevent_req_callback_data(
+ req, struct oplock4_state);
+ NTSTATUS status;
+
+ status = cli_openx_recv(req, state->fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_openx_recv returned %s\n", nt_errstr(status));
+ *state->fnum2 = 0xffff;
+ }
+}
+
+#ifdef HAVE_KERNEL_OPLOCKS_LINUX
+
+struct oplock5_state {
+ int pipe_down_fd;
+};
+
+/*
+ * Async open the file that has a kernel oplock, do an echo to get
+ * that 100% across, close the file to signal to the child fd that the
+ * oplock can be dropped, wait for the open reply.
+ */
+
+static void oplock5_opened(struct tevent_req *subreq);
+static void oplock5_pong(struct tevent_req *subreq);
+static void oplock5_timedout(struct tevent_req *subreq);
+
+static struct tevent_req *oplock5_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ int pipe_down_fd)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct oplock5_state *state = NULL;
+ static uint8_t data = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct oplock5_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->pipe_down_fd = pipe_down_fd;
+
+ subreq = cli_ntcreate_send(
+ state,
+ ev,
+ cli,
+ fname,
+ 0, /* CreatFlags */
+ SEC_FILE_READ_DATA, /* DesiredAccess */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ FILE_NON_DIRECTORY_FILE, /* CreateOptions */
+ 0, /* Impersonation */
+ 0); /* SecurityFlags */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, oplock5_opened, req);
+
+ subreq = cli_echo_send(
+ state,
+ ev,
+ cli,
+ 1,
+ (DATA_BLOB) { .data = &data, .length = sizeof(data) });
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, oplock5_pong, req);
+
+ subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(20, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, oplock5_timedout, req);
+
+ return req;
+}
+
+static void oplock5_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+ uint16_t fnum;
+
+ status = cli_ntcreate_recv(subreq, &fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void oplock5_pong(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct oplock5_state *state = tevent_req_data(
+ req, struct oplock5_state);
+ NTSTATUS status;
+
+ status = cli_echo_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ close(state->pipe_down_fd);
+}
+
+static void oplock5_timedout(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ tevent_req_nterror(req, NT_STATUS_TIMEOUT);
+}
+
+static NTSTATUS oplock5_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_oplock5(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ struct cli_state *cli = NULL;
+ const char *fname = "oplock5.txt";
+ int pipe_down[2], pipe_up[2];
+ pid_t child_pid;
+ uint8_t c = '\0';
+ NTSTATUS status;
+ int ret;
+ bool ok;
+
+ printf("starting oplock5\n");
+
+ if (local_path == NULL) {
+ d_fprintf(stderr, "oplock5 must be given a local path via "
+ "-l <localpath>\n");
+ return false;
+ }
+
+ ret = pipe(pipe_down);
+ if (ret == -1) {
+ d_fprintf(stderr, "pipe() failed: %s\n", strerror(errno));
+ return false;
+ }
+ ret = pipe(pipe_up);
+ if (ret == -1) {
+ d_fprintf(stderr, "pipe() failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ d_fprintf(stderr, "fork() failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ if (child_pid == 0) {
+ char *local_file = NULL;
+ int fd;
+
+ close(pipe_down[1]);
+ close(pipe_up[0]);
+
+ local_file = talloc_asprintf(
+ talloc_tos(), "%s/%s", local_path, fname);
+ if (local_file == 0) {
+ c = 1;
+ goto do_write;
+ }
+ fd = open(local_file, O_RDWR|O_CREAT, 0644);
+ if (fd == -1) {
+ d_fprintf(stderr,
+ "open(%s) in child failed: %s\n",
+ local_file,
+ strerror(errno));
+ c = 2;
+ goto do_write;
+ }
+
+ signal(SIGIO, SIG_IGN);
+
+ ret = fcntl(fd, F_SETLEASE, F_WRLCK);
+ if (ret == -1) {
+ d_fprintf(stderr,
+ "SETLEASE in child failed: %s\n",
+ strerror(errno));
+ c = 3;
+ goto do_write;
+ }
+
+ do_write:
+ ret = sys_write(pipe_up[1], &c, sizeof(c));
+ if (ret == -1) {
+ d_fprintf(stderr,
+ "sys_write failed: %s\n",
+ strerror(errno));
+ exit(4);
+ }
+ ret = sys_read(pipe_down[0], &c, sizeof(c));
+ if (ret == -1) {
+ d_fprintf(stderr,
+ "sys_read failed: %s\n",
+ strerror(errno));
+ exit(5);
+ }
+ exit(0);
+ }
+
+ close(pipe_up[1]);
+ close(pipe_down[0]);
+
+ ret = sys_read(pipe_up[0], &c, sizeof(c));
+ if (ret != 1) {
+ d_fprintf(stderr,
+ "sys_read failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ if (c != 0) {
+ d_fprintf(stderr, "got error code %"PRIu8"\n", c);
+ return false;
+ }
+
+ ok = torture_open_connection(&cli, 0);
+ if (!ok) {
+ d_fprintf(stderr, "torture_open_connection failed\n");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ return false;
+ }
+
+ req = oplock5_send(ev, ev, cli, fname, pipe_down[1]);
+ if (req == NULL) {
+ d_fprintf(stderr, "oplock5_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr,
+ "tevent_req_poll_ntstatus failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = oplock5_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "oplock5 failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
+
+/*
+ Test delete on close semantics.
+ */
+static bool run_deletetest(int dummy)
+{
+ struct cli_state *cli1 = NULL;
+ struct cli_state *cli2 = NULL;
+ const char *fname = "\\delete.file";
+ uint16_t fnum1 = (uint16_t)-1;
+ uint16_t fnum2 = (uint16_t)-1;
+ bool correct = false;
+ NTSTATUS status;
+
+ printf("starting delete test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ /* Test 1 - this should delete the file on close. */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ FILE_DELETE_ON_CLOSE, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[1] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[1] close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_openx(cli1, fname, O_RDWR, DENY_NONE, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[1] open of %s succeeded (should fail)\n", fname);
+ goto fail;
+ }
+
+ printf("first delete on close test succeeded.\n");
+
+ /* Test 2 - this should delete the file on close. */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[2] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[2] setting delete_on_close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[2] close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_NONE, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[2] open of %s succeeded should have been deleted on close !\n", fname);
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[2] close failed (%s)\n", nt_errstr(status));
+ }
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ goto fail;
+ }
+
+ printf("second delete on close test succeeded.\n");
+
+ /* Test 3 - ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[3] open - 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail with a sharing violation - open for delete is only compatible
+ with SHARE_DELETE. */
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[3] open - 2 of %s succeeded - should have failed.\n", fname);
+ goto fail;
+ }
+
+ /* This should succeed. */
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[3] open - 3 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[3] setting delete_on_close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[3] close 1 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[3] close 2 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail - file should no longer be there. */
+
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_NONE, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[3] open of %s succeeded should have been deleted on close !\n", fname);
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[3] close failed (%s)\n", nt_errstr(status));
+ }
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ goto fail;
+ }
+
+ printf("third delete on close test succeeded.\n");
+
+ /* Test 4 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[4] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should succeed. */
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[4] open - 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[4] close - 1 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[4] setting delete_on_close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail - no more opens once delete on close set. */
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[4] open - 3 of %s succeeded ! Should have failed.\n", fname );
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[4] close - 2 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ printf("fourth delete on close test succeeded.\n");
+
+ /* Test 5 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[5] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail - only allowed on NT opens with DELETE access. */
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[5] setting delete_on_close on OpenX file succeeded - should fail !\n");
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[5] close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ printf("fifth delete on close test succeeded.\n");
+
+ /* Test 6 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[6] open of %s failed (%s)\n", fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail - only allowed on NT opens with DELETE access. */
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[6] setting delete_on_close on file with no delete access succeeded - should fail !\n");
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[6] close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ printf("sixth delete on close test succeeded.\n");
+
+ /* Test 7 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[7] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[7] setting delete_on_close on file failed !\n");
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[7] unsetting delete_on_close on file failed !\n");
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[7] close - 1 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* This next open should succeed - we reset the flag. */
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[7] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[7] close - 2 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ printf("seventh delete on close test succeeded.\n");
+
+ /* Test 8 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_open_connection(&cli2, 1)) {
+ printf("[8] failed to open second connection.\n");
+ goto fail;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[8] open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[8] open 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[8] setting delete_on_close on file failed !\n");
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[8] close - 1 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[8] close - 2 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail.. */
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_NONE, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[8] open of %s succeeded should have been deleted on close !\n", fname);
+ goto fail;
+ }
+
+ printf("eighth delete on close test succeeded.\n");
+
+ /* Test 9 ... */
+
+ /* This should fail - we need to set DELETE_ACCESS. */
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF,
+ FILE_DELETE_ON_CLOSE, 0, &fnum1, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[9] open of %s succeeded should have failed!\n", fname);
+ goto fail;
+ }
+
+ printf("ninth delete on close test succeeded.\n");
+
+ /* Test 10 ... */
+
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, FILE_DELETE_ON_CLOSE,
+ 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[10] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should delete the file. */
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[10] close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail.. */
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_NONE, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[10] open of %s succeeded should have been deleted on close !\n", fname);
+ goto fail;
+ }
+
+ printf("tenth delete on close test succeeded.\n");
+
+ /* Test 11 ... */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Can we open a read-only file with delete access? */
+
+ /* Create a readonly file. */
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_READONLY, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[11] open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[11] close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* Now try open for delete access. */
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_ATTRIBUTES|DELETE_ACCESS,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[11] open of %s failed: %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ cli_close(cli1, fnum1);
+
+ printf("eleventh delete on close test succeeded.\n");
+
+ /*
+ * Test 12
+ * like test 4 but with initial delete on close
+ */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OVERWRITE_IF,
+ FILE_DELETE_ON_CLOSE, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] open 2 of %s failed(%s).\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] close 1 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] setting delete_on_close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /* This should fail - no more opens once delete on close set. */
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[12] open 3 of %s succeeded - should fail).\n", fname);
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] unsetting delete_on_close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] open 4 of %s failed (%s)\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] close 2 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("[12] close 3 failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * setting delete on close on the handle does
+ * not unset the initial delete on close...
+ */
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum2, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("[12] open 5 of %s succeeded - should fail).\n", fname);
+ goto fail;
+ } else if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("ntcreate returned %s, expected "
+ "NT_STATUS_OBJECT_NAME_NOT_FOUND\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ printf("twelfth delete on close test succeeded.\n");
+
+
+ printf("finished delete test\n");
+
+ correct = true;
+
+ fail:
+ /* FIXME: This will crash if we aborted before cli2 got
+ * initialized, because these functions don't handle
+ * uninitialized connections. */
+
+ if (fnum1 != (uint16_t)-1) cli_close(cli1, fnum1);
+ if (fnum2 != (uint16_t)-1) cli_close(cli1, fnum2);
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (cli1 && !torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (cli2 && !torture_close_connection(cli2)) {
+ correct = False;
+ }
+ return correct;
+}
+
+struct delete_stream_state {
+ bool closed;
+};
+
+static void delete_stream_unlinked(struct tevent_req *subreq);
+static void delete_stream_closed(struct tevent_req *subreq);
+
+static struct tevent_req *delete_stream_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *base_fname,
+ uint16_t stream_fnum)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct delete_stream_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct delete_stream_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_unlink_send(
+ state,
+ ev,
+ cli,
+ base_fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, delete_stream_unlinked, req);
+
+ subreq = cli_close_send(state, ev, cli, stream_fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, delete_stream_closed, req);
+
+ return req;
+}
+
+static void delete_stream_unlinked(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct delete_stream_state *state = tevent_req_data(
+ req, struct delete_stream_state);
+ NTSTATUS status;
+
+ status = cli_unlink_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ printf("cli_unlink returned %s\n",
+ nt_errstr(status));
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+ if (!state->closed) {
+ /* close reply should have come in first */
+ printf("Not closed\n");
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void delete_stream_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct delete_stream_state *state = tevent_req_data(
+ req, struct delete_stream_state);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ /* also waiting for the unlink to come back */
+ state->closed = true;
+}
+
+static NTSTATUS delete_stream_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_delete_stream(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ struct cli_state *cli = NULL;
+ const char fname[] = "delete_stream";
+ const char fname_stream[] = "delete_stream:Zone.Identifier:$DATA";
+ uint16_t fnum1, fnum2;
+ NTSTATUS status;
+ bool ok;
+
+ printf("Starting stream delete test\n");
+
+ ok = torture_open_connection(&cli, 0);
+ if (!ok) {
+ return false;
+ }
+
+ cli_setatr(cli, fname, 0, 0);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create the file. */
+ status = cli_ntcreate(
+ cli,
+ fname,
+ 0,
+ READ_CONTROL_ACCESS,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0x0,
+ 0x0,
+ &fnum1,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_ntcreate of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ return false;
+ }
+ status = cli_close(cli, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_close of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Now create the stream. */
+ status = cli_ntcreate(
+ cli,
+ fname_stream,
+ 0,
+ FILE_WRITE_DATA,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE,
+ 0x0,
+ 0x0,
+ &fnum1,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "cli_ntcreate of %s failed (%s)\n",
+ fname_stream,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* open it a second time */
+
+ status = cli_ntcreate(
+ cli,
+ fname_stream,
+ 0,
+ FILE_WRITE_DATA,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0x0,
+ 0x0,
+ &fnum2,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "2nd cli_ntcreate of %s failed (%s)\n",
+ fname_stream,
+ nt_errstr(status));
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ return false;
+ }
+
+ req = delete_stream_send(ev, ev, cli, fname, fnum1);
+ if (req == NULL) {
+ d_fprintf(stderr, "delete_stream_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr,
+ "tevent_req_poll_ntstatus failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = delete_stream_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "delete_stream failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "close failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = cli_unlink(
+ cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "unlink failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ Exercise delete on close semantics - use on the PRINT1 share in torture
+ testing.
+ */
+static bool run_delete_print_test(int dummy)
+{
+ struct cli_state *cli1 = NULL;
+ const char *fname = "print_delete.file";
+ uint16_t fnum1 = (uint16_t)-1;
+ bool correct = false;
+ const char *buf = "print file data\n";
+ NTSTATUS status;
+
+ printf("starting print delete test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_writeall(cli1,
+ fnum1,
+ 0,
+ (const uint8_t *)buf,
+ 0, /* offset */
+ strlen(buf), /* size */
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("writing print file data failed (%s)\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close(cli1, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("setting delete_on_close failed (%s)\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ printf("finished print delete test\n");
+
+ correct = true;
+
+ fail:
+
+ if (fnum1 != (uint16_t)-1) {
+ cli_close(cli1, fnum1);
+ }
+
+ if (cli1 && !torture_close_connection(cli1)) {
+ correct = false;
+ }
+ return correct;
+}
+
+static bool run_deletetest_ln(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\delete1";
+ const char *fname_ln = "\\delete1_ln";
+ uint16_t fnum;
+ uint16_t fnum1;
+ NTSTATUS status;
+ bool correct = true;
+ time_t t;
+
+ printf("starting deletetest-ln\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli, fname_ln, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Create the file. */
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Now create a hardlink. */
+ status = cli_hardlink(cli, fname, fname_ln);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("nt hardlink failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Open the original file. */
+ status = cli_ntcreate(cli, fname, 0, FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN_IF, 0, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ntcreate of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ /* Unlink the hard link path. */
+ status = cli_ntcreate(cli, fname_ln, 0, DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ntcreate of %s failed (%s)\n", fname_ln, nt_errstr(status));
+ return false;
+ }
+ status = cli_nt_delete_on_close(cli, fnum1, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) failed to set delete_on_close %s: %s\n",
+ __location__, fname_ln, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close %s failed (%s)\n",
+ fname_ln, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close %s failed (%s)\n",
+ fname, nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure the original file is still there. */
+ status = cli_getatr(cli, fname, NULL, NULL, &t);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s getatr on file %s failed (%s)\n",
+ __location__,
+ fname,
+ nt_errstr(status));
+ correct = False;
+ }
+
+ /* Ensure the link path is gone. */
+ status = cli_getatr(cli, fname_ln, NULL, NULL, &t);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s, getatr for file %s returned wrong error code %s "
+ "- should have been deleted\n",
+ __location__,
+ fname_ln, nt_errstr(status));
+ correct = False;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli, fname_ln, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ printf("finished deletetest-ln\n");
+
+ return correct;
+}
+
+/*
+ print out server properties
+ */
+static bool run_properties(int dummy)
+{
+ struct cli_state *cli;
+ bool correct = True;
+
+ printf("starting properties test\n");
+
+ ZERO_STRUCT(cli);
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ d_printf("Capabilities 0x%08x\n", smb1cli_conn_capabilities(cli->conn));
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+
+
+/* FIRST_DESIRED_ACCESS 0xf019f */
+#define FIRST_DESIRED_ACCESS FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|\
+ FILE_READ_EA| /* 0xf */ \
+ FILE_WRITE_EA|FILE_READ_ATTRIBUTES| /* 0x90 */ \
+ FILE_WRITE_ATTRIBUTES| /* 0x100 */ \
+ DELETE_ACCESS|READ_CONTROL_ACCESS|\
+ WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS /* 0xf0000 */
+/* SECOND_DESIRED_ACCESS 0xe0080 */
+#define SECOND_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \
+ READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+ WRITE_OWNER_ACCESS /* 0xe0000 */
+
+#if 0
+#define THIRD_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \
+ READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+ FILE_READ_DATA|\
+ WRITE_OWNER_ACCESS /* */
+#endif
+
+/*
+ Test ntcreate calls made by xcopy
+ */
+static bool run_xcopy(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\test.txt";
+ bool correct = True;
+ uint16_t fnum1, fnum2;
+ NTSTATUS status;
+
+ printf("starting xcopy test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ status = cli_ntcreate(cli1, fname, 0, FIRST_DESIRED_ACCESS,
+ FILE_ATTRIBUTE_ARCHIVE, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0x4044, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("First open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli1, fname, 0, SECOND_DESIRED_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0x200000, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("second open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+/*
+ Test rename on files open with share delete and no share delete.
+ */
+static bool run_rename(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\test.txt";
+ const char *fname1 = "\\test1.txt";
+ bool correct = True;
+ uint16_t fnum1;
+ uint32_t attr;
+ NTSTATUS status;
+
+ printf("starting rename test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("First open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_rename(cli1, fname, fname1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("First rename failed (SHARE_READ) (this is correct) - %s\n", nt_errstr(status));
+ } else {
+ printf("First rename succeeded (SHARE_READ) - this should have failed !\n");
+ correct = False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close - 1 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+#if 0
+ FILE_SHARE_DELETE|FILE_SHARE_NONE,
+#else
+ FILE_SHARE_DELETE|FILE_SHARE_READ,
+#endif
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Second open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_rename(cli1, fname, fname1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Second rename failed (SHARE_DELETE | SHARE_READ) - this should have succeeded - %s\n", nt_errstr(status));
+ correct = False;
+ } else {
+ printf("Second rename succeeded (SHARE_DELETE | SHARE_READ)\n");
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close - 2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, READ_CONTROL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Third open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+
+ status = cli_rename(cli1, fname, fname1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Third rename failed (SHARE_NONE) - this should have succeeded - %s\n", nt_errstr(status));
+ correct = False;
+ } else {
+ printf("Third rename succeeded (SHARE_NONE)\n");
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close - 3 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /*----*/
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Fourth open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_rename(cli1, fname, fname1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Fourth rename failed (SHARE_READ | SHARE_WRITE) (this is correct) - %s\n", nt_errstr(status));
+ } else {
+ printf("Fourth rename succeeded (SHARE_READ | SHARE_WRITE) - this should have failed !\n");
+ correct = False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close - 4 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /*--*/
+
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Fifth open failed - %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_rename(cli1, fname, fname1, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Fifth rename failed (SHARE_READ | SHARE_WRITE | SHARE_DELETE) - this should have succeeded - %s ! \n", nt_errstr(status));
+ correct = False;
+ } else {
+ printf("Fifth rename succeeded (SHARE_READ | SHARE_WRITE | SHARE_DELETE) (this is correct) - %s\n", nt_errstr(status));
+ }
+
+ /*--*/
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close - 5 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ /* Check that the renamed file has FILE_ATTRIBUTE_ARCHIVE. */
+ status = cli_getatr(cli1, fname1, &attr, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("getatr on file %s failed - %s ! \n",
+ fname1, nt_errstr(status));
+ correct = False;
+ } else {
+ if (attr != FILE_ATTRIBUTE_ARCHIVE) {
+ printf("Renamed file %s has wrong attr 0x%x "
+ "(should be 0x%x)\n",
+ fname1,
+ attr,
+ (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
+ correct = False;
+ } else {
+ printf("Renamed file %s has archive bit set\n", fname1);
+ }
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname1, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+/*
+ Test rename into a directory with an ACL denying it.
+ */
+static bool run_rename_access(int dummy)
+{
+ static struct cli_state *cli = NULL;
+ static struct cli_state *posix_cli = NULL;
+ const char *src = "test.txt";
+ const char *dname = "dir";
+ const char *dst = "dir\\test.txt";
+ const char *dsrc = "test.dir";
+ const char *ddst = "dir\\test.dir";
+ uint16_t fnum = (uint16_t)-1;
+ struct security_descriptor *sd = NULL;
+ struct security_descriptor *newsd = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+ printf("starting rename access test\n");
+
+ /* Windows connection. */
+ if (!torture_open_connection(&cli, 0)) {
+ goto fail;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Posix connection. */
+ if (!torture_open_connection(&posix_cli, 0)) {
+ goto fail;
+ }
+
+ smbXcli_conn_set_sockopt(posix_cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(posix_cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Start with a clean slate. */
+ cli_unlink(cli, src, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli, dst, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, dsrc);
+ cli_rmdir(cli, ddst);
+ cli_rmdir(cli, dname);
+
+ /*
+ * Setup the destination directory with a DENY ACE to
+ * prevent new files within it.
+ */
+ status = cli_ntcreate(cli,
+ dname,
+ 0,
+ FILE_READ_ATTRIBUTES|READ_CONTROL_ACCESS|
+ WRITE_DAC_ACCESS|FILE_READ_DATA|
+ WRITE_OWNER_ACCESS,
+ FILE_ATTRIBUTE_DIRECTORY,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s - %s\n", dname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_query_secdesc(cli,
+ fnum,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_query_secdesc failed for %s (%s)\n",
+ dname, nt_errstr(status));
+ goto fail;
+ }
+
+ newsd = security_descriptor_dacl_create(frame,
+ 0,
+ NULL,
+ NULL,
+ SID_WORLD,
+ SEC_ACE_TYPE_ACCESS_DENIED,
+ SEC_DIR_ADD_FILE|SEC_DIR_ADD_SUBDIR,
+ 0,
+ NULL);
+ if (newsd == NULL) {
+ goto fail;
+ }
+ sd->dacl = security_acl_concatenate(frame,
+ newsd->dacl,
+ sd->dacl);
+ if (sd->dacl == NULL) {
+ goto fail;
+ }
+ status = cli_set_secdesc(cli, fnum, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_set_secdesc failed for %s (%s)\n",
+ dname, nt_errstr(status));
+ goto fail;
+ }
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ dname, nt_errstr(status));
+ goto fail;
+ }
+ /* Now go around the back and chmod to 777 via POSIX. */
+ status = cli_posix_chmod(posix_cli, dname, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_chmod failed for %s (%s)\n",
+ dname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Check we can't create a file within dname via Windows. */
+ status = cli_openx(cli, dst, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ cli_close(posix_cli, fnum);
+ printf("Create of %s should be ACCESS denied, was %s\n",
+ dst, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Make the sample file/directory. */
+ status = cli_openx(cli, src, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", src, nt_errstr(status));
+ goto fail;
+ }
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed (%s)\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_mkdir(cli, dsrc);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_mkdir of %s failed (%s)\n",
+ dsrc, nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * OK - renames of the new file and directory into the
+ * dst directory should fail.
+ */
+
+ status = cli_rename(cli, src, dst, false);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("rename of %s -> %s should be ACCESS denied, was %s\n",
+ src, dst, nt_errstr(status));
+ goto fail;
+ }
+ status = cli_rename(cli, dsrc, ddst, false);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("rename of %s -> %s should be ACCESS denied, was %s\n",
+ src, dst, nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(frame);
+ return true;
+
+ fail:
+
+ if (posix_cli) {
+ torture_close_connection(posix_cli);
+ }
+
+ if (cli) {
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ }
+ cli_unlink(cli, src,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli, dst,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, dsrc);
+ cli_rmdir(cli, ddst);
+ cli_rmdir(cli, dname);
+
+ torture_close_connection(cli);
+ }
+
+ TALLOC_FREE(frame);
+ return false;
+}
+
+/*
+ Test owner rights ACE.
+ */
+static bool run_owner_rights(int dummy)
+{
+ static struct cli_state *cli = NULL;
+ const char *fname = "owner_rights.txt";
+ uint16_t fnum = (uint16_t)-1;
+ struct security_descriptor *sd = NULL;
+ struct security_descriptor *newsd = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+ printf("starting owner rights test\n");
+
+ /* Windows connection. */
+ if (!torture_open_connection(&cli, 0)) {
+ goto fail;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Start with a clean slate. */
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create the test file. */
+ /* Now try and open for read and write-dac. */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Get the original SD. */
+ status = cli_query_secdesc(cli,
+ fnum,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_query_secdesc failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Add an "owner-rights" ACE denying WRITE_DATA,
+ * and an "owner-rights" ACE allowing READ_DATA.
+ */
+
+ newsd = security_descriptor_dacl_create(frame,
+ 0,
+ NULL,
+ NULL,
+ SID_OWNER_RIGHTS,
+ SEC_ACE_TYPE_ACCESS_DENIED,
+ FILE_WRITE_DATA,
+ 0,
+ SID_OWNER_RIGHTS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ FILE_READ_DATA,
+ 0,
+ NULL);
+ if (newsd == NULL) {
+ goto fail;
+ }
+ sd->dacl = security_acl_concatenate(frame,
+ newsd->dacl,
+ sd->dacl);
+ if (sd->dacl == NULL) {
+ goto fail;
+ }
+ status = cli_set_secdesc(cli, fnum, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_set_secdesc failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Try and open for FILE_WRITE_DATA */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Open of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Now try and open for FILE_READ_DATA */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Restore clean slate. */
+ TALLOC_FREE(sd);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create the test file. */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Get the original SD. */
+ status = cli_query_secdesc(cli,
+ fnum,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_query_secdesc failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Add an "owner-rights ACE denying WRITE_DATA,
+ * and an "owner-rights ACE allowing READ_DATA|WRITE_DATA.
+ */
+
+ newsd = security_descriptor_dacl_create(frame,
+ 0,
+ NULL,
+ NULL,
+ SID_OWNER_RIGHTS,
+ SEC_ACE_TYPE_ACCESS_DENIED,
+ FILE_WRITE_DATA,
+ 0,
+ SID_OWNER_RIGHTS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ FILE_READ_DATA|FILE_WRITE_DATA,
+ 0,
+ NULL);
+ if (newsd == NULL) {
+ goto fail;
+ }
+ sd->dacl = security_acl_concatenate(frame,
+ newsd->dacl,
+ sd->dacl);
+ if (sd->dacl == NULL) {
+ goto fail;
+ }
+ status = cli_set_secdesc(cli, fnum, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_set_secdesc failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Try and open for FILE_WRITE_DATA */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Open of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Now try and open for FILE_READ_DATA */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Restore clean slate. */
+ TALLOC_FREE(sd);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+
+ /* Create the test file. */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /* Get the original SD. */
+ status = cli_query_secdesc(cli,
+ fnum,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_query_secdesc failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Add an "authenticated users" ACE allowing READ_DATA,
+ * add an "owner-rights" denying READ_DATA,
+ * and an "authenticated users" ACE allowing WRITE_DATA.
+ */
+
+ newsd = security_descriptor_dacl_create(frame,
+ 0,
+ NULL,
+ NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ FILE_READ_DATA,
+ 0,
+ SID_OWNER_RIGHTS,
+ SEC_ACE_TYPE_ACCESS_DENIED,
+ FILE_READ_DATA,
+ 0,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ FILE_WRITE_DATA,
+ 0,
+ NULL);
+ if (newsd == NULL) {
+ printf("newsd == NULL\n");
+ goto fail;
+ }
+ sd->dacl = security_acl_concatenate(frame,
+ newsd->dacl,
+ sd->dacl);
+ if (sd->dacl == NULL) {
+ printf("sd->dacl == NULL\n");
+ goto fail;
+ }
+ status = cli_set_secdesc(cli, fnum, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_set_secdesc failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Now try and open for FILE_READ_DATA|FILE_WRITE_DATA */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed for %s (%s)\n",
+ fname, nt_errstr(status));
+ goto fail;
+ }
+
+ cli_unlink(cli, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ TALLOC_FREE(frame);
+ return true;
+
+ fail:
+
+ if (cli) {
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ }
+ cli_unlink(cli, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ torture_close_connection(cli);
+ }
+
+ TALLOC_FREE(frame);
+ return false;
+}
+
+/*
+ * Test SMB1-specific open with SEC_FLAG_SYSTEM_SECURITY.
+ * Note this test only works with a user with SeSecurityPrivilege set.
+ *
+ * NB. This is also tested in samba3.base.createx_access
+ * but this makes it very explicit what we're looking for.
+ */
+static bool run_smb1_system_security(int dummy)
+{
+ static struct cli_state *cli = NULL;
+ const char *fname = "system_security.txt";
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+ printf("starting smb1 system security test\n");
+
+ /* SMB1 connection - torture_open_connection() forces this. */
+ if (!torture_open_connection(&cli, 0)) {
+ goto fail;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Start with a clean slate. */
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create the test file. */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Create of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli, fnum);
+
+ /* Open with SEC_FLAG_SYSTEM_SECURITY only. */
+ /*
+ * On SMB1 this succeeds - SMB2 it fails,
+ * see the SMB2-SACL test.
+ */
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ SEC_FLAG_SYSTEM_SECURITY,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of %s - %s\n", fname, nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_close(cli, fnum);
+
+ cli_unlink(cli, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ torture_close_connection(cli);
+ TALLOC_FREE(frame);
+ return true;
+
+ fail:
+
+ if (cli) {
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ }
+ cli_unlink(cli, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ torture_close_connection(cli);
+ }
+
+ TALLOC_FREE(frame);
+ return false;
+}
+
+static bool run_pipe_number(int dummy)
+{
+ struct cli_state *cli1;
+ const char *pipe_name = "\\SPOOLSS";
+ uint16_t fnum;
+ int num_pipes = 0;
+ NTSTATUS status;
+
+ printf("starting pipenumber test\n");
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ while(1) {
+ status = cli_ntcreate(cli1, pipe_name, 0, FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN_IF, 0, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Open of pipe %s failed with error (%s)\n", pipe_name, nt_errstr(status));
+ break;
+ }
+ num_pipes++;
+ printf("\r%6d", num_pipes);
+ }
+
+ printf("pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
+ torture_close_connection(cli1);
+ return True;
+}
+
+/*
+ Test open mode returns on read-only files.
+ */
+static bool run_opentest(int dummy)
+{
+ static struct cli_state *cli1;
+ static struct cli_state *cli2;
+ const char *fname = "\\readonly.file";
+ uint16_t fnum1, fnum2;
+ char buf[20];
+ off_t fsize;
+ bool correct = True;
+ char *tmp_path;
+ NTSTATUS status;
+
+ printf("starting open test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_setatr(cli1, fname, FILE_ATTRIBUTE_READONLY, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_setatr failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_WRITE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ /* This will fail - but the error should be ERRnoaccess, not ERRbadshare. */
+ status = cli_openx(cli1, fname, O_RDWR, DENY_ALL, &fnum2);
+
+ if (check_error(__LINE__, status, ERRDOS, ERRnoaccess,
+ NT_STATUS_ACCESS_DENIED)) {
+ printf("correct error code ERRDOS/ERRnoaccess returned\n");
+ }
+
+ printf("finished open test 1\n");
+
+ cli_close(cli1, fnum1);
+
+ /* Now try not readonly and ensure ERRbadshare is returned. */
+
+ cli_setatr(cli1, fname, 0, 0);
+
+ status = cli_openx(cli1, fname, O_RDONLY, DENY_WRITE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ /* This will fail - but the error should be ERRshare. */
+ status = cli_openx(cli1, fname, O_RDWR, DENY_ALL, &fnum2);
+
+ if (check_error(__LINE__, status, ERRDOS, ERRbadshare,
+ NT_STATUS_SHARING_VIOLATION)) {
+ printf("correct error code ERRDOS/ERRbadshare returned\n");
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("finished open test 2\n");
+
+ /* Test truncate open disposition on file opened for read. */
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(3) open (1) of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ /* write 20 bytes. */
+
+ memset(buf, '\0', 20);
+
+ status = cli_writeall(cli1, fnum1, 0, (uint8_t *)buf, 0, 20, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed (%s)\n", nt_errstr(status));
+ correct = False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(3) close1 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ /* Ensure size == 20. */
+ status = cli_getatr(cli1, fname, NULL, &fsize, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(3) getatr failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ if (fsize != 20) {
+ printf("(3) file size != 20\n");
+ return False;
+ }
+
+ /* Now test if we can truncate a file opened for readonly. */
+ status = cli_openx(cli1, fname, O_RDONLY|O_TRUNC, DENY_NONE, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(3) open (2) of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ /* Ensure size == 0. */
+ status = cli_getatr(cli1, fname, NULL, &fsize, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(3) getatr failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ if (fsize != 0) {
+ printf("(3) file size != 0\n");
+ return False;
+ }
+ printf("finished open test 3\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("Do ctemp tests\n");
+ status = cli_ctemp(cli1, talloc_tos(), "\\", &fnum1, &tmp_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ctemp failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ printf("ctemp gave path %s\n", tmp_path);
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close of temp failed (%s)\n", nt_errstr(status));
+ }
+
+ status = cli_unlink(cli1, tmp_path, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink of temp failed (%s)\n", nt_errstr(status));
+ }
+
+ /* Test the non-io opens... */
+
+ if (!torture_open_connection(&cli2, 1)) {
+ return False;
+ }
+
+ cli_setatr(cli2, fname, 0, 0);
+ cli_unlink(cli2, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ printf("TEST #1 testing 2 non-io opens (no delete)\n");
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #1 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0, FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #1 open 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #1 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #1 close 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #1 passed.\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("TEST #2 testing 2 non-io opens (first with delete)\n");
+
+ status = cli_ntcreate(cli1, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #2 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0, FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #2 open 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #2 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #2 close 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #2 passed.\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("TEST #3 testing 2 non-io opens (second with delete)\n");
+
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #3 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #3 open 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #3 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #3 close 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #3 passed.\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("TEST #4 testing 2 non-io opens (both with delete)\n");
+
+ status = cli_ntcreate(cli1, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #4 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("TEST #4 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("TEST #4 open 2 of %s gave %s (correct error should be %s)\n", fname, nt_errstr(status), "sharing violation");
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #4 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #4 passed.\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("TEST #5 testing 2 non-io opens (both with delete - both with file share delete)\n");
+
+ status = cli_ntcreate(cli1, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #5 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_DELETE,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #5 open 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #5 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #5 close 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #5 passed.\n");
+
+ printf("TEST #6 testing 1 non-io open, one io open\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #6 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0, FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #6 open 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #6 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #6 close 2 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #6 passed.\n");
+
+ printf("TEST #7 testing 1 non-io open, one io open with delete\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #7 open 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_ntcreate(cli2, fname, 0,
+ DELETE_ACCESS|FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_DELETE,
+ FILE_OPEN_IF, 0, 0, &fnum2, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("TEST #7 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("TEST #7 open 2 of %s gave %s (correct error should be %s)\n", fname, nt_errstr(status), "sharing violation");
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #7 close 1 of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ printf("non-io open test #7 passed.\n");
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("TEST #8 testing open without WRITE_ATTRIBUTES, updating close write time.\n");
+ status = cli_ntcreate(cli1, fname, 0, FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #8 open of %s failed (%s)\n", fname, nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+
+ /* Write to ensure we have to update the file time. */
+ status = cli_writeall(cli1, fnum1, 0, (const uint8_t *)"TEST DATA\n", 0, 10,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #8 cli_write failed: %s\n", nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("TEST #8 close of %s failed (%s)\n", fname, nt_errstr(status));
+ correct = false;
+ }
+
+ out:
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+NTSTATUS torture_setup_unix_extensions(struct cli_state *cli)
+{
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+ NTSTATUS status;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ printf("Server doesn't support UNIX CIFS extensions.\n");
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ status = cli_unix_extensions_version(cli, &major, &minor, &caplow,
+ &caphigh);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Server didn't return UNIX CIFS extensions: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ status = cli_set_unix_extensions_capabilities(cli, major, minor,
+ caplow, caphigh);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Server doesn't support setting UNIX CIFS extensions: "
+ "%s.\n", nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Test POSIX open /mkdir calls.
+ */
+static bool run_simple_posix_open_test(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "posix:file";
+ const char *hname = "posix:hlink";
+ const char *sname = "posix:symlink";
+ const char *dname = "posix:dir";
+ char buf[10];
+ char *target = NULL;
+ uint16_t fnum1 = (uint16_t)-1;
+ SMB_STRUCT_STAT sbuf;
+ bool correct = false;
+ NTSTATUS status;
+ size_t nread;
+ const char *fname_windows = "windows_file";
+ uint16_t fnum2 = (uint16_t)-1;
+ bool ok;
+
+ printf("Starting simple POSIX open test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_posix_unlink(cli1, fname);
+ cli_setatr(cli1, dname, 0, 0);
+ cli_posix_rmdir(cli1, dname);
+ cli_setatr(cli1, hname, 0, 0);
+ cli_posix_unlink(cli1, hname);
+ cli_setatr(cli1, sname, 0, 0);
+ cli_posix_unlink(cli1, sname);
+ cli_setatr(cli1, fname_windows, 0, 0);
+ cli_posix_unlink(cli1, fname_windows);
+
+ /* Create a directory. */
+ status = cli_posix_mkdir(cli1, dname, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX mkdir of %s failed (%s)\n", dname, nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL,
+ 0600, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX create of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Test ftruncate - set file size. */
+ status = cli_ftruncate(cli1, fnum1, 1000);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ftruncate failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure st_size == 1000 */
+ status = cli_posix_stat(cli1, fname, &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("stat failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (sbuf.st_ex_size != 1000) {
+ printf("ftruncate - stat size (%u) != 1000\n", (unsigned int)sbuf.st_ex_size);
+ goto out;
+ }
+
+ /* Ensure st_mode == 0600 */
+ if ((sbuf.st_ex_mode & 07777) != 0600) {
+ printf("posix_open - bad permissions 0%o != 0600\n",
+ (unsigned int)(sbuf.st_ex_mode & 07777));
+ goto out;
+ }
+
+ /* Test ftruncate - set file size back to zero. */
+ status = cli_ftruncate(cli1, fnum1, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ftruncate failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Now open the file again for read only. */
+ status = cli_posix_open(cli1, fname, O_RDONLY, 0, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX open of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Now unlink while open. */
+ status = cli_posix_unlink(cli1, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX unlink of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close(2) failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure the file has gone. */
+ status = cli_posix_open(cli1, fname, O_RDONLY, 0, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("POSIX open of %s succeeded, should have been deleted.\n", fname);
+ goto out;
+ }
+
+ /* Create again to test open with O_TRUNC. */
+ status = cli_posix_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, 0600, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX create of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Test ftruncate - set file size. */
+ status = cli_ftruncate(cli1, fnum1, 1000);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ftruncate failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure st_size == 1000 */
+ status = cli_posix_stat(cli1, fname, &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("stat failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (sbuf.st_ex_size != 1000) {
+ printf("ftruncate - stat size (%u) != 1000\n", (unsigned int)sbuf.st_ex_size);
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close(2) failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Re-open with O_TRUNC. */
+ status = cli_posix_open(cli1, fname, O_WRONLY|O_TRUNC, 0600, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX create of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure st_size == 0 */
+ status = cli_posix_stat(cli1, fname, &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("stat failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (sbuf.st_ex_size != 0) {
+ printf("O_TRUNC - stat size (%u) != 0\n", (unsigned int)sbuf.st_ex_size);
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_unlink(cli1, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX unlink of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_open(cli1, dname, O_RDONLY, 0, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX open directory O_RDONLY of %s failed (%s)\n",
+ dname, nt_errstr(status));
+ goto out;
+ }
+
+ cli_close(cli1, fnum1);
+
+ /* What happens when we try and POSIX open a directory for write ? */
+ status = cli_posix_open(cli1, dname, O_RDWR, 0, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("POSIX open of directory %s succeeded, "
+ "should have failed.\n",
+ dname);
+ goto out;
+ } else {
+ if (!check_both_error(__LINE__, status, ERRDOS, EISDIR,
+ NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ goto out;
+ }
+ }
+
+ /* Create the file. */
+ status = cli_posix_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL,
+ 0600, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX create of %s failed (%s)\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Write some data into it. */
+ status = cli_writeall(cli1, fnum1, 0, (const uint8_t *)"TEST DATA\n", 0, 10,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_write failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ cli_close(cli1, fnum1);
+
+ /* Now create a hardlink. */
+ status = cli_posix_hardlink(cli1, fname, hname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX hardlink of %s failed (%s)\n", hname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create a symlink. */
+ status = cli_posix_symlink(cli1, fname, sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n", sname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Open the hardlink for read. */
+ status = cli_posix_open(cli1, hname, O_RDONLY, 0, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX open of %s failed (%s)\n", hname, nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_read(cli1, fnum1, buf, 0, 10, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX read of %s failed (%s)\n", hname,
+ nt_errstr(status));
+ goto out;
+ } else if (nread != 10) {
+ printf("POSIX read of %s failed. Received %ld, expected %d\n",
+ hname, (unsigned long)nread, 10);
+ goto out;
+ }
+
+ if (memcmp(buf, "TEST DATA\n", 10)) {
+ printf("invalid data read from hardlink\n");
+ goto out;
+ }
+
+ /* Do a POSIX lock/unlock. */
+ status = cli_posix_lock(cli1, fnum1, 0, 100, true, READ_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX lock failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Punch a hole in the locked area. */
+ status = cli_posix_unlock(cli1, fnum1, 10, 80);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX unlock failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ cli_close(cli1, fnum1);
+
+ /* Open the symlink for read - this should fail. A POSIX
+ client should not be doing opens on a symlink. */
+ status = cli_posix_open(cli1, sname, O_RDONLY, 0, &fnum1);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("POSIX open of %s succeeded (should have failed)\n", sname);
+ goto out;
+ }
+ ok = check_both_error(
+ __LINE__, status, ERRDOS, ERRbadpath,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ if (!ok) {
+ printf("POSIX open of %s should have failed "
+ "with NT_STATUS_OBJECT_NAME_NOT_FOUND, "
+ "failed with %s instead.\n",
+ sname, nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_readlink(cli1, sname, talloc_tos(), &target, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX readlink on %s failed (%s)\n", sname, nt_errstr(status));
+ goto out;
+ }
+
+ if (strcmp(target, fname) != 0) {
+ printf("POSIX readlink on %s failed to match name %s (read %s)\n",
+ sname, fname, target);
+ goto out;
+ }
+
+ status = cli_posix_rmdir(cli1, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX rmdir failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Check directory opens with a specific permission. */
+ status = cli_posix_mkdir(cli1, dname, 0700);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX mkdir of %s failed (%s)\n", dname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure st_mode == 0700 */
+ status = cli_posix_stat(cli1, dname, &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("stat failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+
+ if ((sbuf.st_ex_mode & 07777) != 0700) {
+ printf("posix_mkdir - bad permissions 0%o != 0700\n",
+ (unsigned int)(sbuf.st_ex_mode & 07777));
+ goto out;
+ }
+
+ /*
+ * Now create a Windows file, and attempt a POSIX unlink.
+ * This should fail with a sharing violation but due to:
+ *
+ * [Bug 9571] Unlink after open causes smbd to panic
+ *
+ * ensure we've fixed the lock ordering violation.
+ */
+
+ status = cli_ntcreate(cli1, fname_windows, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0x0, 0x0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Windows create of %s failed (%s)\n", fname_windows,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now try posix_unlink. */
+ status = cli_posix_unlink(cli1, fname_windows);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ printf("POSIX unlink of %s should fail "
+ "with NT_STATUS_SHARING_VIOLATION "
+ "got %s instead !\n",
+ fname_windows,
+ nt_errstr(status));
+ goto out;
+ }
+
+ cli_close(cli1, fnum2);
+
+ printf("Simple POSIX open test passed\n");
+ correct = true;
+
+ out:
+
+ if (fnum1 != (uint16_t)-1) {
+ cli_close(cli1, fnum1);
+ fnum1 = (uint16_t)-1;
+ }
+
+ if (fnum2 != (uint16_t)-1) {
+ cli_close(cli1, fnum2);
+ fnum2 = (uint16_t)-1;
+ }
+
+ cli_setatr(cli1, sname, 0, 0);
+ cli_posix_unlink(cli1, sname);
+ cli_setatr(cli1, hname, 0, 0);
+ cli_posix_unlink(cli1, hname);
+ cli_setatr(cli1, fname, 0, 0);
+ cli_posix_unlink(cli1, fname);
+ cli_setatr(cli1, dname, 0, 0);
+ cli_posix_rmdir(cli1, dname);
+ cli_setatr(cli1, fname_windows, 0, 0);
+ cli_posix_unlink(cli1, fname_windows);
+
+ if (!torture_close_connection(cli1)) {
+ correct = false;
+ }
+
+ return correct;
+}
+
+/*
+ Test POSIX and Windows ACLs are rejected on symlinks.
+ */
+static bool run_acl_symlink_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "posix_file";
+ const char *sname = "posix_symlink";
+ uint16_t fnum = (uint16_t)-1;
+ bool correct = false;
+ NTSTATUS status;
+ char *posix_acl = NULL;
+ size_t posix_acl_len = 0;
+ char *posix_acl_sym = NULL;
+ size_t posix_acl_len_sym = 0;
+ struct security_descriptor *sd = NULL;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+
+ printf("Starting acl symlink test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+ cli_setatr(cli, sname, 0, 0);
+ cli_posix_unlink(cli, sname);
+
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ READ_CONTROL_ACCESS,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0x0,
+ 0x0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Get the Windows ACL on the file. */
+ status = cli_query_secdesc(cli,
+ fnum,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_query_secdesc failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Get the POSIX ACL on the file. */
+ status = cli_posix_getacl(cli,
+ fname,
+ frame,
+ &posix_acl_len,
+ &posix_acl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_getacl failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Now create a symlink. */
+ status = cli_posix_symlink(cli, fname, sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed (%s)\n",
+ sname,
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Open a handle on the symlink for SD set/get should fail. */
+ status = cli_ntcreate(cli,
+ sname,
+ 0,
+ READ_CONTROL_ACCESS|SEC_STD_WRITE_DAC,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0x0,
+ 0x0,
+ &fnum,
+ NULL);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("Symlink open for getsd/setsd of %s "
+ "succeeded (should fail)\n",
+ sname);
+ goto out;
+ }
+
+ /* Try a stat-open on the symlink, should also fail. */
+ status = cli_ntcreate(cli,
+ sname,
+ 0,
+ FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0x0,
+ 0x0,
+ &fnum,
+ NULL);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("Stat-open of symlink succeeded (should fail)\n");
+ goto out;
+ }
+
+ /* Get the POSIX ACL on the symlink pathname. Should fail. */
+ status = cli_posix_getacl(cli,
+ sname,
+ frame,
+ &posix_acl_len_sym,
+ &posix_acl_sym);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("cli_posix_getacl on a symlink gave %s. "
+ "Should be NT_STATUS_ACCESS_DENIED.\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Set the POSIX ACL on the symlink pathname. Should fail. */
+ status = cli_posix_setacl(cli,
+ sname,
+ posix_acl,
+ posix_acl_len);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("cli_posix_setacl on a symlink gave %s. "
+ "Should be NT_STATUS_ACCESS_DENIED.\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("ACL symlink test passed\n");
+ correct = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ cli_setatr(cli, sname, 0, 0);
+ cli_posix_unlink(cli, sname);
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test POSIX can delete a file containing streams.
+ */
+static bool run_posix_stream_delete(int dummy)
+{
+ struct cli_state *cli1 = NULL;
+ struct cli_state *cli2 = NULL;
+ const char *fname = "streamfile";
+ const char *stream_fname = "streamfile:Zone.Identifier:$DATA";
+ uint16_t fnum1 = (uint16_t)-1;
+ bool correct = false;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX stream delete test\n");
+
+ if (!torture_open_connection(&cli1, 0) ||
+ !torture_open_connection(&cli2, 1)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create the file. */
+ status = cli_ntcreate(cli1,
+ fname,
+ 0,
+ READ_CONTROL_ACCESS,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0x0,
+ 0x0,
+ &fnum1,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+ fnum1 = (uint16_t)-1;
+
+ /* Now create the stream. */
+ status = cli_ntcreate(cli1,
+ stream_fname,
+ 0,
+ FILE_WRITE_DATA,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE,
+ 0x0,
+ 0x0,
+ &fnum1,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate of %s failed (%s)\n",
+ stream_fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Leave the stream handle open... */
+
+ /* POSIX unlink should fail. */
+ status = cli_posix_unlink(cli2, fname);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s succeeded, should have failed\n",
+ fname);
+ goto out;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ printf("cli_posix_unlink of %s failed with (%s) "
+ "should have been NT_STATUS_SHARING_VIOLATION\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Close the stream handle. */
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close of %s failed (%s)\n",
+ stream_fname,
+ nt_errstr(status));
+ goto out;
+ }
+ fnum1 = (uint16_t)-1;
+
+ /* POSIX unlink after stream handle closed should succeed. */
+ status = cli_posix_unlink(cli2, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("POSIX stream delete test passed\n");
+ correct = true;
+
+ out:
+
+ if (fnum1 != (uint16_t)-1) {
+ cli_close(cli1, fnum1);
+ fnum1 = (uint16_t)-1;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_close_connection(cli1)) {
+ correct = false;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test setting EA's are rejected on symlinks.
+ */
+static bool run_ea_symlink_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "posix_file_ea";
+ const char *sname = "posix_symlink_ea";
+ const char *ea_name = "testea_name";
+ const char *ea_value = "testea_value";
+ uint16_t fnum = (uint16_t)-1;
+ bool correct = false;
+ NTSTATUS status;
+ size_t i, num_eas;
+ struct ea_struct *eas = NULL;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+
+ printf("Starting EA symlink test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+ cli_setatr(cli, sname, 0, 0);
+ cli_posix_unlink(cli, sname);
+
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ READ_CONTROL_ACCESS,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_CREATE,
+ 0x0,
+ 0x0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_ntcreate of %s failed (%s)\n",
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Set an EA on the path. */
+ status = cli_set_ea_path(cli,
+ fname,
+ ea_name,
+ ea_value,
+ strlen(ea_value)+1);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_set_ea_path failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create a symlink. */
+ status = cli_posix_symlink(cli, fname, sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed (%s)\n",
+ sname,
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Get the EA list on the path. Should return value set. */
+ status = cli_get_ea_list_path(cli,
+ fname,
+ frame,
+ &num_eas,
+ &eas);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_get_ea_list_path failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure the EA we set is there. */
+ for (i=0; i<num_eas; i++) {
+ if (strcmp(eas[i].name, ea_name) == 0 &&
+ eas[i].value.length == strlen(ea_value)+1 &&
+ memcmp(eas[i].value.data,
+ ea_value,
+ eas[i].value.length) == 0) {
+ break;
+ }
+ }
+
+ if (i == num_eas) {
+ printf("Didn't find EA on pathname %s\n",
+ fname);
+ goto out;
+ }
+
+ num_eas = 0;
+ TALLOC_FREE(eas);
+
+ /* Get the EA list on the symlink. Should return empty list. */
+ status = cli_get_ea_list_path(cli,
+ sname,
+ frame,
+ &num_eas,
+ &eas);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_get_ea_list_path failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (num_eas != 0) {
+ printf("cli_get_ea_list_path failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Set an EA on the symlink. Should fail. */
+ status = cli_set_ea_path(cli,
+ sname,
+ ea_name,
+ ea_value,
+ strlen(ea_value)+1);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("cli_set_ea_path on a symlink gave %s. "
+ "Should be NT_STATUS_ACCESS_DENIED.\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("EA symlink test passed\n");
+ correct = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ cli_setatr(cli, sname, 0, 0);
+ cli_posix_unlink(cli, sname);
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test POSIX locks are OFD-locks.
+ */
+static bool run_posix_ofd_lock_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "posix_file";
+ uint16_t fnum1 = (uint16_t)-1;
+ uint16_t fnum2 = (uint16_t)-1;
+ bool correct = false;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX ofd-lock test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+
+ /* Open the file twice. */
+ status = cli_posix_open(cli, fname, O_RDWR|O_CREAT|O_EXCL,
+ 0600, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("First POSIX open of %s failed\n", fname);
+ goto out;
+ }
+
+ status = cli_posix_open(cli, fname, O_RDWR, 0, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("First POSIX open of %s failed\n", fname);
+ goto out;
+ }
+
+ /* Set a 0-50 lock on fnum1. */
+ status = cli_posix_lock(cli, fnum1, 0, 50, false, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX lock (1) failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Set a 60-100 lock on fnum2. */
+ status = cli_posix_lock(cli, fnum2, 60, 100, false, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX lock (2) failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* close fnum1 - 0-50 lock should go away. */
+ status = cli_close(cli, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+ fnum1 = (uint16_t)-1;
+
+ /* Change the lock context. */
+ cli_setpid(cli, cli_getpid(cli) + 1);
+
+ /* Re-open fnum1. */
+ status = cli_posix_open(cli, fname, O_RDWR, 0, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Third POSIX open of %s failed\n", fname);
+ goto out;
+ }
+
+ /* 60-100 lock should still be there. */
+ status = cli_posix_lock(cli, fnum1, 60, 100, false, WRITE_LOCK);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("POSIX lock 60-100 not there %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* 0-50 lock should be gone. */
+ status = cli_posix_lock(cli, fnum1, 0, 50, false, WRITE_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX lock 0-50 failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ printf("POSIX OFD lock test passed\n");
+ correct = true;
+
+ out:
+
+ if (fnum1 != (uint16_t)-1) {
+ cli_close(cli, fnum1);
+ fnum1 = (uint16_t)-1;
+ }
+ if (fnum2 != (uint16_t)-1) {
+ cli_close(cli, fnum2);
+ fnum2 = (uint16_t)-1;
+ }
+
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+struct posix_blocking_state {
+ struct tevent_context *ev;
+ struct cli_state *cli1;
+ uint16_t fnum1;
+ struct cli_state *cli2;
+ uint16_t fnum2;
+ bool gotblocked;
+ bool gotecho;
+};
+
+static void posix_blocking_locked(struct tevent_req *subreq);
+static void posix_blocking_gotblocked(struct tevent_req *subreq);
+static void posix_blocking_gotecho(struct tevent_req *subreq);
+static void posix_blocking_unlocked(struct tevent_req *subreq);
+
+static struct tevent_req *posix_blocking_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli1,
+ uint16_t fnum1,
+ struct cli_state *cli2,
+ uint16_t fnum2)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct posix_blocking_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct posix_blocking_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli1 = cli1;
+ state->fnum1 = fnum1;
+ state->cli2 = cli2;
+ state->fnum2 = fnum2;
+
+ subreq = cli_posix_lock_send(
+ state,
+ state->ev,
+ state->cli1,
+ state->fnum1,
+ 0,
+ 1,
+ false,
+ WRITE_LOCK);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, posix_blocking_locked, req);
+ return req;
+}
+
+static void posix_blocking_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct posix_blocking_state *state = tevent_req_data(
+ req, struct posix_blocking_state);
+ NTSTATUS status;
+
+ status = cli_posix_lock_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_posix_lock_send(
+ state,
+ state->ev,
+ state->cli2,
+ state->fnum2,
+ 0,
+ 1,
+ true,
+ WRITE_LOCK);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, posix_blocking_gotblocked, req);
+
+ /* Make sure the blocking request is delivered */
+ subreq = cli_echo_send(
+ state,
+ state->ev,
+ state->cli2,
+ 1,
+ (DATA_BLOB) { .data = (uint8_t *)state, .length = 1 });
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, posix_blocking_gotecho, req);
+}
+
+static void posix_blocking_gotblocked(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct posix_blocking_state *state = tevent_req_data(
+ req, struct posix_blocking_state);
+ NTSTATUS status;
+
+ status = cli_posix_lock_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (!state->gotecho) {
+ printf("blocked req got through before echo\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_LOCK_SEQUENCE);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void posix_blocking_gotecho(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct posix_blocking_state *state = tevent_req_data(
+ req, struct posix_blocking_state);
+ NTSTATUS status;
+
+ status = cli_echo_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (state->gotblocked) {
+ printf("blocked req got through before echo\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_LOCK_SEQUENCE);
+ return;
+ }
+ state->gotecho = true;
+
+ subreq = cli_posix_lock_send(
+ state,
+ state->ev,
+ state->cli1,
+ state->fnum1,
+ 0,
+ 1,
+ false,
+ UNLOCK_LOCK);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, posix_blocking_unlocked, req);
+}
+
+static void posix_blocking_unlocked(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_posix_lock_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ /* tevent_req_done in posix_blocking_gotlocked */
+}
+
+static NTSTATUS posix_blocking_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_posix_blocking_lock(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct cli_state *cli1 = NULL, *cli2 = NULL;
+ const char *fname = "posix_blocking";
+ uint16_t fnum1 = UINT16_MAX, fnum2 = UINT16_MAX;
+ struct tevent_req *req = NULL;
+ NTSTATUS status;
+ bool ret = false;
+ bool ok;
+
+ printf("Starting posix blocking lock test\n");
+
+ ev = samba_tevent_context_init(NULL);
+ if (ev == NULL) {
+ return false;
+ }
+
+ ok = torture_open_connection(&cli1, 0);
+ if (!ok) {
+ goto fail;
+ }
+ ok = torture_open_connection(&cli2, 0);
+ if (!ok) {
+ goto fail;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ status = torture_setup_unix_extensions(cli2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_posix_unlink(cli1, fname);
+
+ status = cli_posix_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL,
+ 0600, &fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("First POSIX open of %s failed: %s\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_posix_open(cli2, fname, O_RDWR, 0600, &fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Second POSIX open of %s failed: %s\n",
+ fname,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ req = posix_blocking_send(ev, ev, cli1, fnum1, cli2, fnum2);
+ if (req == NULL) {
+ printf("cli_posix_blocking failed\n");
+ goto fail;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ printf("tevent_req_poll_ntstatus failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ status = posix_blocking_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("posix_blocking_recv returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+
+ if (fnum1 != UINT16_MAX) {
+ cli_close(cli1, fnum1);
+ fnum1 = UINT16_MAX;
+ }
+ if (fnum2 != UINT16_MAX) {
+ cli_close(cli2, fnum2);
+ fnum2 = UINT16_MAX;
+ }
+
+ if (cli1 != NULL) {
+ cli_setatr(cli1, fname, 0, 0);
+ cli_posix_unlink(cli1, fname);
+ }
+
+ ok = true;
+
+ if (cli1 != NULL) {
+ ok &= torture_close_connection(cli1);
+ cli1 = NULL;
+ }
+ if (cli2 != NULL) {
+ ok &= torture_close_connection(cli2);
+ cli2 = NULL;
+ }
+
+ if (!ok) {
+ ret = false;
+ }
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+/*
+ Test POSIX mkdir is case-sensitive.
+ */
+static bool run_posix_mkdir_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname_foo = "POSIX_foo";
+ const char *fname_foo_Foo = "POSIX_foo/Foo";
+ const char *fname_foo_foo = "POSIX_foo/foo";
+ const char *fname_Foo = "POSIX_Foo";
+ const char *fname_Foo_Foo = "POSIX_Foo/Foo";
+ const char *fname_Foo_foo = "POSIX_Foo/foo";
+ bool correct = false;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+ uint16_t fnum = (uint16_t)-1;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX mkdir test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_rmdir(cli, fname_foo_foo);
+ cli_posix_rmdir(cli, fname_foo_Foo);
+ cli_posix_rmdir(cli, fname_foo);
+
+ cli_posix_rmdir(cli, fname_Foo_foo);
+ cli_posix_rmdir(cli, fname_Foo_Foo);
+ cli_posix_rmdir(cli, fname_Foo);
+
+ /*
+ * Create a file POSIX_foo then try
+ * and use it in a directory path by
+ * doing mkdir POSIX_foo/bar.
+ * The mkdir should fail with
+ * NT_STATUS_OBJECT_PATH_NOT_FOUND
+ */
+
+ status = cli_posix_open(cli,
+ fname_foo,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_foo_foo, 0777);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("cli_posix_mkdir of %s should fail with "
+ "NT_STATUS_OBJECT_PATH_NOT_FOUND got "
+ "%s instead\n",
+ fname_foo_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ status = cli_posix_unlink(cli, fname_foo);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ fname_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * Now we've deleted everything, posix_mkdir, posix_rmdir,
+ * posix_open, posix_unlink, on
+ * POSIX_foo/foo should return NT_STATUS_OBJECT_PATH_NOT_FOUND
+ * not silently create POSIX_foo/foo.
+ */
+
+ status = cli_posix_mkdir(cli, fname_foo_foo, 0777);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("cli_posix_mkdir of %s should fail with "
+ "NT_STATUS_OBJECT_PATH_NOT_FOUND got "
+ "%s instead\n",
+ fname_foo_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_rmdir(cli, fname_foo_foo);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("cli_posix_rmdir of %s should fail with "
+ "NT_STATUS_OBJECT_PATH_NOT_FOUND got "
+ "%s instead\n",
+ fname_foo_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_open(cli,
+ fname_foo_foo,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("cli_posix_open of %s should fail with "
+ "NT_STATUS_OBJECT_PATH_NOT_FOUND got "
+ "%s instead\n",
+ fname_foo_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_unlink(cli, fname_foo_foo);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("cli_posix_unlink of %s should fail with "
+ "NT_STATUS_OBJECT_PATH_NOT_FOUND got "
+ "%s instead\n",
+ fname_foo_foo,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_foo, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed\n", fname_foo);
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_Foo, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed\n", fname_Foo);
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_foo_foo, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed\n", fname_foo_foo);
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_foo_Foo, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed\n", fname_foo_Foo);
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_Foo_foo, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed\n", fname_Foo_foo);
+ goto out;
+ }
+
+ status = cli_posix_mkdir(cli, fname_Foo_Foo, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed\n", fname_Foo_Foo);
+ goto out;
+ }
+
+ printf("POSIX mkdir test passed\n");
+ correct = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ cli_posix_rmdir(cli, fname_foo_foo);
+ cli_posix_rmdir(cli, fname_foo_Foo);
+ cli_posix_rmdir(cli, fname_foo);
+
+ cli_posix_rmdir(cli, fname_Foo_foo);
+ cli_posix_rmdir(cli, fname_Foo_Foo);
+ cli_posix_rmdir(cli, fname_Foo);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+struct posix_acl_oplock_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ bool *got_break;
+ bool *acl_ret;
+ NTSTATUS status;
+};
+
+static void posix_acl_oplock_got_break(struct tevent_req *req)
+{
+ struct posix_acl_oplock_state *state = tevent_req_callback_data(
+ req, struct posix_acl_oplock_state);
+ uint16_t fnum;
+ uint8_t level;
+ NTSTATUS status;
+
+ status = cli_smb_oplock_break_waiter_recv(req, &fnum, &level);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_smb_oplock_break_waiter_recv returned %s\n",
+ nt_errstr(status));
+ return;
+ }
+ *state->got_break = true;
+
+ req = cli_oplock_ack_send(state, state->ev, state->cli, fnum,
+ NO_OPLOCK);
+ if (req == NULL) {
+ printf("cli_oplock_ack_send failed\n");
+ return;
+ }
+}
+
+static void posix_acl_oplock_got_acl(struct tevent_req *req)
+{
+ struct posix_acl_oplock_state *state = tevent_req_callback_data(
+ req, struct posix_acl_oplock_state);
+ size_t ret_size = 0;
+ char *ret_data = NULL;
+
+ state->status = cli_posix_getacl_recv(req,
+ state,
+ &ret_size,
+ &ret_data);
+
+ if (!NT_STATUS_IS_OK(state->status)) {
+ printf("cli_posix_getacl_recv returned %s\n",
+ nt_errstr(state->status));
+ }
+ *state->acl_ret = true;
+}
+
+static bool run_posix_acl_oplock_test(int dummy)
+{
+ struct tevent_context *ev;
+ struct cli_state *cli1, *cli2;
+ struct tevent_req *oplock_req, *getacl_req;
+ const char *fname = "posix_acl_oplock";
+ uint16_t fnum;
+ int saved_use_oplocks = use_oplocks;
+ NTSTATUS status;
+ bool correct = true;
+ bool got_break = false;
+ bool acl_ret = false;
+
+ struct posix_acl_oplock_state *state;
+
+ printf("starting posix_acl_oplock test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ use_level_II_oplocks = false;
+ use_oplocks = saved_use_oplocks;
+ return false;
+ }
+
+ if (!torture_open_connection(&cli2, 1)) {
+ use_level_II_oplocks = false;
+ use_oplocks = saved_use_oplocks;
+ return false;
+ }
+
+ /* Setup posix on cli2 only. */
+ status = torture_setup_unix_extensions(cli2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+ smbXcli_conn_set_sockopt(cli2->conn, sockops);
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create the file on the Windows connection. */
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli1, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close1 failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ cli1->use_oplocks = true;
+
+ /* Open with oplock. */
+ status = cli_ntcreate(cli1,
+ fname,
+ 0,
+ FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0,
+ 0,
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ printf("tevent_context_init failed\n");
+ return false;
+ }
+
+ state = talloc_zero(ev, struct posix_acl_oplock_state);
+ if (state == NULL) {
+ printf("talloc failed\n");
+ return false;
+ }
+ state->ev = ev;
+ state->cli = cli1;
+ state->got_break = &got_break;
+ state->acl_ret = &acl_ret;
+
+ oplock_req = cli_smb_oplock_break_waiter_send(
+ talloc_tos(), ev, cli1);
+ if (oplock_req == NULL) {
+ printf("cli_smb_oplock_break_waiter_send failed\n");
+ return false;
+ }
+ tevent_req_set_callback(oplock_req, posix_acl_oplock_got_break, state);
+
+ /* Get ACL on POSIX connection - should break oplock. */
+ getacl_req = cli_posix_getacl_send(talloc_tos(),
+ ev,
+ cli2,
+ fname);
+ if (getacl_req == NULL) {
+ printf("cli_posix_getacl_send failed\n");
+ return false;
+ }
+ tevent_req_set_callback(getacl_req, posix_acl_oplock_got_acl, state);
+
+ while (!got_break || !acl_ret) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret == -1) {
+ printf("tevent_loop_once failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(state->status)) {
+ printf("getacl failed (%s)\n", nt_errstr(state->status));
+ correct = false;
+ }
+
+ status = cli_close(cli1, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close2 failed (%s)\n", nt_errstr(status));
+ correct = false;
+ }
+
+ status = cli_unlink(cli1,
+ fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ correct = false;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = false;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = false;
+ }
+
+ if (!got_break) {
+ correct = false;
+ }
+
+ printf("finished posix acl oplock test\n");
+
+ return correct;
+}
+
+static bool run_posix_acl_shareroot_test(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+ bool correct = false;
+ char *posix_acl = NULL;
+ size_t posix_acl_len = 0;
+ uint16_t num_file_acls = 0;
+ uint16_t num_dir_acls = 0;
+ uint16_t i;
+ uint32_t expected_size = 0;
+ bool got_user = false;
+ bool got_group = false;
+ bool got_other = false;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+
+ printf("starting posix_acl_shareroot test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to setup unix extensions\n");
+ goto out;
+ }
+
+ /* Get the POSIX ACL on the root of the share. */
+ status = cli_posix_getacl(cli,
+ ".",
+ frame,
+ &posix_acl_len,
+ &posix_acl);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_getacl of '.' failed (%s)\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (posix_acl_len < 6 ||
+ SVAL(posix_acl,0) != SMB_POSIX_ACL_VERSION) {
+ printf("getfacl ., unknown POSIX acl version %u.\n",
+ (unsigned int)CVAL(posix_acl,0) );
+ goto out;
+ }
+
+ num_file_acls = SVAL(posix_acl,2);
+ num_dir_acls = SVAL(posix_acl,4);
+ expected_size = SMB_POSIX_ACL_HEADER_SIZE +
+ SMB_POSIX_ACL_ENTRY_SIZE*
+ (num_file_acls+num_dir_acls);
+
+ if (posix_acl_len != expected_size) {
+ printf("incorrect POSIX acl buffer size "
+ "(should be %u, was %u).\n",
+ (unsigned int)expected_size,
+ (unsigned int)posix_acl_len);
+ goto out;
+ }
+
+ /*
+ * We don't need to know what the ACL's are
+ * we just need to know we have at least 3
+ * file entries (u,g,o).
+ */
+
+ for (i = 0; i < num_file_acls; i++) {
+ unsigned char tagtype =
+ CVAL(posix_acl,
+ SMB_POSIX_ACL_HEADER_SIZE+
+ (i*SMB_POSIX_ACL_ENTRY_SIZE));
+
+ switch(tagtype) {
+ case SMB_POSIX_ACL_USER_OBJ:
+ got_user = true;
+ break;
+ case SMB_POSIX_ACL_GROUP_OBJ:
+ got_group = true;
+ break;
+ case SMB_POSIX_ACL_OTHER:
+ got_other = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!got_user) {
+ printf("Missing user entry\n");
+ goto out;
+ }
+
+ if (!got_group) {
+ printf("Missing group entry\n");
+ goto out;
+ }
+
+ if (!got_other) {
+ printf("Missing other entry\n");
+ goto out;
+ }
+
+ correct = true;
+
+ out:
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ printf("finished posix acl shareroot test\n");
+ TALLOC_FREE(frame);
+
+ return correct;
+}
+
+static uint32_t open_attrs_table[] = {
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_ARCHIVE,
+ FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_SYSTEM,
+
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM,
+};
+
+struct trunc_open_results {
+ unsigned int num;
+ uint32_t init_attr;
+ uint32_t trunc_attr;
+ uint32_t result_attr;
+};
+
+static struct trunc_open_results attr_results[] = {
+ { 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+ { 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+ { 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+ { 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+ { 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+ { 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+ { 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+ { 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 119, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+ { 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN },
+ { 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM },
+ { 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }
+};
+
+static bool run_openattrtest(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\openattr.file";
+ uint16_t fnum1;
+ bool correct = True;
+ uint32_t attr;
+ unsigned int i, j, k, l;
+ NTSTATUS status;
+
+ printf("starting open attr test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32_t); i++) {
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli1, fname, 0, FILE_WRITE_DATA,
+ open_attrs_table[i], FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open %d (1) of %s failed (%s)\n", i, fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close %d (1) of %s failed (%s)\n", i, fname, nt_errstr(status));
+ return False;
+ }
+
+ for (j = 0; j < sizeof(open_attrs_table)/sizeof(uint32_t); j++) {
+ status = cli_ntcreate(cli1, fname, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA,
+ open_attrs_table[j],
+ FILE_SHARE_NONE, FILE_OVERWRITE,
+ 0, 0, &fnum1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ for (l = 0; l < sizeof(attr_results)/sizeof(struct trunc_open_results); l++) {
+ if (attr_results[l].num == k) {
+ printf("[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(0x%x:%s)\n",
+ k, open_attrs_table[i],
+ open_attrs_table[j],
+ fname, NT_STATUS_V(status), nt_errstr(status));
+ correct = False;
+ }
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("[%d] trunc open 0x%x -> 0x%x failed with wrong error code %s\n",
+ k, open_attrs_table[i], open_attrs_table[j],
+ nt_errstr(status));
+ correct = False;
+ }
+#if 0
+ printf("[%d] trunc open 0x%x -> 0x%x failed\n", k, open_attrs_table[i], open_attrs_table[j]);
+#endif
+ k++;
+ continue;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close %d (2) of %s failed (%s)\n", j, fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_getatr(cli1, fname, &attr, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("getatr(2) failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+#if 0
+ printf("[%d] getatr check [0x%x] trunc [0x%x] got attr 0x%x\n",
+ k, open_attrs_table[i], open_attrs_table[j], attr );
+#endif
+
+ for (l = 0; l < sizeof(attr_results)/sizeof(struct trunc_open_results); l++) {
+ if (attr_results[l].num == k) {
+ if (attr != attr_results[l].result_attr ||
+ open_attrs_table[i] != attr_results[l].init_attr ||
+ open_attrs_table[j] != attr_results[l].trunc_attr) {
+ printf("getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n",
+ open_attrs_table[i],
+ open_attrs_table[j],
+ (unsigned int)attr,
+ attr_results[l].result_attr);
+ correct = False;
+ }
+ break;
+ }
+ }
+ k++;
+ }
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ printf("open attr test %s.\n", correct ? "passed" : "failed");
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ return correct;
+}
+
+static NTSTATUS list_fn(struct file_info *finfo,
+ const char *name, void *state)
+{
+ int *matched = (int *)state;
+ if (matched != NULL) {
+ *matched += 1;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ test directory listing speed
+ */
+static bool run_dirtest(int dummy)
+{
+ int i;
+ static struct cli_state *cli;
+ uint16_t fnum;
+ struct timeval core_start;
+ bool correct = True;
+ int matched;
+
+ printf("starting directory test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ srandom(0);
+ for (i=0;i<torture_numops;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\%x", (int)random());
+ if (!NT_STATUS_IS_OK(cli_openx(cli, fname, O_RDWR|O_CREAT, DENY_NONE, &fnum))) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ cli_close(cli, fnum);
+ }
+
+ core_start = timeval_current();
+
+ matched = 0;
+ cli_list(cli, "a*.*", 0, list_fn, &matched);
+ printf("Matched %d\n", matched);
+
+ matched = 0;
+ cli_list(cli, "b*.*", 0, list_fn, &matched);
+ printf("Matched %d\n", matched);
+
+ matched = 0;
+ cli_list(cli, "xyzabc", 0, list_fn, &matched);
+ printf("Matched %d\n", matched);
+
+ printf("dirtest core %g seconds\n", timeval_elapsed(&core_start));
+
+ srandom(0);
+ for (i=0;i<torture_numops;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\%x", (int)random());
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ }
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished dirtest\n");
+
+ return correct;
+}
+
+static NTSTATUS del_fn(struct file_info *finfo, const char *mask,
+ void *state)
+{
+ struct cli_state *pcli = (struct cli_state *)state;
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\%s", finfo->name);
+
+ if (strcmp(finfo->name, ".") == 0 || strcmp(finfo->name, "..") == 0)
+ return NT_STATUS_OK;
+
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!NT_STATUS_IS_OK(cli_rmdir(pcli, fname)))
+ printf("del_fn: failed to rmdir %s\n,", fname );
+ } else {
+ if (!NT_STATUS_IS_OK(cli_unlink(pcli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))
+ printf("del_fn: failed to unlink %s\n,", fname );
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ send a raw ioctl - used by the torture code
+*/
+static NTSTATUS cli_raw_ioctl(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t code,
+ DATA_BLOB *blob)
+{
+ uint16_t vwv[3];
+ NTSTATUS status;
+
+ PUSH_LE_U16(vwv + 0, 0, fnum);
+ PUSH_LE_U16(vwv + 1, 0, code >> 16);
+ PUSH_LE_U16(vwv + 2, 0, (code & 0xFFFF));
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBioctl,
+ 0,
+ 3,
+ vwv,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *blob = data_blob_null;
+ return NT_STATUS_OK;
+}
+
+/*
+ sees what IOCTLs are supported
+ */
+bool torture_ioctl_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16_t device, function;
+ uint16_t fnum;
+ const char *fname = "\\ioctl.dat";
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ printf("starting ioctl test\n");
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_openx(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open of %s failed (%s)\n", fname, nt_errstr(status));
+ return False;
+ }
+
+ status = cli_raw_ioctl(cli, fnum, 0x2d0000 | (0x0420<<2), &blob);
+ printf("ioctl device info: %s\n", nt_errstr(status));
+
+ status = cli_raw_ioctl(cli, fnum, IOCTL_QUERY_JOB_INFO, &blob);
+ printf("ioctl job info: %s\n", nt_errstr(status));
+
+ for (device=0;device<0x100;device++) {
+ printf("ioctl test with device = 0x%x\n", device);
+ for (function=0;function<0x100;function++) {
+ uint32_t code = (device<<16) | function;
+
+ status = cli_raw_ioctl(cli, fnum, code, &blob);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("ioctl 0x%x OK : %d bytes\n", (int)code,
+ (int)blob.length);
+ data_blob_free(&blob);
+ }
+ }
+ }
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/*
+ tries variants of chkpath
+ */
+bool torture_chkpath_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16_t fnum;
+ bool ret;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ printf("starting chkpath test\n");
+
+ /* cleanup from an old run */
+ torture_deltree(cli, "\\chkpath.dir");
+
+ status = cli_mkdir(cli, "\\chkpath.dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mkdir1 failed : %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_mkdir(cli, "\\chkpath.dir\\dir2");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mkdir2 failed : %s\n", nt_errstr(status));
+ return False;
+ }
+
+ status = cli_openx(cli, "\\chkpath.dir\\foo.txt", O_RDWR|O_CREAT|O_EXCL,
+ DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open1 failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+ cli_close(cli, fnum);
+
+ status = cli_chkpath(cli, "\\chkpath.dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("chkpath1 failed: %s\n", nt_errstr(status));
+ ret = False;
+ }
+
+ status = cli_chkpath(cli, "\\chkpath.dir\\dir2");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("chkpath2 failed: %s\n", nt_errstr(status));
+ ret = False;
+ }
+
+ status = cli_chkpath(cli, "\\chkpath.dir\\foo.txt");
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = check_error(__LINE__, status, ERRDOS, ERRbadpath,
+ NT_STATUS_NOT_A_DIRECTORY);
+ } else {
+ printf("* chkpath on a file should fail\n");
+ ret = False;
+ }
+
+ status = cli_chkpath(cli, "\\chkpath.dir\\bar.txt");
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = check_error(__LINE__, status, ERRDOS, ERRbadfile,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ printf("* chkpath on a non existent file should fail\n");
+ ret = False;
+ }
+
+ status = cli_chkpath(cli, "\\chkpath.dir\\dirxx\\bar.txt");
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = check_error(__LINE__, status, ERRDOS, ERRbadpath,
+ NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ printf("* chkpath on a non existent component should fail\n");
+ ret = False;
+ }
+
+ torture_deltree(cli, "\\chkpath.dir");
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return ret;
+}
+
+static bool run_eatest(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\eatest.txt";
+ bool correct = True;
+ uint16_t fnum;
+ size_t i, num_eas;
+ struct ea_struct *ea_list = NULL;
+ TALLOC_CTX *mem_ctx = talloc_init("eatest");
+ NTSTATUS status;
+
+ printf("starting eatest\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ status = cli_ntcreate(cli, fname, 0,
+ FIRST_DESIRED_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF,
+ 0x4044, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open failed - %s\n", nt_errstr(status));
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+
+ for (i = 0; i < 10; i++) {
+ fstring ea_name, ea_val;
+
+ slprintf(ea_name, sizeof(ea_name), "EA_%zu", i);
+ memset(ea_val, (char)i+1, i+1);
+ status = cli_set_ea_fnum(cli, fnum, ea_name, ea_val, i+1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ea_set of name %s failed - %s\n", ea_name,
+ nt_errstr(status));
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+ }
+
+ cli_close(cli, fnum);
+ for (i = 0; i < 10; i++) {
+ fstring ea_name, ea_val;
+
+ slprintf(ea_name, sizeof(ea_name), "EA_%zu", i+10);
+ memset(ea_val, (char)i+1, i+1);
+ status = cli_set_ea_path(cli, fname, ea_name, ea_val, i+1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ea_set of name %s failed - %s\n", ea_name,
+ nt_errstr(status));
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+ }
+
+ status = cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ea_get list failed - %s\n", nt_errstr(status));
+ correct = False;
+ }
+
+ printf("num_eas = %d\n", (int)num_eas);
+
+ if (num_eas != 20) {
+ printf("Should be 20 EA's stored... failing.\n");
+ correct = False;
+ }
+
+ for (i = 0; i < num_eas; i++) {
+ printf("%zu: ea_name = %s. Val = ", i, ea_list[i].name);
+ dump_data(0, ea_list[i].value.data,
+ ea_list[i].value.length);
+ }
+
+ /* Setting EA's to zero length deletes them. Test this */
+ printf("Now deleting all EA's - case independent....\n");
+
+#if 1
+ cli_set_ea_path(cli, fname, "", "", 0);
+#else
+ for (i = 0; i < 20; i++) {
+ fstring ea_name;
+ slprintf(ea_name, sizeof(ea_name), "ea_%d", i);
+ status = cli_set_ea_path(cli, fname, ea_name, "", 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ea_set of name %s failed - %s\n", ea_name,
+ nt_errstr(status));
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+ }
+#endif
+
+ status = cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ea_get list failed - %s\n", nt_errstr(status));
+ correct = False;
+ }
+
+ printf("num_eas = %d\n", (int)num_eas);
+ for (i = 0; i < num_eas; i++) {
+ printf("%zu: ea_name = %s. Val = ", i, ea_list[i].name);
+ dump_data(0, ea_list[i].value.data,
+ ea_list[i].value.length);
+ }
+
+ if (num_eas != 0) {
+ printf("deleting EA's failed.\n");
+ correct = False;
+ }
+
+ /* Try and delete a non existent EA. */
+ status = cli_set_ea_path(cli, fname, "foo", "", 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("deleting non-existent EA 'foo' should succeed. %s\n",
+ nt_errstr(status));
+ correct = False;
+ }
+
+ talloc_destroy(mem_ctx);
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static bool run_dirtest1(int dummy)
+{
+ int i;
+ static struct cli_state *cli;
+ uint16_t fnum;
+ int num_seen;
+ bool correct = True;
+
+ printf("starting directory test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+ cli_list(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, del_fn, cli);
+ cli_rmdir(cli, "\\LISTDIR");
+ cli_mkdir(cli, "\\LISTDIR");
+
+ /* Create 1000 files and 1000 directories. */
+ for (i=0;i<1000;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\f%d", i);
+ if (!NT_STATUS_IS_OK(cli_ntcreate(cli, fname, 0, GENERIC_ALL_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF,
+ 0, 0, &fnum, NULL))) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ cli_close(cli, fnum);
+ }
+ for (i=0;i<1000;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\d%d", i);
+ if (!NT_STATUS_IS_OK(cli_mkdir(cli, fname))) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ }
+
+ /* Now ensure that doing an old list sees both files and directories. */
+ num_seen = 0;
+ cli_list_old(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, list_fn, &num_seen);
+ printf("num_seen = %d\n", num_seen );
+ /* We should see 100 files + 1000 directories + . and .. */
+ if (num_seen != 2002)
+ correct = False;
+
+ /* Ensure if we have the "must have" bits we only see the
+ * relevant entries.
+ */
+ num_seen = 0;
+ cli_list_old(cli, "\\LISTDIR\\*", (FILE_ATTRIBUTE_DIRECTORY<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, &num_seen);
+ printf("num_seen = %d\n", num_seen );
+ if (num_seen != 1002)
+ correct = False;
+
+ num_seen = 0;
+ cli_list_old(cli, "\\LISTDIR\\*", (FILE_ATTRIBUTE_ARCHIVE<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, &num_seen);
+ printf("num_seen = %d\n", num_seen );
+ if (num_seen != 1000)
+ correct = False;
+
+ /* Delete everything. */
+ cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+ cli_list(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, del_fn, cli);
+ cli_rmdir(cli, "\\LISTDIR");
+
+#if 0
+ printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+#endif
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished dirtest1\n");
+
+ return correct;
+}
+
+static bool run_error_map_extract(int dummy) {
+
+ static struct cli_state *c_dos;
+ static struct cli_state *c_nt;
+ NTSTATUS status;
+
+ uint32_t error;
+
+ uint32_t errnum;
+ uint8_t errclass;
+
+ NTSTATUS nt_status;
+
+ fstring user;
+
+ /* NT-Error connection */
+
+ disable_spnego = true;
+ if (!(c_nt = open_nbt_connection())) {
+ disable_spnego = false;
+ return False;
+ }
+ disable_spnego = false;
+
+ status = smbXcli_negprot(c_nt->conn,
+ c_nt->timeout,
+ PROTOCOL_CORE,
+ PROTOCOL_NT1,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s rejected the NT-error negprot (%s)\n", host,
+ nt_errstr(status));
+ cli_shutdown(c_nt);
+ return False;
+ }
+
+ status = cli_session_setup_anon(c_nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s rejected the NT-error initial session setup (%s)\n",host, nt_errstr(status));
+ return False;
+ }
+
+ /* DOS-Error connection */
+
+ disable_spnego = true;
+ force_dos_errors = true;
+ if (!(c_dos = open_nbt_connection())) {
+ disable_spnego = false;
+ force_dos_errors = false;
+ return False;
+ }
+ disable_spnego = false;
+ force_dos_errors = false;
+
+ status = smbXcli_negprot(c_dos->conn,
+ c_dos->timeout,
+ PROTOCOL_CORE,
+ PROTOCOL_NT1,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s rejected the DOS-error negprot (%s)\n", host,
+ nt_errstr(status));
+ cli_shutdown(c_dos);
+ return False;
+ }
+
+ status = cli_session_setup_anon(c_dos);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s rejected the DOS-error initial session setup (%s)\n",
+ host, nt_errstr(status));
+ return False;
+ }
+
+ c_nt->map_dos_errors = false;
+ c_dos->map_dos_errors = false;
+
+ for (error=(0xc0000000 | 0x1); error < (0xc0000000| 0xFFF); error++) {
+ struct cli_credentials *user_creds = NULL;
+
+ fstr_sprintf(user, "%X", error);
+
+ user_creds = cli_session_creds_init(talloc_tos(),
+ user,
+ workgroup,
+ NULL, /* realm */
+ password,
+ false, /* use_kerberos */
+ false, /* fallback_after_kerberos */
+ false, /* use_ccache */
+ false); /* password_is_nt_hash */
+ if (user_creds == NULL) {
+ printf("cli_session_creds_init(%s) failed\n", user);
+ return false;
+ }
+
+ status = cli_session_setup_creds(c_nt, user_creds);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("/** Session setup succeeded. This shouldn't happen...*/\n");
+ }
+
+ /* Case #1: 32-bit NT errors */
+ if (!NT_STATUS_IS_DOS(status)) {
+ nt_status = status;
+ } else {
+ printf("/** Dos error on NT connection! (%s) */\n",
+ nt_errstr(status));
+ nt_status = NT_STATUS(0xc0000000);
+ }
+
+ status = cli_session_setup_creds(c_dos, user_creds);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("/** Session setup succeeded. This shouldn't happen...*/\n");
+ }
+
+ /* Case #1: 32-bit NT errors */
+ if (NT_STATUS_IS_DOS(status)) {
+ printf("/** NT error on DOS connection! (%s) */\n",
+ nt_errstr(status));
+ errnum = errclass = 0;
+ } else {
+ errclass = NT_STATUS_DOS_CLASS(status);
+ errnum = NT_STATUS_DOS_CODE(status);
+ }
+
+ if (NT_STATUS_V(nt_status) != error) {
+ printf("/*\t{ This NT error code was 'sqashed'\n\t from %s to %s \n\t during the session setup }\n*/\n",
+ get_nt_error_c_code(talloc_tos(), NT_STATUS(error)),
+ get_nt_error_c_code(talloc_tos(), nt_status));
+ }
+
+ printf("\t{%s,\t%s,\t%s},\n",
+ smb_dos_err_class(errclass),
+ smb_dos_err_name(errclass, errnum),
+ get_nt_error_c_code(talloc_tos(), NT_STATUS(error)));
+
+ TALLOC_FREE(user_creds);
+ }
+ return True;
+}
+
+static bool run_sesssetup_bench(int dummy)
+{
+ static struct cli_state *c;
+ const char *fname = "\\file.dat";
+ uint16_t fnum;
+ NTSTATUS status;
+ int i;
+
+ if (!torture_open_connection(&c, 0)) {
+ return false;
+ }
+
+ status = cli_ntcreate(c, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ FILE_DELETE_ON_CLOSE, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open %s failed: %s\n", fname, nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i<torture_numops; i++) {
+ status = cli_session_setup_creds(c, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) cli_session_setup_creds failed: %s\n",
+ __location__, nt_errstr(status));
+ return false;
+ }
+
+ d_printf("\r%d ", (int)cli_state_get_uid(c));
+
+ status = cli_ulogoff(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) cli_ulogoff failed: %s\n",
+ __location__, nt_errstr(status));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool subst_test(const char *str, const char *user, const char *domain,
+ uid_t uid, gid_t gid, const char *expected)
+{
+ char *subst;
+ bool result = true;
+
+ subst = talloc_sub_specified(talloc_tos(), str, user, NULL, domain, uid, gid);
+
+ if (strcmp(subst, expected) != 0) {
+ printf("sub_specified(%s, %s, %s, %d, %d) returned [%s], expected "
+ "[%s]\n", str, user, domain, (int)uid, (int)gid, subst,
+ expected);
+ result = false;
+ }
+
+ TALLOC_FREE(subst);
+ return result;
+}
+
+static void chain1_open_completion(struct tevent_req *req)
+{
+ uint16_t fnum;
+ NTSTATUS status;
+ status = cli_openx_recv(req, &fnum);
+ TALLOC_FREE(req);
+
+ d_printf("cli_openx_recv returned %s: %d\n",
+ nt_errstr(status),
+ NT_STATUS_IS_OK(status) ? fnum : -1);
+}
+
+static void chain1_write_completion(struct tevent_req *req)
+{
+ size_t written;
+ NTSTATUS status;
+ status = cli_write_andx_recv(req, &written);
+ TALLOC_FREE(req);
+
+ d_printf("cli_write_andx_recv returned %s: %d\n",
+ nt_errstr(status),
+ NT_STATUS_IS_OK(status) ? (int)written : -1);
+}
+
+static void chain1_close_completion(struct tevent_req *req)
+{
+ NTSTATUS status;
+ bool *done = (bool *)tevent_req_callback_data_void(req);
+
+ status = cli_close_recv(req);
+ *done = true;
+
+ TALLOC_FREE(req);
+
+ d_printf("cli_close returned %s\n", nt_errstr(status));
+}
+
+static bool run_chain1(int dummy)
+{
+ struct cli_state *cli1;
+ struct tevent_context *evt = samba_tevent_context_init(NULL);
+ struct tevent_req *reqs[3], *smbreqs[3];
+ bool done = false;
+ const char *str = "foobar";
+ const char *fname = "\\test_chain";
+ NTSTATUS status;
+
+ printf("starting chain1 test\n");
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ reqs[0] = cli_openx_create(talloc_tos(), evt, cli1, fname,
+ O_CREAT|O_RDWR, 0, &smbreqs[0]);
+ if (reqs[0] == NULL) return false;
+ tevent_req_set_callback(reqs[0], chain1_open_completion, NULL);
+
+
+ reqs[1] = cli_write_andx_create(talloc_tos(), evt, cli1, 0, 0,
+ (const uint8_t *)str, 0, strlen(str)+1,
+ smbreqs, 1, &smbreqs[1]);
+ if (reqs[1] == NULL) return false;
+ tevent_req_set_callback(reqs[1], chain1_write_completion, NULL);
+
+ reqs[2] = cli_smb1_close_create(talloc_tos(), evt, cli1, 0, &smbreqs[2]);
+ if (reqs[2] == NULL) return false;
+ tevent_req_set_callback(reqs[2], chain1_close_completion, &done);
+
+ status = smb1cli_req_chain_submit(smbreqs, ARRAY_SIZE(smbreqs));
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ while (!done) {
+ tevent_loop_once(evt);
+ }
+
+ torture_close_connection(cli1);
+ return True;
+}
+
+static void chain2_sesssetup_completion(struct tevent_req *req)
+{
+ NTSTATUS status;
+ status = cli_session_setup_guest_recv(req);
+ d_printf("sesssetup returned %s\n", nt_errstr(status));
+}
+
+static void chain2_tcon_completion(struct tevent_req *req)
+{
+ bool *done = (bool *)tevent_req_callback_data_void(req);
+ NTSTATUS status;
+ status = cli_tcon_andx_recv(req);
+ d_printf("tcon_and_x returned %s\n", nt_errstr(status));
+ *done = true;
+}
+
+static bool run_chain2(int dummy)
+{
+ struct cli_state *cli1;
+ struct tevent_context *evt = samba_tevent_context_init(NULL);
+ struct tevent_req *reqs[2], *smbreqs[2];
+ bool done = false;
+ NTSTATUS status;
+ int flags = CLI_FULL_CONNECTION_FORCE_SMB1;
+
+ printf("starting chain2 test\n");
+ status = cli_start_connection(&cli1, lp_netbios_name(), host, NULL,
+ port_to_use, SMB_SIGNING_DEFAULT, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ reqs[0] = cli_session_setup_guest_create(talloc_tos(), evt, cli1,
+ &smbreqs[0]);
+ if (reqs[0] == NULL) return false;
+ tevent_req_set_callback(reqs[0], chain2_sesssetup_completion, NULL);
+
+ reqs[1] = cli_tcon_andx_create(talloc_tos(), evt, cli1, "IPC$",
+ "?????", NULL, 0, &smbreqs[1]);
+ if (reqs[1] == NULL) return false;
+ tevent_req_set_callback(reqs[1], chain2_tcon_completion, &done);
+
+ status = smb1cli_req_chain_submit(smbreqs, ARRAY_SIZE(smbreqs));
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ while (!done) {
+ tevent_loop_once(evt);
+ }
+
+ torture_close_connection(cli1);
+ return True;
+}
+
+
+struct torture_createdel_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+};
+
+static void torture_createdel_created(struct tevent_req *subreq);
+static void torture_createdel_closed(struct tevent_req *subreq);
+
+static struct tevent_req *torture_createdel_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct torture_createdel_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct torture_createdel_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ subreq = cli_ntcreate_send(
+ state, ev, cli, name, 0,
+ FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN_IF, FILE_DELETE_ON_CLOSE,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, torture_createdel_created, req);
+ return req;
+}
+
+static void torture_createdel_created(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct torture_createdel_state *state = tevent_req_data(
+ req, struct torture_createdel_state);
+ NTSTATUS status;
+ uint16_t fnum;
+
+ status = cli_ntcreate_recv(subreq, &fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("cli_ntcreate_recv returned %s\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ subreq = cli_close_send(state, state->ev, state->cli, fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, torture_createdel_closed, req);
+}
+
+static void torture_createdel_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("cli_close_recv returned %s\n", nt_errstr(status)));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS torture_createdel_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct torture_createdels_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *base_name;
+ int sent;
+ int received;
+ int num_files;
+ struct tevent_req **reqs;
+};
+
+static void torture_createdels_done(struct tevent_req *subreq);
+
+static struct tevent_req *torture_createdels_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *base_name,
+ int num_parallel,
+ int num_files)
+{
+ struct tevent_req *req;
+ struct torture_createdels_state *state;
+ int i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct torture_createdels_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->base_name = talloc_strdup(state, base_name);
+ if (tevent_req_nomem(state->base_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->num_files = MAX(num_parallel, num_files);
+ state->sent = 0;
+ state->received = 0;
+
+ state->reqs = talloc_array(state, struct tevent_req *, num_parallel);
+ if (tevent_req_nomem(state->reqs, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_parallel; i++) {
+ char *name;
+
+ name = talloc_asprintf(state, "%s%8.8d", state->base_name,
+ state->sent);
+ if (tevent_req_nomem(name, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->reqs[i] = torture_createdel_send(
+ state->reqs, state->ev, state->cli, name);
+ if (tevent_req_nomem(state->reqs[i], req)) {
+ return tevent_req_post(req, ev);
+ }
+ name = talloc_move(state->reqs[i], &name);
+ tevent_req_set_callback(state->reqs[i],
+ torture_createdels_done, req);
+ state->sent += 1;
+ }
+ return req;
+}
+
+static void torture_createdels_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct torture_createdels_state *state = tevent_req_data(
+ req, struct torture_createdels_state);
+ size_t i, num_parallel = talloc_array_length(state->reqs);
+ NTSTATUS status;
+ char *name;
+
+ status = torture_createdel_recv(subreq);
+ if (!NT_STATUS_IS_OK(status)){
+ DEBUG(10, ("torture_createdel_recv returned %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(subreq);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ for (i=0; i<num_parallel; i++) {
+ if (subreq == state->reqs[i]) {
+ break;
+ }
+ }
+ if (i == num_parallel) {
+ DEBUG(10, ("received something we did not send\n"));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ TALLOC_FREE(state->reqs[i]);
+
+ if (state->sent >= state->num_files) {
+ tevent_req_done(req);
+ return;
+ }
+
+ name = talloc_asprintf(state, "%s%8.8d", state->base_name,
+ state->sent);
+ if (tevent_req_nomem(name, req)) {
+ return;
+ }
+ state->reqs[i] = torture_createdel_send(state->reqs, state->ev,
+ state->cli, name);
+ if (tevent_req_nomem(state->reqs[i], req)) {
+ return;
+ }
+ name = talloc_move(state->reqs[i], &name);
+ tevent_req_set_callback(state->reqs[i], torture_createdels_done, req);
+ state->sent += 1;
+}
+
+static NTSTATUS torture_createdels_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct swallow_notify_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+ uint32_t completion_filter;
+ bool recursive;
+ bool (*fn)(uint32_t action, const char *name, void *priv);
+ void *priv;
+};
+
+static void swallow_notify_done(struct tevent_req *subreq);
+
+static struct tevent_req *swallow_notify_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t completion_filter,
+ bool recursive,
+ bool (*fn)(uint32_t action,
+ const char *name,
+ void *priv),
+ void *priv)
+{
+ struct tevent_req *req, *subreq;
+ struct swallow_notify_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct swallow_notify_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fnum = fnum;
+ state->completion_filter = completion_filter;
+ state->recursive = recursive;
+ state->fn = fn;
+ state->priv = priv;
+
+ subreq = cli_notify_send(state, state->ev, state->cli, state->fnum,
+ 0xffff, state->completion_filter,
+ state->recursive);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, swallow_notify_done, req);
+ return req;
+}
+
+static void swallow_notify_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct swallow_notify_state *state = tevent_req_data(
+ req, struct swallow_notify_state);
+ NTSTATUS status;
+ uint32_t i, num_changes;
+ struct notify_change *changes;
+
+ status = cli_notify_recv(subreq, state, &num_changes, &changes);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("cli_notify_recv returned %s\n",
+ nt_errstr(status)));
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ for (i=0; i<num_changes; i++) {
+ state->fn(changes[i].action, changes[i].name, state->priv);
+ }
+ TALLOC_FREE(changes);
+
+ subreq = cli_notify_send(state, state->ev, state->cli, state->fnum,
+ 0xffff, state->completion_filter,
+ state->recursive);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, swallow_notify_done, req);
+}
+
+static bool print_notifies(uint32_t action, const char *name, void *priv)
+{
+ if (DEBUGLEVEL > 5) {
+ d_printf("%d %s\n", (int)action, name);
+ }
+ return true;
+}
+
+static void notify_bench_done(struct tevent_req *req)
+{
+ int *num_finished = (int *)tevent_req_callback_data_void(req);
+ *num_finished += 1;
+}
+
+static bool run_notify_bench(int dummy)
+{
+ const char *dname = "\\notify-bench";
+ struct tevent_context *ev;
+ NTSTATUS status;
+ uint16_t dnum;
+ struct tevent_req *req1;
+ struct tevent_req *req2 = NULL;
+ int i, num_unc_names;
+ int num_finished = 0;
+
+ printf("starting notify-bench test\n");
+
+ if (use_multishare_conn) {
+ char **unc_list;
+ unc_list = file_lines_load(multishare_conn_fname,
+ &num_unc_names, 0, NULL);
+ if (!unc_list || num_unc_names <= 0) {
+ d_printf("Failed to load unc names list from '%s'\n",
+ multishare_conn_fname);
+ return false;
+ }
+ TALLOC_FREE(unc_list);
+ } else {
+ num_unc_names = 1;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_printf("tevent_context_init failed\n");
+ return false;
+ }
+
+ for (i=0; i<num_unc_names; i++) {
+ struct cli_state *cli;
+ char *base_fname;
+
+ base_fname = talloc_asprintf(talloc_tos(), "%s\\file%3.3d.",
+ dname, i);
+ if (base_fname == NULL) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, i)) {
+ return false;
+ }
+
+ status = cli_ntcreate(cli, dname, 0,
+ MAXIMUM_ALLOWED_ACCESS,
+ 0, FILE_SHARE_READ|FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN_IF, FILE_DIRECTORY_FILE, 0,
+ &dnum, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not create %s: %s\n", dname,
+ nt_errstr(status));
+ return false;
+ }
+
+ req1 = swallow_notify_send(talloc_tos(), ev, cli, dnum,
+ FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_LAST_WRITE,
+ false, print_notifies, NULL);
+ if (req1 == NULL) {
+ d_printf("Could not create notify request\n");
+ return false;
+ }
+
+ req2 = torture_createdels_send(talloc_tos(), ev, cli,
+ base_fname, 10, torture_numops);
+ if (req2 == NULL) {
+ d_printf("Could not create createdels request\n");
+ return false;
+ }
+ TALLOC_FREE(base_fname);
+
+ tevent_req_set_callback(req2, notify_bench_done,
+ &num_finished);
+ }
+
+ while (num_finished < num_unc_names) {
+ int ret;
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ d_printf("tevent_loop_once failed\n");
+ return false;
+ }
+ }
+
+ if (!tevent_req_poll(req2, ev)) {
+ d_printf("tevent_req_poll failed\n");
+ }
+
+ status = torture_createdels_recv(req2);
+ d_printf("torture_createdels_recv returned %s\n", nt_errstr(status));
+
+ return true;
+}
+
+static bool run_mangle1(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "this_is_a_long_fname_to_be_mangled.txt";
+ uint16_t fnum;
+ fstring alt_name;
+ NTSTATUS status;
+
+ printf("starting mangle1 test\n");
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = cli_ntcreate(cli, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open %s failed: %s\n", fname, nt_errstr(status));
+ return false;
+ }
+ cli_close(cli, fnum);
+
+ status = cli_qpathinfo_alt_name(cli, fname, alt_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_qpathinfo_alt_name failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ d_printf("alt_name: %s\n", alt_name);
+
+ status = cli_openx(cli, alt_name, O_RDONLY, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_openx(%s) failed: %s\n", alt_name,
+ nt_errstr(status));
+ return false;
+ }
+ cli_close(cli, fnum);
+
+ status = cli_qpathinfo1(cli, alt_name, NULL, NULL, NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_qpathinfo1(%s) failed: %s\n", alt_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS mangle_illegal_list_shortname_fn(struct file_info *f,
+ const char *mask,
+ void *state)
+{
+ if (f->short_name == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ if (strlen(f->short_name) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ printf("unexpected shortname: %s\n", f->short_name);
+
+ return NT_STATUS_OBJECT_NAME_INVALID;
+}
+
+static NTSTATUS mangle_illegal_list_name_fn(struct file_info *f,
+ const char *mask,
+ void *state)
+{
+ char *name = state;
+
+ printf("name: %s\n", f->name);
+ fstrcpy(name, f->name);
+ return NT_STATUS_OK;
+}
+
+static bool run_mangle_illegal(int dummy)
+{
+ struct cli_state *cli = NULL;
+ struct cli_state *cli_posix = NULL;
+ const char *fname = "\\MANGLE_ILLEGAL\\this_is_a_long_fname_to_be_mangled.txt";
+ const char *illegal_fname = "MANGLE_ILLEGAL/foo:bar";
+ char *mangled_path = NULL;
+ uint16_t fnum;
+ fstring name;
+ fstring alt_name;
+ NTSTATUS status;
+
+ printf("starting mangle-illegal test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ if (!torture_open_connection(&cli_posix, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli_posix->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli_posix);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ cli_rmdir(cli, "\\MANGLE_ILLEGAL");
+ status = cli_mkdir(cli, "\\MANGLE_ILLEGAL");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mkdir1 failed : %s\n", nt_errstr(status));
+ return False;
+ }
+
+ /*
+ * Create a file with illegal NTFS characters and test that we
+ * get a usable mangled name
+ */
+
+ cli_setatr(cli_posix, illegal_fname, 0, 0);
+ cli_posix_unlink(cli_posix, illegal_fname);
+
+ status = cli_posix_open(cli_posix, illegal_fname, O_RDWR|O_CREAT|O_EXCL,
+ 0600, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX create of %s failed (%s)\n",
+ illegal_fname, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed (%s)\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_list(cli, "\\MANGLE_ILLEGAL\\*", 0, mangle_illegal_list_name_fn, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_list failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ mangled_path = talloc_asprintf(talloc_tos(), "\\MANGLE_ILLEGAL\\%s", name);
+ if (mangled_path == NULL) {
+ return false;
+ }
+
+ status = cli_openx(cli, mangled_path, O_RDONLY, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_openx(%s) failed: %s\n", mangled_path, nt_errstr(status));
+ TALLOC_FREE(mangled_path);
+ return false;
+ }
+ TALLOC_FREE(mangled_path);
+ cli_close(cli, fnum);
+
+ cli_setatr(cli_posix, illegal_fname, 0, 0);
+ cli_posix_unlink(cli_posix, illegal_fname);
+
+ /*
+ * Create a file with a long name and check that we got *no* short name.
+ */
+
+ status = cli_ntcreate(cli, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open %s failed: %s\n", fname, nt_errstr(status));
+ return false;
+ }
+ cli_close(cli, fnum);
+
+ status = cli_list(cli, fname, 0, mangle_illegal_list_shortname_fn, &alt_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_list failed\n");
+ return false;
+ }
+
+ cli_unlink(cli, fname, 0);
+ cli_rmdir(cli, "\\MANGLE_ILLEGAL");
+
+ if (!torture_close_connection(cli_posix)) {
+ return false;
+ }
+
+ if (!torture_close_connection(cli)) {
+ return false;
+ }
+
+ return true;
+}
+
+static size_t null_source(uint8_t *buf, size_t n, void *priv)
+{
+ size_t *to_pull = (size_t *)priv;
+ size_t thistime = *to_pull;
+
+ thistime = MIN(thistime, n);
+ if (thistime == 0) {
+ return 0;
+ }
+
+ memset(buf, 0, thistime);
+ *to_pull -= thistime;
+ return thistime;
+}
+
+static bool run_windows_write(int dummy)
+{
+ struct cli_state *cli1;
+ uint16_t fnum;
+ int i;
+ bool ret = false;
+ const char *fname = "\\writetest.txt";
+ struct timeval start_time;
+ double seconds;
+ double kbytes;
+ NTSTATUS status;
+
+ printf("starting windows_write test\n");
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ status = cli_openx(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("open failed (%s)\n", nt_errstr(status));
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli1->conn, sockops);
+
+ start_time = timeval_current();
+
+ for (i=0; i<torture_numops; i++) {
+ uint8_t c = 0;
+ off_t start = i * torture_blocksize;
+ size_t to_pull = torture_blocksize - 1;
+
+ status = cli_writeall(cli1, fnum, 0, &c,
+ start + torture_blocksize - 1, 1, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_write failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_push(cli1, fnum, 0, i * torture_blocksize, torture_blocksize,
+ null_source, &to_pull);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_push returned: %s\n", nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ seconds = timeval_elapsed(&start_time);
+ kbytes = (double)torture_blocksize * torture_numops;
+ kbytes /= 1024;
+
+ printf("Wrote %d kbytes in %.2f seconds: %d kb/sec\n", (int)kbytes,
+ (double)seconds, (int)(kbytes/seconds));
+
+ ret = true;
+ fail:
+ cli_close(cli1, fnum);
+ cli_unlink(cli1, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ torture_close_connection(cli1);
+ return ret;
+}
+
+static size_t calc_expected_return(struct cli_state *cli, size_t len_requested)
+{
+ size_t max_pdu = 0x1FFFF;
+
+ if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP) {
+ max_pdu = 0xFFFFFF;
+ }
+
+ if (smb1cli_conn_signing_is_active(cli->conn)) {
+ max_pdu = 0x1FFFF;
+ }
+
+ if (smb1cli_conn_encryption_on(cli->conn)) {
+ max_pdu = CLI_BUFFER_SIZE;
+ }
+
+ if ((len_requested & 0xFFFF0000) == 0xFFFF0000) {
+ len_requested &= 0xFFFF;
+ }
+
+ return MIN(len_requested,
+ max_pdu - (MIN_SMB_SIZE + VWV(12) + 1 /* padding byte */));
+}
+
+static bool check_read_call(struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t *buf,
+ size_t len_requested)
+{
+ NTSTATUS status;
+ struct tevent_req *subreq = NULL;
+ ssize_t len_read = 0;
+ size_t len_expected = 0;
+ struct tevent_context *ev = NULL;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ return false;
+ }
+
+ subreq = cli_read_andx_send(talloc_tos(),
+ ev,
+ cli,
+ fnum,
+ 0,
+ len_requested);
+
+ if (!tevent_req_poll_ntstatus(subreq, ev, &status)) {
+ return false;
+ }
+
+ status = cli_read_andx_recv(subreq, &len_read, &buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_read_andx_recv failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(ev);
+
+ len_expected = calc_expected_return(cli, len_requested);
+
+ if (len_expected > 0x10000 && len_read == 0x10000) {
+ /* Windows servers only return a max of 0x10000,
+ doesn't matter if you set CAP_LARGE_READX in
+ the client sessionsetupX call or not. */
+ d_printf("Windows server - returned 0x10000 on a read of 0x%x\n",
+ (unsigned int)len_requested);
+ } else if (len_read != len_expected) {
+ d_printf("read of 0x%x failed: got 0x%x, expected 0x%x\n",
+ (unsigned int)len_requested,
+ (unsigned int)len_read,
+ (unsigned int)len_expected);
+ return false;
+ } else {
+ d_printf("Correct read reply.\n");
+ }
+
+ return true;
+}
+
+/* Test large readX variants. */
+static bool large_readx_tests(struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t *buf)
+{
+ /* A read of 0xFFFF0001 should *always* return 1 byte. */
+ if (check_read_call(cli, fnum, buf, 0xFFFF0001) == false) {
+ return false;
+ }
+ /* A read of 0x10000 should return 0x10000 bytes. */
+ if (check_read_call(cli, fnum, buf, 0x10000) == false) {
+ return false;
+ }
+ /* A read of 0x10000 should return 0x10001 bytes. */
+ if (check_read_call(cli, fnum, buf, 0x10001) == false) {
+ return false;
+ }
+ /* A read of 0x1FFFF - (MIN_SMB_SIZE + VWV(12) should return
+ the requested number of bytes. */
+ if (check_read_call(cli, fnum, buf, 0x1FFFF - (MIN_SMB_SIZE + VWV(12))) == false) {
+ return false;
+ }
+ /* A read of 1MB should return 1MB bytes (on Samba). */
+ if (check_read_call(cli, fnum, buf, 0x100000) == false) {
+ return false;
+ }
+
+ if (check_read_call(cli, fnum, buf, 0x20001) == false) {
+ return false;
+ }
+ if (check_read_call(cli, fnum, buf, 0x22000001) == false) {
+ return false;
+ }
+ if (check_read_call(cli, fnum, buf, 0xFFFE0001) == false) {
+ return false;
+ }
+ return true;
+}
+
+static bool run_large_readx(int dummy)
+{
+ uint8_t *buf = NULL;
+ struct cli_state *cli1 = NULL;
+ struct cli_state *cli2 = NULL;
+ bool correct = false;
+ const char *fname = "\\large_readx.dat";
+ NTSTATUS status;
+ uint16_t fnum1 = UINT16_MAX;
+ uint32_t normal_caps = 0;
+ size_t file_size = 20*1024*1024;
+ TALLOC_CTX *frame = talloc_stackframe();
+ size_t i;
+ struct {
+ const char *name;
+ enum smb_signing_setting signing_setting;
+ enum protocol_types protocol;
+ } runs[] = {
+ {
+ .name = "NT1",
+ .signing_setting = SMB_SIGNING_IF_REQUIRED,
+ .protocol = PROTOCOL_NT1,
+ },{
+ .name = "NT1 - SIGNING_REQUIRED",
+ .signing_setting = SMB_SIGNING_REQUIRED,
+ .protocol = PROTOCOL_NT1,
+ },
+ };
+
+ printf("starting large_readx test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ goto out;
+ }
+
+ normal_caps = smb1cli_conn_capabilities(cli1->conn);
+
+ if (!(normal_caps & CAP_LARGE_READX)) {
+ d_printf("Server doesn't have CAP_LARGE_READX 0x%x\n",
+ (unsigned int)normal_caps);
+ goto out;
+ }
+
+ /* Create a file of size 4MB. */
+ status = cli_ntcreate(cli1, fname, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum1, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open %s failed: %s\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* Write file_size bytes. */
+ buf = talloc_zero_array(frame, uint8_t, file_size);
+ if (buf == NULL) {
+ goto out;
+ }
+
+ status = cli_writeall(cli1,
+ fnum1,
+ 0,
+ buf,
+ 0,
+ file_size,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_writeall failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_close failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ fnum1 = UINT16_MAX;
+
+ for (i=0; i < ARRAY_SIZE(runs); i++) {
+ enum smb_signing_setting saved_signing_setting = signing_state;
+ uint16_t fnum2 = -1;
+
+ if (do_encrypt &&
+ (runs[i].signing_setting == SMB_SIGNING_REQUIRED))
+ {
+ d_printf("skip[%u] - %s\n", (unsigned)i, runs[i].name);
+ continue;
+ }
+
+ d_printf("run[%u] - %s\n", (unsigned)i, runs[i].name);
+
+ signing_state = runs[i].signing_setting;
+ cli2 = open_nbt_connection();
+ signing_state = saved_signing_setting;
+ if (cli2 == NULL) {
+ goto out;
+ }
+
+ status = smbXcli_negprot(cli2->conn,
+ cli2->timeout,
+ runs[i].protocol,
+ runs[i].protocol,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = cli_session_setup_creds(cli2, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = cli_tree_connect(cli2,
+ share,
+ "?????",
+ password);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ cli_set_timeout(cli2, 120000); /* set a really long timeout (2 minutes) */
+
+ normal_caps = smb1cli_conn_capabilities(cli2->conn);
+
+ if (!(normal_caps & CAP_LARGE_READX)) {
+ d_printf("Server doesn't have CAP_LARGE_READX 0x%x\n",
+ (unsigned int)normal_caps);
+ goto out;
+ }
+
+ if (do_encrypt) {
+ if (force_cli_encryption(cli2, share) == false) {
+ goto out;
+ }
+ } else if (SERVER_HAS_UNIX_CIFS(cli2)) {
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+
+ status = cli_unix_extensions_version(cli2,
+ &major, &minor,
+ &caplow, &caphigh);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ status = cli_ntcreate(cli2, fname, 0, FILE_READ_DATA,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN,
+ 0, 0, &fnum2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Second open %s failed: %s\n", fname, nt_errstr(status));
+ goto out;
+ }
+
+ /* All reads must return less than file_size bytes. */
+ if (!large_readx_tests(cli2, fnum2, buf)) {
+ goto out;
+ }
+
+ status = cli_close(cli2, fnum2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_close failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum2 = -1;
+
+ if (!torture_close_connection(cli2)) {
+ goto out;
+ }
+ cli2 = NULL;
+ }
+
+ correct = true;
+ printf("Success on large_readx test\n");
+
+ out:
+
+ if (cli2) {
+ if (!torture_close_connection(cli2)) {
+ correct = false;
+ }
+ }
+
+ if (cli1) {
+ if (fnum1 != UINT16_MAX) {
+ status = cli_close(cli1, fnum1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_close failed: %s\n", nt_errstr(status));
+ }
+ fnum1 = UINT16_MAX;
+ }
+
+ status = cli_unlink(cli1, fname,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("unlink failed (%s)\n", nt_errstr(status));
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = false;
+ }
+ }
+
+ TALLOC_FREE(frame);
+
+ printf("finished large_readx test\n");
+ return correct;
+}
+
+static NTSTATUS msdfs_attribute_list_fn(struct file_info *finfo,
+ const char *mask,
+ void *private_data)
+{
+ uint32_t *p_attr = (uint32_t *)private_data;
+
+ if (strequal(finfo->name, test_filename)) {
+ *p_attr = finfo->attr;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static bool run_msdfs_attribute(int dummy)
+{
+ static struct cli_state *cli;
+ bool correct = false;
+ uint32_t attr = 0;
+ NTSTATUS status;
+
+ printf("Starting MSDFS-ATTRIBUTE test\n");
+
+ if (test_filename == NULL || test_filename[0] == '\0') {
+ printf("MSDFS-ATTRIBUTE test "
+ "needs -f filename-of-msdfs-link\n");
+ return false;
+ }
+
+ /*
+ * NB. We use torture_open_connection_flags() not
+ * torture_open_connection() as the latter forces
+ * SMB1.
+ */
+ if (!torture_open_connection_flags(&cli, 0, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = cli_list(cli,
+ "*",
+ FILE_ATTRIBUTE_DIRECTORY,
+ msdfs_attribute_list_fn,
+ &attr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list failed with %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+ if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+ printf("file %s should have "
+ "FILE_ATTRIBUTE_REPARSE_POINT set. attr = 0x%x\n",
+ test_filename,
+ (unsigned int)attr);
+ goto out;
+ }
+
+ if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("file %s should have "
+ "FILE_ATTRIBUTE_DIRECTORY set. attr = 0x%x\n",
+ test_filename,
+ (unsigned int)attr);
+ goto out;
+ }
+
+ correct = true;
+
+ out:
+
+ torture_close_connection(cli);
+ return correct;
+}
+
+static bool run_cli_echo(int dummy)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+
+ printf("starting cli_echo test\n");
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = cli_echo(cli, 5, data_blob_const("hello", 5));
+
+ d_printf("cli_echo returned %s\n", nt_errstr(status));
+
+ torture_close_connection(cli);
+ return NT_STATUS_IS_OK(status);
+}
+
+static int splice_status(off_t written, void *priv)
+{
+ return true;
+}
+
+static bool run_cli_splice(int dummy)
+{
+ uint8_t *buf = NULL;
+ struct cli_state *cli1 = NULL;
+ bool correct = false;
+ const char *fname_src = "\\splice_src.dat";
+ const char *fname_dst = "\\splice_dst.dat";
+ NTSTATUS status;
+ uint16_t fnum1 = UINT16_MAX;
+ uint16_t fnum2 = UINT16_MAX;
+ size_t file_size = 2*1024*1024;
+ size_t splice_size = 1*1024*1024 + 713;
+ uint8_t digest1[16], digest2[16];
+ off_t written = 0;
+ size_t nread = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ printf("starting cli_splice test\n");
+
+ if (!torture_open_connection(&cli1, 0)) {
+ goto out;
+ }
+
+ cli_unlink(cli1, fname_src,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname_dst,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ /* Create a file */
+ status = cli_ntcreate(cli1, fname_src, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum1, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open %s failed: %s\n", fname_src, nt_errstr(status));
+ goto out;
+ }
+
+ /* Write file_size bytes - must be bigger than splice_size. */
+ buf = talloc_zero_array(frame, uint8_t, file_size);
+ if (buf == NULL) {
+ d_printf("talloc_fail\n");
+ goto out;
+ }
+
+ /* Fill it with random numbers. */
+ generate_random_buffer(buf, file_size);
+
+ /* MD5 the first 1MB + 713 bytes. */
+ gnutls_hash_fast(GNUTLS_DIG_MD5,
+ buf,
+ splice_size,
+ digest1);
+
+ status = cli_writeall(cli1,
+ fnum1,
+ 0,
+ buf,
+ 0,
+ file_size,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_writeall failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_ntcreate(cli1, fname_dst, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+ 0, 0, &fnum2, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open %s failed: %s\n", fname_dst, nt_errstr(status));
+ goto out;
+ }
+
+ /* Now splice 1MB + 713 bytes. */
+ status = cli_splice(cli1,
+ cli1,
+ fnum1,
+ fnum2,
+ splice_size,
+ 0,
+ 0,
+ &written,
+ splice_status,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_splice failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ /* Clear the old buffer. */
+ memset(buf, '\0', file_size);
+
+ /* Read the new file. */
+ status = cli_read(cli1, fnum2, (char *)buf, 0, splice_size, &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_read failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+ if (nread != splice_size) {
+ d_printf("bad read of 0x%x, should be 0x%x\n",
+ (unsigned int)nread,
+ (unsigned int)splice_size);
+ goto out;
+ }
+
+ /* MD5 the first 1MB + 713 bytes. */
+ gnutls_hash_fast(GNUTLS_DIG_MD5,
+ buf,
+ splice_size,
+ digest2);
+
+ /* Must be the same. */
+ if (memcmp(digest1, digest2, 16) != 0) {
+ d_printf("bad MD5 compare\n");
+ goto out;
+ }
+
+ correct = true;
+ printf("Success on cli_splice test\n");
+
+ out:
+
+ if (cli1) {
+ if (fnum1 != UINT16_MAX) {
+ cli_close(cli1, fnum1);
+ }
+ if (fnum2 != UINT16_MAX) {
+ cli_close(cli1, fnum2);
+ }
+
+ cli_unlink(cli1, fname_src,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_unlink(cli1, fname_dst,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!torture_close_connection(cli1)) {
+ correct = false;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+static bool run_uid_regression_test(int dummy)
+{
+ static struct cli_state *cli;
+ int16_t old_vuid;
+ int32_t old_cnum;
+ bool correct = True;
+ struct smbXcli_tcon *tcon_copy = NULL;
+ NTSTATUS status;
+
+ printf("starting uid regression test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Ok - now save then logoff our current user. */
+ old_vuid = cli_state_get_uid(cli);
+
+ status = cli_ulogoff(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) cli_ulogoff failed: %s\n",
+ __location__, nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+
+ cli_state_set_uid(cli, old_vuid);
+
+ /* Try an operation. */
+ status = cli_mkdir(cli, "\\uid_reg_test");
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) cli_mkdir succeeded\n",
+ __location__);
+ correct = false;
+ goto out;
+ } else {
+ /* Should be bad uid. */
+ if (!check_error(__LINE__, status, ERRSRV, ERRbaduid,
+ NT_STATUS_USER_SESSION_DELETED)) {
+ correct = false;
+ goto out;
+ }
+ }
+
+ old_cnum = cli_state_get_tid(cli);
+ /*
+ * This is an SMB1-only test.
+ * Copy the tcon, not "save/restore".
+ *
+ * In SMB1 the cli_tdis() below frees
+ * cli->smb1.tcon so we need a copy
+ * of the struct to put back for the
+ * second tdis call with invalid vuid.
+ *
+ * This is a test-only hack. Real client code
+ * uses cli_state_save_tcon_share()/cli_state_restore_tcon_share().
+ */
+ tcon_copy = smbXcli_tcon_copy(cli, cli->smb1.tcon);
+ if (tcon_copy == NULL) {
+ correct = false;
+ goto out;
+ }
+
+ /* Now try a SMBtdis with the invalid vuid set to zero. */
+ cli_state_set_uid(cli, 0);
+
+ /* This should succeed. */
+ status = cli_tdis(cli);
+
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("First tdis with invalid vuid should succeed.\n");
+ } else {
+ d_printf("First tdis failed (%s)\n", nt_errstr(status));
+ correct = false;
+ cli->smb1.tcon = tcon_copy;
+ goto out;
+ }
+
+ cli->smb1.tcon = tcon_copy;
+ cli_state_set_uid(cli, old_vuid);
+ cli_state_set_tid(cli, old_cnum);
+
+ /* This should fail. */
+ status = cli_tdis(cli);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("Second tdis with invalid vuid should fail - succeeded instead !.\n");
+ correct = false;
+ goto out;
+ } else {
+ /* Should be bad tid. */
+ if (!check_error(__LINE__, status, ERRSRV, ERRinvnid,
+ NT_STATUS_NETWORK_NAME_DELETED)) {
+ correct = false;
+ goto out;
+ }
+ }
+
+ cli_rmdir(cli, "\\uid_reg_test");
+
+ out:
+
+ cli_shutdown(cli);
+ return correct;
+}
+
+
+static const char *illegal_chars = "*\\/?<>|\":";
+static char force_shortname_chars[] = " +,.[];=\177";
+
+static NTSTATUS shortname_del_fn(struct file_info *finfo,
+ const char *mask, void *state)
+{
+ struct cli_state *pcli = (struct cli_state *)state;
+ fstring fname;
+ NTSTATUS status = NT_STATUS_OK;
+
+ slprintf(fname, sizeof(fname), "\\shortname\\%s", finfo->name);
+
+ if (strcmp(finfo->name, ".") == 0 || strcmp(finfo->name, "..") == 0)
+ return NT_STATUS_OK;
+
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ status = cli_rmdir(pcli, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("del_fn: failed to rmdir %s\n,", fname );
+ }
+ } else {
+ status = cli_unlink(pcli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("del_fn: failed to unlink %s\n,", fname );
+ }
+ }
+ return status;
+}
+
+struct sn_state {
+ int matched;
+ int i;
+ bool val;
+};
+
+static NTSTATUS shortname_list_fn(struct file_info *finfo,
+ const char *name, void *state)
+{
+ struct sn_state *s = (struct sn_state *)state;
+ int i = s->i;
+
+#if 0
+ printf("shortname list: i = %d, name = |%s|, shortname = |%s|\n",
+ i, finfo->name, finfo->short_name);
+#endif
+
+ if (strchr(force_shortname_chars, i)) {
+ if (!finfo->short_name) {
+ /* Shortname not created when it should be. */
+ d_printf("(%s) ERROR: Shortname was not created for file %s containing %d\n",
+ __location__, finfo->name, i);
+ s->val = true;
+ }
+ } else if (finfo->short_name){
+ /* Shortname created when it should not be. */
+ d_printf("(%s) ERROR: Shortname %s was created for file %s\n",
+ __location__, finfo->short_name, finfo->name);
+ s->val = true;
+ }
+ s->matched += 1;
+ return NT_STATUS_OK;
+}
+
+static bool run_shortname_test(int dummy)
+{
+ static struct cli_state *cli;
+ bool correct = True;
+ int i;
+ struct sn_state s;
+ char fname[40];
+ NTSTATUS status;
+
+ printf("starting shortname test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ cli_list(cli, "\\shortname\\*", 0, shortname_del_fn, cli);
+ cli_list(cli, "\\shortname\\*", FILE_ATTRIBUTE_DIRECTORY, shortname_del_fn, cli);
+ cli_rmdir(cli, "\\shortname");
+
+ status = cli_mkdir(cli, "\\shortname");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) cli_mkdir of \\shortname failed: %s\n",
+ __location__, nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+
+ if (strlcpy(fname, "\\shortname\\", sizeof(fname)) >= sizeof(fname)) {
+ correct = false;
+ goto out;
+ }
+ if (strlcat(fname, "test .txt", sizeof(fname)) >= sizeof(fname)) {
+ correct = false;
+ goto out;
+ }
+
+ s.val = false;
+
+ for (i = 32; i < 128; i++) {
+ uint16_t fnum = (uint16_t)-1;
+
+ s.i = i;
+
+ if (strchr(illegal_chars, i)) {
+ continue;
+ }
+ fname[15] = i;
+
+ status = cli_ntcreate(cli, fname, 0, GENERIC_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OVERWRITE_IF, 0, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) cli_nt_create of %s failed: %s\n",
+ __location__, fname, nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+ cli_close(cli, fnum);
+
+ s.matched = 0;
+ status = cli_list(cli, "\\shortname\\test*.*", 0,
+ shortname_list_fn, &s);
+ if (s.matched != 1) {
+ d_printf("(%s) failed to list %s: %s\n",
+ __location__, fname, nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+
+ status = cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) failed to delete %s: %s\n",
+ __location__, fname, nt_errstr(status));
+ correct = false;
+ goto out;
+ }
+
+ if (s.val) {
+ correct = false;
+ goto out;
+ }
+ }
+
+ out:
+
+ cli_list(cli, "\\shortname\\*", 0, shortname_del_fn, cli);
+ cli_list(cli, "\\shortname\\*", FILE_ATTRIBUTE_DIRECTORY, shortname_del_fn, cli);
+ cli_rmdir(cli, "\\shortname");
+ torture_close_connection(cli);
+ return correct;
+}
+
+TLDAPRC callback_code;
+
+static void pagedsearch_cb(struct tevent_req *req)
+{
+ TLDAPRC rc;
+ struct tldap_message *msg;
+ char *dn;
+
+ rc = tldap_search_paged_recv(req, talloc_tos(), &msg);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ d_printf("tldap_search_paged_recv failed: %s\n",
+ tldap_rc2string(rc));
+ callback_code = rc;
+ return;
+ }
+ if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+ TALLOC_FREE(msg);
+ return;
+ }
+ if (!tldap_entry_dn(msg, &dn)) {
+ d_printf("tldap_entry_dn failed\n");
+ return;
+ }
+ d_printf("%s\n", dn);
+ TALLOC_FREE(msg);
+}
+
+enum tldap_extended_val {
+ EXTENDED_ZERO = 0,
+ EXTENDED_ONE = 1,
+ EXTENDED_NONE = 2,
+};
+
+/*
+ * Construct an extended dn control with either no value, 0 or 1
+ *
+ * No value and 0 are equivalent (non-hyphenated GUID)
+ * 1 has the hyphenated GUID
+ */
+static struct tldap_control *
+tldap_build_extended_control(enum tldap_extended_val val)
+{
+ struct tldap_control empty_control;
+ struct asn1_data *data;
+
+ ZERO_STRUCT(empty_control);
+
+ if (val != EXTENDED_NONE) {
+ data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH);
+
+ if (!data) {
+ return NULL;
+ }
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return NULL;
+ }
+
+ if (!asn1_write_Integer(data, (int)val)) {
+ return NULL;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return NULL;
+ }
+
+ if (!asn1_blob(data, &empty_control.value)) {
+ return NULL;
+ }
+ }
+
+ empty_control.oid = "1.2.840.113556.1.4.529";
+ empty_control.critical = true;
+
+ return tldap_add_control(talloc_tos(), NULL, 0, &empty_control);
+
+}
+
+static bool tldap_test_dn_guid_format(struct tldap_context *ld, const char *basedn,
+ enum tldap_extended_val control_val)
+{
+ struct tldap_control *control = tldap_build_extended_control(control_val);
+ char *dn = NULL;
+ struct tldap_message **msg;
+ TLDAPRC rc;
+
+ rc = tldap_search(ld, basedn, TLDAP_SCOPE_BASE,
+ "(objectClass=*)", NULL, 0, 0,
+ control, 1, NULL,
+ 0, 0, 0, 0, talloc_tos(), &msg);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ d_printf("tldap_search for domain DN failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+
+ if (!tldap_entry_dn(msg[0], &dn)) {
+ d_printf("tldap_search domain DN fetch failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+
+ d_printf("%s\n", dn);
+ {
+ uint32_t time_low;
+ uint32_t time_mid, time_hi_and_version;
+ uint32_t clock_seq[2];
+ uint32_t node[6];
+ char next;
+
+ switch (control_val) {
+ case EXTENDED_NONE:
+ case EXTENDED_ZERO:
+ /*
+ * When reading GUIDs with hyphens, scanf will treat
+ * hyphen as a hex character (and counts as part of the
+ * width). This creates leftover GUID string which we
+ * check will for with 'next' and closing '>'.
+ */
+ if (12 == sscanf(dn, "<GUID=%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x>%c",
+ &time_low, &time_mid,
+ &time_hi_and_version, &clock_seq[0],
+ &clock_seq[1], &node[0], &node[1],
+ &node[2], &node[3], &node[4],
+ &node[5], &next)) {
+ /* This GUID is good */
+ } else {
+ d_printf("GUID format in control (no hyphens) doesn't match output\n");
+ return false;
+ }
+
+ break;
+ case EXTENDED_ONE:
+ if (12 == sscanf(dn,
+ "<GUID=%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x>%c",
+ &time_low, &time_mid,
+ &time_hi_and_version, &clock_seq[0],
+ &clock_seq[1], &node[0], &node[1],
+ &node[2], &node[3], &node[4],
+ &node[5], &next)) {
+ /* This GUID is good */
+ } else {
+ d_printf("GUID format in control (with hyphens) doesn't match output\n");
+ return false;
+ }
+
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool run_tldap(int dummy)
+{
+ struct tldap_context *ld;
+ int fd;
+ TLDAPRC rc;
+ NTSTATUS status;
+ struct sockaddr_storage addr;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ char *basedn;
+ const char *filter;
+
+ if (!resolve_name(host, &addr, 0, false)) {
+ d_printf("could not find host %s\n", host);
+ return false;
+ }
+ status = open_socket_out(&addr, 389, 9999, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("open_socket_out failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ld = tldap_context_create(talloc_tos(), fd);
+ if (ld == NULL) {
+ close(fd);
+ d_printf("tldap_context_create failed\n");
+ return false;
+ }
+
+ rc = tldap_fetch_rootdse(ld);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ d_printf("tldap_fetch_rootdse failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+
+ basedn = tldap_talloc_single_attribute(
+ tldap_rootdse(ld), "defaultNamingContext", talloc_tos());
+ if (basedn == NULL) {
+ d_printf("no defaultNamingContext\n");
+ return false;
+ }
+ d_printf("defaultNamingContext: %s\n", basedn);
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_printf("tevent_context_init failed\n");
+ return false;
+ }
+
+ rc = tldap_gensec_bind(ld, torture_creds, "ldap", host, NULL,
+ loadparm_init_s3(talloc_tos(),
+ loadparm_s3_helpers()),
+ GENSEC_FEATURE_SIGN | GENSEC_FEATURE_SEAL);
+
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ d_printf("tldap_gensec_bind failed\n");
+ return false;
+ }
+
+ callback_code = TLDAP_SUCCESS;
+
+ req = tldap_search_paged_send(talloc_tos(), ev, ld, basedn,
+ TLDAP_SCOPE_SUB, "(objectclass=*)",
+ NULL, 0, 0,
+ NULL, 0, NULL, 0, 0, 0, 0, 5);
+ if (req == NULL) {
+ d_printf("tldap_search_paged_send failed\n");
+ return false;
+ }
+ tevent_req_set_callback(req, pagedsearch_cb, NULL);
+
+ tevent_req_poll(req, ev);
+
+ TALLOC_FREE(req);
+
+ rc = callback_code;
+
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ d_printf("tldap_search with paging failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+
+ /* test search filters against rootDSE */
+ filter = "(&(|(name=samba)(nextRid<=10000000)(usnChanged>=10)(samba~=ambas)(!(name=s*m*a)))"
+ "(|(name:=samba)(name:dn:2.5.13.5:=samba)(:dn:2.5.13.5:=samba)(!(name=*samba))))";
+
+ rc = tldap_search(ld, "", TLDAP_SCOPE_BASE, filter,
+ NULL, 0, 0, NULL, 0, NULL, 0, 0, 0, 0,
+ talloc_tos(), NULL);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ d_printf("tldap_search with complex filter failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+
+ /*
+ * Tests to check for regression of:
+ *
+ * https://bugzilla.samba.org/show_bug.cgi?id=14029
+ *
+ * TLDAP used here to pick apart the original string DN (with GUID)
+ */
+ if (!tldap_test_dn_guid_format(ld, basedn, EXTENDED_NONE)) {
+ d_printf("tldap_search with extended dn (no val) failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+ if (!tldap_test_dn_guid_format(ld, basedn, EXTENDED_ZERO)) {
+ d_printf("tldap_search with extended dn (0) failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+ if (!tldap_test_dn_guid_format(ld, basedn, EXTENDED_ONE)) {
+ d_printf("tldap_search with extended dn (1) failed: %s\n",
+ tldap_errstr(talloc_tos(), ld, rc));
+ return false;
+ }
+
+ TALLOC_FREE(ld);
+ return true;
+}
+
+/* Torture test to ensure no regression of :
+https://bugzilla.samba.org/show_bug.cgi?id=7084
+*/
+
+static bool run_dir_createtime(int dummy)
+{
+ struct cli_state *cli;
+ const char *dname = "\\testdir_createtime";
+ const char *fname = "\\testdir_createtime\\testfile";
+ NTSTATUS status;
+ struct timespec create_time;
+ struct timespec create_time1;
+ uint16_t fnum;
+ bool ret = false;
+ uint64_t ino;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ /* Ensure ino is zero, SMB2 gets a real one. */
+ ino = 0;
+ } else {
+ /* Ensure ino is -1, SMB1 never gets a real one. */
+ ino = (uint64_t)-1;
+ }
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, dname);
+
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mkdir failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_qpathinfo2(cli,
+ dname,
+ &create_time,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &ino,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_qpathinfo2 returned %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ /* SMB2 should always return an inode. */
+ if (ino == 0) {
+ printf("SMB2 bad inode (0)\n");
+ goto out;
+ }
+ } else {
+ /* SMB1 must always return zero here. */
+ if (ino != 0) {
+ printf("SMB1 bad inode (!0)\n");
+ goto out;
+ }
+ }
+
+ /* Sleep 3 seconds, then create a file. */
+ sleep(3);
+
+ status = cli_openx(cli, fname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_openx failed: %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_qpathinfo2(cli,
+ dname,
+ &create_time1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_qpathinfo2 (2) returned %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (timespec_compare(&create_time1, &create_time)) {
+ printf("run_dir_createtime: create time was updated (error)\n");
+ } else {
+ printf("run_dir_createtime: create time was not updated (correct)\n");
+ ret = true;
+ }
+
+ out:
+
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ cli_rmdir(cli, dname);
+ if (!torture_close_connection(cli)) {
+ ret = false;
+ }
+ return ret;
+}
+
+
+static bool run_streamerror(int dummy)
+{
+ struct cli_state *cli;
+ const char *dname = "\\testdir_streamerror";
+ const char *streamname =
+ "testdir_streamerror:{4c8cc155-6c1e-11d1-8e41-00c04fb9386d}:$DATA";
+ NTSTATUS status;
+ time_t change_time, access_time, write_time;
+ off_t size;
+ uint16_t fnum;
+ uint32_t attr;
+ bool ret = true;
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ torture_deltree(cli, dname);
+
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mkdir failed: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_qpathinfo1(cli, streamname, &change_time, &access_time,
+ &write_time, &size, &attr);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("pathinfo returned %s, expected "
+ "NT_STATUS_OBJECT_NAME_NOT_FOUND\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ status = cli_ntcreate(cli, streamname, 0x16,
+ FILE_READ_DATA|FILE_READ_EA|
+ FILE_READ_ATTRIBUTES|READ_CONTROL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
+ FILE_OPEN, 0, 0, &fnum, NULL);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("ntcreate returned %s, expected "
+ "NT_STATUS_OBJECT_NAME_NOT_FOUND\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+
+ cli_rmdir(cli, dname);
+ return ret;
+}
+
+struct pidtest_state {
+ bool success;
+ uint16_t vwv[1];
+ DATA_BLOB data;
+};
+
+static void pid_echo_done(struct tevent_req *subreq);
+
+static struct tevent_req *pid_echo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct pidtest_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct pidtest_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(state->vwv, 0, 1);
+ state->data = data_blob_const("hello", 5);
+
+ subreq = smb1cli_req_send(state,
+ ev,
+ cli->conn,
+ SMBecho,
+ 0, 0, /* *_flags */
+ 0, 0, /* *_flags2 */
+ cli->timeout,
+ 0xDEADBEEF, /* pid */
+ NULL, /* tcon */
+ NULL, /* session */
+ ARRAY_SIZE(state->vwv), state->vwv,
+ state->data.length, state->data.data);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, pid_echo_done, req);
+ return req;
+}
+
+static void pid_echo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct pidtest_state *state = tevent_req_data(
+ req, struct pidtest_state);
+ NTSTATUS status;
+ uint32_t num_bytes;
+ uint8_t *bytes = NULL;
+ struct iovec *recv_iov = NULL;
+ uint8_t *phdr = NULL;
+ uint16_t pidlow = 0;
+ uint16_t pidhigh = 0;
+ struct smb1cli_req_expected_response expected[] = {
+ {
+ .status = NT_STATUS_OK,
+ .wct = 1,
+ },
+ };
+
+ status = smb1cli_req_recv(subreq, state,
+ &recv_iov,
+ &phdr,
+ NULL, /* pwct */
+ NULL, /* pvwv */
+ NULL, /* pvwv_offset */
+ &num_bytes,
+ &bytes,
+ NULL, /* pbytes_offset */
+ NULL, /* pinbuf */
+ expected, ARRAY_SIZE(expected));
+
+ TALLOC_FREE(subreq);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (num_bytes != state->data.length) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (memcmp(bytes, state->data.data, num_bytes) != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* Check pid low/high == DEADBEEF */
+ pidlow = SVAL(phdr, HDR_PID);
+ if (pidlow != 0xBEEF){
+ printf("Incorrect pidlow 0x%x, should be 0xBEEF\n",
+ (unsigned int)pidlow);
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ pidhigh = SVAL(phdr, HDR_PIDHIGH);
+ if (pidhigh != 0xDEAD){
+ printf("Incorrect pidhigh 0x%x, should be 0xDEAD\n",
+ (unsigned int)pidhigh);
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS pid_echo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_pidhigh(int dummy)
+{
+ bool success = false;
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ printf("starting pid high test\n");
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = pid_echo_send(frame, ev, cli);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = pid_echo_recv(req);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("pid high test ok\n");
+ success = true;
+ }
+
+ fail:
+
+ TALLOC_FREE(frame);
+ torture_close_connection(cli);
+ return success;
+}
+
+/*
+ Test Windows open on a bad POSIX symlink.
+ */
+static bool run_symlink_open_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "non_existant_file";
+ const char *sname = "dangling_symlink";
+ uint16_t fnum = (uint16_t)-1;
+ bool correct = false;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ frame = talloc_stackframe();
+
+ printf("Starting Windows bad symlink open test\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Ensure nothing exists. */
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+ cli_setatr(cli, sname, 0, 0);
+ cli_posix_unlink(cli, sname);
+
+ /* Create a symlink pointing nowhere. */
+ status = cli_posix_symlink(cli, fname, sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed (%s)\n",
+ sname,
+ fname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now ensure that a Windows open doesn't hang. */
+ status = cli_ntcreate(cli,
+ sname,
+ 0,
+ FILE_READ_DATA|FILE_WRITE_DATA,
+ 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN_IF,
+ 0x0,
+ 0x0,
+ &fnum,
+ NULL);
+
+ /*
+ * We get either NT_STATUS_OBJECT_NAME_NOT_FOUND or
+ * NT_STATUS_OBJECT_PATH_NOT_FOUND depending on if
+ * we use O_NOFOLLOW on the server or not.
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND))
+ {
+ correct = true;
+ } else {
+ printf("cli_ntcreate of %s returned %s - should return"
+ " either (%s) or (%s)\n",
+ sname,
+ nt_errstr(status),
+ nt_errstr(NT_STATUS_OBJECT_NAME_NOT_FOUND),
+ nt_errstr(NT_STATUS_OBJECT_PATH_NOT_FOUND));
+ goto out;
+ }
+
+ correct = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli, fnum);
+ fnum = (uint16_t)-1;
+ }
+
+ cli_setatr(cli, sname, 0, 0);
+ cli_posix_unlink(cli, sname);
+ cli_setatr(cli, fname, 0, 0);
+ cli_posix_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+static NTSTATUS smb1_wild_mangle_list_fn(struct file_info *finfo,
+ const char *name,
+ void *state)
+{
+ char **mangled_name_return = (char **)state;
+ bool is_mangled = strchr(finfo->name, '~');
+
+ if (is_mangled) {
+ *mangled_name_return = talloc_strdup(NULL, finfo->name);
+ if (*mangled_name_return == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+static bool run_smb1_wild_mangle_unlink_test(int dummy)
+{
+ static struct cli_state *cli_posix = NULL;
+ static struct cli_state *cli = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ bool correct = false;
+ const char *dname = "smb1_wild_mangle_unlink";
+ const char *aname = "smb1_wild_mangle_unlink/a";
+ const char *star_name = "smb1_wild_mangle_unlink/*";
+ char *windows_unlink_name = NULL;
+ char *mangled_name = NULL;
+ NTSTATUS status;
+
+ printf("Starting SMB1 wild mangle unlink test\n");
+
+ /* Open a Windows connection. */
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Open a POSIX connection. */
+ if (!torture_open_connection(&cli_posix, 0)) {
+ goto out;
+ }
+
+ smbXcli_conn_set_sockopt(cli_posix->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli_posix);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("server doesn't support POSIX\n");
+ goto out;
+ }
+
+ /* Start fresh. */
+ torture_deltree(cli, dname);
+
+ /*
+ * Create two files - 'a' and '*'.
+ * We need POSIX extensions for this as '*'
+ * is not a valid Windows name.
+ */
+
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_mkdir of %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_open(cli_posix,
+ aname,
+ O_RDWR|O_CREAT|O_EXCL,
+ 0660,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open (create) of %s returned %s\n",
+ aname,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = cli_posix_open(cli_posix,
+ star_name,
+ O_RDWR|O_CREAT|O_EXCL,
+ 0660,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open (create) of %s returned %s\n",
+ star_name,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = cli_list(cli,
+ star_name,
+ 0,
+ smb1_wild_mangle_list_fn,
+ &mangled_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list of %s returned %s\n",
+ star_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (mangled_name == NULL) {
+ goto out;
+ }
+
+ printf("mangled_name = %s\n",
+ mangled_name);
+
+ /*
+ * Try a Windows unlink with the mangled name.
+ * This should *NOT* unlink the 'a' name.
+ */
+
+ windows_unlink_name = talloc_asprintf(cli_posix,
+ "%s\\%s",
+ dname,
+ mangled_name);
+
+ status = cli_unlink(cli, windows_unlink_name, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_unlink of %s returned %s\n",
+ windows_unlink_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Does 'a' still exist ? */
+ status = cli_posix_open(cli_posix,
+ aname,
+ O_RDONLY,
+ 0,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open O_RNONLY of %s returned %s\n",
+ aname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ correct = true;
+
+ out:
+
+ TALLOC_FREE(windows_unlink_name);
+ TALLOC_FREE(mangled_name);
+
+ if (cli != NULL) {
+ torture_deltree(cli, dname);
+ torture_close_connection(cli);
+ }
+
+ if (cli_posix != NULL) {
+ torture_close_connection(cli_posix);
+ }
+
+ return correct;
+}
+
+static bool run_smb1_wild_mangle_rename_test(int dummy)
+{
+ static struct cli_state *cli_posix = NULL;
+ static struct cli_state *cli = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ bool correct = false;
+ const char *dname = "smb1_wild_mangle_rename";
+ const char *fooname = "smb1_wild_mangle_rename/foo";
+ const char *foostar_name = "smb1_wild_mangle_rename/fo*";
+ const char *wild_name = "smb1_wild_mangle_rename/*";
+ char *windows_rename_src = NULL;
+ const char *windows_rename_dst = "smb1_wild_mangle_rename\\bar";
+ char *mangled_name = NULL;
+ NTSTATUS status;
+
+ printf("Starting SMB1 wild mangle rename test\n");
+
+ if (!torture_open_connection(&cli_posix, 0)) {
+ return false;
+ }
+
+ smbXcli_conn_set_sockopt(cli_posix->conn, sockops);
+
+ status = torture_setup_unix_extensions(cli_posix);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("server doesn't support POSIX\n");
+ return false;
+ }
+
+ /* Open a Windows connection. */
+ if (!torture_open_connection(&cli, 0)) {
+ goto out;
+ }
+
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ /* Ensure we start from fresh. */
+ torture_deltree(cli, dname);
+
+ /*
+ * Create two files - 'foo' and 'fo*'.
+ * We need POSIX extensions for this as 'fo*'
+ * is not a valid Windows name.
+ */
+
+ status = cli_posix_mkdir(cli_posix, dname, 0770);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s returned %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_posix_open(cli_posix,
+ fooname,
+ O_RDWR|O_CREAT|O_EXCL,
+ 0660,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open (create) of %s returned %s\n",
+ fooname,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = cli_posix_open(cli_posix,
+ foostar_name,
+ O_RDWR|O_CREAT|O_EXCL,
+ 0660,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open (create) of %s returned %s\n",
+ foostar_name,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ /*
+ * Get the mangled name. We can re-use the
+ * previous smb1_wild_mangle_list_fn for this.
+ */
+
+ status = cli_list(cli,
+ wild_name,
+ 0,
+ smb1_wild_mangle_list_fn,
+ &mangled_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list of %s returned %s\n",
+ wild_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (mangled_name == NULL) {
+ goto out;
+ }
+
+ printf("mangled_name = %s\n",
+ mangled_name);
+
+ /*
+ * Try a Windows rename with the mangled name.
+ * This should *NOT* rename the 'foo' name.
+ */
+
+ windows_rename_src = talloc_asprintf(cli_posix,
+ "%s\\%s",
+ dname,
+ mangled_name);
+
+ status = cli_rename(cli,
+ windows_rename_src,
+ windows_rename_dst,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_rename of %s -> %s returned %s\n",
+ windows_rename_src,
+ windows_rename_dst,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Does 'foo' still exist ? */
+ status = cli_posix_open(cli_posix,
+ fooname,
+ O_RDONLY,
+ 0,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open O_RNONLY of %s returned %s\n",
+ fooname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_posix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ correct = true;
+
+ out:
+
+ TALLOC_FREE(mangled_name);
+ TALLOC_FREE(windows_rename_src);
+
+ if (cli != NULL) {
+ torture_deltree(cli, dname);
+ torture_close_connection(cli);
+ }
+
+ torture_close_connection(cli_posix);
+
+ return correct;
+}
+
+/*
+ * Only testing minimal time strings, as the others
+ * need (locale-dependent) guessing at what strftime does and
+ * even may differ in builds.
+ */
+static bool timesubst_test(void)
+{
+ TALLOC_CTX *ctx = NULL;
+ /* Sa 23. Dez 04:33:20 CET 2017 */
+ const struct timeval tv = { 1514000000, 123 };
+ const char* expect_minimal = "20171223_033320";
+ const char* expect_minus = "20171223_033320_000123";
+ char *s;
+ char *env_tz, *orig_tz = NULL;
+ bool result = true;
+
+ ctx = talloc_new(NULL);
+
+ env_tz = getenv("TZ");
+ if(env_tz) {
+ orig_tz = talloc_strdup(ctx, env_tz);
+ }
+ setenv("TZ", "UTC", 1);
+
+ s = minimal_timeval_string(ctx, &tv, false);
+
+ if(!s || strcmp(s, expect_minimal)) {
+ printf("minimal_timeval_string(ctx, tv, false) returned [%s], expected "
+ "[%s]\n", s ? s : "<nil>", expect_minimal);
+ result = false;
+ }
+ TALLOC_FREE(s);
+ s = minimal_timeval_string(ctx, &tv, true);
+ if(!s || strcmp(s, expect_minus)) {
+ printf("minimal_timeval_string(ctx, tv, true) returned [%s], expected "
+ "[%s]\n", s ? s : "<nil>", expect_minus);
+ result = false;
+ }
+ TALLOC_FREE(s);
+
+ if(orig_tz) {
+ setenv("TZ", orig_tz, 1);
+ }
+
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+static bool run_local_substitute(int dummy)
+{
+ bool ok = true;
+
+ ok &= subst_test("%U", "bla", "", -1, -1, "bla");
+ ok &= subst_test("%u%U", "bla", "", -1, -1, "blabla");
+ ok &= subst_test("%g", "", "", -1, -1, "NO_GROUP");
+ ok &= subst_test("%G", "", "", -1, -1, "NO_GROUP");
+ ok &= subst_test("%g", "", "", -1, 0, gidtoname(0));
+ ok &= subst_test("%G", "", "", -1, 0, gidtoname(0));
+ ok &= subst_test("%D%u", "u", "dom", -1, 0, "domu");
+ ok &= subst_test("%i %I", "", "", -1, -1, "0.0.0.0 0.0.0.0");
+ ok &= subst_test("%j %J", "", "", -1, -1, "0_0_0_0 0_0_0_0");
+ /* Substitution depends on current time, so better test the underlying
+ formatting function. At least covers %t. */
+ ok &= timesubst_test();
+
+ /* Different captialization rules in sub_basic... */
+
+ ok &= (strcmp(talloc_sub_basic(talloc_tos(), "BLA", "dom", "%U%D"),
+ "blaDOM") == 0);
+
+ return ok;
+}
+
+static bool run_local_base64(int dummy)
+{
+ int i;
+ bool ret = true;
+
+ for (i=1; i<2000; i++) {
+ DATA_BLOB blob1, blob2;
+ char *b64;
+
+ blob1.data = talloc_array(talloc_tos(), uint8_t, i);
+ blob1.length = i;
+ generate_random_buffer(blob1.data, blob1.length);
+
+ b64 = base64_encode_data_blob(talloc_tos(), blob1);
+ if (b64 == NULL) {
+ d_fprintf(stderr, "base64_encode_data_blob failed "
+ "for %d bytes\n", i);
+ ret = false;
+ }
+ blob2 = base64_decode_data_blob(b64);
+ TALLOC_FREE(b64);
+
+ if (data_blob_cmp(&blob1, &blob2)) {
+ d_fprintf(stderr, "data_blob_cmp failed for %d "
+ "bytes\n", i);
+ ret = false;
+ }
+ TALLOC_FREE(blob1.data);
+ data_blob_free(&blob2);
+ }
+ return ret;
+}
+
+static void parse_fn(const struct gencache_timeout *t,
+ DATA_BLOB blob,
+ void *private_data)
+{
+ return;
+}
+
+static bool run_local_gencache(int dummy)
+{
+ char *val;
+ time_t tm;
+ DATA_BLOB blob;
+ char v;
+ struct memcache *mem;
+ int i;
+
+ mem = memcache_init(NULL, 0);
+ if (mem == NULL) {
+ d_printf("%s: memcache_init failed\n", __location__);
+ return false;
+ }
+ memcache_set_global(mem);
+
+ if (!gencache_set("foo", "bar", time(NULL) + 1000)) {
+ d_printf("%s: gencache_set() failed\n", __location__);
+ return False;
+ }
+
+ if (!gencache_get("foo", NULL, NULL, NULL)) {
+ d_printf("%s: gencache_get() failed\n", __location__);
+ return False;
+ }
+
+ for (i=0; i<1000000; i++) {
+ gencache_parse("foo", parse_fn, NULL);
+ }
+
+ if (!gencache_get("foo", talloc_tos(), &val, &tm)) {
+ d_printf("%s: gencache_get() failed\n", __location__);
+ return False;
+ }
+ TALLOC_FREE(val);
+
+ if (!gencache_get("foo", talloc_tos(), &val, &tm)) {
+ d_printf("%s: gencache_get() failed\n", __location__);
+ return False;
+ }
+
+ if (strcmp(val, "bar") != 0) {
+ d_printf("%s: gencache_get() returned %s, expected %s\n",
+ __location__, val, "bar");
+ TALLOC_FREE(val);
+ return False;
+ }
+
+ TALLOC_FREE(val);
+
+ if (!gencache_del("foo")) {
+ d_printf("%s: gencache_del() failed\n", __location__);
+ return False;
+ }
+ if (gencache_del("foo")) {
+ d_printf("%s: second gencache_del() succeeded\n",
+ __location__);
+ return False;
+ }
+
+ if (gencache_get("foo", talloc_tos(), &val, &tm)) {
+ d_printf("%s: gencache_get() on deleted entry "
+ "succeeded\n", __location__);
+ return False;
+ }
+
+ blob = data_blob_string_const_null("bar");
+ tm = time(NULL) + 60;
+
+ if (!gencache_set_data_blob("foo", blob, tm)) {
+ d_printf("%s: gencache_set_data_blob() failed\n", __location__);
+ return False;
+ }
+
+ if (!gencache_get_data_blob("foo", talloc_tos(), &blob, NULL, NULL)) {
+ d_printf("%s: gencache_get_data_blob() failed\n", __location__);
+ return False;
+ }
+
+ if (strcmp((const char *)blob.data, "bar") != 0) {
+ d_printf("%s: gencache_get_data_blob() returned %s, expected %s\n",
+ __location__, (const char *)blob.data, "bar");
+ data_blob_free(&blob);
+ return False;
+ }
+
+ data_blob_free(&blob);
+
+ if (!gencache_del("foo")) {
+ d_printf("%s: gencache_del() failed\n", __location__);
+ return False;
+ }
+ if (gencache_del("foo")) {
+ d_printf("%s: second gencache_del() succeeded\n",
+ __location__);
+ return False;
+ }
+
+ if (gencache_get_data_blob("foo", talloc_tos(), &blob, NULL, NULL)) {
+ d_printf("%s: gencache_get_data_blob() on deleted entry "
+ "succeeded\n", __location__);
+ return False;
+ }
+
+ v = 1;
+ blob.data = (uint8_t *)&v;
+ blob.length = sizeof(v);
+
+ if (!gencache_set_data_blob("blob", blob, tm)) {
+ d_printf("%s: gencache_set_data_blob() failed\n",
+ __location__);
+ return false;
+ }
+ if (gencache_get("blob", talloc_tos(), &val, &tm)) {
+ d_printf("%s: gencache_get succeeded\n", __location__);
+ return false;
+ }
+
+ return True;
+}
+
+static bool rbt_testflags(struct db_context *db, const char *key,
+ const char *value)
+{
+ bool ret = false;
+ NTSTATUS status;
+ struct db_record *rec;
+
+ rec = dbwrap_fetch_locked(db, db, string_tdb_data(key));
+ if (rec == NULL) {
+ d_fprintf(stderr, "fetch_locked failed\n");
+ goto done;
+ }
+
+ status = dbwrap_record_store(rec, string_tdb_data(value), TDB_MODIFY);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ d_fprintf(stderr, "store TDB_MODIFY unexpected status: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dbwrap_record_store(rec, string_tdb_data("overwriteme"),
+ TDB_INSERT);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "store TDB_INSERT failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dbwrap_record_store(rec, string_tdb_data(value), TDB_INSERT);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ d_fprintf(stderr, "store TDB_INSERT unexpected status: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dbwrap_record_store(rec, string_tdb_data(value), TDB_MODIFY);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "store TDB_MODIFY failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+done:
+ TALLOC_FREE(rec);
+ return ret;
+}
+
+static bool rbt_testval(struct db_context *db, const char *key,
+ const char *value)
+{
+ struct db_record *rec;
+ TDB_DATA data = string_tdb_data(value);
+ bool ret = false;
+ NTSTATUS status;
+ TDB_DATA dbvalue;
+
+ rec = dbwrap_fetch_locked(db, db, string_tdb_data(key));
+ if (rec == NULL) {
+ d_fprintf(stderr, "fetch_locked failed\n");
+ goto done;
+ }
+ status = dbwrap_record_store(rec, data, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "store failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+ TALLOC_FREE(rec);
+
+ rec = dbwrap_fetch_locked(db, db, string_tdb_data(key));
+ if (rec == NULL) {
+ d_fprintf(stderr, "second fetch_locked failed\n");
+ goto done;
+ }
+
+ dbvalue = dbwrap_record_get_value(rec);
+ if ((dbvalue.dsize != data.dsize)
+ || (memcmp(dbvalue.dptr, data.dptr, data.dsize) != 0)) {
+ d_fprintf(stderr, "Got wrong data back\n");
+ goto done;
+ }
+
+ ret = true;
+ done:
+ TALLOC_FREE(rec);
+ return ret;
+}
+
+static int local_rbtree_traverse_read(struct db_record *rec, void *private_data)
+{
+ int *count2 = (int *)private_data;
+ (*count2)++;
+ return 0;
+}
+
+static int local_rbtree_traverse_delete(struct db_record *rec, void *private_data)
+{
+ int *count2 = (int *)private_data;
+ (*count2)++;
+ dbwrap_record_delete(rec);
+ return 0;
+}
+
+static bool run_local_rbtree(int dummy)
+{
+ struct db_context *db;
+ bool ret = false;
+ int i;
+ NTSTATUS status;
+ int count = 0;
+ int count2 = 0;
+
+ db = db_open_rbt(NULL);
+
+ if (db == NULL) {
+ d_fprintf(stderr, "db_open_rbt failed\n");
+ return false;
+ }
+
+ if (!rbt_testflags(db, "firstkey", "firstval")) {
+ goto done;
+ }
+
+ for (i = 0; i < 999; i++) {
+ char key[sizeof("key-9223372036854775807")];
+ char value[sizeof("value-9223372036854775807")];
+
+ snprintf(key, sizeof(key), "key%ld", random());
+ snprintf(value, sizeof(value) ,"value%ld", random());
+
+ if (!rbt_testval(db, key, value)) {
+ goto done;
+ }
+
+ snprintf(value, sizeof(value) ,"value%ld", random());
+
+ if (!rbt_testval(db, key, value)) {
+ goto done;
+ }
+ }
+
+ ret = true;
+ count = 0; count2 = 0;
+ status = dbwrap_traverse_read(db, local_rbtree_traverse_read,
+ &count2, &count);
+ printf("%s: read1: %d %d, %s\n", __func__, count, count2, nt_errstr(status));
+ if ((count != count2) || (count != 1000)) {
+ ret = false;
+ }
+ count = 0; count2 = 0;
+ status = dbwrap_traverse(db, local_rbtree_traverse_delete,
+ &count2, &count);
+ printf("%s: delete: %d %d, %s\n", __func__, count, count2, nt_errstr(status));
+ if ((count != count2) || (count != 1000)) {
+ ret = false;
+ }
+ count = 0; count2 = 0;
+ status = dbwrap_traverse_read(db, local_rbtree_traverse_read,
+ &count2, &count);
+ printf("%s: read2: %d %d, %s\n", __func__, count, count2, nt_errstr(status));
+ if ((count != count2) || (count != 0)) {
+ ret = false;
+ }
+
+ done:
+ TALLOC_FREE(db);
+ return ret;
+}
+
+
+/*
+ local test for character set functions
+
+ This is a very simple test for the functionality in convert_string_error()
+ */
+static bool run_local_convert_string(int dummy)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ const char *test_strings[2] = { "March", "M\303\244rz" };
+ char dst[7];
+ int i;
+
+ for (i=0; i<2; i++) {
+ const char *str = test_strings[i];
+ int len = strlen(str);
+ size_t converted_size;
+ bool ret;
+
+ memset(dst, 'X', sizeof(dst));
+
+ /* first try with real source length */
+ ret = convert_string_error(CH_UNIX, CH_UTF8,
+ str, len,
+ dst, sizeof(dst),
+ &converted_size);
+ if (ret != true) {
+ d_fprintf(stderr, "Failed to convert '%s' to CH_DISPLAY\n", str);
+ goto failed;
+ }
+
+ if (converted_size != len) {
+ d_fprintf(stderr, "Converted size of '%s' should be %d - got %d\n",
+ str, len, (int)converted_size);
+ goto failed;
+ }
+
+ if (strncmp(str, dst, converted_size) != 0) {
+ d_fprintf(stderr, "Expected '%s' to match '%s'\n", str, dst);
+ goto failed;
+ }
+
+ if (strlen(str) != converted_size) {
+ d_fprintf(stderr, "Expected '%s' length %d - got %d\n", str,
+ (int)strlen(str), (int)converted_size);
+ goto failed;
+ }
+
+ if (dst[converted_size] != 'X') {
+ d_fprintf(stderr, "Expected no termination of '%s'\n", dst);
+ goto failed;
+ }
+
+ /* now with srclen==-1, this causes the nul to be
+ * converted too */
+ ret = convert_string_error(CH_UNIX, CH_UTF8,
+ str, -1,
+ dst, sizeof(dst),
+ &converted_size);
+ if (ret != true) {
+ d_fprintf(stderr, "Failed to convert '%s' to CH_DISPLAY\n", str);
+ goto failed;
+ }
+
+ if (converted_size != len+1) {
+ d_fprintf(stderr, "Converted size of '%s' should be %d - got %d\n",
+ str, len, (int)converted_size);
+ goto failed;
+ }
+
+ if (strncmp(str, dst, converted_size) != 0) {
+ d_fprintf(stderr, "Expected '%s' to match '%s'\n", str, dst);
+ goto failed;
+ }
+
+ if (len+1 != converted_size) {
+ d_fprintf(stderr, "Expected '%s' length %d - got %d\n", str,
+ len+1, (int)converted_size);
+ goto failed;
+ }
+
+ if (dst[converted_size] != 'X') {
+ d_fprintf(stderr, "Expected no termination of '%s'\n", dst);
+ goto failed;
+ }
+
+ }
+
+
+ TALLOC_FREE(tmp_ctx);
+ return true;
+failed:
+ TALLOC_FREE(tmp_ctx);
+ return false;
+}
+
+static bool run_local_string_to_sid(int dummy) {
+ struct dom_sid sid;
+
+ if (string_to_sid(&sid, "S--1-5-32-545")) {
+ printf("allowing S--1-5-32-545\n");
+ return false;
+ }
+ if (string_to_sid(&sid, "S-1-5-32-+545")) {
+ printf("allowing S-1-5-32-+545\n");
+ return false;
+ }
+ if (string_to_sid(&sid, "S-1-2-3-4-5-6-7-8-9-0-1-2-3-4-5-6-7-8-9-0")) {
+ printf("allowing S-1-2-3-4-5-6-7-8-9-0-1-2-3-4-5-6-7-8-9-0\n");
+ return false;
+ }
+ if (string_to_sid(&sid, "S-1-5-32-545-abc")) {
+ printf("allowing S-1-5-32-545-abc\n");
+ return false;
+ }
+ if (string_to_sid(&sid, "S-300-5-32-545")) {
+ printf("allowing S-300-5-32-545\n");
+ return false;
+ }
+ if (string_to_sid(&sid, "S-1-0xfffffffffffffe-32-545")) {
+ printf("allowing S-1-0xfffffffffffffe-32-545\n");
+ return false;
+ }
+ if (string_to_sid(&sid, "S-1-0xffffffffffff-5294967297-545")) {
+ printf("allowing S-1-0xffffffffffff-5294967297-545\n");
+ return false;
+ }
+ if (!string_to_sid(&sid, "S-1-0xfffffffffffe-32-545")) {
+ printf("could not parse S-1-0xfffffffffffe-32-545\n");
+ return false;
+ }
+ if (!string_to_sid(&sid, "S-1-5-32-545")) {
+ printf("could not parse S-1-5-32-545\n");
+ return false;
+ }
+ if (!dom_sid_equal(&sid, &global_sid_Builtin_Users)) {
+ struct dom_sid_buf buf;
+ printf("mis-parsed S-1-5-32-545 as %s\n",
+ dom_sid_str_buf(&sid, &buf));
+ return false;
+ }
+ return true;
+}
+
+static bool sid_to_string_test(const char *expected) {
+ char *str;
+ bool res = true;
+ struct dom_sid sid;
+
+ if (!string_to_sid(&sid, expected)) {
+ printf("could not parse %s\n", expected);
+ return false;
+ }
+
+ str = dom_sid_string(NULL, &sid);
+ if (strcmp(str, expected)) {
+ printf("Comparison failed (%s != %s)\n", str, expected);
+ res = false;
+ }
+ TALLOC_FREE(str);
+ return res;
+}
+
+static bool run_local_sid_to_string(int dummy) {
+ if (!sid_to_string_test("S-1-0xffffffffffff-1-1-1-1-1-1-1-1-1-1-1-1"))
+ return false;
+ if (!sid_to_string_test("S-1-545"))
+ return false;
+ if (!sid_to_string_test("S-255-3840-1-1-1-1"))
+ return false;
+ return true;
+}
+
+static bool run_local_binary_to_sid(int dummy) {
+ ssize_t ret;
+ struct dom_sid *sid = talloc(NULL, struct dom_sid);
+ static const uint8_t good_binary_sid[] = {
+ 0x1, /* revision number */
+ 15, /* num auths */
+ 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, /* id_auth */
+ 0x1, 0x1, 0x1, 0x1, /* auth[0] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[1] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[2] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[3] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[4] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[5] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[6] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[7] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[8] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[9] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[10] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[11] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[12] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[13] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[14] */
+ };
+
+ static const uint8_t long_binary_sid[] = {
+ 0x1, /* revision number */
+ 15, /* num auths */
+ 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, /* id_auth */
+ 0x1, 0x1, 0x1, 0x1, /* auth[0] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[1] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[2] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[3] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[4] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[5] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[6] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[7] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[8] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[9] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[10] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[11] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[12] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[13] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[14] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[15] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[16] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[17] */
+ };
+
+ static const uint8_t long_binary_sid2[] = {
+ 0x1, /* revision number */
+ 32, /* num auths */
+ 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, /* id_auth */
+ 0x1, 0x1, 0x1, 0x1, /* auth[0] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[1] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[2] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[3] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[4] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[5] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[6] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[7] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[8] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[9] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[10] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[11] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[12] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[13] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[14] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[15] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[16] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[17] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[18] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[19] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[20] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[21] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[22] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[23] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[24] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[25] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[26] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[27] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[28] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[29] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[30] */
+ 0x1, 0x1, 0x1, 0x1, /* auth[31] */
+ };
+
+ ret = sid_parse(good_binary_sid, sizeof(good_binary_sid), sid);
+ if (ret == -1) {
+ return false;
+ }
+ ret = sid_parse(long_binary_sid2, sizeof(long_binary_sid2), sid);
+ if (ret != -1) {
+ return false;
+ }
+ ret = sid_parse(long_binary_sid, sizeof(long_binary_sid), sid);
+ if (ret != -1) {
+ return false;
+ }
+ return true;
+}
+
+/* Split a path name into filename and stream name components. Canonicalise
+ * such that an implicit $DATA token is always explicit.
+ *
+ * The "specification" of this function can be found in the
+ * run_local_stream_name() function in torture.c, I've tried those
+ * combinations against a W2k3 server.
+ */
+
+static NTSTATUS split_ntfs_stream_name(TALLOC_CTX *mem_ctx, const char *fname,
+ char **pbase, char **pstream)
+{
+ char *base = NULL;
+ char *stream = NULL;
+ char *sname; /* stream name */
+ const char *stype; /* stream type */
+
+ DEBUG(10, ("split_ntfs_stream_name called for [%s]\n", fname));
+
+ sname = strchr_m(fname, ':');
+
+ if (sname == NULL) {
+ if (pbase != NULL) {
+ base = talloc_strdup(mem_ctx, fname);
+ NT_STATUS_HAVE_NO_MEMORY(base);
+ }
+ goto done;
+ }
+
+ if (pbase != NULL) {
+ base = talloc_strndup(mem_ctx, fname, PTR_DIFF(sname, fname));
+ NT_STATUS_HAVE_NO_MEMORY(base);
+ }
+
+ sname += 1;
+
+ stype = strchr_m(sname, ':');
+
+ if (stype == NULL) {
+ sname = talloc_strdup(mem_ctx, sname);
+ stype = "$DATA";
+ }
+ else {
+ if (strcasecmp_m(stype, ":$DATA") != 0) {
+ /*
+ * If there is an explicit stream type, so far we only
+ * allow $DATA. Is there anything else allowed? -- vl
+ */
+ DEBUG(10, ("[%s] is an invalid stream type\n", stype));
+ TALLOC_FREE(base);
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ sname = talloc_strndup(mem_ctx, sname, PTR_DIFF(stype, sname));
+ stype += 1;
+ }
+
+ if (sname == NULL) {
+ TALLOC_FREE(base);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (sname[0] == '\0') {
+ /*
+ * no stream name, so no stream
+ */
+ goto done;
+ }
+
+ if (pstream != NULL) {
+ stream = talloc_asprintf(mem_ctx, "%s:%s", sname, stype);
+ if (stream == NULL) {
+ TALLOC_FREE(sname);
+ TALLOC_FREE(base);
+ return NT_STATUS_NO_MEMORY;
+ }
+ /*
+ * upper-case the type field
+ */
+ (void)strupper_m(strchr_m(stream, ':')+1);
+ }
+
+ done:
+ if (pbase != NULL) {
+ *pbase = base;
+ }
+ if (pstream != NULL) {
+ *pstream = stream;
+ }
+ return NT_STATUS_OK;
+}
+
+static bool test_stream_name(const char *fname, const char *expected_base,
+ const char *expected_stream,
+ NTSTATUS expected_status)
+{
+ NTSTATUS status;
+ char *base = NULL;
+ char *stream = NULL;
+
+ status = split_ntfs_stream_name(talloc_tos(), fname, &base, &stream);
+ if (!NT_STATUS_EQUAL(status, expected_status)) {
+ goto error;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return true;
+ }
+
+ if (base == NULL) goto error;
+
+ if (strcmp(expected_base, base) != 0) goto error;
+
+ if ((expected_stream != NULL) && (stream == NULL)) goto error;
+ if ((expected_stream == NULL) && (stream != NULL)) goto error;
+
+ if ((stream != NULL) && (strcmp(expected_stream, stream) != 0))
+ goto error;
+
+ TALLOC_FREE(base);
+ TALLOC_FREE(stream);
+ return true;
+
+ error:
+ d_fprintf(stderr, "Do test_stream(%s, %s, %s, %s)\n",
+ fname, expected_base ? expected_base : "<NULL>",
+ expected_stream ? expected_stream : "<NULL>",
+ nt_errstr(expected_status));
+ d_fprintf(stderr, "-> base=%s, stream=%s, status=%s\n",
+ base ? base : "<NULL>", stream ? stream : "<NULL>",
+ nt_errstr(status));
+ TALLOC_FREE(base);
+ TALLOC_FREE(stream);
+ return false;
+}
+
+static bool run_local_stream_name(int dummy)
+{
+ bool ret = true;
+
+ ret &= test_stream_name(
+ "bla", "bla", NULL, NT_STATUS_OK);
+ ret &= test_stream_name(
+ "bla::$DATA", "bla", NULL, NT_STATUS_OK);
+ ret &= test_stream_name(
+ "bla:blub:", "bla", NULL, NT_STATUS_OBJECT_NAME_INVALID);
+ ret &= test_stream_name(
+ "bla::", NULL, NULL, NT_STATUS_OBJECT_NAME_INVALID);
+ ret &= test_stream_name(
+ "bla::123", "bla", NULL, NT_STATUS_OBJECT_NAME_INVALID);
+ ret &= test_stream_name(
+ "bla:$DATA", "bla", "$DATA:$DATA", NT_STATUS_OK);
+ ret &= test_stream_name(
+ "bla:x:$DATA", "bla", "x:$DATA", NT_STATUS_OK);
+ ret &= test_stream_name(
+ "bla:x", "bla", "x:$DATA", NT_STATUS_OK);
+
+ return ret;
+}
+
+static bool data_blob_equal(DATA_BLOB a, DATA_BLOB b)
+{
+ if (a.length != b.length) {
+ printf("a.length=%d != b.length=%d\n",
+ (int)a.length, (int)b.length);
+ return false;
+ }
+ if (memcmp(a.data, b.data, a.length) != 0) {
+ printf("a.data and b.data differ\n");
+ return false;
+ }
+ return true;
+}
+
+static bool run_local_memcache(int dummy)
+{
+ struct memcache *cache;
+ DATA_BLOB k1, k2, k3, k4, k5;
+ DATA_BLOB d1, d3;
+ DATA_BLOB v1, v3;
+
+ TALLOC_CTX *mem_ctx;
+ char *ptr1 = NULL;
+ char *ptr2 = NULL;
+ char *ptr3 = NULL;
+
+ char *str1, *str2;
+ size_t size1, size2;
+ bool ret = false;
+
+ mem_ctx = talloc_init("foo");
+ if (mem_ctx == NULL) {
+ return false;
+ }
+
+ /* STAT_CACHE TESTS */
+
+ cache = memcache_init(NULL, sizeof(void *) == 8 ? 200 : 100);
+
+ if (cache == NULL) {
+ printf("memcache_init failed\n");
+ return false;
+ }
+
+ d1 = data_blob_const("d1", 2);
+ d3 = data_blob_const("d3", 2);
+
+ k1 = data_blob_const("d1", 2);
+ k2 = data_blob_const("d2", 2);
+ k3 = data_blob_const("d3", 2);
+ k4 = data_blob_const("d4", 2);
+ k5 = data_blob_const("d5", 2);
+
+ memcache_add(cache, STAT_CACHE, k1, d1);
+
+ if (!memcache_lookup(cache, STAT_CACHE, k1, &v1)) {
+ printf("could not find k1\n");
+ return false;
+ }
+ if (!data_blob_equal(d1, v1)) {
+ return false;
+ }
+
+ memcache_add(cache, STAT_CACHE, k1, d3);
+
+ if (!memcache_lookup(cache, STAT_CACHE, k1, &v3)) {
+ printf("could not find replaced k1\n");
+ return false;
+ }
+ if (!data_blob_equal(d3, v3)) {
+ return false;
+ }
+
+ TALLOC_FREE(cache);
+
+ /* GETWD_CACHE TESTS */
+ str1 = talloc_strdup(mem_ctx, "string1");
+ if (str1 == NULL) {
+ return false;
+ }
+ ptr2 = str1; /* Keep an alias for comparison. */
+
+ str2 = talloc_strdup(mem_ctx, "string2");
+ if (str2 == NULL) {
+ return false;
+ }
+
+ cache = memcache_init(NULL, sizeof(void *) == 8 ? 200 : 100);
+ if (cache == NULL) {
+ printf("memcache_init failed\n");
+ return false;
+ }
+
+ memcache_add_talloc(cache, GETWD_CACHE, k2, &str1);
+ /* str1 == NULL now. */
+ ptr1 = memcache_lookup_talloc(cache, GETWD_CACHE, k2);
+ if (ptr1 == NULL) {
+ printf("could not find k2\n");
+ return false;
+ }
+ if (ptr1 != ptr2) {
+ printf("fetch of k2 got wrong string\n");
+ return false;
+ }
+
+ /* Add a blob to ensure k2 gets purged. */
+ d3 = data_blob_talloc_zero(mem_ctx, 180);
+ memcache_add(cache, STAT_CACHE, k3, d3);
+
+ ptr2 = memcache_lookup_talloc(cache, GETWD_CACHE, k2);
+ if (ptr2 != NULL) {
+ printf("Did find k2, should have been purged\n");
+ return false;
+ }
+
+ /*
+ * Test that talloc size also is accounted in memcache and
+ * causes purge of other object.
+ */
+
+ str1 = talloc_zero_size(mem_ctx, 100);
+ str2 = talloc_zero_size(mem_ctx, 100);
+
+ memcache_add_talloc(cache, GETWD_CACHE, k4, &str1);
+ memcache_add_talloc(cache, GETWD_CACHE, k5, &str1);
+
+ ptr3 = memcache_lookup_talloc(cache, GETWD_CACHE, k4);
+ if (ptr3 != NULL) {
+ printf("Did find k4, should have been purged\n");
+ return false;
+ }
+
+ /*
+ * Test that adding a duplicate non-talloced
+ * key/value on top of a talloced key/value takes account
+ * of the talloc_freed value size.
+ */
+ TALLOC_FREE(cache);
+ TALLOC_FREE(mem_ctx);
+
+ mem_ctx = talloc_init("key_replace");
+ if (mem_ctx == NULL) {
+ return false;
+ }
+
+ cache = memcache_init(NULL, sizeof(void *) == 8 ? 200 : 100);
+ if (cache == NULL) {
+ return false;
+ }
+
+ /*
+ * Add a 100 byte talloced string. This will
+ * store a (4 or 8 byte) pointer and record the
+ * total talloced size.
+ */
+ str1 = talloc_zero_size(mem_ctx, 100);
+ memcache_add_talloc(cache, GETWD_CACHE, k4, &str1);
+ /*
+ * Now overwrite with a small talloced
+ * value. This should fit in the existing size
+ * and the total talloced size should be removed
+ * from the cache size.
+ */
+ str1 = talloc_zero_size(mem_ctx, 2);
+ memcache_add_talloc(cache, GETWD_CACHE, k4, &str1);
+ /*
+ * Now store a 20 byte string. If the
+ * total talloced size wasn't accounted for
+ * and removed in the overwrite, then this
+ * will evict k4.
+ */
+ str2 = talloc_zero_size(mem_ctx, 20);
+ memcache_add_talloc(cache, GETWD_CACHE, k5, &str2);
+
+ ptr3 = memcache_lookup_talloc(cache, GETWD_CACHE, k4);
+ if (ptr3 == NULL) {
+ printf("Did not find k4, should not have been purged\n");
+ return false;
+ }
+
+ TALLOC_FREE(cache);
+ TALLOC_FREE(mem_ctx);
+
+ mem_ctx = talloc_init("foo");
+ if (mem_ctx == NULL) {
+ return false;
+ }
+
+ cache = memcache_init(NULL, 0);
+ if (cache == NULL) {
+ return false;
+ }
+
+ str1 = talloc_strdup(mem_ctx, "string1");
+ if (str1 == NULL) {
+ return false;
+ }
+ str2 = talloc_strdup(mem_ctx, "string2");
+ if (str2 == NULL) {
+ return false;
+ }
+ memcache_add_talloc(cache, SINGLETON_CACHE_TALLOC,
+ data_blob_string_const("torture"), &str1);
+ size1 = talloc_total_size(cache);
+
+ memcache_add_talloc(cache, SINGLETON_CACHE_TALLOC,
+ data_blob_string_const("torture"), &str2);
+ size2 = talloc_total_size(cache);
+
+ printf("size1=%d, size2=%d\n", (int)size1, (int)size2);
+
+ if (size2 > size1) {
+ printf("memcache leaks memory!\n");
+ goto fail;
+ }
+
+ ret = true;
+ fail:
+ TALLOC_FREE(cache);
+ return ret;
+}
+
+static void wbclient_done(struct tevent_req *req)
+{
+ wbcErr wbc_err;
+ struct winbindd_response *wb_resp;
+ int *i = (int *)tevent_req_callback_data_void(req);
+
+ wbc_err = wb_trans_recv(req, req, &wb_resp);
+ TALLOC_FREE(req);
+ *i += 1;
+ d_printf("wb_trans_recv %d returned %s\n", *i, wbcErrorString(wbc_err));
+}
+
+static bool run_wbclient_multi_ping(int dummy)
+{
+ struct tevent_context *ev;
+ struct wb_context **wb_ctx;
+ struct winbindd_request wb_req;
+ bool result = false;
+ int i, j;
+
+ BlockSignals(True, SIGPIPE);
+
+ ev = tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ wb_ctx = talloc_array(ev, struct wb_context *, torture_nprocs);
+ if (wb_ctx == NULL) {
+ goto fail;
+ }
+
+ ZERO_STRUCT(wb_req);
+ wb_req.cmd = WINBINDD_PING;
+
+ d_printf("torture_nprocs=%d, numops=%d\n", (int)torture_nprocs, (int)torture_numops);
+
+ for (i=0; i<torture_nprocs; i++) {
+ wb_ctx[i] = wb_context_init(ev, NULL);
+ if (wb_ctx[i] == NULL) {
+ goto fail;
+ }
+ for (j=0; j<torture_numops; j++) {
+ struct tevent_req *req;
+ req = wb_trans_send(ev, ev, wb_ctx[i],
+ (j % 2) == 0, &wb_req);
+ if (req == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(req, wbclient_done, &i);
+ }
+ }
+
+ i = 0;
+
+ while (i < torture_nprocs * torture_numops) {
+ tevent_loop_once(ev);
+ }
+
+ result = true;
+ fail:
+ TALLOC_FREE(ev);
+ return result;
+}
+
+static bool dbtrans_inc(struct db_context *db)
+{
+ struct db_record *rec;
+ uint32_t val;
+ bool ret = false;
+ NTSTATUS status;
+ TDB_DATA value;
+
+ rec = dbwrap_fetch_locked(db, db, string_term_tdb_data("transtest"));
+ if (rec == NULL) {
+ printf(__location__ "fetch_lock failed\n");
+ return false;
+ }
+
+ value = dbwrap_record_get_value(rec);
+
+ if (value.dsize != sizeof(uint32_t)) {
+ printf(__location__ "value.dsize = %d\n",
+ (int)value.dsize);
+ goto fail;
+ }
+
+ memcpy(&val, value.dptr, sizeof(val));
+ val += 1;
+
+ status = dbwrap_record_store(
+ rec, make_tdb_data((uint8_t *)&val, sizeof(val)), 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ "store failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(rec);
+ return ret;
+}
+
+static bool run_local_dbtrans(int dummy)
+{
+ struct db_context *db;
+ struct db_record *rec;
+ NTSTATUS status;
+ uint32_t initial;
+ int res;
+ TDB_DATA value;
+
+ db = db_open(talloc_tos(), "transtest.tdb", 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_1,
+ DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ printf("Could not open transtest.db\n");
+ return false;
+ }
+
+ res = dbwrap_transaction_start(db);
+ if (res != 0) {
+ printf(__location__ "transaction_start failed\n");
+ return false;
+ }
+
+ rec = dbwrap_fetch_locked(db, db, string_term_tdb_data("transtest"));
+ if (rec == NULL) {
+ printf(__location__ "fetch_lock failed\n");
+ return false;
+ }
+
+ value = dbwrap_record_get_value(rec);
+
+ if (value.dptr == NULL) {
+ initial = 0;
+ status = dbwrap_record_store(
+ rec, make_tdb_data((uint8_t *)&initial,
+ sizeof(initial)),
+ 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ "store returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ TALLOC_FREE(rec);
+
+ res = dbwrap_transaction_commit(db);
+ if (res != 0) {
+ printf(__location__ "transaction_commit failed\n");
+ return false;
+ }
+
+ while (true) {
+ uint32_t val, val2;
+ int i;
+
+ res = dbwrap_transaction_start(db);
+ if (res != 0) {
+ printf(__location__ "transaction_start failed\n");
+ break;
+ }
+
+ status = dbwrap_fetch_uint32_bystring(db, "transtest", &val);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ "dbwrap_fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ break;
+ }
+
+ for (i=0; i<10; i++) {
+ if (!dbtrans_inc(db)) {
+ return false;
+ }
+ }
+
+ status = dbwrap_fetch_uint32_bystring(db, "transtest", &val2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ "dbwrap_fetch_uint32 failed: %s\n",
+ nt_errstr(status));
+ break;
+ }
+
+ if (val2 != val + 10) {
+ printf(__location__ "val=%d, val2=%d\n",
+ (int)val, (int)val2);
+ break;
+ }
+
+ printf("val2=%d\r", val2);
+
+ res = dbwrap_transaction_commit(db);
+ if (res != 0) {
+ printf(__location__ "transaction_commit failed\n");
+ break;
+ }
+ }
+
+ TALLOC_FREE(db);
+ return true;
+}
+
+/*
+ * Just a dummy test to be run under a debugger. There's no real way
+ * to inspect the tevent_poll specific function from outside of
+ * tevent_poll.c.
+ */
+
+static bool run_local_tevent_poll(int dummy)
+{
+ struct tevent_context *ev;
+ struct tevent_fd *fd1, *fd2;
+ bool result = false;
+
+ ev = tevent_context_init_byname(NULL, "poll");
+ if (ev == NULL) {
+ d_fprintf(stderr, "tevent_context_init_byname failed\n");
+ goto fail;
+ }
+
+ fd1 = tevent_add_fd(ev, ev, 2, 0, NULL, NULL);
+ if (fd1 == NULL) {
+ d_fprintf(stderr, "tevent_add_fd failed\n");
+ goto fail;
+ }
+ fd2 = tevent_add_fd(ev, ev, 3, 0, NULL, NULL);
+ if (fd2 == NULL) {
+ d_fprintf(stderr, "tevent_add_fd failed\n");
+ goto fail;
+ }
+ TALLOC_FREE(fd2);
+
+ fd2 = tevent_add_fd(ev, ev, 1, 0, NULL, NULL);
+ if (fd2 == NULL) {
+ d_fprintf(stderr, "tevent_add_fd failed\n");
+ goto fail;
+ }
+
+ result = true;
+fail:
+ TALLOC_FREE(ev);
+ return result;
+}
+
+static bool run_local_hex_encode_buf(int dummy)
+{
+ char buf[17];
+ uint8_t src[8];
+ size_t i;
+
+ for (i=0; i<sizeof(src); i++) {
+ src[i] = i;
+ }
+ hex_encode_buf(buf, src, sizeof(src));
+ if (strcmp(buf, "0001020304050607") != 0) {
+ return false;
+ }
+ hex_encode_buf(buf, NULL, 0);
+ if (buf[0] != '\0') {
+ return false;
+ }
+ return true;
+}
+
+static const char *remove_duplicate_addrs2_test_strings_vector[] = {
+ "0.0.0.0",
+ "::0",
+ "1.2.3.1",
+ "0.0.0.0",
+ "0.0.0.0",
+ "1.2.3.2",
+ "1.2.3.3",
+ "1.2.3.4",
+ "1.2.3.5",
+ "::0",
+ "1.2.3.6",
+ "1.2.3.7",
+ "::0",
+ "::0",
+ "::0",
+ "1.2.3.8",
+ "1.2.3.9",
+ "1.2.3.10",
+ "1.2.3.11",
+ "1.2.3.12",
+ "1.2.3.13",
+ "1001:1111:1111:1000:0:1111:1111:1111",
+ "1.2.3.1",
+ "1.2.3.2",
+ "1.2.3.3",
+ "1.2.3.12",
+ "::0",
+ "::0"
+};
+
+static const char *remove_duplicate_addrs2_test_strings_result[] = {
+ "1.2.3.1",
+ "1.2.3.2",
+ "1.2.3.3",
+ "1.2.3.4",
+ "1.2.3.5",
+ "1.2.3.6",
+ "1.2.3.7",
+ "1.2.3.8",
+ "1.2.3.9",
+ "1.2.3.10",
+ "1.2.3.11",
+ "1.2.3.12",
+ "1.2.3.13",
+ "1001:1111:1111:1000:0:1111:1111:1111"
+};
+
+static bool run_local_remove_duplicate_addrs2(int dummy)
+{
+ struct samba_sockaddr test_vector[28];
+ size_t count, i;
+
+ /* Construct the sockaddr_storage test vector. */
+ for (i = 0; i < 28; i++) {
+ struct addrinfo hints;
+ struct addrinfo *res = NULL;
+ int ret;
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ ret = getaddrinfo(remove_duplicate_addrs2_test_strings_vector[i],
+ NULL,
+ &hints,
+ &res);
+ if (ret) {
+ fprintf(stderr, "getaddrinfo failed on [%s]\n",
+ remove_duplicate_addrs2_test_strings_vector[i]);
+ return false;
+ }
+ memset(&test_vector[i], '\0', sizeof(test_vector[i]));
+ memcpy(&test_vector[i].u.ss,
+ res->ai_addr,
+ res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ count = remove_duplicate_addrs2(test_vector, i);
+
+ if (count != 14) {
+ fprintf(stderr, "count wrong (%zu) should be 14\n",
+ count);
+ return false;
+ }
+
+ for (i = 0; i < count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), &test_vector[i].u.ss);
+
+ if (strcmp(addr, remove_duplicate_addrs2_test_strings_result[i]) != 0) {
+ fprintf(stderr, "mismatch on [%zu] [%s] [%s]\n",
+ i,
+ addr,
+ remove_duplicate_addrs2_test_strings_result[i]);
+ return false;
+ }
+ }
+
+ printf("run_local_remove_duplicate_addrs2: success\n");
+ return true;
+}
+
+static bool run_local_tdb_opener(int dummy)
+{
+ TDB_CONTEXT *t;
+ unsigned v = 0;
+
+ while (1) {
+ t = tdb_open("test.tdb", 1000, TDB_CLEAR_IF_FIRST,
+ O_RDWR|O_CREAT, 0755);
+ if (t == NULL) {
+ perror("tdb_open failed");
+ return false;
+ }
+ tdb_close(t);
+
+ v += 1;
+ printf("\r%u", v);
+ }
+ return true;
+}
+
+static bool run_local_tdb_writer(int dummy)
+{
+ TDB_CONTEXT *t;
+ unsigned v = 0;
+ TDB_DATA val;
+
+ t = tdb_open("test.tdb", 1000, 0, O_RDWR|O_CREAT, 0755);
+ if (t == 0) {
+ perror("tdb_open failed");
+ return 1;
+ }
+
+ val.dptr = (uint8_t *)&v;
+ val.dsize = sizeof(v);
+
+ while (1) {
+ TDB_DATA data;
+ int ret;
+
+ ret = tdb_store(t, val, val, 0);
+ if (ret != 0) {
+ printf("%s\n", tdb_errorstr(t));
+ }
+ v += 1;
+ printf("\r%u", v);
+
+ data = tdb_fetch(t, val);
+ if (data.dptr != NULL) {
+ SAFE_FREE(data.dptr);
+ }
+ }
+ return true;
+}
+
+static bool run_local_canonicalize_path(int dummy)
+{
+ const char *src[] = {
+ "/foo/..",
+ "/..",
+ "/foo/bar/../baz",
+ "/foo/././",
+ "/../foo",
+ ".././././",
+ ".././././../../../boo",
+ "./..",
+ "/",
+ "/../../",
+ "/foo/../",
+ "/./././",
+ "/./././.",
+ "/.../././.",
+ "/./././.foo",
+ "/./././.foo.",
+ "/./././foo.",
+ "/foo/bar/..",
+ "/foo/bar/../baz/",
+ "////////////////",
+ "/////////./././././.",
+ "/./.././../.boo/../baz",
+ "/a/component/path",
+ "/a/component/path/",
+ "/a/component/path/..",
+ "/a/component/../path/",
+ "///a/./././///component/../////path/",
+ NULL
+ };
+ const char *dst[] = {
+ "/",
+ "/",
+ "/foo/baz",
+ "/foo",
+ "/foo",
+ "/",
+ "/boo",
+ "/",
+ "/",
+ "/",
+ "/",
+ "/",
+ "/",
+ "/...",
+ "/.foo",
+ "/.foo.",
+ "/foo.",
+ "/foo",
+ "/foo/baz",
+ "/",
+ "/",
+ "/baz",
+ "/a/component/path",
+ "/a/component/path",
+ "/a/component",
+ "/a/path",
+ "/a/path",
+ NULL
+ };
+ unsigned int i;
+
+ for (i = 0; src[i] != NULL; i++) {
+ char *d = canonicalize_absolute_path(talloc_tos(), src[i]);
+ if (d == NULL) {
+ perror("talloc fail\n");
+ return false;
+ }
+ if (strcmp(d, dst[i]) != 0) {
+ d_fprintf(stderr,
+ "canonicalize mismatch %s -> %s != %s",
+ src[i], d, dst[i]);
+ return false;
+ }
+ talloc_free(d);
+ }
+ return true;
+}
+struct session_setup_nt1_truncated_state {
+ uint16_t vwv[13];
+ uint8_t bytes[20];
+};
+
+static void smb1_session_setup_nt1_truncated_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb1_session_setup_nt1_truncated_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn *conn)
+{
+ uint16_t *vwv = NULL;
+ uint8_t *bytes = NULL;
+ const char *pass = "12345678";
+ const char *uname = "z";
+ struct session_setup_nt1_truncated_state *state = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct session_setup_nt1_truncated_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ vwv = &state->vwv[0];
+ bytes = &state->bytes[0];
+
+ SCVAL(vwv+0, 0, 0xff);
+ SCVAL(vwv+0, 1, 0);
+ SSVAL(vwv+1, 0, 0);
+ SSVAL(vwv+2, 0, 8192);
+ SSVAL(vwv+3, 0, 2);
+ SSVAL(vwv+4, 0, 1);
+ SIVAL(vwv+5, 0, 0);
+ SSVAL(vwv+7, 0, strlen(pass)); /* OEMPasswordLen */
+ SSVAL(vwv+8, 0, 0); /* UnicodePasswordLen */
+ SSVAL(vwv+9, 0, 0); /* reserved */
+ SSVAL(vwv+10, 0, 0); /* reserved */
+ SIVAL(vwv+11, 0, CAP_STATUS32);
+
+ memcpy(bytes, pass, strlen(pass));
+ bytes += strlen(pass);
+ memcpy(bytes, uname, strlen(uname)+1);
+
+ subreq = smb1cli_req_send(state, ev, conn,
+ SMBsesssetupX,
+ 0, /* additional_flags */
+ 0, /* clear_flags */
+ 0, /* additional_flags2 */
+ 0, /* clear_flags2 */
+ 10000, /* timeout_msec */
+ getpid(),
+ NULL, /* tcon */
+ NULL, /* session */
+ 13, /* wct */
+ state->vwv,
+ strlen(pass), /* Truncate length at password. */
+ state->bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ smb1_session_setup_nt1_truncated_done,
+ req);
+ return req;
+}
+
+static void smb1_session_setup_nt1_truncated_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct session_setup_nt1_truncated_state *state =
+ tevent_req_data(req,
+ struct session_setup_nt1_truncated_state);
+ NTSTATUS status;
+ struct smb1cli_req_expected_response expected[] = {
+ {
+ .status = NT_STATUS_OK,
+ .wct = 3,
+ },
+ };
+
+ status = smb1cli_req_recv(subreq, state,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* pvwv_offset */
+ NULL,
+ NULL,
+ NULL, /* pbytes_offset */
+ NULL,
+ expected, ARRAY_SIZE(expected));
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS smb1_session_setup_nt1_truncated_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_smb1_truncated_sesssetup(int dummy)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ struct smbXcli_conn *conn;
+ struct sockaddr_storage ss;
+ NTSTATUS status;
+ int fd;
+ bool ok;
+
+ printf("Starting send truncated SMB1 sesssetup.\n");
+
+ ok = resolve_name(host, &ss, 0x20, true);
+ if (!ok) {
+ d_fprintf(stderr, "Could not resolve name %s\n", host);
+ return false;
+ }
+
+ status = open_socket_out(&ss, 445, 10000, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "open_socket_out failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ conn = smbXcli_conn_create(talloc_tos(), fd, host, SMB_SIGNING_OFF, 0,
+ NULL, 0, NULL);
+ if (conn == NULL) {
+ d_fprintf(stderr, "smbXcli_conn_create failed\n");
+ return false;
+ }
+
+ status = smbXcli_negprot(conn,
+ 0,
+ PROTOCOL_NT1,
+ PROTOCOL_NT1,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "smbXcli_negprot failed!\n");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ return false;
+ }
+
+ req = smb1_session_setup_nt1_truncated_send(ev, ev, conn);
+ if (req == NULL) {
+ d_fprintf(stderr, "smb1_session_setup_nt1_truncated_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr, "tevent_req_poll failed with status %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb1_session_setup_nt1_truncated_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "smb1_session_setup_nt1_truncated_recv returned "
+ "%s, expected NT_STATUS_OK\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ TALLOC_FREE(conn);
+ return true;
+}
+
+struct smb1_negotiate_exit_state {
+ int dummy;
+};
+
+static void smb1_negotiate_exit_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb1_negotiate_exit_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn *conn)
+{
+ struct smb1_negotiate_exit_state *state = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_negotiate_exit_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = smb1cli_req_send(state, ev, conn,
+ SMBexit,
+ 0, /* additional_flags */
+ 0, /* clear_flags */
+ 0, /* additional_flags2 */
+ 0, /* clear_flags2 */
+ 10000, /* timeout_msec */
+ getpid(),
+ NULL, /* tcon */
+ NULL, /* session */
+ 0, /* wct */
+ NULL,
+ 0,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ smb1_negotiate_exit_done,
+ req);
+ return req;
+}
+
+static void smb1_negotiate_exit_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb1_negotiate_exit_state *state =
+ tevent_req_data(req,
+ struct smb1_negotiate_exit_state);
+ NTSTATUS status;
+ struct smb1cli_req_expected_response expected[] = {
+ {
+ .status = NT_STATUS_OK,
+ .wct = 0,
+ },
+ };
+
+ status = smb1cli_req_recv(subreq, state,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* pvwv_offset */
+ NULL,
+ NULL,
+ NULL, /* pbytes_offset */
+ NULL,
+ expected, ARRAY_SIZE(expected));
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS smb1_negotiate_exit_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool do_smb1_exit(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn *conn)
+{
+ struct tevent_req *req;
+ bool ok;
+ NTSTATUS status;
+ NTSTATUS expected_status = NT_STATUS_DOS(ERRSRV, ERRinvnid);;
+
+ req = smb1_negotiate_exit_send(ev, ev, conn);
+ if (req == NULL) {
+ d_fprintf(stderr, "smb1_negotiate_exit_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr, "tevent_req_poll failed with status %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb1_negotiate_exit_recv(req);
+ if (!NT_STATUS_EQUAL(status, expected_status)) {
+ d_fprintf(stderr, "smb1_negotiate_exit_recv returned "
+ "%s, expected ERRSRV, ERRinvnid\n",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static bool run_smb1_negotiate_exit(int dummy)
+{
+ struct tevent_context *ev;
+ struct smbXcli_conn *conn;
+ struct sockaddr_storage ss;
+ NTSTATUS status;
+ int fd;
+ bool ok;
+
+ printf("Starting send SMB1 negotiate+exit.\n");
+
+ ok = resolve_name(host, &ss, 0x20, true);
+ if (!ok) {
+ d_fprintf(stderr, "Could not resolve name %s\n", host);
+ return false;
+ }
+
+ status = open_socket_out(&ss, 445, 10000, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "open_socket_out failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ conn = smbXcli_conn_create(talloc_tos(), fd, host, SMB_SIGNING_OFF, 0,
+ NULL, 0, NULL);
+ if (conn == NULL) {
+ d_fprintf(stderr, "smbXcli_conn_create failed\n");
+ return false;
+ }
+
+ status = smbXcli_negprot(conn,
+ 0,
+ PROTOCOL_NT1,
+ PROTOCOL_NT1,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "smbXcli_negprot failed!\n");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ return false;
+ }
+
+ /*
+ * Call do_smb1_exit twice to catch a server crash, the
+ * server sends the first return code then crashes.
+ */
+ ok = do_smb1_exit(ev, ev, conn);
+ if (!ok) {
+ d_fprintf(stderr, "do_smb1_exit (1) failed\n");
+ return false;
+ }
+ ok = do_smb1_exit(ev, ev, conn);
+ if (!ok) {
+ d_fprintf(stderr, "do_smb1_exit (2) failed\n");
+ return false;
+ }
+
+ TALLOC_FREE(conn);
+ return true;
+}
+
+static bool run_smb1_negotiate_tcon(int dummy)
+{
+ struct cli_state *cli = NULL;
+ uint16_t cnum = 0;
+ uint16_t max_xmit = 0;
+ NTSTATUS status;
+
+ printf("Starting send SMB1 negotiate+tcon.\n");
+ cli = open_nbt_connection();
+ if (cli == NULL) {
+ d_fprintf(stderr, "open_nbt_connection failed!\n");
+ return false;
+ }
+ smbXcli_conn_set_sockopt(cli->conn, sockops);
+
+ status = smbXcli_negprot(cli->conn,
+ 0,
+ PROTOCOL_NT1,
+ PROTOCOL_NT1,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "smbXcli_negprot failed %s!\n",
+ nt_errstr(status));
+ return false;
+ }
+ status = cli_raw_tcon(cli,
+ share,
+ "",
+ "?????",
+ &max_xmit,
+ &cnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ d_fprintf(stderr, "cli_raw_tcon failed - got %s "
+ "(should get NT_STATUS_ACCESS_DENIED)!\n",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static bool run_ign_bad_negprot(int dummy)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ struct smbXcli_conn *conn;
+ struct sockaddr_storage ss;
+ NTSTATUS status;
+ int fd;
+ bool ok;
+
+ printf("starting ignore bad negprot\n");
+
+ ok = resolve_name(host, &ss, 0x20, true);
+ if (!ok) {
+ d_fprintf(stderr, "Could not resolve name %s\n", host);
+ return false;
+ }
+
+ status = open_socket_out(&ss, 445, 10000, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "open_socket_out failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ conn = smbXcli_conn_create(talloc_tos(), fd, host, SMB_SIGNING_OFF, 0,
+ NULL, 0, NULL);
+ if (conn == NULL) {
+ d_fprintf(stderr, "smbXcli_conn_create failed\n");
+ return false;
+ }
+
+ status = smbXcli_negprot(conn,
+ 0,
+ PROTOCOL_CORE,
+ PROTOCOL_CORE,
+ NULL,
+ NULL,
+ NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "smbXcli_negprot succeeded!\n");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ return false;
+ }
+
+ req = smb1cli_session_setup_nt1_send(
+ ev, ev, conn, 0, getpid(), NULL, 65503, 2, 1, 0, "", "",
+ data_blob_null, data_blob_null, 0x40,
+ "Windows 2000 2195", "Windows 2000 5.0");
+ if (req == NULL) {
+ d_fprintf(stderr, "smb1cli_session_setup_nt1_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr, "tevent_req_poll failed\n");
+ return false;
+ }
+
+ status = smb1cli_session_setup_nt1_recv(req, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
+ d_fprintf(stderr, "smb1cli_session_setup_nt1_recv returned "
+ "%s, expected NT_STATUS_CONNECTION_RESET\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ TALLOC_FREE(conn);
+
+ printf("starting ignore bad negprot\n");
+
+ return true;
+}
+
+
+static double create_procs(bool (*fn)(int), bool *result)
+{
+ int i, status;
+ volatile pid_t *child_status;
+ volatile bool *child_status_out;
+ int synccount;
+ int tries = 8;
+ struct timeval start;
+
+ synccount = 0;
+
+ child_status = (volatile pid_t *)anonymous_shared_allocate(sizeof(pid_t)*torture_nprocs);
+ if (!child_status) {
+ printf("Failed to setup shared memory\n");
+ return -1;
+ }
+
+ child_status_out = (volatile bool *)anonymous_shared_allocate(sizeof(bool)*torture_nprocs);
+ if (!child_status_out) {
+ printf("Failed to setup result status shared memory\n");
+ return -1;
+ }
+
+ for (i = 0; i < torture_nprocs; i++) {
+ child_status[i] = 0;
+ child_status_out[i] = True;
+ }
+
+ start = timeval_current();
+
+ for (i=0;i<torture_nprocs;i++) {
+ procnum = i;
+ if (fork() == 0) {
+ pid_t mypid = getpid();
+ sys_srandom(((int)mypid) ^ ((int)time(NULL)));
+
+ slprintf(myname,sizeof(myname),"CLIENT%d", i);
+
+ while (1) {
+ if (torture_open_connection(&current_cli, i)) break;
+ if (tries-- == 0) {
+ printf("pid %d failed to start\n", (int)getpid());
+ _exit(1);
+ }
+ smb_msleep(10);
+ }
+
+ child_status[i] = getpid();
+
+ while (child_status[i] && timeval_elapsed(&start) < 5) smb_msleep(2);
+
+ child_status_out[i] = fn(i);
+ _exit(0);
+ }
+ }
+
+ do {
+ synccount = 0;
+ for (i=0;i<torture_nprocs;i++) {
+ if (child_status[i]) synccount++;
+ }
+ if (synccount == torture_nprocs) break;
+ smb_msleep(10);
+ } while (timeval_elapsed(&start) < 30);
+
+ if (synccount != torture_nprocs) {
+ printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
+ *result = False;
+ return timeval_elapsed(&start);
+ }
+
+ /* start the client load */
+ start = timeval_current();
+
+ for (i=0;i<torture_nprocs;i++) {
+ child_status[i] = 0;
+ }
+
+ printf("%d clients started\n", torture_nprocs);
+
+ for (i=0;i<torture_nprocs;i++) {
+ while (waitpid(0, &status, 0) == -1 && errno == EINTR) /* noop */ ;
+ }
+
+ printf("\n");
+
+ for (i=0;i<torture_nprocs;i++) {
+ if (!child_status_out[i]) {
+ *result = False;
+ }
+ }
+ return timeval_elapsed(&start);
+}
+
+#define FLAG_MULTIPROC 1
+
+static struct {
+ const char *name;
+ bool (*fn)(int);
+ unsigned flags;
+} torture_ops[] = {
+ {
+ .name = "FDPASS",
+ .fn = run_fdpasstest,
+ },
+ {
+ .name = "LOCK1",
+ .fn = run_locktest1,
+ },
+ {
+ .name = "LOCK2",
+ .fn = run_locktest2,
+ },
+ {
+ .name = "LOCK3",
+ .fn = run_locktest3,
+ },
+ {
+ .name = "LOCK4",
+ .fn = run_locktest4,
+ },
+ {
+ .name = "LOCK5",
+ .fn = run_locktest5,
+ },
+ {
+ .name = "LOCK6",
+ .fn = run_locktest6,
+ },
+ {
+ .name = "LOCK7",
+ .fn = run_locktest7,
+ },
+ {
+ .name = "LOCK8",
+ .fn = run_locktest8,
+ },
+ {
+ .name = "LOCK9A",
+ .fn = run_locktest9a,
+ },
+ {
+ .name = "LOCK9B",
+ .fn = run_locktest9b,
+ },
+ {
+ .name = "LOCK10",
+ .fn = run_locktest10,
+ },
+ {
+ .name = "LOCK11",
+ .fn = run_locktest11,
+ },
+ {
+ .name = "LOCK12",
+ .fn = run_locktest12,
+ },
+ {
+ .name = "LOCK13",
+ .fn = run_locktest13,
+ },
+ {
+ .name = "UNLINK",
+ .fn = run_unlinktest,
+ },
+ {
+ .name = "BROWSE",
+ .fn = run_browsetest,
+ },
+ {
+ .name = "ATTR",
+ .fn = run_attrtest,
+ },
+ {
+ .name = "TRANS2",
+ .fn = run_trans2test,
+ },
+ {
+ .name = "MAXFID",
+ .fn = run_maxfidtest,
+ .flags = FLAG_MULTIPROC,
+ },
+ {
+ .name = "TORTURE",
+ .fn = run_torture,
+ .flags = FLAG_MULTIPROC,
+ },
+ {
+ .name = "RANDOMIPC",
+ .fn = run_randomipc,
+ },
+ {
+ .name = "NEGNOWAIT",
+ .fn = run_negprot_nowait,
+ },
+ {
+ .name = "NBENCH",
+ .fn = run_nbench,
+ },
+ {
+ .name = "NBENCH2",
+ .fn = run_nbench2,
+ },
+ {
+ .name = "OPLOCK1",
+ .fn = run_oplock1,
+ },
+ {
+ .name = "OPLOCK2",
+ .fn = run_oplock2,
+ },
+ {
+ .name = "OPLOCK4",
+ .fn = run_oplock4,
+ },
+#ifdef HAVE_KERNEL_OPLOCKS_LINUX
+ {
+ .name = "OPLOCK5",
+ .fn = run_oplock5,
+ },
+#endif
+ {
+ .name = "DIR",
+ .fn = run_dirtest,
+ },
+ {
+ .name = "DIR1",
+ .fn = run_dirtest1,
+ },
+ {
+ .name = "DIR-CREATETIME",
+ .fn = run_dir_createtime,
+ },
+ {
+ .name = "DENY1",
+ .fn = torture_denytest1,
+ },
+ {
+ .name = "DENY2",
+ .fn = torture_denytest2,
+ },
+ {
+ .name = "TCON",
+ .fn = run_tcon_test,
+ },
+ {
+ .name = "TCONDEV",
+ .fn = run_tcon_devtype_test,
+ },
+ {
+ .name = "RW1",
+ .fn = run_readwritetest,
+ },
+ {
+ .name = "RW2",
+ .fn = run_readwritemulti,
+ .flags = FLAG_MULTIPROC
+ },
+ {
+ .name = "RW3",
+ .fn = run_readwritelarge,
+ },
+ {
+ .name = "RW-SIGNING",
+ .fn = run_readwritelarge_signtest,
+ },
+ {
+ .name = "OPEN",
+ .fn = run_opentest,
+ },
+ {
+ .name = "POSIX",
+ .fn = run_simple_posix_open_test,
+ },
+ {
+ .name = "POSIX-APPEND",
+ .fn = run_posix_append,
+ },
+ {
+ .name = "POSIX-SYMLINK-ACL",
+ .fn = run_acl_symlink_test,
+ },
+ {
+ .name = "POSIX-SYMLINK-EA",
+ .fn = run_ea_symlink_test,
+ },
+ {
+ .name = "POSIX-STREAM-DELETE",
+ .fn = run_posix_stream_delete,
+ },
+ {
+ .name = "POSIX-OFD-LOCK",
+ .fn = run_posix_ofd_lock_test,
+ },
+ {
+ .name = "POSIX-BLOCKING-LOCK",
+ .fn = run_posix_blocking_lock,
+ },
+ {
+ .name = "POSIX-MKDIR",
+ .fn = run_posix_mkdir_test,
+ },
+ {
+ .name = "POSIX-ACL-OPLOCK",
+ .fn = run_posix_acl_oplock_test,
+ },
+ {
+ .name = "POSIX-ACL-SHAREROOT",
+ .fn = run_posix_acl_shareroot_test,
+ },
+ {
+ .name = "POSIX-LS-WILDCARD",
+ .fn = run_posix_ls_wildcard_test,
+ },
+ {
+ .name = "POSIX-LS-SINGLE",
+ .fn = run_posix_ls_single_test,
+ },
+ {
+ .name = "POSIX-READLINK",
+ .fn = run_posix_readlink_test,
+ },
+ {
+ .name = "POSIX-STAT",
+ .fn = run_posix_stat_test,
+ },
+ {
+ .name = "POSIX-SYMLINK-PARENT",
+ .fn = run_posix_symlink_parent_test,
+ },
+ {
+ .name = "POSIX-SYMLINK-CHMOD",
+ .fn = run_posix_symlink_chmod_test,
+ },
+ {
+ .name = "POSIX-SYMLINK-RENAME",
+ .fn = run_posix_symlink_rename_test,
+ },
+ {
+ .name = "POSIX-DIR-DEFAULT-ACL",
+ .fn = run_posix_dir_default_acl_test,
+ },
+ {
+ .name = "POSIX-SYMLINK-GETPATHINFO",
+ .fn = run_posix_symlink_getpathinfo_test,
+ },
+ {
+ .name = "POSIX-SYMLINK-SETPATHINFO",
+ .fn = run_posix_symlink_setpathinfo_test,
+ },
+ {
+ .name = "WINDOWS-BAD-SYMLINK",
+ .fn = run_symlink_open_test,
+ },
+ {
+ .name = "SMB1-WILD-MANGLE-UNLINK",
+ .fn = run_smb1_wild_mangle_unlink_test,
+ },
+ {
+ .name = "SMB1-WILD-MANGLE-RENAME",
+ .fn = run_smb1_wild_mangle_rename_test,
+ },
+ {
+ .name = "CASE-INSENSITIVE-CREATE",
+ .fn = run_case_insensitive_create,
+ },
+ {
+ .name = "ASYNC-ECHO",
+ .fn = run_async_echo,
+ },
+ {
+ .name = "UID-REGRESSION-TEST",
+ .fn = run_uid_regression_test,
+ },
+ {
+ .name = "SHORTNAME-TEST",
+ .fn = run_shortname_test,
+ },
+ {
+ .name = "ADDRCHANGE",
+ .fn = run_addrchange,
+ },
+#if 1
+ {
+ .name = "OPENATTR",
+ .fn = run_openattrtest,
+ },
+#endif
+ {
+ .name = "XCOPY",
+ .fn = run_xcopy,
+ },
+ {
+ .name = "RENAME",
+ .fn = run_rename,
+ },
+ {
+ .name = "RENAME-ACCESS",
+ .fn = run_rename_access,
+ },
+ {
+ .name = "OWNER-RIGHTS",
+ .fn = run_owner_rights,
+ },
+ {
+ .name = "DELETE",
+ .fn = run_deletetest,
+ },
+ {
+ .name = "DELETE-STREAM",
+ .fn = run_delete_stream,
+ },
+ {
+ .name = "DELETE-PRINT",
+ .fn = run_delete_print_test,
+ },
+ {
+ .name = "DELETE-LN",
+ .fn = run_deletetest_ln,
+ },
+ {
+ .name = "PROPERTIES",
+ .fn = run_properties,
+ },
+ {
+ .name = "MANGLE",
+ .fn = torture_mangle,
+ },
+ {
+ .name = "MANGLE1",
+ .fn = run_mangle1,
+ },
+ {
+ .name = "MANGLE-ILLEGAL",
+ .fn = run_mangle_illegal,
+ },
+ {
+ .name = "W2K",
+ .fn = run_w2ktest,
+ },
+ {
+ .name = "TRANS2SCAN",
+ .fn = torture_trans2_scan,
+ },
+ {
+ .name = "NTTRANSSCAN",
+ .fn = torture_nttrans_scan,
+ },
+ {
+ .name = "UTABLE",
+ .fn = torture_utable,
+ },
+ {
+ .name = "CASETABLE",
+ .fn = torture_casetable,
+ },
+ {
+ .name = "ERRMAPEXTRACT",
+ .fn = run_error_map_extract,
+ },
+ {
+ .name = "PIPE_NUMBER",
+ .fn = run_pipe_number,
+ },
+ {
+ .name = "TCON2",
+ .fn = run_tcon2_test,
+ },
+ {
+ .name = "IOCTL",
+ .fn = torture_ioctl_test,
+ },
+ {
+ .name = "CHKPATH",
+ .fn = torture_chkpath_test,
+ },
+ {
+ .name = "FDSESS",
+ .fn = run_fdsesstest,
+ },
+ {
+ .name = "EATEST",
+ .fn = run_eatest,
+ },
+ {
+ .name = "SESSSETUP_BENCH",
+ .fn = run_sesssetup_bench,
+ },
+ {
+ .name = "CHAIN1",
+ .fn = run_chain1,
+ },
+ {
+ .name = "CHAIN2",
+ .fn = run_chain2,
+ },
+ {
+ .name = "CHAIN3",
+ .fn = run_chain3,
+ },
+ {
+ .name = "WINDOWS-WRITE",
+ .fn = run_windows_write,
+ },
+ {
+ .name = "LARGE_READX",
+ .fn = run_large_readx,
+ },
+ {
+ .name = "MSDFS-ATTRIBUTE",
+ .fn = run_msdfs_attribute,
+ },
+ {
+ .name = "NTTRANS-CREATE",
+ .fn = run_nttrans_create,
+ },
+ {
+ .name = "NTTRANS-FSCTL",
+ .fn = run_nttrans_fsctl,
+ },
+ {
+ .name = "CLI_ECHO",
+ .fn = run_cli_echo,
+ },
+ {
+ .name = "CLI_SPLICE",
+ .fn = run_cli_splice,
+ },
+ {
+ .name = "TLDAP",
+ .fn = run_tldap,
+ },
+ {
+ .name = "STREAMERROR",
+ .fn = run_streamerror,
+ },
+ {
+ .name = "NOTIFY-BENCH",
+ .fn = run_notify_bench,
+ },
+ {
+ .name = "NOTIFY-BENCH2",
+ .fn = run_notify_bench2,
+ },
+ {
+ .name = "NOTIFY-BENCH3",
+ .fn = run_notify_bench3,
+ },
+ {
+ .name = "BAD-NBT-SESSION",
+ .fn = run_bad_nbt_session,
+ },
+ {
+ .name = "IGN-BAD-NEGPROT",
+ .fn = run_ign_bad_negprot,
+ },
+ {
+ .name = "SMB-ANY-CONNECT",
+ .fn = run_smb_any_connect,
+ },
+ {
+ .name = "NOTIFY-ONLINE",
+ .fn = run_notify_online,
+ },
+ {
+ .name = "SMB2-BASIC",
+ .fn = run_smb2_basic,
+ },
+ {
+ .name = "SMB2-NEGPROT",
+ .fn = run_smb2_negprot,
+ },
+ {
+ .name = "SMB2-ANONYMOUS",
+ .fn = run_smb2_anonymous,
+ },
+ {
+ .name = "SMB2-SESSION-RECONNECT",
+ .fn = run_smb2_session_reconnect,
+ },
+ {
+ .name = "SMB2-TCON-DEPENDENCE",
+ .fn = run_smb2_tcon_dependence,
+ },
+ {
+ .name = "SMB2-MULTI-CHANNEL",
+ .fn = run_smb2_multi_channel,
+ },
+ {
+ .name = "SMB2-SESSION-REAUTH",
+ .fn = run_smb2_session_reauth,
+ },
+ {
+ .name = "SMB2-FTRUNCATE",
+ .fn = run_smb2_ftruncate,
+ },
+ {
+ .name = "SMB2-DIR-FSYNC",
+ .fn = run_smb2_dir_fsync,
+ },
+ {
+ .name = "SMB2-PATH-SLASH",
+ .fn = run_smb2_path_slash,
+ },
+ {
+ .name = "SMB1-SYSTEM-SECURITY",
+ .fn = run_smb1_system_security,
+ },
+ {
+ .name = "SMB2-SACL",
+ .fn = run_smb2_sacl,
+ },
+ {
+ .name = "SMB2-QUOTA1",
+ .fn = run_smb2_quota1,
+ },
+ {
+ .name = "SMB2-INVALID-PIPENAME",
+ .fn = run_smb2_invalid_pipename,
+ },
+ {
+ .name = "SMB2-STREAM-ACL",
+ .fn = run_smb2_stream_acl,
+ },
+ {
+ .name = "SMB2-LIST-DIR-ASYNC",
+ .fn = run_list_dir_async_test,
+ },
+ {
+ .name = "SMB2-DEL-ON-CLOSE-NONEMPTY",
+ .fn = run_delete_on_close_non_empty,
+ },
+ {
+ .name = "SMB2-DEL-ON-CLOSE-NONWRITE-DELETE-YES",
+ .fn = run_delete_on_close_nonwrite_delete_yes_test,
+ },
+ {
+ .name = "SMB2-DEL-ON-CLOSE-NONWRITE-DELETE-NO",
+ .fn = run_delete_on_close_nonwrite_delete_no_test,
+ },
+ {
+ .name = "SMB2-DFS-PATHS",
+ .fn = run_smb2_dfs_paths,
+ },
+ {
+ .name = "SMB2-NON-DFS-SHARE",
+ .fn = run_smb2_non_dfs_share,
+ },
+ {
+ .name = "SMB2-DFS-SHARE-NON-DFS-PATH",
+ .fn = run_smb2_dfs_share_non_dfs_path,
+ },
+ {
+ .name = "SMB2-DFS-FILENAME-LEADING-BACKSLASH",
+ .fn = run_smb2_dfs_filename_leading_backslash,
+ },
+ {
+ .name = "SMB2-PIPE-READ-ASYNC-DISCONNECT",
+ .fn = run_smb2_pipe_read_async_disconnect,
+ },
+ {
+ .name = "SMB1-TRUNCATED-SESSSETUP",
+ .fn = run_smb1_truncated_sesssetup,
+ },
+ {
+ .name = "SMB1-NEGOTIATE-EXIT",
+ .fn = run_smb1_negotiate_exit,
+ },
+ {
+ .name = "SMB1-NEGOTIATE-TCON",
+ .fn = run_smb1_negotiate_tcon,
+ },
+ {
+ .name = "SMB1-DFS-PATHS",
+ .fn = run_smb1_dfs_paths,
+ },
+ {
+ .name = "SMB1-DFS-SEARCH-PATHS",
+ .fn = run_smb1_dfs_search_paths,
+ },
+ {
+ .name = "SMB1-DFS-OPERATIONS",
+ .fn = run_smb1_dfs_operations,
+ },
+ {
+ .name = "SMB1-DFS-BADPATH",
+ .fn = run_smb1_dfs_check_badpath,
+ },
+ {
+ .name = "CLEANUP1",
+ .fn = run_cleanup1,
+ },
+ {
+ .name = "CLEANUP2",
+ .fn = run_cleanup2,
+ },
+ {
+ .name = "CLEANUP4",
+ .fn = run_cleanup4,
+ },
+ {
+ .name = "OPLOCK-CANCEL",
+ .fn = run_oplock_cancel,
+ },
+ {
+ .name = "PIDHIGH",
+ .fn = run_pidhigh,
+ },
+ {
+ .name = "LOCAL-SUBSTITUTE",
+ .fn = run_local_substitute,
+ },
+ {
+ .name = "LOCAL-GENCACHE",
+ .fn = run_local_gencache,
+ },
+ {
+ .name = "LOCAL-DBWRAP-WATCH1",
+ .fn = run_dbwrap_watch1,
+ },
+ {
+ .name = "LOCAL-DBWRAP-WATCH2",
+ .fn = run_dbwrap_watch2,
+ },
+ {
+ .name = "LOCAL-DBWRAP-WATCH3",
+ .fn = run_dbwrap_watch3,
+ },
+ {
+ .name = "LOCAL-DBWRAP-WATCH4",
+ .fn = run_dbwrap_watch4,
+ },
+ {
+ .name = "LOCAL-DBWRAP-DO-LOCKED1",
+ .fn = run_dbwrap_do_locked1,
+ },
+ {
+ .name = "LOCAL-MESSAGING-READ1",
+ .fn = run_messaging_read1,
+ },
+ {
+ .name = "LOCAL-MESSAGING-READ2",
+ .fn = run_messaging_read2,
+ },
+ {
+ .name = "LOCAL-MESSAGING-READ3",
+ .fn = run_messaging_read3,
+ },
+ {
+ .name = "LOCAL-MESSAGING-READ4",
+ .fn = run_messaging_read4,
+ },
+ {
+ .name = "LOCAL-MESSAGING-FDPASS1",
+ .fn = run_messaging_fdpass1,
+ },
+ {
+ .name = "LOCAL-MESSAGING-FDPASS2",
+ .fn = run_messaging_fdpass2,
+ },
+ {
+ .name = "LOCAL-MESSAGING-FDPASS2a",
+ .fn = run_messaging_fdpass2a,
+ },
+ {
+ .name = "LOCAL-MESSAGING-FDPASS2b",
+ .fn = run_messaging_fdpass2b,
+ },
+ {
+ .name = "LOCAL-MESSAGING-SEND-ALL",
+ .fn = run_messaging_send_all,
+ },
+ {
+ .name = "LOCAL-BASE64",
+ .fn = run_local_base64,
+ },
+ {
+ .name = "LOCAL-RBTREE",
+ .fn = run_local_rbtree,
+ },
+ {
+ .name = "LOCAL-MEMCACHE",
+ .fn = run_local_memcache,
+ },
+ {
+ .name = "LOCAL-STREAM-NAME",
+ .fn = run_local_stream_name,
+ },
+ {
+ .name = "LOCAL-STR-MATCH-MSWILD",
+ .fn = run_str_match_mswild,
+ },
+ {
+ .name = "LOCAL-STR-MATCH-REGEX-SUB1",
+ .fn = run_str_match_regex_sub1,
+ },
+ {
+ .name = "WBCLIENT-MULTI-PING",
+ .fn = run_wbclient_multi_ping,
+ },
+ {
+ .name = "LOCAL-string_to_sid",
+ .fn = run_local_string_to_sid,
+ },
+ {
+ .name = "LOCAL-sid_to_string",
+ .fn = run_local_sid_to_string,
+ },
+ {
+ .name = "LOCAL-binary_to_sid",
+ .fn = run_local_binary_to_sid,
+ },
+ {
+ .name = "LOCAL-DBTRANS",
+ .fn = run_local_dbtrans,
+ },
+ {
+ .name = "LOCAL-TEVENT-POLL",
+ .fn = run_local_tevent_poll,
+ },
+ {
+ .name = "LOCAL-CONVERT-STRING",
+ .fn = run_local_convert_string,
+ },
+ {
+ .name = "LOCAL-CONV-AUTH-INFO",
+ .fn = run_local_conv_auth_info,
+ },
+ {
+ .name = "LOCAL-hex_encode_buf",
+ .fn = run_local_hex_encode_buf,
+ },
+ {
+ .name = "LOCAL-IDMAP-TDB-COMMON",
+ .fn = run_idmap_tdb_common_test,
+ },
+ {
+ .name = "LOCAL-remove_duplicate_addrs2",
+ .fn = run_local_remove_duplicate_addrs2,
+ },
+ {
+ .name = "local-tdb-opener",
+ .fn = run_local_tdb_opener,
+ },
+ {
+ .name = "local-tdb-writer",
+ .fn = run_local_tdb_writer,
+ },
+ {
+ .name = "LOCAL-DBWRAP-CTDB1",
+ .fn = run_local_dbwrap_ctdb1,
+ },
+ {
+ .name = "LOCAL-BENCH-PTHREADPOOL",
+ .fn = run_bench_pthreadpool,
+ },
+ {
+ .name = "LOCAL-PTHREADPOOL-TEVENT",
+ .fn = run_pthreadpool_tevent,
+ },
+ {
+ .name = "LOCAL-G-LOCK1",
+ .fn = run_g_lock1,
+ },
+ {
+ .name = "LOCAL-G-LOCK2",
+ .fn = run_g_lock2,
+ },
+ {
+ .name = "LOCAL-G-LOCK3",
+ .fn = run_g_lock3,
+ },
+ {
+ .name = "LOCAL-G-LOCK4",
+ .fn = run_g_lock4,
+ },
+ {
+ .name = "LOCAL-G-LOCK4A",
+ .fn = run_g_lock4a,
+ },
+ {
+ .name = "LOCAL-G-LOCK5",
+ .fn = run_g_lock5,
+ },
+ {
+ .name = "LOCAL-G-LOCK6",
+ .fn = run_g_lock6,
+ },
+ {
+ .name = "LOCAL-G-LOCK7",
+ .fn = run_g_lock7,
+ },
+ {
+ .name = "LOCAL-G-LOCK8",
+ .fn = run_g_lock8,
+ },
+ {
+ .name = "LOCAL-G-LOCK-PING-PONG",
+ .fn = run_g_lock_ping_pong,
+ },
+ {
+ .name = "LOCAL-CANONICALIZE-PATH",
+ .fn = run_local_canonicalize_path,
+ },
+ {
+ .name = "LOCAL-NAMEMAP-CACHE1",
+ .fn = run_local_namemap_cache1,
+ },
+ {
+ .name = "LOCAL-IDMAP-CACHE1",
+ .fn = run_local_idmap_cache1,
+ },
+ {
+ .name = "qpathinfo-bufsize",
+ .fn = run_qpathinfo_bufsize,
+ },
+ {
+ .name = "hide-new-files-timeout",
+ .fn = run_hidenewfiles,
+ },
+ {
+ .name = "hide-new-files-timeout-showdirs",
+ .fn = run_hidenewfiles_showdirs,
+ },
+#ifdef CLUSTER_SUPPORT
+ {
+ .name = "ctdbd-conn1",
+ .fn = run_ctdbd_conn1,
+ },
+#endif
+ {
+ .name = "readdir-timestamp",
+ .fn = run_readdir_timestamp,
+ },
+ {
+ .name = "rpc-scale",
+ .fn = run_rpc_scale,
+ },
+ {
+ .name = "LOCAL-TDB-VALIDATE",
+ .fn = run_tdb_validate,
+ },
+ {
+ .name = NULL,
+ },
+};
+
+/****************************************************************************
+run a specified test or "ALL"
+****************************************************************************/
+static bool run_test(const char *name)
+{
+ bool ret = True;
+ bool result = True;
+ bool found = False;
+ int i;
+ double t;
+ if (strequal(name,"ALL")) {
+ for (i=0;torture_ops[i].name;i++) {
+ run_test(torture_ops[i].name);
+ }
+ found = True;
+ }
+
+ for (i=0;torture_ops[i].name;i++) {
+ fstr_sprintf(randomfname, "\\XX%x",
+ (unsigned)random());
+
+ if (strequal(name, torture_ops[i].name)) {
+ found = True;
+ printf("Running %s\n", name);
+ if (torture_ops[i].flags & FLAG_MULTIPROC) {
+ t = create_procs(torture_ops[i].fn, &result);
+ if (!result) {
+ ret = False;
+ printf("TEST %s FAILED!\n", name);
+ }
+ } else {
+ struct timeval start;
+ start = timeval_current();
+ if (!torture_ops[i].fn(0)) {
+ ret = False;
+ printf("TEST %s FAILED!\n", name);
+ }
+ t = timeval_elapsed(&start);
+ }
+ printf("%s took %g secs\n\n", name, t);
+ }
+ }
+
+ if (!found) {
+ printf("Did not find a test named %s\n", name);
+ ret = False;
+ }
+
+ return ret;
+}
+
+
+static void usage(void)
+{
+ int i;
+
+ printf("WARNING samba4 test suite is much more complete nowadays.\n");
+ printf("Please use samba4 torture.\n\n");
+
+ printf("Usage: smbtorture //server/share <options> TEST1 TEST2 ...\n");
+
+ printf("\t-d debuglevel\n");
+ printf("\t-U user%%pass\n");
+ printf("\t-k use kerberos\n");
+ printf("\t-N numprocs\n");
+ printf("\t-n my_netbios_name\n");
+ printf("\t-W workgroup\n");
+ printf("\t-o num_operations\n");
+ printf("\t-O socket_options\n");
+ printf("\t-m maximum protocol\n");
+ printf("\t-L use oplocks\n");
+ printf("\t-c CLIENT.TXT specify client load file for NBENCH\n");
+ printf("\t-A showall\n");
+ printf("\t-p port\n");
+ printf("\t-s seed\n");
+ printf("\t-b unclist_filename specify multiple shares for multiple connections\n");
+ printf("\t-f filename filename to test\n");
+ printf("\t-e encrypt\n");
+ printf("\n\n");
+
+ printf("tests are:");
+ for (i=0;torture_ops[i].name;i++) {
+ printf(" %s", torture_ops[i].name);
+ }
+ printf("\n");
+
+ printf("default test is ALL\n");
+
+ exit(1);
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ int opt, i;
+ char *p;
+ int gotuser = 0;
+ int gotpass = 0;
+ bool correct = True;
+ TALLOC_CTX *frame = talloc_stackframe();
+ int seed = time(NULL);
+ struct loadparm_context *lp_ctx = NULL;
+
+#ifdef HAVE_SETBUFFER
+ setbuffer(stdout, NULL, 0);
+#endif
+
+ setup_logging("smbtorture", DEBUG_STDOUT);
+
+ smb_init_locale();
+ fault_setup();
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ fprintf(stderr,
+ "Failed to initialise the global parameter structure.\n");
+ return 1;
+ }
+
+ if (is_default_dyn_CONFIGFILE()) {
+ if(getenv("SMB_CONF_PATH")) {
+ set_dyn_CONFIGFILE(getenv("SMB_CONF_PATH"));
+ }
+ }
+ lp_load_global(get_dyn_CONFIGFILE());
+ load_interfaces();
+
+ if (argc < 2) {
+ usage();
+ }
+
+ for(p = argv[1]; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+
+ if (strncmp(argv[1], "//", 2)) {
+ usage();
+ }
+
+ fstrcpy(host, &argv[1][2]);
+ p = strchr_m(&host[2],'/');
+ if (!p) {
+ usage();
+ }
+ *p = 0;
+ fstrcpy(share, p+1);
+
+ fstrcpy(myname, get_myname(talloc_tos()));
+ if (!*myname) {
+ fprintf(stderr, "Failed to get my hostname.\n");
+ return 1;
+ }
+
+ if (*username == 0 && getenv("LOGNAME")) {
+ fstrcpy(username,getenv("LOGNAME"));
+ }
+
+ argc--;
+ argv++;
+
+ fstrcpy(workgroup, lp_workgroup());
+
+ while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ll:d:Aec:ks:b:B:f:"))
+ != EOF) {
+ switch (opt) {
+ case 'p':
+ port_to_use = atoi(optarg);
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'W':
+ fstrcpy(workgroup,optarg);
+ break;
+ case 'm':
+ lpcfg_set_cmdline(lp_ctx, "client max protocol", optarg);
+ break;
+ case 'N':
+ torture_nprocs = atoi(optarg);
+ break;
+ case 'o':
+ torture_numops = atoi(optarg);
+ break;
+ case 'd':
+ lpcfg_set_cmdline(lp_ctx, "log level", optarg);
+ break;
+ case 'O':
+ sockops = optarg;
+ break;
+ case 'L':
+ use_oplocks = True;
+ break;
+ case 'l':
+ local_path = optarg;
+ break;
+ case 'A':
+ torture_showall = True;
+ break;
+ case 'n':
+ fstrcpy(myname, optarg);
+ break;
+ case 'c':
+ client_txt = optarg;
+ break;
+ case 'e':
+ do_encrypt = true;
+ break;
+ case 'k':
+#ifdef HAVE_KRB5
+ use_kerberos = True;
+#else
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#endif
+ break;
+ case 'U':
+ gotuser = 1;
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ gotpass = 1;
+ }
+ break;
+ case 'b':
+ fstrcpy(multishare_conn_fname, optarg);
+ use_multishare_conn = True;
+ break;
+ case 'B':
+ torture_blocksize = atoi(optarg);
+ break;
+ case 'f':
+ test_filename = SMB_STRDUP(optarg);
+ break;
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ usage();
+ }
+ }
+
+ d_printf("using seed %d\n", seed);
+
+ srandom(seed);
+
+ if(use_kerberos && !gotuser) gotpass = True;
+
+ while (!gotpass) {
+ char pwd[256] = {0};
+ int rc;
+
+ rc = samba_getpass("Password:", pwd, sizeof(pwd), false, false);
+ if (rc == 0) {
+ fstrcpy(password, pwd);
+ gotpass = 1;
+ }
+ }
+
+ printf("host=%s share=%s user=%s myname=%s\n",
+ host, share, username, myname);
+
+ torture_creds = cli_session_creds_init(frame,
+ username,
+ workgroup,
+ NULL, /* realm */
+ password,
+ use_kerberos,
+ false, /* fallback_after_kerberos */
+ false, /* use_ccache */
+ false); /* password_is_nt_hash */
+ if (torture_creds == NULL) {
+ d_printf("cli_session_creds_init() failed.\n");
+ exit(1);
+ }
+
+ if (argc == optind) {
+ correct = run_test("ALL");
+ } else {
+ for (i=optind;i<argc;i++) {
+ if (!run_test(argv[i])) {
+ correct = False;
+ }
+ }
+ }
+
+ TALLOC_FREE(frame);
+
+ if (correct) {
+ return(0);
+ } else {
+ return(1);
+ }
+}
diff --git a/source3/torture/utable.c b/source3/torture/utable.c
new file mode 100644
index 0000000..059cae9
--- /dev/null
+++ b/source3/torture/utable.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - unicode table dumper
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/proto.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "lib/util/string_wrappers.h"
+
+bool torture_utable(int dummy)
+{
+ struct cli_state *cli;
+ fstring fname, alt_name;
+ uint16_t fnum;
+ smb_ucs2_t c2;
+ int c, len, fd;
+ int chars_allowed=0, alt_allowed=0;
+ uint8_t valid[0x10000];
+
+ printf("starting utable\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ memset(valid, 0, sizeof(valid));
+
+ torture_deltree(cli, "\\utable");
+ cli_mkdir(cli, "\\utable");
+
+ for (c=1; c < 0x10000; c++) {
+ size_t size = 0;
+ char *p;
+
+ SSVAL(&c2, 0, c);
+ fstrcpy(fname, "\\utable\\x");
+ p = fname+strlen(fname);
+ if (!convert_string(CH_UTF16LE, CH_UNIX,
+ &c2, 2,
+ p, sizeof(fname)-strlen(fname),&size)) {
+ d_printf("convert_string %s failed !\n", fname);
+ continue;
+ }
+ len = size;
+ p[len] = 0;
+ fstrcat(fname,"_a_long_extension");
+
+ if (!NT_STATUS_IS_OK(cli_openx(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE, &fnum))) {
+ continue;
+ }
+
+ chars_allowed++;
+
+ cli_qpathinfo_alt_name(cli, fname, alt_name);
+
+ if (strncmp(alt_name, "X_A_L", 5) != 0) {
+ alt_allowed++;
+ valid[c] = 1;
+ d_printf("fname=[%s] alt_name=[%s]\n", fname, alt_name);
+ }
+
+ cli_close(cli, fnum);
+ cli_unlink(cli, fname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (c % 100 == 0) {
+ printf("%d (%d/%d)\r", c, chars_allowed, alt_allowed);
+ }
+ }
+ printf("%d (%d/%d)\n", c, chars_allowed, alt_allowed);
+
+ cli_rmdir(cli, "\\utable");
+
+ d_printf("%d chars allowed %d alt chars allowed\n", chars_allowed, alt_allowed);
+
+ fd = open("valid.dat", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ d_printf("Failed to create valid.dat - %s", strerror(errno));
+ return False;
+ }
+ if (write(fd, valid, 0x10000) != 0x10000) {
+ d_printf("Failed to create valid.dat - %s", strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ d_printf("wrote valid.dat\n");
+
+ return True;
+}
+
+
+static char *form_name(int c)
+{
+ static fstring fname;
+ smb_ucs2_t c2;
+ char *p;
+ size_t len = 0;
+
+ fstrcpy(fname, "\\utable\\");
+ p = fname+strlen(fname);
+ SSVAL(&c2, 0, c);
+
+ if (!convert_string(CH_UTF16LE, CH_UNIX,
+ &c2, 2,
+ p, sizeof(fname)-strlen(fname), &len)) {
+ d_printf("form_name: convert string %s failed\n",
+ fname);
+ return NULL;
+ }
+ p[len] = 0;
+ return fname;
+}
+
+bool torture_casetable(int dummy)
+{
+ static struct cli_state *cli;
+ char *fname;
+ uint16_t fnum;
+ int c, i;
+#define MAX_EQUIVALENCE 8
+ smb_ucs2_t equiv[0x10000][MAX_EQUIVALENCE];
+ printf("starting casetable\n");
+
+ if (!torture_open_connection(&cli, 0)) {
+ return False;
+ }
+
+ memset(equiv, 0, sizeof(equiv));
+
+ torture_deltree(cli, "\\utable");
+ if (!NT_STATUS_IS_OK(cli_mkdir(cli, "\\utable"))) {
+ printf("Failed to create utable directory!\n");
+ return False;
+ }
+
+ for (c=1; c < 0x10000; c++) {
+ off_t size;
+
+ if (c == '.' || c == '\\') continue;
+
+ printf("%04x (%c)\n", c, isprint(c)?c:'.');
+
+ fname = form_name(c);
+ if (!NT_STATUS_IS_OK(cli_ntcreate(cli, fname, 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0, &fnum, NULL))) {
+ printf("Failed to create file with char %04x\n", c);
+ continue;
+ }
+
+ size = 0;
+
+ if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
+ cli, fnum, NULL, &size,
+ NULL, NULL, NULL, NULL, NULL))) {
+ continue;
+ }
+
+ if (size > 0) {
+ /* found a character equivalence! */
+ int c2[MAX_EQUIVALENCE];
+
+ if (size/sizeof(int) >= MAX_EQUIVALENCE) {
+ printf("too many chars match?? size=%ld c=0x%04x\n",
+ (unsigned long)size, c);
+ cli_close(cli, fnum);
+ return False;
+ }
+
+ cli_read(cli, fnum, (char *)c2, 0, size, NULL);
+ printf("%04x: ", c);
+ equiv[c][0] = c;
+ for (i=0; i<size/sizeof(int); i++) {
+ printf("%04x ", c2[i]);
+ equiv[c][i+1] = c2[i];
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+
+ cli_writeall(cli, fnum, 0, (uint8_t *)&c, size, sizeof(c),
+ NULL);
+ cli_close(cli, fnum);
+ }
+
+ torture_deltree(cli, "\\utable");
+
+ return True;
+}
diff --git a/source3/torture/vfstest.c b/source3/torture/vfstest.c
new file mode 100644
index 0000000..b25dfdc
--- /dev/null
+++ b/source3/torture/vfstest.c
@@ -0,0 +1,650 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module tester
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+ Copyright (C) Jelmer Vernooij 2002,2003
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "locking/share_mode_lock.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "lib/cmdline/cmdline.h"
+#include "vfstest.h"
+#include "../libcli/smbreadline/smbreadline.h"
+#include "auth.h"
+#include "serverid.h"
+#include "messages.h"
+#include "libcli/security/security.h"
+#include "lib/smbd_shim.h"
+#include "system/filesys.h"
+#include "lib/global_contexts.h"
+#include "lib/param/param.h"
+
+/* List to hold groups of commands */
+static struct cmd_list {
+ struct cmd_list *prev, *next;
+ struct cmd_set *cmd_set;
+} *cmd_list;
+
+/* shall we do talloc_report after each command? */
+static int memreports = 0;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(const char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+ char **matches;
+ int i, count=0;
+ struct cmd_list *commands = cmd_list;
+
+ if (start)
+ return NULL;
+
+ /* make sure we have a list of valid commands */
+ if (!commands)
+ return NULL;
+
+ matches = SMB_MALLOC_ARRAY(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+
+ matches[count++] = SMB_STRDUP(text);
+ if (!matches[0]) return NULL;
+
+ while (commands && count < MAX_COMPLETIONS-1)
+ {
+ if (!commands->cmd_set)
+ break;
+
+ for (i=0; commands->cmd_set[i].name; i++)
+ {
+ if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+ commands->cmd_set[i].fn)
+ {
+ matches[count] = SMB_STRDUP(commands->cmd_set[i].name);
+ if (!matches[count])
+ return NULL;
+ count++;
+ }
+ }
+
+ commands = commands->next;
+ }
+
+ if (count == 2) {
+ SAFE_FREE(matches[0]);
+ matches[0] = SMB_STRDUP(matches[1]);
+ }
+ matches[count] = NULL;
+ return matches;
+}
+
+static char *next_command(TALLOC_CTX *ctx, char **cmdstr)
+{
+ char *command;
+ char *p;
+
+ if (!cmdstr || !(*cmdstr))
+ return NULL;
+
+ p = strchr_m(*cmdstr, ';');
+ if (p)
+ *p = '\0';
+ command = talloc_strdup(ctx, *cmdstr);
+
+ /* Pass back the remaining cmdstring
+ (a trailing delimiter ";" does also work),
+ or NULL at last cmdstring.
+ */
+ *cmdstr = p ? p + 1 : p;
+
+ return command;
+}
+
+/* Load specified configuration file */
+static NTSTATUS cmd_conf(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: %s <smb.conf>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (!lp_load_with_shares(argv[1])) {
+ printf("Error loading \"%s\"\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ printf("\"%s\" successfully loaded\n", argv[1]);
+ return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+static NTSTATUS cmd_help(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+
+ /* Usage */
+ if (argc > 2) {
+ printf("Usage: %s [command]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ if (argc == 2) {
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+ if (strequal(argv[1], tmp_set->name)) {
+ if (tmp_set->usage &&
+ tmp_set->usage[0])
+ printf("%s\n", tmp_set->usage);
+ else
+ printf("No help for %s\n", tmp_set->name);
+
+ return NT_STATUS_OK;
+ }
+
+ tmp_set++;
+ }
+ }
+
+ printf("No such command: %s\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ /* List all commands */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+
+ printf("%15s\t\t%s\n", tmp_set->name,
+ tmp_set->description ? tmp_set->description:
+ "");
+
+ tmp_set++;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+static NTSTATUS cmd_debuglevel(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc > 2) {
+ printf("Usage: %s [debuglevel]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2) {
+ struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", argv[1]);
+ }
+
+ printf("debuglevel is %d\n", DEBUGLEVEL);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_freemem(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ /* Cleanup */
+ talloc_destroy(mem_ctx);
+ mem_ctx = NULL;
+ vfs->data = NULL;
+ vfs->data_size = 0;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ /* Cleanup */
+ talloc_destroy(mem_ctx);
+
+ exit(0);
+ return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static struct cmd_set vfstest_commands[] = {
+
+ { .name = "GENERAL OPTIONS" },
+
+ { "conf", cmd_conf, "Load smb configuration file", "conf <smb.conf>" },
+ { "help", cmd_help, "Get help on commands", "" },
+ { "?", cmd_help, "Get help on commands", "" },
+ { "debuglevel", cmd_debuglevel, "Set debug level", "" },
+ { "freemem", cmd_freemem, "Free currently allocated buffers", "" },
+ { "exit", cmd_quit, "Exit program", "" },
+ { "quit", cmd_quit, "Exit program", "" },
+
+ { .name = NULL }
+};
+
+static struct cmd_set separator_command[] = {
+ {
+ .name = "---------------",
+ .description = "----------------------"
+ },
+ {
+ .name = NULL,
+ },
+};
+
+
+extern struct cmd_set vfs_commands[];
+static struct cmd_set *vfstest_command_list[] = {
+ vfstest_commands,
+ vfs_commands,
+ NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+ struct cmd_list *entry;
+
+ if (!(entry = SMB_MALLOC_P(struct cmd_list))) {
+ DEBUG(0, ("out of memory\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(entry);
+
+ entry->cmd_set = cmd_set;
+ DLIST_ADD(cmd_list, entry);
+}
+
+static NTSTATUS do_cmd(struct vfs_state *vfs, struct cmd_set *cmd_entry, char *cmd)
+{
+ const char *p = cmd;
+ const char **argv = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *buf;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ int argc = 0;
+
+ /* Count number of arguments first time through the loop then
+ allocate memory and strdup them. */
+
+ again:
+ while(next_token_talloc(mem_ctx, &p, &buf, " ")) {
+ if (argv) {
+ argv[argc] = talloc_strdup(argv, buf);
+ }
+ argc++;
+ }
+
+ if (!argv) {
+ /* Create argument list */
+
+ argv = talloc_zero_array(mem_ctx, const char *, argc);
+ if (argv == NULL) {
+ fprintf(stderr, "out of memory\n");
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ p = cmd;
+ argc = 0;
+
+ goto again;
+ }
+
+ /* Call the function */
+
+ if (cmd_entry->fn) {
+ /* Run command */
+ result = cmd_entry->fn(vfs, mem_ctx, argc, (const char **)argv);
+ } else {
+ fprintf (stderr, "Invalid command\n");
+ goto done;
+ }
+
+ done:
+
+ /* Cleanup */
+
+ if (argv) {
+ char **_argv = discard_const_p(char *, argv);
+ TALLOC_FREE(_argv);
+ argv = NULL;
+ }
+
+ if (memreports != 0) {
+ talloc_report_full(mem_ctx, stdout);
+ }
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+/* Process a command entered at the prompt or as part of -c */
+static NTSTATUS process_cmd(struct vfs_state *vfs, char *cmd)
+{
+ struct cmd_list *temp_list;
+ bool found = False;
+ char *buf;
+ const char *p = cmd;
+ NTSTATUS result = NT_STATUS_OK;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ int len = 0;
+
+ if (cmd[strlen(cmd) - 1] == '\n')
+ cmd[strlen(cmd) - 1] = '\0';
+
+ if (!next_token_talloc(mem_ctx, &p, &buf, " ")) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ /* Strip the trailing \n if it exists */
+ len = strlen(buf);
+ if (buf[len-1] == '\n')
+ buf[len-1] = '\0';
+
+ /* Search for matching commands */
+
+ for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+ struct cmd_set *temp_set = temp_list->cmd_set;
+
+ while(temp_set->name) {
+ if (strequal(buf, temp_set->name)) {
+ found = True;
+ result = do_cmd(vfs, temp_set, cmd);
+
+ goto done;
+ }
+ temp_set++;
+ }
+ }
+
+ done:
+ if (!found && buf[0]) {
+ printf("command not found: %s\n", buf);
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ printf("result was %s\n", nt_errstr(result));
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+static void process_file(struct vfs_state *pvfs, char *filename) {
+ FILE *file;
+ char command[3 * PATH_MAX];
+
+ if (*filename == '-') {
+ file = stdin;
+ } else {
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ printf("vfstest: error reading file (%s)!", filename);
+ printf("errno n.%d: %s", errno, strerror(errno));
+ exit(-1);
+ }
+ }
+
+ while (fgets(command, 3 * PATH_MAX, file) != NULL) {
+ process_cmd(pvfs, command);
+ }
+
+ if (file != stdin) {
+ fclose(file);
+ }
+}
+
+static void vfstest_exit_server(const char * const reason) _NORETURN_;
+static void vfstest_exit_server(const char * const reason)
+{
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+static void vfstest_exit_server_cleanly(const char * const reason) _NORETURN_;
+static void vfstest_exit_server_cleanly(const char * const reason)
+{
+ vfstest_exit_server("normal exit");
+}
+
+struct smb_request *vfstest_get_smbreq(TALLOC_CTX *mem_ctx,
+ struct vfs_state *vfs)
+{
+ struct smb_request *result;
+ uint8_t *inbuf;
+
+ result = talloc_zero(mem_ctx, struct smb_request);
+ if (result == NULL) {
+ return NULL;
+ }
+ result->sconn = vfs->conn->sconn;
+ result->mid = ++vfs->mid;
+
+ inbuf = talloc_array(result, uint8_t, smb_size);
+ if (inbuf == NULL) {
+ goto fail;
+ }
+ SSVAL(inbuf, smb_mid, result->mid);
+ smb_setlen(inbuf, smb_size-4);
+ result->inbuf = inbuf;
+ return result;
+fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+/* Main function */
+
+int main(int argc, const char *argv[])
+{
+ char *cmdstr = NULL;
+ struct cmd_set **cmd_set;
+ struct conn_struct_tos *c = NULL;
+ struct vfs_state *vfs;
+ int opt;
+ int i;
+ char *filename = NULL;
+ char *cwd = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct auth_session_info *session_info = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ bool ok;
+
+ /* make sure the vars that get altered (4th field) are in
+ a fixed location or certain compilers complain */
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "file",
+ .shortName = 'f',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &filename,
+ },
+ {
+ .longName = "command",
+ .shortName = 'c',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &cmdstr,
+ .val = 0,
+ .descrip = "Execute specified list of commands",
+ },
+ {
+ .longName = "memreport",
+ .shortName = 'm',
+ .argInfo = POPT_ARG_INT,
+ .arg = &memreports,
+ .descrip = "Report memory left on talloc stackframe after each command",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ static const struct smbd_shim vfstest_shim_fns =
+ {
+ .exit_server = vfstest_exit_server,
+ .exit_server_cleanly = vfstest_exit_server_cleanly,
+ };
+
+ smb_init_locale();
+
+ setlinebuf(stdout);
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+ true /* require_smbconf */);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context("vfstest", argc, argv, long_options, 0);
+ if (pc == NULL) {
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ /* TODO: check output */
+ reload_services(NULL, NULL, false);
+
+ per_thread_cwd_check();
+
+ set_smbd_shim(&vfstest_shim_fns);
+
+ /* Load command lists */
+
+ cmd_set = vfstest_command_list;
+
+ while(*cmd_set) {
+ add_command_set(*cmd_set);
+ add_command_set(separator_command);
+ cmd_set++;
+ }
+
+ /* some basic initialization stuff */
+ sec_init();
+ init_guest_session_info(frame);
+ locking_init();
+ vfs = talloc_zero(frame, struct vfs_state);
+ if (vfs == NULL) {
+ return 1;
+ }
+ status = make_session_info_guest(vfs, &session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return 1;
+ }
+
+ /* Provided by libreplace if not present. Always mallocs. */
+ cwd = get_current_dir_name();
+ if (cwd == NULL) {
+ return -1;
+ }
+
+ status = create_conn_struct_tos_cwd(global_messaging_context(),
+ -1,
+ cwd,
+ session_info,
+ &c);
+ SAFE_FREE(cwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return 1;
+ }
+ vfs->conn = c->conn;
+
+ vfs->conn->share_access = FILE_GENERIC_ALL;
+ vfs->conn->read_only = false;
+
+ file_init(vfs->conn->sconn);
+ for (i=0; i < 1024; i++)
+ vfs->files[i] = NULL;
+
+ if (!posix_locking_init(false)) {
+ return 1;
+ }
+
+ /* Do we have a file input? */
+ if (filename && filename[0]) {
+ process_file(vfs, filename);
+ return 0;
+ }
+
+ /* Do anything specified with -c */
+ if (cmdstr && cmdstr[0]) {
+ char *cmd;
+ char *p = cmdstr;
+
+ while((cmd=next_command(frame, &p)) != NULL) {
+ status = process_cmd(vfs, cmd);
+ }
+
+ TALLOC_FREE(cmd);
+ return NT_STATUS_IS_OK(status) ? 0 : 1;
+ }
+
+ /* Loop around accepting commands */
+
+ while(1) {
+ char *line = NULL;
+
+ line = smb_readline("vfstest $> ", NULL, completion_fn);
+
+ if (line == NULL) {
+ break;
+ }
+
+ if (line[0] != '\n') {
+ status = process_cmd(vfs, line);
+ }
+ SAFE_FREE(line);
+ }
+
+ TALLOC_FREE(vfs);
+ TALLOC_FREE(frame);
+ return NT_STATUS_IS_OK(status) ? 0 : 1;
+}
diff --git a/source3/torture/vfstest.h b/source3/torture/vfstest.h
new file mode 100644
index 0000000..bc28d49
--- /dev/null
+++ b/source3/torture/vfstest.h
@@ -0,0 +1,51 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module tester
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+struct func_entry {
+ char *name;
+ int (*fn)(struct connection_struct *conn, const char *path);
+};
+
+struct vfs_state {
+ struct connection_struct *conn;
+ uint64_t mid;
+ struct files_struct *files[1024];
+ struct smb_Dir *currentdir;
+ void *data;
+ size_t data_size;
+};
+
+struct smb_request *vfstest_get_smbreq(TALLOC_CTX *mem_ctx,
+ struct vfs_state *vfs);
+
+struct cmd_set {
+ const char *name;
+ NTSTATUS (*fn)(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc,
+ const char **argv);
+ const char *description;
+ const char *usage;
+};
+
+NTSTATUS cmd_test_chain(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv);
diff --git a/source3/torture/vfstest_chain.c b/source3/torture/vfstest_chain.c
new file mode 100644
index 0000000..174117b
--- /dev/null
+++ b/source3/torture/vfstest_chain.c
@@ -0,0 +1,342 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test smbd chain routines
+
+ Copyright (C) Volker Lendecke 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfstest.h"
+#include "smbd/smbd.h"
+
+static const uint8_t nonchain1_data[] =
+{0x00,0x00,0x00,0xBE,0xFF,0x53,0x4D,0x42,0x72,0x00,0x00,0x00,0x00,0x18,0x43
+,0xC8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+,0xFE,0xFF,0x00,0x00,0x01,0x00,0x00,0x9B,0x00,0x02,0x50,0x43,0x20,0x4E,0x45
+,0x54,0x57,0x4F,0x52,0x4B,0x20,0x50,0x52,0x4F,0x47,0x52,0x41,0x4D,0x20,0x31
+,0x2E,0x30,0x00,0x02,0x4D,0x49,0x43,0x52,0x4F,0x53,0x4F,0x46,0x54,0x20,0x4E
+,0x45,0x54,0x57,0x4F,0x52,0x4B,0x53,0x20,0x31,0x2E,0x30,0x33,0x00,0x02,0x4D
+,0x49,0x43,0x52,0x4F,0x53,0x4F,0x46,0x54,0x20,0x4E,0x45,0x54,0x57,0x4F,0x52
+,0x4B,0x53,0x20,0x33,0x2E,0x30,0x00,0x02,0x4C,0x41,0x4E,0x4D,0x41,0x4E,0x31
+,0x2E,0x30,0x00,0x02,0x4C,0x4D,0x31,0x2E,0x32,0x58,0x30,0x30,0x32,0x00,0x02
+,0x44,0x4F,0x53,0x20,0x4C,0x41,0x4E,0x4D,0x41,0x4E,0x32,0x2E,0x31,0x00,0x02
+,0x4C,0x41,0x4E,0x4D,0x41,0x4E,0x32,0x2E,0x31,0x00,0x02,0x53,0x61,0x6D,0x62
+,0x61,0x00,0x02,0x4E,0x54,0x20,0x4C,0x41,0x4E,0x4D,0x41,0x4E,0x20,0x31,0x2E
+,0x30,0x00,0x02,0x4E,0x54,0x20,0x4C,0x4D,0x20,0x30,0x2E,0x31,0x32,0x00};
+
+static const uint8_t nonchain2_data[] =
+{0x00,0x00,0x00,0xA4,0xFF,0x53,0x4D,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x43
+,0xC8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF
+,0x00,0xE7,0x00,0x00,0x02,0x00,0x0C,0xFF,0x00,0x00,0x00,0xFF,0xFF,0x02,0x00
+,0x01,0x00,0x00,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x00,0x00,0x54,0x00,0x00
+,0x80,0x69,0x00,0x60,0x51,0x06,0x06,0x2B,0x06,0x01,0x05,0x05,0x02,0xA0,0x47
+,0x30,0x45,0xA0,0x0E,0x30,0x0C,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0x82,0x37
+,0x02,0x02,0x0A,0xA2,0x33,0x04,0x31,0x4E,0x54,0x4C,0x4D,0x53,0x53,0x50,0x00
+,0x01,0x00,0x00,0x00,0x15,0x82,0x08,0x60,0x09,0x00,0x09,0x00,0x20,0x00,0x00
+,0x00,0x08,0x00,0x08,0x00,0x29,0x00,0x00,0x00,0x57,0x4F,0x52,0x4B,0x47,0x52
+,0x4F,0x55,0x50,0x46,0x52,0x45,0x45,0x42,0x53,0x44,0x38,0x55,0x00,0x6E,0x00
+,0x69,0x00,0x78,0x00,0x00,0x00,0x53,0x00,0x61,0x00,0x6D,0x00,0x62,0x00,0x61
+,0x00,0x00,0x00};
+
+static const uint8_t chain1_data[] =
+{0x00,0x00,0x00,0x57,0xFF,0x53,0x4D,0x42,0x2D,0x00,0x00,0x00,0x00,0x88,0x03
+,0xC8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00
+,0x00,0xE7,0x64,0x00,0x05,0x00,0x0F,0x2F,0x00,0x44,0x00,0x67,0x19,0x20,0x00
+,0x40,0xDD,0x44,0x4F,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x01
+,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x04,0x00
+,0x54,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+,0x00};
+
+static const uint8_t chain2_data[] =
+{0x00,0x00,0x00,0x8C,0xFF,0x53,0x4D,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x43
+,0xC8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF
+,0x03,0xE7,0x00,0x00,0x02,0x00,0x0D,0x75,0x00,0x58,0x00,0xFF,0xFF,0x02,0x00
+,0x03,0xE7,0x04,0xE7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54
+,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x00,0x6E,0x00,0x69
+,0x00,0x78,0x00,0x00,0x00,0x53,0x00,0x61,0x00,0x6D,0x00,0x62,0x00,0x61,0x00
+,0x00,0x00,0x04,0xFF,0xFF,0x00,0x00,0x08,0x00,0x01,0x00,0x29,0x00,0x00,0x5C
+,0x00,0x5C,0x00,0x31,0x00,0x32,0x00,0x37,0x00,0x2E,0x00,0x30,0x00,0x2E,0x00
+,0x30,0x00,0x2E,0x00,0x31,0x00,0x5C,0x00,0x49,0x00,0x50,0x00,0x43,0x00,0x24
+,0x00,0x00,0x00,0x3F,0x3F,0x3F,0x3F,0x3F,0x00};
+
+static const uint8_t bug_8360_data[] =
+{0x00,0x00,0x00,0xE9,0xFF,0x53,0x4D,0x42,0x2F,0x00,0x00,0x00,0x00,0x08,0x03
+,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00
+,0x94,0x00,0x64,0x00,0xA5,0x45,0x0C,0x0A,0x00,0x3C,0x00,0xFA,0x4B,0x00,0x00
+,0x00,0x00,0xFE,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x9D,0x00,0x4C
+,0x00,0x9E,0x00,0x00,0x05,0xFA,0x4B,0x03,0x00,0x90,0x26,0x00,0x00,0x00,0x00
+,0x00,0x00,0x41,0x4E,0x4D,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58
+,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58};
+
+static const uint8_t invalid1_data[] =
+{0x00,0x00,0x0A,0x2E,0xFF,0x53,0x4D,0x42,0x72,0x00,0x00,0x00,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9
+,0xA9,0xA9,0xA9,0xA9,0xA9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD5,0x15,0x00,0x00,0x81,0x0B,0x00,0x77
+,0x00,0x02,0x50,0x43,0x20,0x4E,0x45,0x54,0x57,0x4F,0x52,0x4B,0x20,0x50,0x52
+,0x4F,0x47,0x52,0x41,0x4D,0x20,0x31,0x2E,0x30,0x00,0x02,0x4D,0x49,0x43,0x52
+,0x4F,0x53,0x4F,0x46,0x54,0x20,0x4E,0x45,0x54,0x57,0x4F,0x52,0x4B,0x53,0x20
+,0x33,0x2E,0x30,0x00,0x02,0x44,0x4F,0x53,0x20,0x4C,0x4D,0x31,0x2E,0x32,0x58
+,0x30,0x30,0x32,0x00,0x02,0x44,0x4F,0x53,0x20,0x4C,0x41,0x4E,0x4D,0x41,0x4E
+,0x32,0x2E,0x31,0x00,0x02,0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x20,0x66,0x6F
+,0x72,0x20,0x57,0x6F,0x72,0x6B,0x67,0x72,0x6F,0x75,0x70,0x73,0x20,0x33,0x2E
+,0x31,0x61,0x00,0x02,0x4E,0x54,0x20,0x4C,0x4D,0x20,0x30,0x2E,0x31,0x32,0x00};
+
+static const uint8_t invalid2_data[] =
+{0x00,0x00,0x01,0x60,0xFF,0x53,0x4D,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x07
+,0x00,0x00,0x00,0x00,0x00,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74
+,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00,0x74,0x00
+,0x74,0x00,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x00
+,0x00,0x04,0x00,0x0D,0x75,0x00,0x54,0x00,0x68,0x0B,0x02,0x00,0x00,0x00,0x04
+,0x06,0x03,0x80,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0xD4,0x00,0x00,0x00
+,0x17,0x00,0x00,0x00,0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x20,0x37,0x20,0x50
+,0x72,0x6F,0x00,0x57,0x49,0x4E,0x37,0x00,0x00,0x00,0x04,0xFF,0x00,0x91,0x00
+,0x08,0x00,0x18,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+,0x5C,0x5C,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x31,0x2E,0x38,0x36,0x5C
+,0x49,0x50,0x43,0x24,0x00,0x3F,0x3F,0x3F,0x3F,0x3F,0x00};
+/* end binary data. size = 356 bytes */
+
+NTSTATUS cmd_test_chain(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ bool ret = true;
+ unsigned chain_length;
+ struct smb_request **requests;
+
+ ret &= !smb1_is_chain(nonchain1_data);
+ ret &= !smb1_is_chain(nonchain2_data);
+
+ ret &= smb1_is_chain(chain1_data);
+
+ chain_length = smb1_chain_length(chain1_data);
+ ret &= (chain_length == 3);
+
+ ret &= smb1_is_chain(chain2_data);
+
+ chain_length = smb1_chain_length(chain2_data);
+ ret &= (chain_length == 2);
+
+ ret &= smb1_is_chain(bug_8360_data);
+
+ chain_length = smb1_chain_length(bug_8360_data);
+ ret &= (chain_length == 2);
+
+ ret &= !smb1_is_chain(invalid1_data);
+
+ chain_length = smb1_chain_length(invalid1_data);
+ ret &= (chain_length == 1);
+
+ ret &= !smb1_is_chain(invalid2_data);
+
+ chain_length = smb1_chain_length(invalid2_data);
+ ret &= (chain_length == 0);
+
+ ret &= smb1_parse_chain(talloc_tos(), chain1_data,
+ NULL, false, 0,
+ &requests, &chain_length);
+ ret &= (chain_length == 3);
+
+ ret &= smb1_parse_chain(talloc_tos(), chain2_data,
+ NULL, false, 0,
+ &requests, &chain_length);
+ ret &= (chain_length == 2);
+
+ return ret ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source3/torture/wbc_async.c b/source3/torture/wbc_async.c
new file mode 100644
index 0000000..9560e36
--- /dev/null
+++ b/source3/torture/wbc_async.c
@@ -0,0 +1,758 @@
+/*
+ Unix SMB/CIFS implementation.
+ Infrastructure for async winbind requests
+ Copyright (C) Volker Lendecke 2008
+
+ ** NOTE! The following LGPL license applies to the wbclient
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include <talloc.h>
+#include <tevent.h>
+#include "lib/async_req/async_sock.h"
+#include "nsswitch/winbind_struct_protocol.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "wbc_async.h"
+#include "lib/util/blocking.h"
+
+wbcErr map_wbc_err_from_errno(int error)
+{
+ switch(error) {
+ case EPERM:
+ case EACCES:
+ return WBC_ERR_AUTH_ERROR;
+ case ENOMEM:
+ return WBC_ERR_NO_MEMORY;
+ case EIO:
+ default:
+ return WBC_ERR_UNKNOWN_FAILURE;
+ }
+}
+
+bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
+{
+ enum tevent_req_state state;
+ uint64_t error;
+ if (!tevent_req_is_error(req, &state, &error)) {
+ *pwbc_err = WBC_ERR_SUCCESS;
+ return false;
+ }
+
+ switch (state) {
+ case TEVENT_REQ_USER_ERROR:
+ *pwbc_err = error;
+ break;
+ case TEVENT_REQ_TIMED_OUT:
+ *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
+ break;
+ case TEVENT_REQ_NO_MEMORY:
+ *pwbc_err = WBC_ERR_NO_MEMORY;
+ break;
+ default:
+ *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
+ break;
+ }
+ return true;
+}
+
+wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
+{
+ wbcErr wbc_err;
+
+ if (tevent_req_is_wbcerr(req, &wbc_err)) {
+ return wbc_err;
+ }
+
+ return WBC_ERR_SUCCESS;
+}
+
+struct wbc_debug_ops {
+ void (*debug)(void *context, enum wbcDebugLevel level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+ void *context;
+};
+
+struct wb_context {
+ struct tevent_queue *queue;
+ int fd;
+ bool is_priv;
+ const char *dir;
+ struct wbc_debug_ops debug_ops;
+};
+
+static int make_nonstd_fd(int fd)
+{
+ size_t i;
+ int sys_errno = 0;
+ int fds[3];
+ size_t num_fds = 0;
+
+ if (fd == -1) {
+ return -1;
+ }
+ while (fd < 3) {
+ fds[num_fds++] = fd;
+ fd = dup(fd);
+ if (fd == -1) {
+ sys_errno = errno;
+ break;
+ }
+ }
+ for (i=0; i<num_fds; i++) {
+ close(fds[i]);
+ }
+ if (fd == -1) {
+ errno = sys_errno;
+ }
+ return fd;
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode.
+ Set close on exec also.
+****************************************************************************/
+
+static int make_safe_fd(int fd)
+{
+ int result, flags;
+ int new_fd = make_nonstd_fd(fd);
+
+ if (new_fd == -1) {
+ goto fail;
+ }
+
+ result = set_blocking(new_fd, false);
+ if (result == -1) {
+ goto fail;
+ }
+
+ /* Socket should be closed on exec() */
+#ifdef FD_CLOEXEC
+ result = flags = fcntl(new_fd, F_GETFD, 0);
+ if (flags >= 0) {
+ flags |= FD_CLOEXEC;
+ result = fcntl( new_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+ goto fail;
+ }
+#endif
+ return new_fd;
+
+ fail:
+ if (new_fd != -1) {
+ int sys_errno = errno;
+ close(new_fd);
+ errno = sys_errno;
+ }
+ return -1;
+}
+
+/* Just put a prototype to avoid moving the whole function around */
+static const char *winbindd_socket_dir(void);
+
+struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx, const char* dir)
+{
+ struct wb_context *result;
+
+ result = talloc_zero(mem_ctx, struct wb_context);
+ if (result == NULL) {
+ return NULL;
+ }
+ result->queue = tevent_queue_create(result, "wb_trans");
+ if (result->queue == NULL) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+ result->fd = -1;
+ result->is_priv = false;
+
+ if (dir != NULL) {
+ result->dir = talloc_strdup(result, dir);
+ } else {
+ result->dir = winbindd_socket_dir();
+ }
+ if (result->dir == NULL) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+ return result;
+}
+
+struct wb_connect_state {
+ int dummy;
+};
+
+static void wbc_connect_connected(struct tevent_req *subreq);
+
+static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const char *dir)
+{
+ struct tevent_req *result, *subreq;
+ struct wb_connect_state *state;
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ char *path = NULL;
+ wbcErr wbc_err;
+
+ result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (wb_ctx->fd != -1) {
+ close(wb_ctx->fd);
+ wb_ctx->fd = -1;
+ }
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(dir, &st) == -1) {
+ wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
+ goto post_status;
+ }
+
+ if (!S_ISDIR(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
+ goto post_status;
+ }
+
+ /* Connect to socket */
+
+ path = talloc_asprintf(mem_ctx, "%s/%s", dir,
+ WINBINDD_SOCKET_NAME);
+ if (path == NULL) {
+ goto nomem;
+ }
+
+ sunaddr.sun_family = AF_UNIX;
+ strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
+ TALLOC_FREE(path);
+
+ /* If socket file doesn't exist, don't bother trying to connect
+ with retry. This is an attempt to make the system usable when
+ the winbindd daemon is not running. */
+
+ if ((lstat(sunaddr.sun_path, &st) == -1)
+ || !S_ISSOCK(st.st_mode)
+ || (st.st_uid != 0 && st.st_uid != geteuid())) {
+ wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
+ goto post_status;
+ }
+
+ wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
+ if (wb_ctx->fd == -1) {
+ wbc_err = map_wbc_err_from_errno(errno);
+ goto post_status;
+ }
+
+ subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
+ (struct sockaddr *)(void *)&sunaddr,
+ sizeof(sunaddr), NULL, NULL, NULL);
+ if (subreq == NULL) {
+ goto nomem;
+ }
+ tevent_req_set_callback(subreq, wbc_connect_connected, result);
+ return result;
+
+ post_status:
+ tevent_req_error(result, wbc_err);
+ return tevent_req_post(result, ev);
+ nomem:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wbc_connect_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int res, err;
+
+ res = async_connect_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (res == -1) {
+ tevent_req_error(req, map_wbc_err_from_errno(err));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static wbcErr wb_connect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_wbcerr(req);
+}
+
+static const char *winbindd_socket_dir(void)
+{
+ if (nss_wrapper_enabled()) {
+ const char *env_dir;
+
+ env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
+ if (env_dir != NULL) {
+ return env_dir;
+ }
+ }
+
+ return WINBINDD_SOCKET_DIR;
+}
+
+struct wb_open_pipe_state {
+ struct wb_context *wb_ctx;
+ struct tevent_context *ev;
+ bool need_priv;
+ struct winbindd_request wb_req;
+};
+
+static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
+static void wb_open_pipe_ping_done(struct tevent_req *subreq);
+static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
+static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
+
+static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ bool need_priv)
+{
+ struct tevent_req *result, *subreq;
+ struct wb_open_pipe_state *state;
+
+ result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
+ if (result == NULL) {
+ return NULL;
+ }
+ state->wb_ctx = wb_ctx;
+ state->ev = ev;
+ state->need_priv = need_priv;
+
+ if (wb_ctx->fd != -1) {
+ close(wb_ctx->fd);
+ wb_ctx->fd = -1;
+ }
+
+ subreq = wb_connect_send(state, ev, wb_ctx, wb_ctx->dir);
+ if (subreq == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
+ result);
+ return result;
+
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_open_pipe_state *state = tevent_req_data(
+ req, struct wb_open_pipe_state);
+ wbcErr wbc_err;
+
+ wbc_err = wb_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!WBC_ERROR_IS_OK(wbc_err)) {
+ state->wb_ctx->is_priv = true;
+ tevent_req_error(req, wbc_err);
+ return;
+ }
+
+ ZERO_STRUCT(state->wb_req);
+ state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
+ state->wb_req.pid = getpid();
+ (void)snprintf(state->wb_req.client_name,
+ sizeof(state->wb_req.client_name),
+ "%s",
+ "TORTURE");
+
+ subreq = wb_simple_trans_send(state, state->ev, NULL,
+ state->wb_ctx->fd, &state->wb_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
+}
+
+static void wb_open_pipe_ping_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_open_pipe_state *state = tevent_req_data(
+ req, struct wb_open_pipe_state);
+ struct winbindd_response *wb_resp;
+ int ret, err;
+
+ ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, map_wbc_err_from_errno(err));
+ return;
+ }
+
+ if (!state->need_priv) {
+ tevent_req_done(req);
+ return;
+ }
+
+ state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
+ state->wb_req.pid = getpid();
+
+ subreq = wb_simple_trans_send(state, state->ev, NULL,
+ state->wb_ctx->fd, &state->wb_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
+}
+
+static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_open_pipe_state *state = tevent_req_data(
+ req, struct wb_open_pipe_state);
+ struct winbindd_response *wb_resp = NULL;
+ int ret, err;
+
+ ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, map_wbc_err_from_errno(err));
+ return;
+ }
+
+ close(state->wb_ctx->fd);
+ state->wb_ctx->fd = -1;
+
+ subreq = wb_connect_send(state, state->ev, state->wb_ctx,
+ (char *)wb_resp->extra_data.data);
+ TALLOC_FREE(wb_resp);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
+}
+
+static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_open_pipe_state *state = tevent_req_data(
+ req, struct wb_open_pipe_state);
+ wbcErr wbc_err;
+
+ wbc_err = wb_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!WBC_ERROR_IS_OK(wbc_err)) {
+ tevent_req_error(req, wbc_err);
+ return;
+ }
+ state->wb_ctx->is_priv = true;
+ tevent_req_done(req);
+}
+
+static wbcErr wb_open_pipe_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_wbcerr(req);
+}
+
+struct wb_trans_state {
+ struct wb_trans_state *prev, *next;
+ struct wb_context *wb_ctx;
+ struct tevent_context *ev;
+ struct winbindd_request *wb_req;
+ struct winbindd_response *wb_resp;
+ bool need_priv;
+};
+
+static bool closed_fd(int fd)
+{
+ struct timeval tv;
+ fd_set r_fds;
+ int selret;
+
+ if (fd == -1) {
+ return true;
+ }
+
+ FD_ZERO(&r_fds);
+ FD_SET(fd, &r_fds);
+ ZERO_STRUCT(tv);
+
+ selret = select(fd+1, &r_fds, NULL, NULL, &tv);
+ if (selret == -1) {
+ return true;
+ }
+ if (selret == 0) {
+ return false;
+ }
+ return (FD_ISSET(fd, &r_fds));
+}
+
+static void wb_trans_trigger(struct tevent_req *req, void *private_data);
+static void wb_trans_connect_done(struct tevent_req *subreq);
+static void wb_trans_done(struct tevent_req *subreq);
+static void wb_trans_retry_wait_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx, bool need_priv,
+ struct winbindd_request *wb_req)
+{
+ struct tevent_req *req;
+ struct wb_trans_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->wb_ctx = wb_ctx;
+ state->ev = ev;
+ state->wb_req = wb_req;
+ state->need_priv = need_priv;
+
+ if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger,
+ NULL)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void wb_trans_trigger(struct tevent_req *req, void *private_data)
+{
+ struct wb_trans_state *state = tevent_req_data(
+ req, struct wb_trans_state);
+ struct tevent_req *subreq;
+
+ if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) {
+ close(state->wb_ctx->fd);
+ state->wb_ctx->fd = -1;
+ }
+
+ if ((state->wb_ctx->fd == -1)
+ || (state->need_priv && !state->wb_ctx->is_priv)) {
+ subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
+ state->need_priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_trans_connect_done, req);
+ return;
+ }
+
+ state->wb_req->pid = getpid();
+
+ subreq = wb_simple_trans_send(state, state->ev, NULL,
+ state->wb_ctx->fd, state->wb_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_trans_done, req);
+}
+
+static bool wb_trans_retry(struct tevent_req *req,
+ struct wb_trans_state *state,
+ wbcErr wbc_err)
+{
+ struct tevent_req *subreq;
+
+ if (WBC_ERROR_IS_OK(wbc_err)) {
+ return false;
+ }
+
+ if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
+ /*
+ * Winbind not around or we can't connect to the pipe. Fail
+ * immediately.
+ */
+ tevent_req_error(req, wbc_err);
+ return true;
+ }
+
+ /*
+ * The transfer as such failed, retry after one second
+ */
+
+ if (state->wb_ctx->fd != -1) {
+ close(state->wb_ctx->fd);
+ state->wb_ctx->fd = -1;
+ }
+
+ subreq = tevent_wakeup_send(state, state->ev,
+ tevent_timeval_current_ofs(1, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return true;
+ }
+ tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
+ return true;
+}
+
+static void wb_trans_retry_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_trans_state *state = tevent_req_data(
+ req, struct wb_trans_state);
+ bool ret;
+
+ ret = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ret) {
+ tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
+ return;
+ }
+
+ subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
+ state->need_priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_trans_connect_done, req);
+}
+
+static void wb_trans_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_trans_state *state = tevent_req_data(
+ req, struct wb_trans_state);
+ wbcErr wbc_err;
+
+ wbc_err = wb_open_pipe_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (wb_trans_retry(req, state, wbc_err)) {
+ return;
+ }
+
+ subreq = wb_simple_trans_send(state, state->ev, NULL,
+ state->wb_ctx->fd, state->wb_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_trans_done, req);
+}
+
+static void wb_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_trans_state *state = tevent_req_data(
+ req, struct wb_trans_state);
+ int ret, err;
+
+ ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
+ TALLOC_FREE(subreq);
+ if ((ret == -1)
+ && wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse)
+{
+ struct wb_trans_state *state = tevent_req_data(
+ req, struct wb_trans_state);
+ wbcErr wbc_err;
+
+ if (tevent_req_is_wbcerr(req, &wbc_err)) {
+ return wbc_err;
+ }
+
+ *presponse = talloc_move(mem_ctx, &state->wb_resp);
+ return WBC_ERR_SUCCESS;
+}
+
+/********************************************************************
+ * Debug wrapper functions, modeled (with lot's of code copied as is)
+ * after the tevent debug wrapper functions
+ ********************************************************************/
+
+/*
+ this allows the user to choose their own debug function
+*/
+int wbcSetDebug(struct wb_context *wb_ctx,
+ void (*debug)(void *context,
+ enum wbcDebugLevel level,
+ const char *fmt,
+ va_list ap) PRINTF_ATTRIBUTE(3,0),
+ void *context)
+{
+ wb_ctx->debug_ops.debug = debug;
+ wb_ctx->debug_ops.context = context;
+ return 0;
+}
+
+/*
+ debug function for wbcSetDebugStderr
+*/
+static void wbcDebugStderr(void *private_data,
+ enum wbcDebugLevel level,
+ const char *fmt,
+ va_list ap) PRINTF_ATTRIBUTE(3,0);
+static void wbcDebugStderr(void *private_data,
+ enum wbcDebugLevel level,
+ const char *fmt, va_list ap)
+{
+ if (level <= WBC_DEBUG_WARNING) {
+ vfprintf(stderr, fmt, ap);
+ }
+}
+
+/*
+ convenience function to setup debug messages on stderr
+ messages of level WBC_DEBUG_WARNING and higher are printed
+*/
+int wbcSetDebugStderr(struct wb_context *wb_ctx)
+{
+ return wbcSetDebug(wb_ctx, wbcDebugStderr, wb_ctx);
+}
+
+/*
+ * log a message
+ *
+ * The default debug action is to ignore debugging messages.
+ * This is the most appropriate action for a library.
+ * Applications using the library must decide where to
+ * redirect debugging messages
+*/
+void wbcDebug(struct wb_context *wb_ctx, enum wbcDebugLevel level,
+ const char *fmt, ...)
+{
+ va_list ap;
+ if (!wb_ctx) {
+ return;
+ }
+ if (wb_ctx->debug_ops.debug == NULL) {
+ return;
+ }
+ va_start(ap, fmt);
+ wb_ctx->debug_ops.debug(wb_ctx->debug_ops.context, level, fmt, ap);
+ va_end(ap);
+}
diff --git a/source3/torture/wbc_async.h b/source3/torture/wbc_async.h
new file mode 100644
index 0000000..9cd6a93
--- /dev/null
+++ b/source3/torture/wbc_async.h
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ Headers for the async winbind client library
+ Copyright (C) Volker Lendecke 2008
+
+ ** NOTE! The following LGPL license applies to the wbclient
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBC_ASYNC_H_
+#define _WBC_ASYNC_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include "nsswitch/libwbclient/wbclient.h"
+#include "nsswitch/wb_reqtrans.h"
+
+struct wb_context;
+struct winbindd_request;
+struct winbindd_response;
+
+enum wbcDebugLevel {
+ WBC_DEBUG_FATAL,
+ WBC_DEBUG_ERROR,
+ WBC_DEBUG_WARNING,
+ WBC_DEBUG_TRACE
+};
+
+struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx, bool need_priv,
+ struct winbindd_request *wb_req);
+wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse);
+struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx, const char* dir);
+int wbcSetDebug(struct wb_context *wb_ctx,
+ void (*debug)(void *context,
+ enum wbcDebugLevel level,
+ const char *fmt,
+ va_list ap) PRINTF_ATTRIBUTE(3,0),
+ void *context);
+int wbcSetDebugStderr(struct wb_context *wb_ctx);
+void wbcDebug(struct wb_context *wb_ctx, enum wbcDebugLevel level,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(3,0);
+
+/* Definitions from wb_reqtrans.c */
+wbcErr map_wbc_err_from_errno(int error);
+
+bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err);
+wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req);
+
+/* Async functions from wbc_idmap.c */
+
+struct tevent_req *wbcSidToUid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const struct wbcDomainSid *sid);
+wbcErr wbcSidToUid_recv(struct tevent_req *req, uid_t *puid);
+
+struct tevent_req *wbcUidToSid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ uid_t uid);
+wbcErr wbcUidToSid_recv(struct tevent_req *req, struct wbcDomainSid *psid);
+
+struct tevent_req *wbcSidToGid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const struct wbcDomainSid *sid);
+wbcErr wbcSidToGid_recv(struct tevent_req *req, gid_t *pgid);
+
+struct tevent_req *wbcGidToSid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ gid_t gid);
+wbcErr wbcGidToSid_recv(struct tevent_req *req, struct wbcDomainSid *psid);
+
+/* Async functions from wbc_pam.c */
+struct tevent_req *wbcAuthenticateUserEx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const struct wbcAuthUserParams *params);
+wbcErr wbcAuthenticateUserEx_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/* Async functions from wbc_sid.c */
+struct tevent_req *wbcLookupName_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const char *domain,
+ const char *name);
+wbcErr wbcLookupName_recv(struct tevent_req *req,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type);
+struct tevent_req *wbcLookupSid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const struct wbcDomainSid *sid);
+wbcErr wbcLookupSid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **pdomain,
+ char **pname,
+ enum wbcSidType *pname_type);
+
+/* Async functions from wbc_util.c */
+
+struct tevent_req *wbcPing_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx);
+wbcErr wbcPing_recv(struct tevent_req *req);
+
+struct tevent_req *wbcInterfaceVersion_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx);
+wbcErr wbcInterfaceVersion_recv(struct tevent_req *req,
+ uint32_t *interface_version);
+
+struct tevent_req *wbcInfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx);
+wbcErr wbcInfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char *winbind_separator,
+ char **version_string);
+
+struct tevent_req *wbcNetbiosName_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx);
+wbcErr wbcNetbiosName_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **netbios_name);
+
+struct tevent_req *wbcDomainName_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx);
+wbcErr wbcDomainName_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **netbios_name);
+
+struct tevent_req *wbcInterfaceDetails_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx);
+wbcErr wbcInterfaceDetails_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct wbcInterfaceDetails **details);
+
+struct tevent_req *wbcDomainInfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wb_context *wb_ctx,
+ const char *domain);
+wbcErr wbcDomainInfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct wbcDomainInfo **dinfo);
+
+#endif /*_WBC_ASYNC_H_*/
diff --git a/source3/torture/wscript_build b/source3/torture/wscript_build
new file mode 100644
index 0000000..1d25200
--- /dev/null
+++ b/source3/torture/wscript_build
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+
+bld.SAMBA3_BINARY('locktest2',
+ source='locktest2.c',
+ deps='''
+ talloc
+ smbconf
+ libsmb
+ LOCKING
+ ''',
+ for_selftest=True)
+
+TORTURE3_ADDITIONAL_SOURCE=""
+
+if bld.env.with_ctdb:
+ TORTURE3_ADDITIONAL_SOURCE += ' test_ctdbd_conn.c'
+
+bld.SAMBA3_BINARY('smbtorture' + bld.env.suffix3,
+ source='''
+ torture.c
+ nbio.c
+ scanner.c
+ utable.c
+ denytest.c
+ mangle_test.c
+ nbench.c
+ test_async_echo.c
+ test_addrchange.c
+ test_matching.c
+ test_posix_append.c
+ test_posix.c
+ test_nttrans_create.c
+ test_nttrans_fsctl.c
+ test_case_insensitive.c
+ test_notify_online.c
+ test_chain3.c
+ test_smb2.c
+ test_smb1_dfs.c
+ test_authinfo_structs.c
+ test_smbsock_any_connect.c
+ test_cleanup.c
+ test_notify.c
+ ../lib/tevent_barrier.c
+ test_dbwrap_watch.c
+ test_dbwrap_do_locked.c
+ test_idmap_tdb_common.c
+ test_dbwrap_ctdb.c
+ test_buffersize.c
+ test_messaging_read.c
+ test_messaging_fd_passing.c
+ test_messaging_send_all.c
+ test_oplock_cancel.c
+ test_pthreadpool_tevent.c
+ bench_pthreadpool.c
+ wbc_async.c
+ test_g_lock.c
+ test_namemap_cache.c
+ test_idmap_cache.c
+ test_hidenewfiles.c
+ test_readdir_timestamp.c
+ test_rpc_scale.c
+ test_tdb_validate.c
+ ''' + TORTURE3_ADDITIONAL_SOURCE,
+ deps='''
+ talloc
+ smbconf
+ libsmb
+ msrpc3
+ TLDAP
+ RPC_NDR_ECHO
+ WB_REQTRANS
+ LOCKING
+ NDR_OPEN_FILES
+ idmap
+ IDMAP_TDB_COMMON
+ libcli_lsa3
+ samba-cluster-support
+ util_sd
+ TDB_VALIDATE
+ ''',
+ cflags='-DWINBINDD_SOCKET_DIR=\"%s\"' % bld.env.WINBINDD_SOCKET_DIR,
+ for_selftest=True)
+
+bld.SAMBA3_BINARY('msgtest',
+ source='msgtest.c',
+ deps='''
+ talloc
+ smbconf
+ ''',
+ install=False)
+
+bld.SAMBA3_BINARY('msg_sink',
+ source='msg_sink.c',
+ deps='''
+ talloc
+ smbconf
+ ''',
+ install=False)
+
+bld.SAMBA3_BINARY('msg_source',
+ source='msg_source.c',
+ deps='''
+ talloc
+ smbconf
+ ''',
+ install=False)
+
+bld.SAMBA3_BINARY('pdbtest',
+ source='pdbtest.c',
+ deps='''
+ talloc
+ pdb
+ CMDLINE_S3
+ AUTH_COMMON
+ auth
+ ''',
+ for_selftest=True)
+
+if bld.CONFIG_SET('WITH_SMB1SERVER'):
+ SMB1_SOURCES = 'vfstest_chain.c'
+else:
+ SMB1_SOURCES = ''
+
+bld.SAMBA3_BINARY('vfstest',
+ source='''
+ cmd_vfs.c
+ vfstest.c
+ ''' + SMB1_SOURCES,
+ deps='''
+ vfs
+ CMDLINE_S3
+ smbconf
+ SMBREADLINE
+ ''',
+ for_selftest=True)