diff options
Diffstat (limited to 'fs/cifs/smb2ops.c')
| -rw-r--r-- | fs/cifs/smb2ops.c | 99 |
1 files changed, 86 insertions, 13 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 11dde4b24f8a..757da3e54d3d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
| @@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid, | |||
| 532 | int rc; | 532 | int rc; |
| 533 | unsigned int ret_data_len; | 533 | unsigned int ret_data_len; |
| 534 | struct copychunk_ioctl *pcchunk; | 534 | struct copychunk_ioctl *pcchunk; |
| 535 | char *retbuf = NULL; | 535 | struct copychunk_ioctl_rsp *retbuf = NULL; |
| 536 | struct cifs_tcon *tcon; | ||
| 537 | int chunks_copied = 0; | ||
| 538 | bool chunk_sizes_updated = false; | ||
| 536 | 539 | ||
| 537 | pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); | 540 | pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); |
| 538 | 541 | ||
| @@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid, | |||
| 547 | 550 | ||
| 548 | /* Note: request_res_key sets res_key null only if rc !=0 */ | 551 | /* Note: request_res_key sets res_key null only if rc !=0 */ |
| 549 | if (rc) | 552 | if (rc) |
| 550 | return rc; | 553 | goto cchunk_out; |
| 551 | 554 | ||
| 552 | /* For now array only one chunk long, will make more flexible later */ | 555 | /* For now array only one chunk long, will make more flexible later */ |
| 553 | pcchunk->ChunkCount = __constant_cpu_to_le32(1); | 556 | pcchunk->ChunkCount = __constant_cpu_to_le32(1); |
| 554 | pcchunk->Reserved = 0; | 557 | pcchunk->Reserved = 0; |
| 555 | pcchunk->SourceOffset = cpu_to_le64(src_off); | ||
| 556 | pcchunk->TargetOffset = cpu_to_le64(dest_off); | ||
| 557 | pcchunk->Length = cpu_to_le32(len); | ||
| 558 | pcchunk->Reserved2 = 0; | 558 | pcchunk->Reserved2 = 0; |
| 559 | 559 | ||
| 560 | /* Request that server copy to target from src file identified by key */ | 560 | tcon = tlink_tcon(trgtfile->tlink); |
| 561 | rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink), | ||
| 562 | trgtfile->fid.persistent_fid, | ||
| 563 | trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, | ||
| 564 | true /* is_fsctl */, (char *)pcchunk, | ||
| 565 | sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len); | ||
| 566 | 561 | ||
| 567 | /* BB need to special case rc = EINVAL to alter chunk size */ | 562 | while (len > 0) { |
| 563 | pcchunk->SourceOffset = cpu_to_le64(src_off); | ||
| 564 | pcchunk->TargetOffset = cpu_to_le64(dest_off); | ||
| 565 | pcchunk->Length = | ||
| 566 | cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); | ||
| 568 | 567 | ||
| 569 | cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len); | 568 | /* Request server copy to target from src identified by key */ |
| 569 | rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, | ||
| 570 | trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, | ||
| 571 | true /* is_fsctl */, (char *)pcchunk, | ||
| 572 | sizeof(struct copychunk_ioctl), (char **)&retbuf, | ||
| 573 | &ret_data_len); | ||
| 574 | if (rc == 0) { | ||
| 575 | if (ret_data_len != | ||
| 576 | sizeof(struct copychunk_ioctl_rsp)) { | ||
| 577 | cifs_dbg(VFS, "invalid cchunk response size\n"); | ||
| 578 | rc = -EIO; | ||
| 579 | goto cchunk_out; | ||
| 580 | } | ||
| 581 | if (retbuf->TotalBytesWritten == 0) { | ||
| 582 | cifs_dbg(FYI, "no bytes copied\n"); | ||
| 583 | rc = -EIO; | ||
| 584 | goto cchunk_out; | ||
| 585 | } | ||
| 586 | /* | ||
| 587 | * Check if server claimed to write more than we asked | ||
| 588 | */ | ||
| 589 | if (le32_to_cpu(retbuf->TotalBytesWritten) > | ||
| 590 | le32_to_cpu(pcchunk->Length)) { | ||
| 591 | cifs_dbg(VFS, "invalid copy chunk response\n"); | ||
| 592 | rc = -EIO; | ||
| 593 | goto cchunk_out; | ||
| 594 | } | ||
| 595 | if (le32_to_cpu(retbuf->ChunksWritten) != 1) { | ||
| 596 | cifs_dbg(VFS, "invalid num chunks written\n"); | ||
| 597 | rc = -EIO; | ||
| 598 | goto cchunk_out; | ||
| 599 | } | ||
| 600 | chunks_copied++; | ||
| 601 | |||
| 602 | src_off += le32_to_cpu(retbuf->TotalBytesWritten); | ||
| 603 | dest_off += le32_to_cpu(retbuf->TotalBytesWritten); | ||
| 604 | len -= le32_to_cpu(retbuf->TotalBytesWritten); | ||
| 605 | |||
| 606 | cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", | ||
| 607 | le32_to_cpu(retbuf->ChunksWritten), | ||
| 608 | le32_to_cpu(retbuf->ChunkBytesWritten), | ||
| 609 | le32_to_cpu(retbuf->TotalBytesWritten)); | ||
| 610 | } else if (rc == -EINVAL) { | ||
| 611 | if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) | ||
| 612 | goto cchunk_out; | ||
| 613 | |||
| 614 | cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", | ||
| 615 | le32_to_cpu(retbuf->ChunksWritten), | ||
| 616 | le32_to_cpu(retbuf->ChunkBytesWritten), | ||
| 617 | le32_to_cpu(retbuf->TotalBytesWritten)); | ||
| 618 | |||
| 619 | /* | ||
| 620 | * Check if this is the first request using these sizes, | ||
| 621 | * (ie check if copy succeed once with original sizes | ||
| 622 | * and check if the server gave us different sizes after | ||
| 623 | * we already updated max sizes on previous request). | ||
| 624 | * if not then why is the server returning an error now | ||
| 625 | */ | ||
| 626 | if ((chunks_copied != 0) || chunk_sizes_updated) | ||
| 627 | goto cchunk_out; | ||
| 628 | |||
| 629 | /* Check that server is not asking us to grow size */ | ||
| 630 | if (le32_to_cpu(retbuf->ChunkBytesWritten) < | ||
| 631 | tcon->max_bytes_chunk) | ||
| 632 | tcon->max_bytes_chunk = | ||
| 633 | le32_to_cpu(retbuf->ChunkBytesWritten); | ||
| 634 | else | ||
| 635 | goto cchunk_out; /* server gave us bogus size */ | ||
| 636 | |||
| 637 | /* No need to change MaxChunks since already set to 1 */ | ||
| 638 | chunk_sizes_updated = true; | ||
| 639 | } | ||
| 640 | } | ||
| 570 | 641 | ||
| 642 | cchunk_out: | ||
| 571 | kfree(pcchunk); | 643 | kfree(pcchunk); |
| 572 | return rc; | 644 | return rc; |
| 573 | } | 645 | } |
| @@ -1247,6 +1319,7 @@ struct smb_version_operations smb30_operations = { | |||
| 1247 | .create_lease_buf = smb3_create_lease_buf, | 1319 | .create_lease_buf = smb3_create_lease_buf, |
| 1248 | .parse_lease_buf = smb3_parse_lease_buf, | 1320 | .parse_lease_buf = smb3_parse_lease_buf, |
| 1249 | .clone_range = smb2_clone_range, | 1321 | .clone_range = smb2_clone_range, |
| 1322 | .validate_negotiate = smb3_validate_negotiate, | ||
| 1250 | }; | 1323 | }; |
| 1251 | 1324 | ||
| 1252 | struct smb_version_values smb20_values = { | 1325 | struct smb_version_values smb20_values = { |
