torture: add locking tests for copychunk
[obnox/samba/samba-obnox.git] / source4 / torture / smb2 / ioctl.c
index 7a1c3850f7ce91ee7801352c2580bb15fe02e7dd..8cada1588a44747c9b3071ead5fa4d9e04d47e6c 100644 (file)
@@ -45,17 +45,11 @@ static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
        smb2_util_unlink(tree, FNAME);
 
        status = torture_smb2_testfile(tree, FNAME, &h);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("create write\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "create write");
 
        ZERO_ARRAY(buf);
        status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("failed write\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "write");
 
        ZERO_STRUCT(ioctl);
        ioctl.smb2.level = RAW_IOCTL_SMB2;
@@ -65,10 +59,13 @@ static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
        ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
 
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_ENUM_SNAPS failed\n");
-               return false;
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
+        || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
+               torture_comment(torture,
+                       "FSCTL_SRV_ENUM_SNAPS not supported, skipping\n");
+               return true;
        }
+       torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
 
        return true;
 }
@@ -90,17 +87,11 @@ static bool test_ioctl_req_resume_key(struct torture_context *torture,
        smb2_util_unlink(tree, FNAME);
 
        status = torture_smb2_testfile(tree, FNAME, &h);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("create write\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "create write");
 
        ZERO_ARRAY(buf);
        status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("failed write\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "write");
 
        ZERO_STRUCT(ioctl);
        ioctl.smb2.level = RAW_IOCTL_SMB2;
@@ -110,16 +101,12 @@ static bool test_ioctl_req_resume_key(struct torture_context *torture,
        ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
 
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_REQUEST_RESUME_KEY failed\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
 
        ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
                        (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_req_resume_key_rsp");
 
        ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
 
@@ -127,7 +114,44 @@ static bool test_ioctl_req_resume_key(struct torture_context *torture,
        return true;
 }
 
-static bool test_setup_copy_chunk(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+static uint64_t patt_hash(uint64_t off)
+{
+       return off;
+}
+
+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,
+                         uint64_t patt_off)
+{
+       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, patt_off += 8) {
+               uint64_t data = BVAL(r.out.data.data, i);
+               torture_assert_u64_equal(torture, data, patt_hash(patt_off),
+                                        talloc_asprintf(torture, "read data "
+                                                        "pattern bad at %llu\n",
+                                                        (unsigned long long)i));
+       }
+
+       talloc_free(r.out.data.data);
+       return true;
+}
+
+static bool test_setup_copy_chunk(struct torture_context *torture,
+                                 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
                                  uint32_t nchunks,
                                  struct smb2_handle *src_h,
                                  uint64_t src_size,
@@ -139,6 +163,7 @@ static bool test_setup_copy_chunk(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
        struct req_resume_key_rsp res_key;
        NTSTATUS status;
        enum ndr_err_code ndr_ret;
+       uint64_t i;
        uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
        if (buf == NULL) {
                printf("no mem for file data buffer\n");
@@ -149,31 +174,25 @@ static bool test_setup_copy_chunk(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
        smb2_util_unlink(tree, FNAME2);
 
        status = torture_smb2_testfile(tree, FNAME, src_h);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("create write\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "create write");
 
        if (src_size > 0) {
-               status = smb2_util_write(tree, *src_h, buf, 0, src_size);
-               if (!NT_STATUS_IS_OK(status)) {
-                       printf("failed src write\n");
-                       return false;
+               for (i = 0; i <= src_size - 8; i += 8) {
+                       SBVAL(buf, i, patt_hash(i));
                }
+               status = smb2_util_write(tree, *src_h, buf, 0, src_size);
+               torture_assert_ntstatus_ok(torture, status, "src write");
        }
 
        status = torture_smb2_testfile(tree, FNAME2, dest_h);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("create write\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status, "create write");
 
        if (dest_size > 0) {
-               status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
-               if (!NT_STATUS_IS_OK(status)) {
-                       printf("failed dest write\n");
-                       return false;
+               for (i = 0; i <= dest_size - 8; i += 8) {
+                       SBVAL(buf, i, patt_hash(i));
                }
+               status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
+               torture_assert_ntstatus_ok(torture, status, "dest write");
        }
 
        ZERO_STRUCTPN(ioctl);
@@ -185,16 +204,15 @@ static bool test_setup_copy_chunk(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
        ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
 
        status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_REQUEST_RESUME_KEY failed\n");
-               return false;
-       }
+       torture_assert_ntstatus_ok(torture, status,
+                                  "FSCTL_SRV_REQUEST_RESUME_KEY");
+
 
        ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
                        (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_req_resume_key_rsp");
 
        ZERO_STRUCTPN(ioctl);
        ioctl->smb2.level = RAW_IOCTL_SMB2;
@@ -216,26 +234,18 @@ static bool test_setup_copy_chunk(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
 }
 
 
-static bool check_copy_chunk_rsp(struct srv_copychunk_rsp *cc_rsp,
+static bool check_copy_chunk_rsp(struct torture_context *torture,
+                                struct srv_copychunk_rsp *cc_rsp,
                                 uint32_t ex_chunks_written,
                                 uint32_t ex_chunk_bytes_written,
                                 uint32_t ex_total_bytes_written)
 {
-       if (cc_rsp->chunks_written != ex_chunks_written) {
-               printf("expected %u chunks, got %u\n",
-                      ex_chunks_written, cc_rsp->chunks_written);
-               return false;
-       }
-       if (cc_rsp->chunk_bytes_written != ex_chunk_bytes_written) {
-               printf("expected %u chunk bytes remaining, got %u\n",
-                      ex_chunk_bytes_written, cc_rsp->chunk_bytes_written);
-               return false;
-       }
-       if (cc_rsp->total_bytes_written != ex_total_bytes_written) {
-               printf("expected %u total bytes, got %u\n",
-                      ex_total_bytes_written, cc_rsp->total_bytes_written);
-               return false;
-       }
+       torture_assert_int_equal(torture, cc_rsp->chunks_written,
+                                ex_chunks_written, "num chunks");
+       torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
+                                ex_chunk_bytes_written, "chunk bytes written");
+       torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
+                                ex_total_bytes_written, "chunk total bytes");
        return true;
 }
 
@@ -252,7 +262,7 @@ static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
        enum ndr_err_code ndr_ret;
        bool ok;
 
-       ok = test_setup_copy_chunk(tree, tmp_ctx,
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
                                   1, /* 1 chunk */
                                   &src_h, 4096, /* fill 4096 byte src file */
                                   &dest_h, 0,  /* 0 byte dest file */
@@ -270,24 +280,19 @@ static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
        ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
                                       &cc_copy,
                        (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_push_srv_copychunk_copy");
 
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_COPYCHUNK failed\n");
-               return false;
-       }
+       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);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_srv_copychunk_rsp");
 
-       ok = check_copy_chunk_rsp(&cc_rsp,
+       ok = check_copy_chunk_rsp(torture, &cc_rsp,
                                  1,    /* chunks written */
                                  0,    /* chunk bytes unsuccessfully written */
                                  4096); /* total bytes written */
@@ -295,6 +300,11 @@ static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
                return false;
        }
 
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
+       if (!ok) {
+               return false;
+       }
+
        smb2_util_close(tree, src_h);
        smb2_util_close(tree, dest_h);
        talloc_free(tmp_ctx);
@@ -314,7 +324,7 @@ static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
        enum ndr_err_code ndr_ret;
        bool ok;
 
-       ok = test_setup_copy_chunk(tree, tmp_ctx,
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
                                   2, /* chunks */
                                   &src_h, 8192, /* src file */
                                   &dest_h, 0,  /* dest file */
@@ -336,24 +346,19 @@ static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
        ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
                                       &cc_copy,
                        (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_push_srv_copychunk_copy");
 
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_COPYCHUNK failed\n");
-               return false;
-       }
+       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);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_srv_copychunk_rsp");
 
-       ok = check_copy_chunk_rsp(&cc_rsp,
+       ok = check_copy_chunk_rsp(torture, &cc_rsp,
                                  2,    /* chunks written */
                                  0,    /* chunk bytes unsuccessfully written */
                                  8192);        /* total bytes written */
@@ -380,7 +385,7 @@ static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
        enum ndr_err_code ndr_ret;
        bool ok;
 
-       ok = test_setup_copy_chunk(tree, tmp_ctx,
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
                                   2, /* chunks */
                                   &src_h, 100, /* src file */
                                   &dest_h, 0,  /* dest file */
@@ -402,24 +407,19 @@ static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
        ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
                                       &cc_copy,
                        (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_push_srv_copychunk_copy");
 
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_COPYCHUNK failed\n");
-               return false;
-       }
+       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);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_srv_copychunk_rsp");
 
-       ok = check_copy_chunk_rsp(&cc_rsp,
+       ok = check_copy_chunk_rsp(torture, &cc_rsp,
                                  2,    /* chunks written */
                                  0,    /* chunk bytes unsuccessfully written */
                                  100); /* total bytes written */
@@ -427,6 +427,11 @@ static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
                return false;
        }
 
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
+       if (!ok) {
+               return false;
+       }
+
        smb2_util_close(tree, src_h);
        smb2_util_close(tree, dest_h);
        talloc_free(tmp_ctx);
@@ -446,7 +451,7 @@ static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
        enum ndr_err_code ndr_ret;
        bool ok;
 
-       ok = test_setup_copy_chunk(tree, tmp_ctx,
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
                                   2, /* chunks */
                                   &src_h, 8192, /* src file */
                                   &dest_h, 4096, /* dest file */
@@ -469,24 +474,19 @@ static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
        ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
                                       &cc_copy,
                        (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_push_srv_copychunk_copy");
 
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_COPYCHUNK failed\n");
-               return false;
-       }
+       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);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               return false;
-       }
+       torture_assert_ndr_success(torture, ndr_ret,
+                                  "ndr_pull_srv_copychunk_rsp");
 
-       ok = check_copy_chunk_rsp(&cc_rsp,
+       ok = check_copy_chunk_rsp(torture, &cc_rsp,
                                  2,    /* chunks written */
                                  0,    /* chunk bytes unsuccessfully written */
                                  8192); /* total bytes written */
@@ -494,6 +494,11 @@ static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
                return false;
        }
 
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
+       if (!ok) {
+               return false;
+       }
+
        smb2_util_close(tree, src_h);
        smb2_util_close(tree, dest_h);
        talloc_free(tmp_ctx);
@@ -513,7 +518,7 @@ static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
        enum ndr_err_code ndr_ret;
        bool ok;
 
-       ok = test_setup_copy_chunk(tree, tmp_ctx,
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
                                   2, /* chunks */
                                   &src_h, 4096, /* src file */
                                   &dest_h, 0,  /* dest file */
@@ -535,31 +540,316 @@ static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
        ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
                                       &cc_copy,
                        (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
+       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,
+                                 2,    /* chunks written */
+                                 0,    /* chunk bytes unsuccessfully written */
+                                 8192); /* total bytes written */
+       if (!ok) {
+               return false;
+       }
+
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
+       if (!ok) {
                return false;
        }
 
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
+       if (!ok) {
+               return false;
+       }
+
+       smb2_util_close(tree, src_h);
+       smb2_util_close(tree, dest_h);
+       talloc_free(tmp_ctx);
+       return true;
+}
+
+static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
+                                        struct smb2_tree *tree)
+{
+       struct smb2_handle src_h;
+       struct smb2_handle dest_h;
+       NTSTATUS status;
+       union smb_ioctl ioctl;
+       TALLOC_CTX *tmp_ctx = talloc_new(tree);
+       struct srv_copychunk_copy cc_copy;
+       struct srv_copychunk_rsp cc_rsp;
+       enum ndr_err_code ndr_ret;
+       bool ok;
+
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+                                  1, /* chunks */
+                                  &src_h, 4096, /* src file */
+                                  &dest_h, 0,  /* dest file */
+                                  &cc_copy,
+                                  &ioctl);
+       if (!ok) {
+               return false;
+       }
+
+       /* send huge chunk length request */
+       cc_copy.chunks[0].source_off = 0;
+       cc_copy.chunks[0].target_off = 0;
+       cc_copy.chunks[0].length = UINT_MAX;
+
+       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, "marshalling request");
+
        status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-       if (!NT_STATUS_IS_OK(status)) {
-               printf("FSCTL_SRV_COPYCHUNK failed\n");
+       torture_assert_ntstatus_equal(torture, status,
+                                     NT_STATUS_INVALID_PARAMETER,
+                                     "bad oversize chunk response");
+
+       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, "unmarshalling response");
+
+       torture_comment(torture, "limit max chunks, got %u\n",
+                       cc_rsp.chunks_written);
+       torture_comment(torture, "limit max chunk len, got %u\n",
+                       cc_rsp.chunk_bytes_written);
+       torture_comment(torture, "limit max total bytes, got %u\n",
+                       cc_rsp.total_bytes_written);
+
+       smb2_util_close(tree, src_h);
+       smb2_util_close(tree, dest_h);
+       talloc_free(tmp_ctx);
+       return true;
+}
+
+static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
+                                         struct smb2_tree *tree)
+{
+       struct smb2_handle src_h;
+       struct smb2_handle src_h2;
+       struct smb2_handle dest_h;
+       NTSTATUS status;
+       union smb_ioctl ioctl;
+       TALLOC_CTX *tmp_ctx = talloc_new(tree);
+       struct srv_copychunk_copy cc_copy;
+       struct srv_copychunk_rsp cc_rsp;
+       enum ndr_err_code ndr_ret;
+       bool ok;
+       struct smb2_lock lck;
+       struct smb2_lock_element el[1];
+
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+                                  1, /* chunks */
+                                  &src_h, 4096, /* src file */
+                                  &dest_h, 0,  /* dest file */
+                                  &cc_copy,
+                                  &ioctl);
+       if (!ok) {
                return false;
        }
 
+       cc_copy.chunks[0].source_off = 0;
+       cc_copy.chunks[0].target_off = 0;
+       cc_copy.chunks[0].length = 4096;
+
+       /* open and lock the copychunk src file */
+       status = torture_smb2_testfile(tree, FNAME, &src_h2);
+       torture_assert_ntstatus_ok(torture, status, "2nd src open");
+
+       lck.in.lock_count       = 0x0001;
+       lck.in.lock_sequence    = 0x00000000;
+       lck.in.file.handle      = src_h2;
+       lck.in.locks            = el;
+       el[0].offset            = cc_copy.chunks[0].source_off;
+       el[0].length            = cc_copy.chunks[0].length;
+       el[0].reserved          = 0;
+       el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
+
+       status = smb2_lock(tree, &lck);
+       torture_assert_ntstatus_ok(torture, status, "lock");
+
+       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);
+       /*
+        * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
+        *
+        * Edgar Olougouna @ MS wrote:
+        * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
+        * discrepancy observed between Windows versions, we confirm that the
+        * behavior change is expected.
+        *
+        * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
+        * to move the chunks from the source to the destination.
+        * These ReadFile/WriteFile APIs go through the byte-range lock checks,
+        * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
+        *
+        * Prior to Windows Server 2012, CopyChunk used mapped sections to move
+        * the data. And byte range locks are not enforced on mapped I/O, and
+        * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
+        */
+       torture_assert_ntstatus_equal(torture, status,
+                                     NT_STATUS_FILE_LOCK_CONFLICT,
+                                     "FSCTL_SRV_COPYCHUNK locked");
+
+       /* should get cc response data with the lock conflict status */
+       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,
+                                 0,    /* chunks written */
+                                 0,    /* chunk bytes unsuccessfully written */
+                                 0);   /* total bytes written */
+
+       lck.in.lock_count       = 0x0001;
+       lck.in.lock_sequence    = 0x00000001;
+       lck.in.file.handle      = src_h2;
+       lck.in.locks            = el;
+       el[0].offset            = cc_copy.chunks[0].source_off;
+       el[0].length            = cc_copy.chunks[0].length;
+       el[0].reserved          = 0;
+       el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
+       status = smb2_lock(tree, &lck);
+       torture_assert_ntstatus_ok(torture, status, "unlock");
+
+       status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
+       torture_assert_ntstatus_ok(torture, status,
+                                  "FSCTL_SRV_COPYCHUNK unlocked");
+
        ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
                                       &cc_rsp,
                        (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
+       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 */
+                                 4096); /* total bytes written */
+       if (!ok) {
                return false;
        }
 
-       ok = check_copy_chunk_rsp(&cc_rsp,
-                                 2,    /* chunks written */
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
+       if (!ok) {
+               return false;
+       }
+
+       smb2_util_close(tree, src_h2);
+       smb2_util_close(tree, src_h);
+       smb2_util_close(tree, dest_h);
+       talloc_free(tmp_ctx);
+       return true;
+}
+
+static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
+                                          struct smb2_tree *tree)
+{
+       struct smb2_handle src_h;
+       struct smb2_handle dest_h;
+       struct smb2_handle dest_h2;
+       NTSTATUS status;
+       union smb_ioctl ioctl;
+       TALLOC_CTX *tmp_ctx = talloc_new(tree);
+       struct srv_copychunk_copy cc_copy;
+       struct srv_copychunk_rsp cc_rsp;
+       enum ndr_err_code ndr_ret;
+       bool ok;
+       struct smb2_lock lck;
+       struct smb2_lock_element el[1];
+
+       ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+                                  1, /* chunks */
+                                  &src_h, 4096, /* src file */
+                                  &dest_h, 4096,       /* dest file */
+                                  &cc_copy,
+                                  &ioctl);
+       if (!ok) {
+               return false;
+       }
+
+       cc_copy.chunks[0].source_off = 0;
+       cc_copy.chunks[0].target_off = 0;
+       cc_copy.chunks[0].length = 4096;
+
+       /* open and lock the copychunk dest file */
+       status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
+       torture_assert_ntstatus_ok(torture, status, "2nd src open");
+
+       lck.in.lock_count       = 0x0001;
+       lck.in.lock_sequence    = 0x00000000;
+       lck.in.file.handle      = dest_h2;
+       lck.in.locks            = el;
+       el[0].offset            = cc_copy.chunks[0].target_off;
+       el[0].length            = cc_copy.chunks[0].length;
+       el[0].reserved          = 0;
+       el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
+
+       status = smb2_lock(tree, &lck);
+       torture_assert_ntstatus_ok(torture, status, "lock");
+
+       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_equal(torture, status,
+                                     NT_STATUS_FILE_LOCK_CONFLICT,
+                                     "FSCTL_SRV_COPYCHUNK locked");
+
+       lck.in.lock_count       = 0x0001;
+       lck.in.lock_sequence    = 0x00000001;
+       lck.in.file.handle      = dest_h2;
+       lck.in.locks            = el;
+       el[0].offset            = cc_copy.chunks[0].target_off;
+       el[0].length            = cc_copy.chunks[0].length;
+       el[0].reserved          = 0;
+       el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
+       status = smb2_lock(tree, &lck);
+       torture_assert_ntstatus_ok(torture, status, "unlock");
+
+       status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
+       torture_assert_ntstatus_ok(torture, status,
+                                  "FSCTL_SRV_COPYCHUNK unlocked");
+
+       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 */
-                                 8192); /* total bytes written */
+                                 4096); /* total bytes written */
+       if (!ok) {
+               return false;
+       }
+
+       ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
        if (!ok) {
                return false;
        }
 
+       smb2_util_close(tree, dest_h2);
        smb2_util_close(tree, src_h);
        smb2_util_close(tree, dest_h);
        talloc_free(tmp_ctx);
@@ -587,6 +877,12 @@ struct torture_suite *torture_smb2_ioctl_init(void)
                                     test_ioctl_copy_chunk_over);
        torture_suite_add_1smb2_test(suite, "copy_chunk_append",
                                     test_ioctl_copy_chunk_append);
+       torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
+                                    test_ioctl_copy_chunk_limits);
+       torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
+                                    test_ioctl_copy_chunk_src_lck);
+       torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
+                                    test_ioctl_copy_chunk_dest_lck);
 
        suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");