aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/smb2ops.c98
-rw-r--r--fs/cifs/smb2pdu.c9
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
642cchunk_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 */