|
@@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid,
|
|
|
int rc;
|
|
|
unsigned int ret_data_len;
|
|
|
struct copychunk_ioctl *pcchunk;
|
|
|
- char *retbuf = NULL;
|
|
|
+ struct copychunk_ioctl_rsp *retbuf = NULL;
|
|
|
+ struct cifs_tcon *tcon;
|
|
|
+ int chunks_copied = 0;
|
|
|
+ bool chunk_sizes_updated = false;
|
|
|
|
|
|
pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
|
|
|
|
|
@@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid,
|
|
|
|
|
|
/* Note: request_res_key sets res_key null only if rc !=0 */
|
|
|
if (rc)
|
|
|
- return rc;
|
|
|
+ goto cchunk_out;
|
|
|
|
|
|
/* For now array only one chunk long, will make more flexible later */
|
|
|
pcchunk->ChunkCount = __constant_cpu_to_le32(1);
|
|
|
pcchunk->Reserved = 0;
|
|
|
- pcchunk->SourceOffset = cpu_to_le64(src_off);
|
|
|
- pcchunk->TargetOffset = cpu_to_le64(dest_off);
|
|
|
- pcchunk->Length = cpu_to_le32(len);
|
|
|
pcchunk->Reserved2 = 0;
|
|
|
|
|
|
- /* Request that server copy to target from src file identified by key */
|
|
|
- rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
|
|
|
- trgtfile->fid.persistent_fid,
|
|
|
- trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
|
|
|
- true /* is_fsctl */, (char *)pcchunk,
|
|
|
- sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len);
|
|
|
+ tcon = tlink_tcon(trgtfile->tlink);
|
|
|
|
|
|
- /* BB need to special case rc = EINVAL to alter chunk size */
|
|
|
+ while (len > 0) {
|
|
|
+ pcchunk->SourceOffset = cpu_to_le64(src_off);
|
|
|
+ pcchunk->TargetOffset = cpu_to_le64(dest_off);
|
|
|
+ pcchunk->Length =
|
|
|
+ cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
|
|
|
|
|
|
- cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
|
|
|
+ /* Request server copy to target from src identified by key */
|
|
|
+ rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
|
|
+ trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
|
|
|
+ true /* is_fsctl */, (char *)pcchunk,
|
|
|
+ sizeof(struct copychunk_ioctl), (char **)&retbuf,
|
|
|
+ &ret_data_len);
|
|
|
+ if (rc == 0) {
|
|
|
+ if (ret_data_len !=
|
|
|
+ sizeof(struct copychunk_ioctl_rsp)) {
|
|
|
+ cifs_dbg(VFS, "invalid cchunk response size\n");
|
|
|
+ rc = -EIO;
|
|
|
+ goto cchunk_out;
|
|
|
+ }
|
|
|
+ if (retbuf->TotalBytesWritten == 0) {
|
|
|
+ cifs_dbg(FYI, "no bytes copied\n");
|
|
|
+ rc = -EIO;
|
|
|
+ goto cchunk_out;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Check if server claimed to write more than we asked
|
|
|
+ */
|
|
|
+ if (le32_to_cpu(retbuf->TotalBytesWritten) >
|
|
|
+ le32_to_cpu(pcchunk->Length)) {
|
|
|
+ cifs_dbg(VFS, "invalid copy chunk response\n");
|
|
|
+ rc = -EIO;
|
|
|
+ goto cchunk_out;
|
|
|
+ }
|
|
|
+ if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
|
|
|
+ cifs_dbg(VFS, "invalid num chunks written\n");
|
|
|
+ rc = -EIO;
|
|
|
+ goto cchunk_out;
|
|
|
+ }
|
|
|
+ chunks_copied++;
|
|
|
+
|
|
|
+ src_off += le32_to_cpu(retbuf->TotalBytesWritten);
|
|
|
+ dest_off += le32_to_cpu(retbuf->TotalBytesWritten);
|
|
|
+ len -= le32_to_cpu(retbuf->TotalBytesWritten);
|
|
|
+
|
|
|
+ cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n",
|
|
|
+ le32_to_cpu(retbuf->ChunksWritten),
|
|
|
+ le32_to_cpu(retbuf->ChunkBytesWritten),
|
|
|
+ le32_to_cpu(retbuf->TotalBytesWritten));
|
|
|
+ } else if (rc == -EINVAL) {
|
|
|
+ if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
|
|
|
+ goto cchunk_out;
|
|
|
+
|
|
|
+ cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n",
|
|
|
+ le32_to_cpu(retbuf->ChunksWritten),
|
|
|
+ le32_to_cpu(retbuf->ChunkBytesWritten),
|
|
|
+ le32_to_cpu(retbuf->TotalBytesWritten));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check if this is the first request using these sizes,
|
|
|
+ * (ie check if copy succeed once with original sizes
|
|
|
+ * and check if the server gave us different sizes after
|
|
|
+ * we already updated max sizes on previous request).
|
|
|
+ * if not then why is the server returning an error now
|
|
|
+ */
|
|
|
+ if ((chunks_copied != 0) || chunk_sizes_updated)
|
|
|
+ goto cchunk_out;
|
|
|
+
|
|
|
+ /* Check that server is not asking us to grow size */
|
|
|
+ if (le32_to_cpu(retbuf->ChunkBytesWritten) <
|
|
|
+ tcon->max_bytes_chunk)
|
|
|
+ tcon->max_bytes_chunk =
|
|
|
+ le32_to_cpu(retbuf->ChunkBytesWritten);
|
|
|
+ else
|
|
|
+ goto cchunk_out; /* server gave us bogus size */
|
|
|
+
|
|
|
+ /* No need to change MaxChunks since already set to 1 */
|
|
|
+ chunk_sizes_updated = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+cchunk_out:
|
|
|
kfree(pcchunk);
|
|
|
return rc;
|
|
|
}
|
|
@@ -1247,6 +1319,7 @@ struct smb_version_operations smb30_operations = {
|
|
|
.create_lease_buf = smb3_create_lease_buf,
|
|
|
.parse_lease_buf = smb3_parse_lease_buf,
|
|
|
.clone_range = smb2_clone_range,
|
|
|
+ .validate_negotiate = smb3_validate_negotiate,
|
|
|
};
|
|
|
|
|
|
struct smb_version_values smb20_values = {
|