forked from mirrors/linux
		
	Introduce cifs_copy_file_range()
The earlier changes to copy range for cifs unintentionally disabled the more common form of server side copy. The patch introduces the file_operations helper cifs_copy_file_range() which is used by the syscall copy_file_range. The new file operations helper allows us to perform server side copies for SMB2.0 and 2.1 servers as well as SMB 3.0+ servers which do not support the ioctl FSCTL_DUPLICATE_EXTENTS_TO_FILE. The new helper uses the ioctl FSCTL_SRV_COPYCHUNK_WRITE to perform server side copies. The helper is called by vfs_copy_file_range() only once an attempt to clone the file using the ioctl FSCTL_DUPLICATE_EXTENTS_TO_FILE has failed. Signed-off-by: Sachin Prabhu <sprabhu@redhat.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> CC: Stable <stable@vger.kernel.org> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
		
							parent
							
								
									312bbc5946
								
							
						
					
					
						commit
						620d8745b3
					
				
					 5 changed files with 110 additions and 68 deletions
				
			
		| 
						 | 
					@ -972,6 +972,86 @@ static int cifs_clone_file_range(struct file *src_file, loff_t off,
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ssize_t cifs_file_copychunk_range(unsigned int xid,
 | 
				
			||||||
 | 
									struct file *src_file, loff_t off,
 | 
				
			||||||
 | 
									struct file *dst_file, loff_t destoff,
 | 
				
			||||||
 | 
									size_t len, unsigned int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct inode *src_inode = file_inode(src_file);
 | 
				
			||||||
 | 
						struct inode *target_inode = file_inode(dst_file);
 | 
				
			||||||
 | 
						struct cifsFileInfo *smb_file_src;
 | 
				
			||||||
 | 
						struct cifsFileInfo *smb_file_target;
 | 
				
			||||||
 | 
						struct cifs_tcon *src_tcon;
 | 
				
			||||||
 | 
						struct cifs_tcon *target_tcon;
 | 
				
			||||||
 | 
						ssize_t rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cifs_dbg(FYI, "copychunk range\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (src_inode == target_inode) {
 | 
				
			||||||
 | 
							rc = -EINVAL;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!src_file->private_data || !dst_file->private_data) {
 | 
				
			||||||
 | 
							rc = -EBADF;
 | 
				
			||||||
 | 
							cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = -EXDEV;
 | 
				
			||||||
 | 
						smb_file_target = dst_file->private_data;
 | 
				
			||||||
 | 
						smb_file_src = src_file->private_data;
 | 
				
			||||||
 | 
						src_tcon = tlink_tcon(smb_file_src->tlink);
 | 
				
			||||||
 | 
						target_tcon = tlink_tcon(smb_file_target->tlink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (src_tcon->ses != target_tcon->ses) {
 | 
				
			||||||
 | 
							cifs_dbg(VFS, "source and target of copy not on same server\n");
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Note: cifs case is easier than btrfs since server responsible for
 | 
				
			||||||
 | 
						 * checks for proper open modes and file type and if it wants
 | 
				
			||||||
 | 
						 * server could even support copy of range where source = target
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						lock_two_nondirectories(target_inode, src_inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cifs_dbg(FYI, "about to flush pages\n");
 | 
				
			||||||
 | 
						/* should we flush first and last page first */
 | 
				
			||||||
 | 
						truncate_inode_pages(&target_inode->i_data, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (target_tcon->ses->server->ops->copychunk_range)
 | 
				
			||||||
 | 
							rc = target_tcon->ses->server->ops->copychunk_range(xid,
 | 
				
			||||||
 | 
								smb_file_src, smb_file_target, off, len, destoff);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							rc = -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* force revalidate of size and timestamps of target file now
 | 
				
			||||||
 | 
						 * that target is updated on the server
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						CIFS_I(target_inode)->time = 0;
 | 
				
			||||||
 | 
						/* although unlocking in the reverse order from locking is not
 | 
				
			||||||
 | 
						 * strictly necessary here it is a little cleaner to be consistent
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						unlock_two_nondirectories(src_inode, target_inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
 | 
				
			||||||
 | 
									struct file *dst_file, loff_t destoff,
 | 
				
			||||||
 | 
									size_t len, unsigned int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int xid = get_xid();
 | 
				
			||||||
 | 
						ssize_t rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff,
 | 
				
			||||||
 | 
										len, flags);
 | 
				
			||||||
 | 
						free_xid(xid);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct file_operations cifs_file_ops = {
 | 
					const struct file_operations cifs_file_ops = {
 | 
				
			||||||
	.read_iter = cifs_loose_read_iter,
 | 
						.read_iter = cifs_loose_read_iter,
 | 
				
			||||||
	.write_iter = cifs_file_write_iter,
 | 
						.write_iter = cifs_file_write_iter,
 | 
				
			||||||
| 
						 | 
					@ -984,6 +1064,7 @@ const struct file_operations cifs_file_ops = {
 | 
				
			||||||
	.splice_read = generic_file_splice_read,
 | 
						.splice_read = generic_file_splice_read,
 | 
				
			||||||
	.llseek = cifs_llseek,
 | 
						.llseek = cifs_llseek,
 | 
				
			||||||
	.unlocked_ioctl	= cifs_ioctl,
 | 
						.unlocked_ioctl	= cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.setlease = cifs_setlease,
 | 
						.setlease = cifs_setlease,
 | 
				
			||||||
	.fallocate = cifs_fallocate,
 | 
						.fallocate = cifs_fallocate,
 | 
				
			||||||
| 
						 | 
					@ -1001,6 +1082,7 @@ const struct file_operations cifs_file_strict_ops = {
 | 
				
			||||||
	.splice_read = generic_file_splice_read,
 | 
						.splice_read = generic_file_splice_read,
 | 
				
			||||||
	.llseek = cifs_llseek,
 | 
						.llseek = cifs_llseek,
 | 
				
			||||||
	.unlocked_ioctl	= cifs_ioctl,
 | 
						.unlocked_ioctl	= cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.setlease = cifs_setlease,
 | 
						.setlease = cifs_setlease,
 | 
				
			||||||
	.fallocate = cifs_fallocate,
 | 
						.fallocate = cifs_fallocate,
 | 
				
			||||||
| 
						 | 
					@ -1018,6 +1100,7 @@ const struct file_operations cifs_file_direct_ops = {
 | 
				
			||||||
	.mmap = cifs_file_mmap,
 | 
						.mmap = cifs_file_mmap,
 | 
				
			||||||
	.splice_read = generic_file_splice_read,
 | 
						.splice_read = generic_file_splice_read,
 | 
				
			||||||
	.unlocked_ioctl  = cifs_ioctl,
 | 
						.unlocked_ioctl  = cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.llseek = cifs_llseek,
 | 
						.llseek = cifs_llseek,
 | 
				
			||||||
	.setlease = cifs_setlease,
 | 
						.setlease = cifs_setlease,
 | 
				
			||||||
| 
						 | 
					@ -1035,6 +1118,7 @@ const struct file_operations cifs_file_nobrl_ops = {
 | 
				
			||||||
	.splice_read = generic_file_splice_read,
 | 
						.splice_read = generic_file_splice_read,
 | 
				
			||||||
	.llseek = cifs_llseek,
 | 
						.llseek = cifs_llseek,
 | 
				
			||||||
	.unlocked_ioctl	= cifs_ioctl,
 | 
						.unlocked_ioctl	= cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.setlease = cifs_setlease,
 | 
						.setlease = cifs_setlease,
 | 
				
			||||||
	.fallocate = cifs_fallocate,
 | 
						.fallocate = cifs_fallocate,
 | 
				
			||||||
| 
						 | 
					@ -1051,6 +1135,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
 | 
				
			||||||
	.splice_read = generic_file_splice_read,
 | 
						.splice_read = generic_file_splice_read,
 | 
				
			||||||
	.llseek = cifs_llseek,
 | 
						.llseek = cifs_llseek,
 | 
				
			||||||
	.unlocked_ioctl	= cifs_ioctl,
 | 
						.unlocked_ioctl	= cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.setlease = cifs_setlease,
 | 
						.setlease = cifs_setlease,
 | 
				
			||||||
	.fallocate = cifs_fallocate,
 | 
						.fallocate = cifs_fallocate,
 | 
				
			||||||
| 
						 | 
					@ -1067,6 +1152,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
 | 
				
			||||||
	.mmap = cifs_file_mmap,
 | 
						.mmap = cifs_file_mmap,
 | 
				
			||||||
	.splice_read = generic_file_splice_read,
 | 
						.splice_read = generic_file_splice_read,
 | 
				
			||||||
	.unlocked_ioctl  = cifs_ioctl,
 | 
						.unlocked_ioctl  = cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.llseek = cifs_llseek,
 | 
						.llseek = cifs_llseek,
 | 
				
			||||||
	.setlease = cifs_setlease,
 | 
						.setlease = cifs_setlease,
 | 
				
			||||||
| 
						 | 
					@ -1078,6 +1164,7 @@ const struct file_operations cifs_dir_ops = {
 | 
				
			||||||
	.release = cifs_closedir,
 | 
						.release = cifs_closedir,
 | 
				
			||||||
	.read    = generic_read_dir,
 | 
						.read    = generic_read_dir,
 | 
				
			||||||
	.unlocked_ioctl  = cifs_ioctl,
 | 
						.unlocked_ioctl  = cifs_ioctl,
 | 
				
			||||||
 | 
						.copy_file_range = cifs_copy_file_range,
 | 
				
			||||||
	.clone_file_range = cifs_clone_file_range,
 | 
						.clone_file_range = cifs_clone_file_range,
 | 
				
			||||||
	.llseek = generic_file_llseek,
 | 
						.llseek = generic_file_llseek,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,6 +139,11 @@ extern ssize_t	cifs_listxattr(struct dentry *, char *, size_t);
 | 
				
			||||||
# define cifs_listxattr NULL
 | 
					# define cifs_listxattr NULL
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern ssize_t cifs_file_copychunk_range(unsigned int xid,
 | 
				
			||||||
 | 
										struct file *src_file, loff_t off,
 | 
				
			||||||
 | 
										struct file *dst_file, loff_t destoff,
 | 
				
			||||||
 | 
										size_t len, unsigned int flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 | 
					extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 | 
				
			||||||
#ifdef CONFIG_CIFS_NFSD_EXPORT
 | 
					#ifdef CONFIG_CIFS_NFSD_EXPORT
 | 
				
			||||||
extern const struct export_operations cifs_export_ops;
 | 
					extern const struct export_operations cifs_export_ops;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -408,10 +408,10 @@ struct smb_version_operations {
 | 
				
			||||||
	char * (*create_lease_buf)(u8 *, u8);
 | 
						char * (*create_lease_buf)(u8 *, u8);
 | 
				
			||||||
	/* parse lease context buffer and return oplock/epoch info */
 | 
						/* parse lease context buffer and return oplock/epoch info */
 | 
				
			||||||
	__u8 (*parse_lease_buf)(void *, unsigned int *);
 | 
						__u8 (*parse_lease_buf)(void *, unsigned int *);
 | 
				
			||||||
	int (*copychunk_range)(const unsigned int,
 | 
						ssize_t (*copychunk_range)(const unsigned int,
 | 
				
			||||||
			struct cifsFileInfo *src_file,
 | 
								struct cifsFileInfo *src_file,
 | 
				
			||||||
			struct cifsFileInfo *target_file, u64 src_off, u64 len,
 | 
								struct cifsFileInfo *target_file,
 | 
				
			||||||
			u64 dest_off);
 | 
								u64 src_off, u64 len, u64 dest_off);
 | 
				
			||||||
	int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src,
 | 
						int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src,
 | 
				
			||||||
			struct cifsFileInfo *target_file, u64 src_off, u64 len,
 | 
								struct cifsFileInfo *target_file, u64 src_off, u64 len,
 | 
				
			||||||
			u64 dest_off);
 | 
								u64 dest_off);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,63 +34,6 @@
 | 
				
			||||||
#include "cifs_ioctl.h"
 | 
					#include "cifs_ioctl.h"
 | 
				
			||||||
#include <linux/btrfs.h>
 | 
					#include <linux/btrfs.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
 | 
					 | 
				
			||||||
			  struct file *dst_file)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct inode *src_inode = file_inode(src_file);
 | 
					 | 
				
			||||||
	struct inode *target_inode = file_inode(dst_file);
 | 
					 | 
				
			||||||
	struct cifsFileInfo *smb_file_src;
 | 
					 | 
				
			||||||
	struct cifsFileInfo *smb_file_target;
 | 
					 | 
				
			||||||
	struct cifs_tcon *src_tcon;
 | 
					 | 
				
			||||||
	struct cifs_tcon *target_tcon;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cifs_dbg(FYI, "ioctl copychunk range\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!src_file->private_data || !dst_file->private_data) {
 | 
					 | 
				
			||||||
		rc = -EBADF;
 | 
					 | 
				
			||||||
		cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = -EXDEV;
 | 
					 | 
				
			||||||
	smb_file_target = dst_file->private_data;
 | 
					 | 
				
			||||||
	smb_file_src = src_file->private_data;
 | 
					 | 
				
			||||||
	src_tcon = tlink_tcon(smb_file_src->tlink);
 | 
					 | 
				
			||||||
	target_tcon = tlink_tcon(smb_file_target->tlink);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (src_tcon->ses != target_tcon->ses) {
 | 
					 | 
				
			||||||
		cifs_dbg(VFS, "source and target of copy not on same server\n");
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Note: cifs case is easier than btrfs since server responsible for
 | 
					 | 
				
			||||||
	 * checks for proper open modes and file type and if it wants
 | 
					 | 
				
			||||||
	 * server could even support copy of range where source = target
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	lock_two_nondirectories(target_inode, src_inode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cifs_dbg(FYI, "about to flush pages\n");
 | 
					 | 
				
			||||||
	/* should we flush first and last page first */
 | 
					 | 
				
			||||||
	truncate_inode_pages(&target_inode->i_data, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (target_tcon->ses->server->ops->copychunk_range)
 | 
					 | 
				
			||||||
		rc = target_tcon->ses->server->ops->copychunk_range(xid,
 | 
					 | 
				
			||||||
			smb_file_src, smb_file_target, 0, src_inode->i_size, 0);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		rc = -EOPNOTSUPP;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* force revalidate of size and timestamps of target file now
 | 
					 | 
				
			||||||
	   that target is updated on the server */
 | 
					 | 
				
			||||||
	CIFS_I(target_inode)->time = 0;
 | 
					 | 
				
			||||||
	/* although unlocking in the reverse order from locking is not
 | 
					 | 
				
			||||||
	   strictly necessary here it is a little cleaner to be consistent */
 | 
					 | 
				
			||||||
	unlock_two_nondirectories(src_inode, target_inode);
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
 | 
					static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
 | 
				
			||||||
			unsigned long srcfd)
 | 
								unsigned long srcfd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -129,7 +72,8 @@ static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
 | 
				
			||||||
	if (S_ISDIR(src_inode->i_mode))
 | 
						if (S_ISDIR(src_inode->i_mode))
 | 
				
			||||||
		goto out_fput;
 | 
							goto out_fput;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = cifs_file_copychunk_range(xid, src_file.file, dst_file);
 | 
						rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0,
 | 
				
			||||||
 | 
										src_inode->i_size, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_fput:
 | 
					out_fput:
 | 
				
			||||||
	fdput(src_file);
 | 
						fdput(src_file);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -592,7 +592,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static ssize_t
 | 
				
			||||||
smb2_copychunk_range(const unsigned int xid,
 | 
					smb2_copychunk_range(const unsigned int xid,
 | 
				
			||||||
			struct cifsFileInfo *srcfile,
 | 
								struct cifsFileInfo *srcfile,
 | 
				
			||||||
			struct cifsFileInfo *trgtfile, u64 src_off,
 | 
								struct cifsFileInfo *trgtfile, u64 src_off,
 | 
				
			||||||
| 
						 | 
					@ -605,6 +605,7 @@ smb2_copychunk_range(const unsigned int xid,
 | 
				
			||||||
	struct cifs_tcon *tcon;
 | 
						struct cifs_tcon *tcon;
 | 
				
			||||||
	int chunks_copied = 0;
 | 
						int chunks_copied = 0;
 | 
				
			||||||
	bool chunk_sizes_updated = false;
 | 
						bool chunk_sizes_updated = false;
 | 
				
			||||||
 | 
						ssize_t bytes_written, total_bytes_written = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
 | 
						pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -669,14 +670,16 @@ smb2_copychunk_range(const unsigned int xid,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			chunks_copied++;
 | 
								chunks_copied++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			src_off += le32_to_cpu(retbuf->TotalBytesWritten);
 | 
								bytes_written = le32_to_cpu(retbuf->TotalBytesWritten);
 | 
				
			||||||
			dest_off += le32_to_cpu(retbuf->TotalBytesWritten);
 | 
								src_off += bytes_written;
 | 
				
			||||||
			len -= le32_to_cpu(retbuf->TotalBytesWritten);
 | 
								dest_off += bytes_written;
 | 
				
			||||||
 | 
								len -= bytes_written;
 | 
				
			||||||
 | 
								total_bytes_written += bytes_written;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n",
 | 
								cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n",
 | 
				
			||||||
				le32_to_cpu(retbuf->ChunksWritten),
 | 
									le32_to_cpu(retbuf->ChunksWritten),
 | 
				
			||||||
				le32_to_cpu(retbuf->ChunkBytesWritten),
 | 
									le32_to_cpu(retbuf->ChunkBytesWritten),
 | 
				
			||||||
				le32_to_cpu(retbuf->TotalBytesWritten));
 | 
									bytes_written);
 | 
				
			||||||
		} else if (rc == -EINVAL) {
 | 
							} else if (rc == -EINVAL) {
 | 
				
			||||||
			if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
 | 
								if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
 | 
				
			||||||
				goto cchunk_out;
 | 
									goto cchunk_out;
 | 
				
			||||||
| 
						 | 
					@ -713,7 +716,10 @@ smb2_copychunk_range(const unsigned int xid,
 | 
				
			||||||
cchunk_out:
 | 
					cchunk_out:
 | 
				
			||||||
	kfree(pcchunk);
 | 
						kfree(pcchunk);
 | 
				
			||||||
	kfree(retbuf);
 | 
						kfree(retbuf);
 | 
				
			||||||
	return rc;
 | 
						if (rc)
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return total_bytes_written;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue