static bool test_session_reauth1(struct torture_context *tctx, struct smbcli_state *cli) { NTSTATUS status; struct smb_composite_sesssetup io; int fnum, num; const int dlen = 255; char *data; char fname[256]; char buf[dlen+1]; bool ok = true; uint16_t vuid1 = cli->session->vuid; data = generate_random_str(tctx, dlen); torture_assert(tctx, (data != NULL), "memory allocation failed"); snprintf(fname, sizeof(fname), "raw_session_reconnect_%.8s.dat", data); fnum = smbcli_nt_create_full(cli->tree, fname, 0, SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); torture_assert_ntstatus_ok_goto(tctx, smbcli_nt_error(cli->tree), ok, done, "create file"); torture_assert_goto(tctx, fnum > 0, ok, done, "create file"); num = smbcli_smbwrite(cli->tree, fnum, data, 0, dlen); torture_assert_int_equal_goto(tctx, num, dlen, ok, done, "write file"); ZERO_STRUCT(io); io.in.sesskey = cli->transport->negotiate.sesskey; io.in.capabilities = cli->transport->negotiate.capabilities; io.in.credentials = cmdline_credentials; io.in.workgroup = lpcfg_workgroup(tctx->lp_ctx); io.in.gensec_settings = lpcfg_gensec_settings(tctx, tctx->lp_ctx); status = smb_composite_sesssetup(cli->session, &io); torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "setup2"); torture_assert_int_equal_goto(tctx, io.out.vuid, vuid1, ok, done, "setup2"); buf[dlen] = '\0'; num = smbcli_read(cli->tree, fnum, &buf, 0, dlen); torture_assert_int_equal_goto(tctx, num, dlen, ok, done, "read file"); torture_assert_str_equal_goto(tctx, buf, data, ok, done, "read file"); done: talloc_free(data); if (fnum > 0) { status = smbcli_close(cli->tree, fnum); torture_assert_ntstatus_ok(tctx, status, "close"); } return ok; }
NTSTATUS gpo_copy_file(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, const char *nt_path, const char *unix_path) { NTSTATUS result; int fnum; int fd = 0; char *data = NULL; static int io_bufsize = 64512; int read_size = io_bufsize; off_t nread = 0; if ((fnum = smbcli_open(cli->tree, nt_path, O_RDONLY, DENY_NONE)) == -1) { result = NT_STATUS_NO_SUCH_FILE; goto out; } if ((fd = open(unix_path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) { result = map_nt_error_from_unix(errno); goto out; } if ((data = talloc_size(mem_ctx, read_size)) == NULL) { result = NT_STATUS_NO_MEMORY; goto out; } while (1) { int n = smbcli_read(cli->tree, fnum, data, nread, read_size); if (n <= 0) break; if (write(fd, data, n) != n) { break; } nread += n; } result = NT_STATUS_OK; out: SAFE_FREE(data); if (fnum) { smbcli_close(cli->tree, fnum); } if (fd) { close(fd); } return result; }
/* check that a stream has the right contents */ static BOOL check_stream(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname, const char *sname, const char *value) { int fnum; const char *full_name; uint8_t *buf; ssize_t ret; full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname); fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE); if (value == NULL) { if (fnum != -1) { printf("should have failed stream open of %s\n", full_name); return False; } return True; } if (fnum == -1) { printf("Failed to open stream '%s' - %s\n", full_name, smbcli_errstr(cli->tree)); return False; } buf = talloc_size(mem_ctx, strlen(value)+11); ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11); if (ret != strlen(value)) { printf("Failed to read %lu bytes from stream '%s' - got %d\n", (long)strlen(value), full_name, (int)ret); return False; } if (memcmp(buf, value, strlen(value)) != 0) { printf("Bad data in stream\n"); return False; } smbcli_close(cli->tree, fnum); return True; }
/* test seek ops */ static BOOL test_seek(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) { union smb_seek io; union smb_fileinfo finfo; union smb_setfileinfo sfinfo; NTSTATUS status; BOOL ret = True; int fnum, fnum2; const char *fname = BASEDIR "\\test.txt"; uint8_t c[2]; if (!torture_setup_dir(cli, BASEDIR)) { return False; } fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE); if (fnum == -1) { printf("Failed to open test.txt - %s\n", smbcli_errstr(cli->tree)); ret = False; goto done; } finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum; printf("Trying bad handle\n"); io.lseek.in.file.fnum = fnum+1; io.lseek.in.mode = SEEK_MODE_START; io.lseek.in.offset = 0; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); printf("Trying simple seek\n"); io.lseek.in.file.fnum = fnum; io.lseek.in.mode = SEEK_MODE_START; io.lseek.in.offset = 17; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, 17); status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 0); printf("Trying relative seek\n"); io.lseek.in.file.fnum = fnum; io.lseek.in.mode = SEEK_MODE_CURRENT; io.lseek.in.offset = -3; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, 14); printf("Trying end seek\n"); io.lseek.in.file.fnum = fnum; io.lseek.in.mode = SEEK_MODE_END; io.lseek.in.offset = 0; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); finfo.generic.level = RAW_FILEINFO_ALL_INFO; finfo.all_info.in.file.fnum = fnum; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, finfo.all_info.out.size); printf("Trying max seek\n"); io.lseek.in.file.fnum = fnum; io.lseek.in.mode = SEEK_MODE_START; io.lseek.in.offset = -1; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, 0xffffffff); printf("Testing position information change\n"); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 0); printf("Trying max overflow\n"); io.lseek.in.file.fnum = fnum; io.lseek.in.mode = SEEK_MODE_CURRENT; io.lseek.in.offset = 1000; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, 999); printf("Testing position information change\n"); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 0); printf("trying write to update offset\n"); ZERO_STRUCT(c); if (smbcli_write(cli->tree, fnum, 0, c, 0, 2) != 2) { printf("Write failed - %s\n", smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Testing position information change\n"); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 0); io.lseek.in.file.fnum = fnum; io.lseek.in.mode = SEEK_MODE_CURRENT; io.lseek.in.offset = 0; status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, 2); if (smbcli_read(cli->tree, fnum, c, 0, 1) != 1) { printf("Read failed - %s\n", smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Testing position information change\n"); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 1); status = smb_raw_seek(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.lseek.out.offset, 1); printf("Testing position information\n"); fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE); if (fnum2 == -1) { printf("2nd open failed - %s\n", smbcli_errstr(cli->tree)); ret = False; goto done; } sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; sfinfo.position_information.in.file.fnum = fnum2; sfinfo.position_information.in.position = 25; status = smb_raw_setfileinfo(cli->tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum2; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 25); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 1); printf("position_information via paths\n"); sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; sfinfo.position_information.in.file.path = fname; sfinfo.position_information.in.position = 32; status = smb_raw_setpathinfo(cli->tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.fnum = fnum2; status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 25); finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; finfo.position_information.in.file.path = fname; status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(finfo.position_information.out.position, 0); done: smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); return ret; }
bool torture_samba3_checkfsp(struct torture_context *torture, struct smbcli_state *cli) { const char *fname = "test.txt"; const char *dirname = "testdir"; int fnum; NTSTATUS status; bool ret = true; TALLOC_CTX *mem_ctx; ssize_t nread; char buf[16]; struct smbcli_tree *tree2; torture_assert(torture, mem_ctx = talloc_init("torture_samba3_checkfsp"), "talloc_init failed\n"); torture_assert_ntstatus_equal(torture, torture_second_tcon(torture, cli->session, torture_setting_string(torture, "share", NULL), &tree2), NT_STATUS_OK, "creating second tcon"); /* Try a read on an invalid FID */ nread = smbcli_read(cli->tree, 4711, buf, 0, sizeof(buf)); CHECK_STATUS(torture, smbcli_nt_error(cli->tree), NT_STATUS_INVALID_HANDLE); /* Try a read on a directory handle */ torture_assert(torture, torture_setup_dir(cli, dirname), "creating test directory"); /* Open the directory */ { union smb_open io; io.generic.level = RAW_OPEN_NTCREATEX; io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; io.ntcreatex.in.root_fid.fnum = 0; io.ntcreatex.in.security_flags = 0; io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; io.ntcreatex.in.alloc_size = 0; io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; io.ntcreatex.in.create_options = 0; io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; io.ntcreatex.in.fname = dirname; status = smb_raw_open(cli->tree, mem_ctx, &io); if (!NT_STATUS_IS_OK(status)) { torture_result(torture, TORTURE_FAIL, "smb_open on the directory failed: %s\n", nt_errstr(status)); ret = false; goto done; } fnum = io.ntcreatex.out.file.fnum; } /* Try a read on the directory */ nread = smbcli_read(cli->tree, fnum, buf, 0, sizeof(buf)); if (nread >= 0) { torture_result(torture, TORTURE_FAIL, "smbcli_read on a directory succeeded, expected " "failure\n"); ret = false; } CHECK_STATUS(torture, smbcli_nt_error(cli->tree), NT_STATUS_INVALID_DEVICE_REQUEST); /* Same test on the second tcon */ nread = smbcli_read(tree2, fnum, buf, 0, sizeof(buf)); if (nread >= 0) { torture_result(torture, TORTURE_FAIL, "smbcli_read on a directory succeeded, expected " "failure\n"); ret = false; } CHECK_STATUS(torture, smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE); smbcli_close(cli->tree, fnum); /* Try a normal file read on a second tcon */ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); if (fnum == -1) { torture_result(torture, TORTURE_FAIL, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); ret = false; goto done; } nread = smbcli_read(tree2, fnum, buf, 0, sizeof(buf)); CHECK_STATUS(torture, smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE); smbcli_close(cli->tree, fnum); done: smbcli_deltree(cli->tree, dirname); talloc_free(mem_ctx); return ret; }
bool torture_casetable(struct torture_context *tctx, struct smbcli_state *cli) { char *fname; int fnum; int c, i; #define MAX_EQUIVALENCE 8 codepoint_t equiv[0x10000][MAX_EQUIVALENCE]; torture_comment(tctx, "Determining upper/lower case table\n"); memset(equiv, 0, sizeof(equiv)); torture_assert(tctx, torture_setup_dir(cli, "\\utable"), "Error setting up dir \\utable"); for (c=1; c < 0x10000; c++) { size_t size; if (c == '.' || c == '\\') continue; torture_comment(tctx, "%04x (%c)\n", c, isprint(c)?c:'.'); fname = form_name(lp_iconv_convenience(tctx->lp_ctx), c); fnum = smbcli_nt_create_full(cli->tree, fname, 0, #if 0 SEC_RIGHT_MAXIMUM_ALLOWED, #else SEC_RIGHTS_FILE_ALL, #endif FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); torture_assert(tctx, fnum != -1, talloc_asprintf(tctx, "Failed to create file with char %04x\n", c)); size = 0; if (NT_STATUS_IS_ERR(smbcli_qfileinfo(cli->tree, 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) { torture_comment(tctx, "too many chars match?? size=%d c=0x%04x\n", (int)size, c); smbcli_close(cli->tree, fnum); return false; } smbcli_read(cli->tree, fnum, c2, 0, size); torture_comment(tctx, "%04x: ", c); equiv[c][0] = c; for (i=0; i<size/sizeof(int); i++) { torture_comment(tctx, "%04x ", c2[i]); equiv[c][i+1] = c2[i]; } torture_comment(tctx, "\n"); } smbcli_write(cli->tree, fnum, 0, &c, size, sizeof(c)); smbcli_close(cli->tree, fnum); } smbcli_unlink(cli->tree, "\\utable\\*"); smbcli_rmdir(cli->tree, "\\utable"); return true; }
static bool rw_torture(struct torture_context *tctx, struct smbcli_state *c) { const char *lockfname = "\\torture.lck"; char *fname; int fnum; int fnum2; pid_t pid2, pid = getpid(); int i, j; uint8_t buf[1024]; bool correct = true; fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL, DENY_NONE); if (fnum2 == -1) fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE); if (fnum2 == -1) { torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree)); return false; } generate_random_buffer(buf, sizeof(buf)); for (i=0;i<torture_numops;i++) { uint_t n = (uint_t)random()%10; if (i % 10 == 0) { if (torture_setting_bool(tctx, "progress", true)) { torture_comment(tctx, "%d\r", i); fflush(stdout); } } asprintf(&fname, "\\torture.%u", n); if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) { return false; } fnum = smbcli_open(c->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL); if (fnum == -1) { torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree)); correct = false; break; } if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) { torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree)); correct = false; } for (j=0;j<50;j++) { if (smbcli_write(c->tree, fnum, 0, buf, sizeof(pid)+(j*sizeof(buf)), sizeof(buf)) != sizeof(buf)) { torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree)); correct = false; } } pid2 = 0; if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) { torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree)); correct = false; } if (pid2 != pid) { torture_comment(tctx, "data corruption!\n"); correct = false; } if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) { torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree)); correct = false; } if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) { torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree)); correct = false; } if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) { torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree)); correct = false; } free(fname); } smbcli_close(c->tree, fnum2); smbcli_unlink(c->tree, lockfname); torture_comment(tctx, "%d\n", i); return correct; }
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; }
/* 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; }
/* test write ops */ static BOOL test_write(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_WRITE\n"); io.generic.level = RAW_WRITE_WRITE; fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); if (fnum == -1) { printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)); ret = False; goto done; } printf("Trying zero write\n"); io.write.in.file.fnum = fnum; io.write.in.count = 0; io.write.in.offset = 0; io.write.in.remaining = 0; io.write.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.write.out.nwritten, 0); setup_buffer(buf, seed, maxsize); printf("Trying small write\n"); io.write.in.count = 9; io.write.in.offset = 4; io.write.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.write.out.nwritten, io.write.in.count); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf+4, seed, 9); CHECK_VALUE(IVAL(buf,0), 0); setup_buffer(buf, seed, maxsize); printf("Trying large write\n"); io.write.in.count = 4000; io.write.in.offset = 0; io.write.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.write.out.nwritten, 4000); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed, 4000); printf("Trying bad fnum\n"); io.write.in.file.fnum = fnum+1; io.write.in.count = 4000; io.write.in.offset = 0; io.write.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); printf("Setting file as sparse\n"); status = torture_set_sparse(cli->tree, fnum); CHECK_STATUS(status, NT_STATUS_OK); if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { printf("skipping large file tests - CAP_LARGE_FILES not set\n"); goto done; } if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { printf("skipping large file tests - CAP_LARGE_FILES not set\n"); goto done; } printf("Trying 2^32 offset\n"); setup_buffer(buf, seed, maxsize); io.write.in.file.fnum = fnum; io.write.in.count = 4000; io.write.in.offset = 0xFFFFFFFF - 2000; io.write.in.data = buf; status = smb_raw_write(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VALUE(io.write.out.nwritten, 4000); CHECK_ALL_INFO(io.write.in.count + (uint64_t)io.write.in.offset, size); memset(buf, 0, maxsize); if (smbcli_read(cli->tree, fnum, buf, io.write.in.offset, 4000) != 4000) { printf("read failed at %s\n", __location__); ret = False; goto done; } CHECK_BUFFER(buf, seed, 4000); done: smbcli_close(cli->tree, fnum); smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); return ret; }
/* test writex ops */ static BOOL test_writex(struct 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; }
static NTSTATUS gp_get_file (struct smbcli_tree *tree, const char *remote_src, const char *local_dst) { int fh_remote, fh_local; uint8_t *buf; size_t nread = 0; size_t buf_size = 1024; size_t file_size; uint16_t attr; /* Open the remote file */ fh_remote = smbcli_open(tree, remote_src, O_RDONLY, DENY_NONE); if (fh_remote == -1) { DEBUG(0, ("Failed to open remote file: %s\n", remote_src)); return NT_STATUS_UNSUCCESSFUL; } /* Open the local file */ fh_local = open(local_dst, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fh_local == -1) { DEBUG(0, ("Failed to open local file: %s\n", local_dst)); return NT_STATUS_UNSUCCESSFUL; } /* Get the remote file size for error checking */ if (NT_STATUS_IS_ERR(smbcli_qfileinfo(tree, fh_remote, &attr, &file_size, NULL, NULL, NULL, NULL, NULL)) && NT_STATUS_IS_ERR(smbcli_getattrE(tree, fh_remote, &attr, &file_size, NULL, NULL, NULL))) { DEBUG(0, ("Failed to get remote file size: %s\n", smbcli_errstr(tree))); return NT_STATUS_UNSUCCESSFUL; } buf = talloc_zero_array(tree, uint8_t, buf_size); NT_STATUS_HAVE_NO_MEMORY(buf); /* Copy the contents of the file */ while (1) { int n = smbcli_read(tree, fh_remote, buf, nread, buf_size); if (n <= 0) { break; } if (write(fh_local, buf, n) != n) { DEBUG(0, ("Short write while copying file.\n")); talloc_free(buf); return NT_STATUS_UNSUCCESSFUL; } nread += n; } /* Bytes read should match the file size, or the copy was incomplete */ if (nread != file_size) { DEBUG(0, ("Remote/local file size mismatch after copying file: " "%s (remote %zu, local %zu).\n", remote_src, file_size, nread)); talloc_free(buf); return NT_STATUS_UNSUCCESSFUL; } /* Close the files */ smbcli_close(tree, fh_remote); close(fh_local); talloc_free(buf); return NT_STATUS_OK; }