The branch, master has been updated via 177692c torture/ioctl: add test_ioctl_sparse_copy_chunk test via 048068e torture/ioctl: add sparse_compressed test via 200bab0 torture/ioctl: extend sparse_hole_dealloc test via c7f1284 torture/ioctl: add sparse_hole_dealloc test via af9a99c torture/ioctl: fix check_[zero/pattern]() for len=0 from 618af83 vfs_prealloc: Remove call to gpfs_prealloc
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 177692c0042558fc5b8812645a7c67769ebade6b Author: David Disseldorp <dd...@samba.org> Date: Mon Feb 9 12:09:35 2015 +0100 torture/ioctl: add test_ioctl_sparse_copy_chunk test This test copies unallocated and allocated ranges from a sparse file into a sparse and non-sparse destination file using FSCTL_SRV_COPYCHUNK. 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): Thu Feb 12 03:19:32 CET 2015 on sn-devel-104 commit 048068e83632fa217939afa70c5c3b4213c91bb0 Author: David Disseldorp <dd...@samba.org> Date: Mon Feb 9 12:09:34 2015 +0100 torture/ioctl: add sparse_compressed test This test checks whether a file marked with sparse and compression attributes is deallocated following FSCTL_SET_ZERO_DATA. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 200bab04202210ac1019e1c64857c589d3f89890 Author: David Disseldorp <dd...@samba.org> Date: Mon Feb 9 12:09:33 2015 +0100 torture/ioctl: extend sparse_hole_dealloc test Check whether unwritten extents in a sparse file are allocated. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit c7f1284f03f5b473d2159e20723c9461964081b2 Author: David Disseldorp <dd...@samba.org> Date: Mon Feb 9 12:09:32 2015 +0100 torture/ioctl: add sparse_hole_dealloc test This test finds the minimum length at which a zeroed range in a sparse file is deallocated by the underlying filesystem. It also checks whether zeroed neighbours are merged for deallocation. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit af9a99c35c46f0f82d4cf07b79442257731b966c Author: David Disseldorp <dd...@samba.org> Date: Mon Feb 9 12:09:31 2015 +0100 torture/ioctl: fix check_[zero/pattern]() for len=0 Subtraction currently triggers an underflow. Signed-off-by: David Disseldorp <dd...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: source4/torture/smb2/ioctl.c | 568 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 567 insertions(+), 1 deletion(-) Changeset truncated at 500 lines: diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c index 344ff0e..931a013 100644 --- a/source4/torture/smb2/ioctl.c +++ b/source4/torture/smb2/ioctl.c @@ -3,7 +3,7 @@ test suite for SMB2 ioctl operations - Copyright (C) David Disseldorp 2011-2013 + Copyright (C) David Disseldorp 2011-2015 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -164,6 +164,10 @@ static bool check_pattern(struct torture_context *torture, struct smb2_read r; NTSTATUS status; + if (len == 0) { + return true; + } + ZERO_STRUCT(r); r.in.file.handle = h; r.in.length = len; @@ -194,6 +198,10 @@ static bool check_zero(struct torture_context *torture, struct smb2_read r; NTSTATUS status; + if (len == 0) { + return true; + } + ZERO_STRUCT(r); r.in.file.handle = h; r.in.length = len; @@ -3287,6 +3295,558 @@ static bool test_ioctl_sparse_punch(struct torture_context *torture, } /* + * Find the point at which a zeroed range in a sparse file is deallocated by the + * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k + * increments. Also check whether zeroed neighbours are merged for deallocation. + */ +static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t file_size; + uint64_t hlen; + uint64_t dealloc_chunk_len = 0; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + 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 1"); + + /* check for FS sparse 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"); + } + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + file_size = 1024 * 1024; + + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + file_size, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* check allocated ranges, should be fully allocated */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* 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, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, file_size, + "unexpected far len"); + + /* punch holes in sizes of 1k increments */ + for (hlen = 0; hlen <= file_size; hlen += 4096) { + + /* punch a hole from zero to the current increment */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + hlen); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* ensure hole is zeroed, and pattern is consistent */ + ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, fh, hlen, + file_size - hlen, hlen); + torture_assert(torture, ok, "allocated pattern range"); + + /* Check allocated ranges, hole might have been deallocated */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES"); + if ((hlen == file_size) && (far_count == 0)) { + /* hole covered entire file, deallocation occurred */ + dealloc_chunk_len = file_size; + break; + } + + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + if (far_rsp[0].file_off != 0) { + /* + * We now know the hole punch length needed to trigger a + * deallocation on this FS... + */ + dealloc_chunk_len = hlen; + torture_comment(torture, "hole punch %lu@0 resulted in " + "deallocation of %lu@0\n", hlen, + far_rsp[0].file_off); + torture_assert_u64_equal(torture, + file_size - far_rsp[0].len, + far_rsp[0].file_off, + "invalid alloced range"); + break; + } + } + + if (dealloc_chunk_len == 0) { + torture_comment(torture, "strange, this FS never deallocates" + "zeroed ranges in sparse files\n"); + return true; /* FS specific, not a failure */ + } + + /* + * Check whether deallocation occurs when the (now known) + * deallocation chunk size is punched via two ZERO_DATA requests. + * I.e. Does the FS merge the two ranges and deallocate the chunk? + * NTFS on Windows Server 2012 does not. + */ + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + file_size, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* divide dealloc chunk size by two, to use as punch length */ + hlen = dealloc_chunk_len >> 1; + + /* + * /half of dealloc chunk size 1M\ + * | | + * /offset 0 | /dealloc chunk size | + * |------------------ |-------------------|-------------------| + * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern | + */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + hlen); /* beyond final zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + hlen, /* off */ + dealloc_chunk_len); /* beyond final */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* ensure holes are zeroed, and pattern is consistent */ + ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len, + file_size - dealloc_chunk_len, dealloc_chunk_len); + torture_assert(torture, ok, "allocated pattern range"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + + if ((far_count == 0) && (dealloc_chunk_len == file_size)) { + torture_comment(torture, "holes merged for deallocation of " + "full file\n"); + return true; + } + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + if (far_rsp[0].file_off == dealloc_chunk_len) { + torture_comment(torture, "holes merged for deallocation of " + "%lu chunk\n", dealloc_chunk_len); + torture_assert_u64_equal(torture, + file_size - far_rsp[0].len, + far_rsp[0].file_off, + "invalid alloced range"); + } else { + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected deallocation"); + torture_comment(torture, "holes not merged for deallocation\n"); + } + + smb2_util_close(tree, fh); + + /* + * Check whether an unwritten range is allocated when a sparse file is + * written to at an offset past the dealloc chunk size: + * + * /dealloc chunk size + * /offset 0 | + * |------------------ |-------------------| + * | unwritten | pattern | + */ + 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 1"); + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + ok = write_pattern(torture, tree, tmp_ctx, fh, + dealloc_chunk_len, /* off */ + 1024, /* len */ + dealloc_chunk_len); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len + 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, 1, + "unexpected response len"); + if (far_rsp[0].file_off == 0) { + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len + 1024, + "unexpected far len"); + torture_comment(torture, "unwritten range fully allocated\n"); + } else { + torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len, + "unexpected deallocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, 1024, + "unexpected far len"); + torture_comment(torture, "unwritten range not allocated\n"); + } + + ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len); + torture_assert(torture, ok, "sparse zeroed range"); + + ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len, + 1024, dealloc_chunk_len); + torture_assert(torture, ok, "allocated pattern range"); + + /* unsparse, should now be fully allocated */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + dealloc_chunk_len + 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, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected deallocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, + dealloc_chunk_len + 1024, + "unexpected far len"); + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* check whether a file with compression and sparse attrs can be deallocated */ +static bool test_ioctl_sparse_compressed(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle fh; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t file_size = 1024 * 1024; + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + + 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 1"); + + /* check for FS sparse file and compression support */ + 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_ioctl_compress_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, "FS compression not supported\n"); + } + + /* set compression and write some data */ + status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh, + COMPRESSION_FORMAT_DEFAULT); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION"); + + ok = write_pattern(torture, tree, tmp_ctx, fh, + 0, /* off */ + file_size, /* len */ + 0); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* set sparse - now sparse and compressed */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* check allocated ranges, should be fully alloced */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* 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, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, file_size, + "unexpected far len"); + + /* zero (hole-punch) all data, with sparse and compressed attrs */ + status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size); /* beyond_final_zero */ + torture_assert_ntstatus_ok(torture, status, "zero_data"); + + /* + * Windows Server 2012 still deallocates a zeroed range when a sparse + * file carries the compression attribute. + */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh, + 0, /* off */ + file_size, /* len */ + &far_rsp, + &far_count); + torture_assert_ntstatus_ok(torture, status, + "FSCTL_QUERY_ALLOCATED_RANGES req failed"); + if (far_count == 0) { + torture_comment(torture, "sparse & compressed file " + "deallocated after hole-punch\n"); + } else { + torture_assert_u64_equal(torture, far_count, 1, + "unexpected response len"); + torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, + "unexpected far off"); + torture_assert_u64_equal(torture, far_rsp[0].len, file_size, + "unexpected far len"); + torture_comment(torture, "sparse & compressed file fully " + "allocated after hole-punch\n"); + } + + smb2_util_close(tree, fh); + talloc_free(tmp_ctx); + return true; +} + +/* + * Create a sparse file, then attempt to copy unallocated and allocated ranges + * into a target file using FSCTL_SRV_COPYCHUNK. + */ +static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle src_h; + struct smb2_handle dest_h; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + bool ok; + uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */ + struct file_alloced_range_buf *far_rsp = NULL; + uint64_t far_count = 0; + union smb_ioctl ioctl; + struct srv_copychunk_copy cc_copy; + struct srv_copychunk_rsp cc_rsp; + enum ndr_err_code ndr_ret; + + ok = test_setup_create_fill(torture, tree, tmp_ctx, + FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "setup file"); + + /* check for FS sparse file support */ + status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &src_h, + &ok); + torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS"); + smb2_util_close(tree, src_h); + if (!ok) { + torture_skip(torture, "Sparse files not supported\n"); + } + + ok = test_setup_copy_chunk(torture, tree, tmp_ctx, + 1, /* chunks */ + &src_h, 0, /* src file */ + SEC_RIGHTS_FILE_ALL, + &dest_h, 0, /* dest file */ + SEC_RIGHTS_FILE_ALL, + &cc_copy, + &ioctl); + torture_assert(torture, ok, "setup copy chunk error"); + + /* set sparse */ + status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE"); + + /* start after dealloc_chunk_len, to create an unwritten sparse range */ + ok = write_pattern(torture, tree, tmp_ctx, src_h, + dealloc_chunk_len, /* off */ + 1024, /* len */ + dealloc_chunk_len); /* pattern offset */ + torture_assert(torture, ok, "write pattern"); + + /* Skip test if 64k chunk is allocated - FS specific */ + status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h, + 0, /* off */ + dealloc_chunk_len + 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, 1, + "unexpected response len"); + if (far_rsp[0].file_off == 0) { + torture_skip(torture, "unwritten range fully allocated\n"); + } + + torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len, + "unexpected allocation"); + torture_assert_u64_equal(torture, far_rsp[0].len, 1024, + "unexpected far len"); + + /* copy-chunk unallocated + written ranges into non-sparse dest */ + + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = dealloc_chunk_len + 1024; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK"); + + ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, + &cc_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_pull_srv_copychunk_rsp"); + + ok = check_copy_chunk_rsp(torture, &cc_rsp, + 1, /* chunks written */ + 0, /* chunk bytes unsuccessfully written */ + dealloc_chunk_len + 1024); /* bytes written */ + torture_assert(torture, ok, "bad copy chunk response data"); -- Samba Shared Repository