static bool wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len) { while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) { if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; } return true; }
/* test a write that hits a byte range lock and send the close after the write */ static bool test_mux_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { union smb_write io; NTSTATUS status; int fnum; bool ret = true; struct smbcli_request *req; printf("Testing multiplexed lock/write/close\n"); fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); if (fnum == -1) { printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree)); ret = false; goto done; } cli->session->pid = 1; /* lock a range */ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 0, 4, 0, WRITE_LOCK))) { printf("lock failed in mux_write - %s\n", smbcli_errstr(cli->tree)); ret = false; goto done; } cli->session->pid = 2; /* send an async write */ io.generic.level = RAW_WRITE_WRITEX; io.writex.in.file.fnum = fnum; io.writex.in.offset = 0; io.writex.in.wmode = 0; io.writex.in.remaining = 0; io.writex.in.count = 4; io.writex.in.data = (const uint8_t *)&fnum; req = smb_raw_write_send(cli->tree, &io); /* unlock the range */ cli->session->pid = 1; smbcli_unlock(cli->tree, fnum, 0, 4); /* and recv the async write reply */ status = smb_raw_write_recv(req, &io); CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); smbcli_close(cli->tree, fnum); done: return ret; }
static BOOL try_lock(struct smbcli_state *c, int fstype, int fd, uint_t start, uint_t len, enum brl_type op) { struct flock lock; switch (fstype) { case FSTYPE_SMB: return smbcli_lock(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 test_one(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES], struct record *rec) { unsigned int conn = rec->conn; unsigned int f = rec->f; uint64_t start = rec->start; uint64_t len = rec->len; enum brl_type op = rec->lock_type; int server; bool ret[NSERVERS]; NTSTATUS status[NSERVERS]; switch (rec->lock_op) { case OP_LOCK: /* set a lock */ for (server=0;server<NSERVERS;server++) { NTSTATUS res; struct smbcli_tree *tree=cli[server][conn]->tree; int fn=fnum[server][conn][f]; if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) { res=smbcli_lock(tree, fn, start, len, LOCK_TIMEOUT, rec->lock_op); } else { union smb_lock parms; int ltype; struct smb_lock_entry lock[1]; parms.lockx.level = RAW_LOCK_LOCKX; parms.lockx.in.file.fnum = fn; ltype = (rec->lock_op == READ_LOCK? 1 : 0); ltype |= LOCKING_ANDX_LARGE_FILES; parms.lockx.in.mode = ltype; parms.lockx.in.timeout = LOCK_TIMEOUT; parms.lockx.in.ulock_cnt = 0; parms.lockx.in.lock_cnt = 1; lock[0].pid = rec->pid; lock[0].offset = start; lock[0].count = len; parms.lockx.in.locks = &lock[0]; res = smb_raw_lock(tree, &parms); } ret[server] = NT_STATUS_IS_OK(res); status[server] = smbcli_nt_error(cli[server][conn]->tree); if (!exact_error_codes && NT_STATUS_EQUAL(status[server], NT_STATUS_FILE_LOCK_CONFLICT)) { status[server] = NT_STATUS_LOCK_NOT_GRANTED; } } if (showall || !NT_STATUS_EQUAL(status[0],status[1])) { printf("lock conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n", conn, f, (double)start, (double)len, op==READ_LOCK?"READ_LOCK":"WRITE_LOCK", nt_errstr(status[0]), nt_errstr(status[1])); } if (!NT_STATUS_EQUAL(status[0],status[1])) return false; break; case OP_UNLOCK: /* unset a lock */ for (server=0;server<NSERVERS;server++) { NTSTATUS res; struct smbcli_tree *tree=cli[server][conn]->tree; int fn=fnum[server][conn][f]; if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) { res=smbcli_unlock(tree, fn, start, len); } else { union smb_lock parms; struct smb_lock_entry lock[1]; parms.lockx.level = RAW_LOCK_LOCKX; parms.lockx.in.file.fnum = fn; parms.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; parms.lockx.in.timeout = 0; parms.lockx.in.ulock_cnt = 1; parms.lockx.in.lock_cnt = 0; lock[0].pid = rec->pid; lock[0].count = len; lock[0].offset = start; parms.lockx.in.locks = &lock[0]; res = smb_raw_lock(tree, &parms); } ret[server] = NT_STATUS_IS_OK(res); status[server] = smbcli_nt_error(cli[server][conn]->tree); } if (showall || (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) { printf("unlock conn=%u f=%u range=%.0f(%.0f) -> %s:%s\n", conn, f, (double)start, (double)len, nt_errstr(status[0]), nt_errstr(status[1])); } if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) return false; break; case OP_REOPEN: /* reopen the file */ for (server=0;server<NSERVERS;server++) { smbcli_close(cli[server][conn]->tree, fnum[server][conn][f]); fnum[server][conn][f] = -1; } for (server=0;server<NSERVERS;server++) { fnum[server][conn][f] = smbcli_open(cli[server][conn]->tree, FILENAME, O_RDWR|O_CREAT, DENY_NONE); if (fnum[server][conn][f] == -1) { printf("failed to reopen on share%d\n", server); return false; } } if (showall) { printf("reopen conn=%u f=%u\n", conn, f); } break; } return true; }
/* test read ops */ static BOOL test_read(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { union smb_read io; NTSTATUS status; BOOL ret = True; int fnum; uint8_t *buf; const int maxsize = 90000; const char *fname = BASEDIR "\\test.txt"; const char *test_data = "TEST DATA"; uint_t seed = time(NULL); buf = talloc_zero_size(mem_ctx, maxsize); if (!torture_setup_dir(cli, BASEDIR)) { return False; } printf("Testing RAW_READ_READ\n"); io.generic.level = RAW_READ_READ; fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); if (fnum == -1) { printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Trying empty file read\n"); io.read.in.file.fnum = fnum; io.read.in.count = 1; io.read.in.offset = 0; io.read.in.remaining = 0; io.read.out.data = buf; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.read.out.nread, 0); printf("Trying zero file read\n"); io.read.in.count = 0; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.read.out.nread, 0); printf("Trying bad fnum\n"); io.read.in.file.fnum = fnum+1; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); io.read.in.file.fnum = fnum; smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data)); printf("Trying small read\n"); io.read.in.file.fnum = fnum; io.read.in.offset = 0; io.read.in.remaining = 0; io.read.in.count = strlen(test_data); status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.read.out.nread, strlen(test_data)); if (memcmp(buf, test_data, strlen(test_data)) != 0) { ret = False; printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); goto done; } printf("Trying short read\n"); io.read.in.offset = 1; io.read.in.count = strlen(test_data); status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.read.out.nread, strlen(test_data)-1); if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { ret = False; printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); goto done; } if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { printf("Trying max offset\n"); io.read.in.offset = ~0; io.read.in.count = strlen(test_data); status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.read.out.nread, 0); } setup_buffer(buf, seed, maxsize); smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize); memset(buf, 0, maxsize); printf("Trying large read\n"); io.read.in.offset = 0; io.read.in.count = ~0; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_BUFFER(buf, seed, io.read.out.nread); printf("Trying locked region\n"); cli->session->pid++; if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) { printf("Failed to lock file at %d\n", __LINE__); ret = False; goto done; } cli->session->pid--; memset(buf, 0, maxsize); io.read.in.offset = 0; io.read.in.count = ~0; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); done: smbcli_close(cli->tree, fnum); smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); return ret; }
static bool torture_locktest7(struct torture_context *tctx, struct smbcli_state *cli1) { const char *fname = BASEDIR "\\lockt7.lck"; int fnum1; int fnum2 = -1; size_t size; uint8_t buf[200]; bool correct = false; torture_assert(tctx, torture_setup_dir(cli1, BASEDIR), talloc_asprintf(tctx, "Unable to set up %s", BASEDIR)); fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); memset(buf, 0, sizeof(buf)); torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf), "Failed to create file"); cli1->session->pid = 1; torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK), talloc_asprintf(tctx, "Unable to apply read lock on range 130:4, error was %s", smbcli_errstr(cli1->tree))); torture_comment(tctx, "pid1 successfully locked range 130:4 for READ\n"); torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4, talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s)", smbcli_errstr(cli1->tree))); torture_comment(tctx, "pid1 successfully read the range 130:4\n"); if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) { torture_comment(tctx, "pid1 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)"); } else { torture_fail(tctx, "pid1 successfully wrote to the range 130:4 (should be denied)"); } cli1->session->pid = 2; if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) { torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); } else { torture_comment(tctx, "pid2 successfully read the range 130:4\n"); } if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) { torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)"); } else { torture_fail(tctx, "pid2 successfully wrote to the range 130:4 (should be denied)"); } cli1->session->pid = 1; smbcli_unlock(cli1->tree, fnum1, 130, 4); torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 130, 4, 0, WRITE_LOCK), talloc_asprintf(tctx, "Unable to apply write lock on range 130:4, error was %s", smbcli_errstr(cli1->tree))); torture_comment(tctx, "pid1 successfully locked range 130:4 for WRITE\n"); torture_assert(tctx, smbcli_read(cli1->tree, fnum1, buf, 130, 4) == 4, talloc_asprintf(tctx, "pid1 unable to read the range 130:4, error was %s", smbcli_errstr(cli1->tree))); torture_comment(tctx, "pid1 successfully read the range 130:4\n"); torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) == 4, talloc_asprintf(tctx, "pid1 unable to write to the range 130:4, error was %s", smbcli_errstr(cli1->tree))); torture_comment(tctx, "pid1 successfully wrote to the range 130:4\n"); cli1->session->pid = 2; if (smbcli_read(cli1->tree, fnum1, buf, 130, 4) != 4) { torture_comment(tctx, "pid2 unable to read the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); torture_assert_ntstatus_equal(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)"); } else { torture_fail(tctx, "pid2 successfully read the range 130:4 (should be denied)"); } if (smbcli_write(cli1->tree, fnum1, 0, buf, 130, 4) != 4) { torture_comment(tctx, "pid2 unable to write to the range 130:4, error was %s\n", smbcli_errstr(cli1->tree)); if (!NT_STATUS_EQUAL(smbcli_nt_error(cli1->tree), NT_STATUS_FILE_LOCK_CONFLICT)) { torture_comment(tctx, "Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT) (%s)\n", __location__); goto fail; } } else { torture_comment(tctx, "pid2 successfully wrote to the range 130:4 (should be denied) (%s)\n", __location__); goto fail; } torture_comment(tctx, "Testing truncate of locked file.\n"); fnum2 = smbcli_open(cli1->tree, fname, O_RDWR|O_TRUNC, DENY_NONE); torture_assert(tctx, fnum2 != -1, "Unable to truncate locked file"); torture_comment(tctx, "Truncated locked file.\n"); torture_assert_ntstatus_ok(tctx, smbcli_getatr(cli1->tree, fname, NULL, &size, NULL), talloc_asprintf(tctx, "getatr failed (%s)", smbcli_errstr(cli1->tree))); torture_assert(tctx, size == 0, talloc_asprintf(tctx, "Unable to truncate locked file. Size was %u", (unsigned)size)); cli1->session->pid = 1; smbcli_unlock(cli1->tree, fnum1, 130, 4); correct = true; fail: smbcli_close(cli1->tree, fnum1); smbcli_close(cli1->tree, fnum2); smbcli_unlink(cli1->tree, fname); return correct; }
/* test readx ops */ static BOOL test_readx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { union smb_read io; NTSTATUS status; BOOL ret = True; int fnum; uint8_t *buf; const int maxsize = 90000; const char *fname = BASEDIR "\\test.txt"; const char *test_data = "TEST DATA"; uint_t seed = time(NULL); buf = talloc_zero_size(mem_ctx, maxsize); if (!torture_setup_dir(cli, BASEDIR)) { return False; } printf("Testing RAW_READ_READX\n"); fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); if (fnum == -1) { printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Trying empty file read\n"); io.generic.level = RAW_READ_READX; io.readx.in.file.fnum = fnum; io.readx.in.mincnt = 1; io.readx.in.maxcnt = 1; io.readx.in.offset = 0; io.readx.in.remaining = 0; io.readx.in.read_for_execute = False; io.readx.out.data = buf; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, 0); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); printf("Trying zero file read\n"); io.readx.in.mincnt = 0; io.readx.in.maxcnt = 0; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, 0); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); printf("Trying bad fnum\n"); io.readx.in.file.fnum = fnum+1; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); io.readx.in.file.fnum = fnum; smbcli_write(cli->tree, fnum, 0, test_data, 0, strlen(test_data)); printf("Trying small read\n"); io.readx.in.file.fnum = fnum; io.readx.in.offset = 0; io.readx.in.remaining = 0; io.readx.in.read_for_execute = False; io.readx.in.mincnt = strlen(test_data); io.readx.in.maxcnt = strlen(test_data); status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, strlen(test_data)); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); if (memcmp(buf, test_data, strlen(test_data)) != 0) { ret = False; printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); goto done; } printf("Trying short read\n"); io.readx.in.offset = 1; io.readx.in.mincnt = strlen(test_data); io.readx.in.maxcnt = strlen(test_data); status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, strlen(test_data)-1); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { ret = False; printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); goto done; } if (cli->transport->negotiate.capabilities & CAP_LARGE_FILES) { printf("Trying max offset\n"); io.readx.in.offset = 0xffffffff; io.readx.in.mincnt = strlen(test_data); io.readx.in.maxcnt = strlen(test_data); status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, 0); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); } setup_buffer(buf, seed, maxsize); smbcli_write(cli->tree, fnum, 0, buf, 0, maxsize); memset(buf, 0, maxsize); printf("Trying large read\n"); io.readx.in.offset = 0; io.readx.in.mincnt = 0xFFFF; io.readx.in.maxcnt = 0xFFFF; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); CHECK_BUFFER(buf, seed, io.readx.out.nread); printf("Trying extra large read\n"); io.readx.in.offset = 0; io.readx.in.mincnt = 100; io.readx.in.maxcnt = 80000; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); if (lp_parm_bool(-1, "torture", "samba3", False)) { printf("SAMBA3: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 80000); } else { CHECK_VALUE(io.readx.out.nread, 0); } CHECK_BUFFER(buf, seed, io.readx.out.nread); printf("Trying mincnt > maxcnt\n"); memset(buf, 0, maxsize); io.readx.in.offset = 0; io.readx.in.mincnt = 30000; io.readx.in.maxcnt = 20000; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); CHECK_BUFFER(buf, seed, io.readx.out.nread); printf("Trying mincnt < maxcnt\n"); memset(buf, 0, maxsize); io.readx.in.offset = 0; io.readx.in.mincnt = 20000; io.readx.in.maxcnt = 30000; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.remaining, 0xFFFF); CHECK_VALUE(io.readx.out.compaction_mode, 0); CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); CHECK_BUFFER(buf, seed, io.readx.out.nread); if (cli->transport->negotiate.capabilities & CAP_LARGE_READX) { printf("Trying large readx\n"); io.readx.in.offset = 0; io.readx.in.mincnt = 0; io.readx.in.maxcnt = 0x10000 - 1; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, 0xFFFF); io.readx.in.maxcnt = 0x10000; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); if (lp_parm_bool(-1, "torture", "samba3", False)) { printf("SAMBA3: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 0x10000); } else { CHECK_VALUE(io.readx.out.nread, 0); } io.readx.in.maxcnt = 0x10001; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); if (lp_parm_bool(-1, "torture", "samba3", False)) { printf("SAMBA3: large read extension\n"); CHECK_VALUE(io.readx.out.nread, 0x10001); } else { CHECK_VALUE(io.readx.out.nread, 0); } } printf("Trying locked region\n"); cli->session->pid++; if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 103, 1, 0, WRITE_LOCK))) { printf("Failed to lock file at %d\n", __LINE__); ret = False; goto done; } cli->session->pid--; memset(buf, 0, maxsize); io.readx.in.offset = 0; io.readx.in.mincnt = 100; io.readx.in.maxcnt = 200; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { printf("skipping large file tests - CAP_LARGE_FILES not set\n"); goto done; } printf("Trying large offset read\n"); io.readx.in.offset = ((uint64_t)0x2) << 32; io.readx.in.mincnt = 10; io.readx.in.maxcnt = 10; status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, 0); if (NT_STATUS_IS_ERR(smbcli_lock64(cli->tree, fnum, io.readx.in.offset, 1, 0, WRITE_LOCK))) { printf("Failed to lock file at %d\n", __LINE__); ret = False; goto done; } status = smb_raw_read(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.readx.out.nread, 0); done: smbcli_close(cli->tree, fnum); smbcli_deltree(cli->tree, BASEDIR); return ret; }
/* 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 torture_locktest1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) { const char *fname = BASEDIR "\\lockt1.lck"; int fnum1, fnum2, fnum3; time_t t1, t2; unsigned int lock_timeout; if (!torture_setup_dir(cli1, BASEDIR)) { return false; } fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); fnum2 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); fnum3 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx, "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree))); torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK), talloc_asprintf(tctx, "lock1 failed (%s)", smbcli_errstr(cli1->tree))); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), "lock2 succeeded! This is a locking bug\n"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), "lock2 succeeded! This is a locking bug\n"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT)) return false; torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, 5, 9, 0, WRITE_LOCK), talloc_asprintf(tctx, "lock1 failed (%s)", smbcli_errstr(cli1->tree))); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 5, 9, 0, WRITE_LOCK)), "lock2 succeeded! This is a locking bug"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), "lock2 succeeded! This is a locking bug"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), "lock2 succeeded! This is a locking bug"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT)) return false; lock_timeout = (6 + (random() % 20)); torture_comment(tctx, "Testing lock timeout with timeout=%u\n", lock_timeout); t1 = time(NULL); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, lock_timeout * 1000, WRITE_LOCK)), "lock3 succeeded! This is a locking bug\n"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT)) return false; t2 = time(NULL); if (t2 - t1 < 5) { torture_fail(tctx, "error: This server appears not to support timed lock requests"); } torture_comment(tctx, "server slept for %u seconds for a %u second timeout\n", (unsigned int)(t2-t1), lock_timeout); torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum2), talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree))); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum3, 0, 4, 0, WRITE_LOCK)), "lock4 succeeded! This is a locking bug"); if (!check_error(__location__, cli2, ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT)) return false; torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli1->tree))); torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum3), talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli2->tree))); torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname), talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree))); return true; }
/* looks at lock upgrade/downgrade. */ static bool torture_locktest5(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) { const char *fname = BASEDIR "\\lockt5.lck"; int fnum1, fnum2, fnum3; bool ret; uint8_t buf[1000]; bool correct = true; if (!torture_setup_dir(cli1, BASEDIR)) { return false; } fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); fnum3 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); memset(buf, 0, sizeof(buf)); torture_assert(tctx, smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) == sizeof(buf), "Failed to create file"); /* Check for NT bug... */ ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 1, 0, READ_LOCK)); smbcli_close(cli1->tree, fnum1); fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "this server %s the NT locking bug\n", ret ? "doesn't have" : "has"); smbcli_close(cli1->tree, fnum1); fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); smbcli_unlock(cli1->tree, fnum3, 0, 1); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 1, 1, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "the same process %s overlay a write with a read lock\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK)); EXPECTED(ret, false); torture_comment(tctx, "a different processs %s get a read lock on the first process lock stack\n", ret?"can":"cannot"); /* Unlock the process 2 lock. */ smbcli_unlock(cli2->tree, fnum2, 0, 4); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum3, 0, 4, 0, READ_LOCK)); EXPECTED(ret, false); torture_comment(tctx, "the same processs on a different fnum %s get a read lock\n", ret?"can":"cannot"); /* Unlock the process 1 fnum3 lock. */ smbcli_unlock(cli1->tree, fnum3, 0, 4); /* Stack 2 more locks here. */ ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "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(smbcli_unlock(cli1->tree, fnum1, 0, 4)) && NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "the first unlock removes the %s lock\n", ret?"WRITE":"READ"); /* Unlock the process 2 lock. */ smbcli_unlock(cli2->tree, fnum2, 0, 4); /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */ ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 1, 1)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)); EXPECTED(ret, true); torture_comment(tctx, "the same process %s unlock the stack of 3 locks\n", ret?"can":"cannot"); /* Ensure the next unlock fails. */ ret = NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 0, 4)); EXPECTED(ret, false); torture_comment(tctx, "the same process %s count the lock stack\n", !ret?"can":"cannot"); /* Ensure connection 2 can get a write lock. */ ret = NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 0, 4, 0, WRITE_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "a different processs %s get a write lock on the unlocked stack\n", ret?"can":"cannot"); torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree))); torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2), talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree))); torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum3), talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree))); torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname), talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree))); return correct; }
/* looks at overlapping locks */ static bool torture_locktest4(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) { const char *fname = BASEDIR "\\lockt4.lck"; int fnum1, fnum2, f; bool ret; uint8_t buf[1000]; bool correct = true; if (!torture_setup_dir(cli1, BASEDIR)) { return false; } fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); memset(buf, 0, sizeof(buf)); if (smbcli_write(cli1->tree, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) { torture_comment(tctx, "Failed to create file\n"); correct = false; goto fail; } ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 2, 4, 0, WRITE_LOCK)); EXPECTED(ret, false); torture_comment(tctx, "the same process %s set overlapping write locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 10, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 12, 4, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "the same process %s set overlapping read locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 20, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 22, 4, 0, WRITE_LOCK)); EXPECTED(ret, false); torture_comment(tctx, "a different connection %s set overlapping write locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 30, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, 32, 4, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "a different connection %s set overlapping read locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 40, 4, 0, WRITE_LOCK))) && NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 42, 4, 0, WRITE_LOCK))); EXPECTED(ret, false); torture_comment(tctx, "a different pid %s set overlapping write locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 50, 4, 0, READ_LOCK))) && NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 52, 4, 0, READ_LOCK))); EXPECTED(ret, true); torture_comment(tctx, "a different pid %s set overlapping read locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 60, 4, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "the same process %s set the same read lock twice\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 70, 4, 0, WRITE_LOCK)); EXPECTED(ret, false); torture_comment(tctx, "the same process %s set the same write lock twice\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 80, 4, 0, WRITE_LOCK)); EXPECTED(ret, false); torture_comment(tctx, "the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 90, 4, 0, READ_LOCK)); EXPECTED(ret, true); torture_comment(tctx, "the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK((cli1->session->pid = 1, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, WRITE_LOCK))) && NT_STATUS_IS_OK((cli1->session->pid = 2, smbcli_lock(cli1->tree, fnum1, 100, 4, 0, READ_LOCK))); EXPECTED(ret, false); torture_comment(tctx, "a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 110, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 112, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 110, 6)); EXPECTED(ret, false); torture_comment(tctx, "the same process %s coalesce read locks\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 120, 4, 0, WRITE_LOCK)) && (smbcli_read(cli2->tree, fnum2, buf, 120, 4) == 4); EXPECTED(ret, false); torture_comment(tctx, "this server %s strict write locking\n", ret?"doesn't do":"does"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 130, 4, 0, READ_LOCK)) && (smbcli_write(cli2->tree, fnum2, 0, buf, 130, 4) == 4); EXPECTED(ret, false); torture_comment(tctx, "this server %s strict read locking\n", ret?"doesn't do":"does"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 140, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 140, 4)); EXPECTED(ret, true); torture_comment(tctx, "this server %s do recursive read locking\n", ret?"does":"doesn't"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 150, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)) && (smbcli_read(cli2->tree, fnum2, buf, 150, 4) == 4) && !(smbcli_write(cli2->tree, fnum2, 0, buf, 150, 4) == 4) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 150, 4)); EXPECTED(ret, true); torture_comment(tctx, "this server %s do recursive lock overlays\n", ret?"does":"doesn't"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 160, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 160, 4)) && (smbcli_write(cli2->tree, fnum2, 0, buf, 160, 4) == 4) && (smbcli_read(cli2->tree, fnum2, buf, 160, 4) == 4); EXPECTED(ret, true); torture_comment(tctx, "the same process %s remove a read lock using write locking\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 170, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 170, 4)) && (smbcli_write(cli2->tree, fnum2, 0, buf, 170, 4) == 4) && (smbcli_read(cli2->tree, fnum2, buf, 170, 4) == 4); EXPECTED(ret, true); torture_comment(tctx, "the same process %s remove a write lock using read locking\n", ret?"can":"cannot"); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, WRITE_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 190, 4, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_unlock(cli1->tree, fnum1, 190, 4)) && !(smbcli_write(cli2->tree, fnum2, 0, buf, 190, 4) == 4) && (smbcli_read(cli2->tree, fnum2, buf, 190, 4) == 4); EXPECTED(ret, true); torture_comment(tctx, "the same process %s remove the first lock first\n", ret?"does":"doesn't"); smbcli_close(cli1->tree, fnum1); smbcli_close(cli2->tree, fnum2); fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); f = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE); ret = NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 0, 8, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, f, 0, 1, 0, READ_LOCK)) && NT_STATUS_IS_OK(smbcli_close(cli1->tree, fnum1)) && ((fnum1 = smbcli_open(cli1->tree, fname, O_RDWR, DENY_NONE)) != -1) && NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, 7, 1, 0, WRITE_LOCK)); smbcli_close(cli1->tree, f); smbcli_close(cli1->tree, fnum1); EXPECTED(ret, true); torture_comment(tctx, "the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't"); fail: smbcli_close(cli1->tree, fnum1); smbcli_close(cli2->tree, fnum2); smbcli_unlink(cli1->tree, fname); return correct; }
/* This test checks that 1) the server supports the full offset range in lock requests */ static bool torture_locktest3(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) { const char *fname = BASEDIR "\\lockt3.lck"; int fnum1, fnum2, i; uint32_t offset; extern int torture_numops; #define NEXT_OFFSET offset += (~(uint32_t)0) / torture_numops torture_comment(tctx, "Testing 32 bit offset ranges"); if (!torture_setup_dir(cli1, BASEDIR)) { return false; } fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree))); fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree))); torture_comment(tctx, "Establishing %d locks\n", torture_numops); for (offset=i=0;i<torture_numops;i++) { NEXT_OFFSET; torture_assert_ntstatus_ok(tctx, smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK), talloc_asprintf(tctx, "lock1 %d failed (%s)", i, smbcli_errstr(cli1->tree))); torture_assert_ntstatus_ok(tctx, smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK), talloc_asprintf(tctx, "lock2 %d failed (%s)", i, smbcli_errstr(cli1->tree))); } torture_comment(tctx, "Testing %d locks\n", torture_numops); for (offset=i=0;i<torture_numops;i++) { NEXT_OFFSET; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-2, 1, 0, WRITE_LOCK)), talloc_asprintf(tctx, "error: lock1 %d succeeded!", i)); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-1, 1, 0, WRITE_LOCK)), talloc_asprintf(tctx, "error: lock2 %d succeeded!", i)); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli1->tree, fnum1, offset-1, 1, 0, WRITE_LOCK)), talloc_asprintf(tctx, "error: lock3 %d succeeded!", i)); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli2->tree, fnum2, offset-2, 1, 0, WRITE_LOCK)), talloc_asprintf(tctx, "error: lock4 %d succeeded!", i)); } torture_comment(tctx, "Removing %d locks\n", torture_numops); for (offset=i=0;i<torture_numops;i++) { NEXT_OFFSET; torture_assert_ntstatus_ok(tctx, smbcli_unlock(cli1->tree, fnum1, offset-1, 1), talloc_asprintf(tctx, "unlock1 %d failed (%s)", i, smbcli_errstr(cli1->tree))); torture_assert_ntstatus_ok(tctx, smbcli_unlock(cli2->tree, fnum2, offset-2, 1), talloc_asprintf(tctx, "unlock2 %d failed (%s)", i, smbcli_errstr(cli1->tree))); } torture_assert_ntstatus_ok(tctx, smbcli_close(cli1->tree, fnum1), talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli1->tree))); torture_assert_ntstatus_ok(tctx, smbcli_close(cli2->tree, fnum2), talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli2->tree))); torture_assert_ntstatus_ok(tctx, smbcli_unlink(cli1->tree, fname), talloc_asprintf(tctx, "unlink failed (%s)", smbcli_errstr(cli1->tree))); return true; }
/* 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 torture_locktest2(struct torture_context *tctx, struct smbcli_state *cli) { const char *fname = BASEDIR "\\lockt2.lck"; int fnum1, fnum2, fnum3; if (!torture_setup_dir(cli, BASEDIR)) { return false; } torture_comment(tctx, "Testing pid context\n"); cli->session->pid = 1; fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); torture_assert(tctx, fnum1 != -1, talloc_asprintf(tctx, "open of %s failed (%s)", fname, smbcli_errstr(cli->tree))); fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); torture_assert(tctx, fnum2 != -1, talloc_asprintf(tctx, "open2 of %s failed (%s)", fname, smbcli_errstr(cli->tree))); cli->session->pid = 2; fnum3 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); torture_assert(tctx, fnum3 != -1, talloc_asprintf(tctx, "open3 of %s failed (%s)\n", fname, smbcli_errstr(cli->tree))); cli->session->pid = 1; torture_assert_ntstatus_ok(tctx, smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK), talloc_asprintf(tctx, "lock1 failed (%s)", smbcli_errstr(cli->tree))); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum1, 0, 4, 0, WRITE_LOCK)), "WRITE lock1 succeeded! This is a locking bug"); if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, WRITE_LOCK)), "WRITE lock2 succeeded! This is a locking bug"); if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum2, 0, 4, 0, READ_LOCK)), "READ lock2 succeeded! This is a locking bug"); if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT)) return false; torture_assert_ntstatus_ok(tctx, smbcli_lock(cli->tree, fnum1, 100, 4, 0, WRITE_LOCK), talloc_asprintf(tctx, "lock at 100 failed (%s)", smbcli_errstr(cli->tree))); cli->session->pid = 2; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 100, 4)), "unlock at 100 succeeded! This is a locking bug"); torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 4)), "unlock1 succeeded! This is a locking bug"); if (!check_error(__location__, cli, ERRDOS, ERRnotlocked, NT_STATUS_RANGE_NOT_LOCKED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_unlock(cli->tree, fnum1, 0, 8)), "unlock2 succeeded! This is a locking bug"); if (!check_error(__location__, cli, ERRDOS, ERRnotlocked, NT_STATUS_RANGE_NOT_LOCKED)) return false; torture_assert(tctx, !NT_STATUS_IS_OK(smbcli_lock(cli->tree, fnum3, 0, 4, 0, WRITE_LOCK)), "lock3 succeeded! This is a locking bug"); if (!check_error(__location__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false; cli->session->pid = 1; torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum1), talloc_asprintf(tctx, "close1 failed (%s)", smbcli_errstr(cli->tree))); torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum2), talloc_asprintf(tctx, "close2 failed (%s)", smbcli_errstr(cli->tree))); torture_assert_ntstatus_ok(tctx, smbcli_close(cli->tree, fnum3), talloc_asprintf(tctx, "close3 failed (%s)", smbcli_errstr(cli->tree))); return true; }
/* test write unlock ops */ static BOOL test_writeunlock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { union smb_write io; NTSTATUS status; BOOL ret = True; int fnum; uint8_t *buf; const int maxsize = 90000; const char *fname = BASEDIR "\\test.txt"; uint_t seed = time(NULL); union smb_fileinfo finfo; buf = talloc_zero_size(mem_ctx, maxsize); if (!torture_setup_dir(cli, BASEDIR)) { return False; } printf("Testing RAW_WRITE_WRITEUNLOCK\n"); io.generic.level = RAW_WRITE_WRITEUNLOCK; fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); if (fnum == -1) { printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Trying zero write\n"); io.writeunlock.in.file.fnum = fnum; io.writeunlock.in.count = 0; io.writeunlock.in.offset = 0; io.writeunlock.in.remaining = 0; io.writeunlock.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count); setup_buffer(buf, seed, maxsize); printf("Trying small write\n"); io.writeunlock.in.count = 9; io.writeunlock.in.offset = 4; io.writeunlock.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf+4, seed, 9); CHECK_VALUE(IVAL(buf,0), 0); setup_buffer(buf, seed, maxsize); smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, 0, WRITE_LOCK); status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf+4, seed, 9); CHECK_VALUE(IVAL(buf,0), 0); setup_buffer(buf, seed, maxsize); printf("Trying large write\n"); io.writeunlock.in.count = 4000; io.writeunlock.in.offset = 0; io.writeunlock.in.data = buf; smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, 0, WRITE_LOCK); status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writeunlock.out.nwritten, 4000); status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed, 4000); printf("Trying bad fnum\n"); io.writeunlock.in.file.fnum = fnum+1; io.writeunlock.in.count = 4000; io.writeunlock.in.offset = 0; io.writeunlock.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); printf("Setting file as sparse\n"); status = torture_set_sparse(cli->tree, fnum); CHECK_STATUS(status, NT_STATUS_OK); if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { printf("skipping large file tests - CAP_LARGE_FILES not set\n"); goto done; } printf("Trying 2^32 offset\n"); setup_buffer(buf, seed, maxsize); io.writeunlock.in.file.fnum = fnum; io.writeunlock.in.count = 4000; io.writeunlock.in.offset = 0xFFFFFFFF - 2000; io.writeunlock.in.data = buf; smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, 0, WRITE_LOCK); status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writeunlock.out.nwritten, 4000); CHECK_ALL_INFO(io.writeunlock.in.count + (uint64_t)io.writeunlock.in.offset, size); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed, 4000); done: smbcli_close(cli->tree, fnum); smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); return ret; }
/* test writex ops */ static BOOL test_writex(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { union smb_write io; NTSTATUS status; BOOL ret = True; int fnum, i; uint8_t *buf; const int maxsize = 90000; const char *fname = BASEDIR "\\test.txt"; uint_t seed = time(NULL); union smb_fileinfo finfo; int max_bits=63; if (!lp_parm_bool(-1, "torture", "dangerous", False)) { max_bits=33; printf("dangerous not set - limiting range of test to 2^%d\n", max_bits); } buf = talloc_zero_size(mem_ctx, maxsize); if (!torture_setup_dir(cli, BASEDIR)) { return False; } printf("Testing RAW_WRITE_WRITEX\n"); io.generic.level = RAW_WRITE_WRITEX; fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); if (fnum == -1) { printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Trying zero write\n"); io.writex.in.file.fnum = fnum; io.writex.in.offset = 0; io.writex.in.wmode = 0; io.writex.in.remaining = 0; io.writex.in.count = 0; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, 0); setup_buffer(buf, seed, maxsize); printf("Trying small write\n"); io.writex.in.count = 9; io.writex.in.offset = 4; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf+4, seed, 9); CHECK_VALUE(IVAL(buf,0), 0); setup_buffer(buf, seed, maxsize); printf("Trying large write\n"); io.writex.in.count = 4000; io.writex.in.offset = 0; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, 4000); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed, 4000); printf("Trying bad fnum\n"); io.writex.in.file.fnum = fnum+1; io.writex.in.count = 4000; io.writex.in.offset = 0; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); printf("Testing wmode\n"); io.writex.in.file.fnum = fnum; io.writex.in.count = 1; io.writex.in.offset = 0; io.writex.in.wmode = 1; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); io.writex.in.wmode = 2; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); printf("Trying locked region\n"); cli->session->pid++; if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 3, 1, 0, WRITE_LOCK))) { printf("Failed to lock file at %s\n", __location__); ret = False; goto done; } cli->session->pid--; io.writex.in.wmode = 0; io.writex.in.count = 4; io.writex.in.offset = 0; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); printf("Setting file as sparse\n"); status = torture_set_sparse(cli->tree, fnum); CHECK_STATUS(status, NT_STATUS_OK); if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { printf("skipping large file tests - CAP_LARGE_FILES not set\n"); goto done; } printf("Trying 2^32 offset\n"); setup_buffer(buf, seed, maxsize); io.writex.in.file.fnum = fnum; io.writex.in.count = 4000; io.writex.in.offset = 0xFFFFFFFF - 2000; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, 4000); CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed, 4000); for (i=33;i<max_bits;i++) { printf("Trying 2^%d offset\n", i); setup_buffer(buf, seed+1, maxsize); io.writex.in.file.fnum = fnum; io.writex.in.count = 4000; io.writex.in.offset = ((uint64_t)1) << i; io.writex.in.data = buf; status = smb_raw_write(cli->tree, &io); if (i>33 && NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { break; } CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.writex.out.nwritten, 4000); CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed+1, 4000); } printf("limit is 2^%d\n", i); setup_buffer(buf, seed, maxsize); done: smbcli_close(cli->tree, fnum); smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); return ret; }