diff options
Diffstat (limited to '')
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(¶ms, &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 */ + ¶m_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(¤t_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) |