The branch, master has been updated via cd87d9f torture: basic FSCTL_SET_ZERO_DATA tests via 5d8bfb5 torture: add file zero-range checker via 750e67a torture: malformed FSCTL_QUERY_ALLOCATED_RANGES tests via 10e86bf torture: basic FSCTL_QUERY_ALLOCATED_RANGES test via 9a9cae9 torture: split pattern write into helper function via 3fcbfa9 torture: test oversize FSCTL_SET_SPARSE request from abe499b torture: Correctly initialize array size
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit cd87d9f78228a9eb3ebbc0deda41e9c1094905ca Author: David Disseldorp <dd...@samba.org> Date: Tue Sep 2 20:07:20 2014 +0200 torture: basic FSCTL_SET_ZERO_DATA tests Attempt to punch holes in a file using FSCTL_SET_ZERO_DATA. Check that the resulting data is zeroed. Also confirm that when the file is marked sparse, the zeroed range is no longer allocated. Finally, check that removing the sparse flag causes any holes to be unsparsed. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> Autobuild-User(master): Jeremy Allison <j...@samba.org> Autobuild-Date(master): Mon Sep 8 21:40:34 CEST 2014 on sn-devel-104 commit 5d8bfb5c41bbddb3c404bd177048fe2e396ce8f2 Author: David Disseldorp <dd...@samba.org> Date: Tue Sep 2 20:07:19 2014 +0200 torture: add file zero-range checker Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 750e67aeb2f86a6b67dc949e2585ee9a585a628e Author: David Disseldorp <dd...@samba.org> Date: Tue Sep 2 20:07:18 2014 +0200 torture: malformed FSCTL_QUERY_ALLOCATED_RANGES tests Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 10e86bf481d09913c174f92847c5c1b879f3688c Author: David Disseldorp <dd...@samba.org> Date: Tue Sep 2 20:07:17 2014 +0200 torture: basic FSCTL_QUERY_ALLOCATED_RANGES test Add a test and helper function for FSCTL_QUERY_ALLOCATED_RANGES requests. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 9a9cae9555cc612e7f8d227595a0b931a41801dc Author: David Disseldorp <dd...@samba.org> Date: Tue Sep 2 20:07:16 2014 +0200 torture: split pattern write into helper function This allows for patterned writes after file creation, as needed for sparse file integrity testing. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 3fcbfa967ac61da85a959fada5a4d92b54f459eb Author: David Disseldorp <dd...@samba.org> Date: Tue Sep 2 20:07:15 2014 +0200 torture: test oversize FSCTL_SET_SPARSE request Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: source4/torture/smb2/ioctl.c | 610 ++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 593 insertions(+), 17 deletions(-) Changeset truncated at 500 lines: diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c index df98b04..344ff0e 100644 --- a/source4/torture/smb2/ioctl.c +++ b/source4/torture/smb2/ioctl.c @@ -119,6 +119,42 @@ static uint64_t patt_hash(uint64_t off) return off; } +static bool write_pattern(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len, + uint64_t patt_off) +{ + NTSTATUS status; + uint64_t i; + uint8_t *buf; + uint64_t buf_off = 0; + + if (len == 0) { + return true; + } + + buf = talloc_zero_size(mem_ctx, len); + torture_assert(torture, (buf != NULL), "no memory for file data buf"); + + for (i = 0; i <= len - 8; i += 8) { + SBVAL(buf, i, patt_hash(patt_off)); + patt_off += 8; + } + + while (len > 0) { + uint64_t io_sz = MIN(1024 * 1024, len); + status = smb2_util_write(tree, h, + buf + buf_off, off, io_sz); + torture_assert_ntstatus_ok(torture, status, "file write"); + + len -= io_sz; + buf_off += io_sz; + off += io_sz; + } + + return true; +} + static bool check_pattern(struct torture_context *torture, struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_handle h, uint64_t off, uint64_t len, @@ -150,6 +186,36 @@ static bool check_pattern(struct torture_context *torture, return true; } +static bool check_zero(struct torture_context *torture, + struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle h, uint64_t off, uint64_t len) +{ + uint64_t i; + struct smb2_read r; + NTSTATUS status; + + ZERO_STRUCT(r); + r.in.file.handle = h; + r.in.length = len; + r.in.offset = off; + status = smb2_read(tree, mem_ctx, &r); + torture_assert_ntstatus_ok(torture, status, "read"); + + torture_assert_u64_equal(torture, r.out.data.length, len, + "read data len mismatch"); + + for (i = 0; i <= len - 8; i += 8) { + uint64_t data = BVAL(r.out.data.data, i); + torture_assert_u64_equal(torture, data, 0, + talloc_asprintf(mem_ctx, "read zero " + "bad at %llu\n", + (unsigned long long)i)); + } + + talloc_free(r.out.data.data); + return true; +} + static bool test_setup_open(struct torture_context *torture, struct smb2_tree *tree, TALLOC_CTX *mem_ctx, const char *fname, @@ -189,11 +255,7 @@ static bool test_setup_create_fill(struct torture_context *torture, uint32_t desired_access, uint32_t file_attributes) { - NTSTATUS status; bool ok; - uint64_t i; - uint8_t *buf = talloc_zero_size(mem_ctx, size); - torture_assert(torture, (buf != NULL), "no memory for file data buf"); smb2_util_unlink(tree, fname); @@ -205,19 +267,8 @@ static bool test_setup_create_fill(struct torture_context *torture, torture_assert(torture, ok, "file open"); if (size > 0) { - uint64_t cur_off = 0; - for (i = 0; i <= size - 8; i += 8) { - SBVAL(buf, i, patt_hash(i)); - } - while (size > 0) { - uint64_t io_sz = MIN(1024 * 1024, size); - status = smb2_util_write(tree, *fh, - buf + cur_off, cur_off, io_sz); - torture_assert_ntstatus_ok(torture, status, "file write"); - - size -= io_sz; - cur_off += io_sz; - } + ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0); + torture_assert(torture, ok, "write pattern"); } return true; } @@ -2718,6 +2769,523 @@ static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture, return true; } +static bool test_ioctl_sparse_set_oversize(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_ioctl ioctl; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + uint8_t buf[100]; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_response_size = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + /* + * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER + * Windows still successfully processes the request. + */ + ZERO_ARRAY(buf); + buf[0] = 0xFF; /* attempt to set sparse */ + ioctl.smb2.in.out.data = buf; + ioctl.smb2.in.out.length = ARRAY_SIZE(buf); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after set"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_SPARSE; + ioctl.smb2.in.max_response_size = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_ARRAY(buf); /* clear sparse */ + ioctl.smb2.in.out.data = buf; + ioctl.smb2.in.out.length = ARRAY_SIZE(buf); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr after clear"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static NTSTATUS test_ioctl_qar_req(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + int64_t req_off, + int64_t req_len, + struct file_alloced_range_buf **_rsp, + uint64_t *_rsp_count) +{ + union smb_ioctl ioctl; + NTSTATUS status; + enum ndr_err_code ndr_ret; + struct file_alloced_range_buf far_buf; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES; + ioctl.smb2.in.max_response_size = 1024; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + far_buf.file_off = req_off; + far_buf.len = req_len; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &far_buf, + (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf); + if (ndr_ret != NDR_ERR_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; + goto err_out; + } + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + if (!NT_STATUS_IS_OK(status)) { + goto err_out; + } + + if (ioctl.smb2.out.out.length == 0) { + goto done; + } + + if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) { + torture_comment(torture, "invalid qry_alloced rsp len: %zd:", + ioctl.smb2.out.out.length); + status = NT_STATUS_INVALID_VIEW_SIZE; + goto err_out; + } + + far_count = (ioctl.smb2.out.out.length / sizeof(far_buf)); + far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf, + far_count); + if (far_rsp == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + + for (i = 0; i < far_count; i++) { + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &far_rsp[i], + (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf); + if (ndr_ret != NDR_ERR_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; + goto err_out; + } + } + +done: + *_rsp = far_rsp; + *_rsp_count = far_count; + status = NT_STATUS_OK; +err_out: + talloc_free(tmp_ctx); + return status; +} + +static bool test_ioctl_sparse_qar(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + bool is_sparse; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + /* zero length file, shouldn't have any ranges */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, !is_sparse, "sparse attr before set"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 0, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse); + torture_assert_ntstatus_ok(torture, status, "test_sparse_get"); + torture_assert(torture, is_sparse, "no sparse attr after set"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 1024, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 0, + "unexpected response len"); + + /* write into the (now) sparse file at 4k offset */ + ok = write_pattern(torture, tree, tmp_ctx, fh, + 4096, /* off */ + 1024, /* len */ + 4096); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* query range before write off, it should be alloced */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 4096, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset"); + torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len"); + + /* + * Query range before and past write, it should be allocated up to the + * end of the write. + */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + 8192, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset"); + torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + union smb_ioctl ioctl; + struct file_alloced_range_buf far_buf; + NTSTATUS status; + enum ndr_err_code ndr_ret; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + size_t old_len; + + /* zero length file, shouldn't have any ranges */ + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + if (!ok) { + smb2_util_close(tree, fh); + torture_skip(torture, "Sparse files not supported\n"); + } + + /* no allocated ranges, no space for range response, should pass */ + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES; + ioctl.smb2.in.max_response_size = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + far_buf.file_off = 0; + far_buf.len = 1024; + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &far_buf, + (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf); + torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES"); + + /* write into the file at 4k offset */ + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + 1024, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* allocated range, no space for range response, should fail */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_BUFFER_TOO_SMALL, "qar no space"); + + /* oversize (2x) file_alloced_range_buf in request, should pass */ + ioctl.smb2.in.max_response_size = 1024; + old_len = ioctl.smb2.in.out.length; + ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out, + (ioctl.smb2.in.out.length * 2)); + torture_assert(torture, ok, "2x data buffer"); + memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data, + old_len); + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "qar too big"); + + /* no file_alloced_range_buf in request, should fail */ + data_blob_free(&ioctl.smb2.in.out); + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_INVALID_PARAMETER, "qar empty"); + + return true; +} + +/* + * 2.3.57 FSCTL_SET_ZERO_DATA Request + * + * How an implementation zeros data within a file is implementation-dependent. + * A file system MAY choose to deallocate regions of disk space that have been + * zeroed.<50> + * <50> + * ... NTFS might deallocate disk space in the file if the file is stored on an + * NTFS volume, and the file is sparse or compressed. It will free any allocated + * space in chunks of 64 kilobytes that begin at an offset that is a multiple of + * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte + * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not + * deallocated. + */ +static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle fh, + int64_t off, + int64_t beyond_final_zero) +{ + union smb_ioctl ioctl; + NTSTATUS status; + enum ndr_err_code ndr_ret; + struct file_zero_data_info zdata_info; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = fh; + ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA; + ioctl.smb2.in.max_response_size = 0; + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + zdata_info.file_off = off; + zdata_info.beyond_final_zero = beyond_final_zero; -- Samba Shared Repository