diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/smb2ops.c | 98 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 9 |
2 files changed, 93 insertions, 14 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 11dde4b24f8a..a3968eeb6fac 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 | } |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d65270c290a1..87f430ecb85f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -1214,10 +1214,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, | |||
1214 | rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); | 1214 | rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); |
1215 | rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; | 1215 | rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; |
1216 | 1216 | ||
1217 | if (rc != 0) { | 1217 | if ((rc != 0) && (rc != -EINVAL)) { |
1218 | if (tcon) | 1218 | if (tcon) |
1219 | cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); | 1219 | cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); |
1220 | goto ioctl_exit; | 1220 | goto ioctl_exit; |
1221 | } else if (rc == -EINVAL) { | ||
1222 | if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && | ||
1223 | (opcode != FSCTL_SRV_COPYCHUNK)) { | ||
1224 | if (tcon) | ||
1225 | cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); | ||
1226 | goto ioctl_exit; | ||
1227 | } | ||
1221 | } | 1228 | } |
1222 | 1229 | ||
1223 | /* check if caller wants to look at return data or just return rc */ | 1230 | /* check if caller wants to look at return data or just return rc */ |