From eb817368f50c1cbe1bd07044124aad7db6330e3a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 18 May 2021 10:37:59 +0900 Subject: [PATCH] cifsd: add support for FSCTL_DUPLICATE_EXTENTS_TO_FILE Add support for FSCTL_DUPLICATE_EXTENTS_TO_FILE in smb2 ioctl. Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 52 ++++++++++++++++++++++++++++++++++++++++++++- fs/cifsd/smb2pdu.h | 8 +++++++ fs/cifsd/smbfsctl.h | 1 + fs/cifsd/vfs.c | 2 +- fs/cifsd/vfs.h | 3 ++- 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3fd266a94996..e5d3a5790a81 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -4622,7 +4622,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, FILE_PERSISTENT_ACLS | FILE_UNICODE_ON_DISK | FILE_CASE_PRESERVED_NAMES | - FILE_CASE_SENSITIVE_SEARCH); + FILE_CASE_SENSITIVE_SEARCH | + FILE_SUPPORTS_BLOCK_REFCOUNTING); info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); @@ -7330,6 +7331,55 @@ int smb2_ioctl(struct ksmbd_work *work) nbytes = sizeof(struct reparse_data_buffer); break; } + case FSCTL_DUPLICATE_EXTENTS_TO_FILE: + { + struct ksmbd_file *fp_in, *fp_out = NULL; + struct duplicate_extents_to_file *dup_ext; + loff_t src_off, dst_off, length, cloned; + + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); + if (!fp_in) { + ksmbd_err("not found file handle in duplicate extent to file\n"); + ret = -ENOENT; + goto out; + } + + fp_out = ksmbd_lookup_fd_fast(work, id); + if (!fp_out) { + ksmbd_err("not found fp\n"); + ret = -ENOENT; + goto dup_ext_out; + } + + src_off = le64_to_cpu(dup_ext->SourceFileOffset); + dst_off = le64_to_cpu(dup_ext->TargetFileOffset); + length = le64_to_cpu(dup_ext->ByteCount); + cloned = vfs_clone_file_range(fp_in->filp, src_off, fp_out->filp, + dst_off, length, 0); + if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { + ret = -EOPNOTSUPP; + goto dup_ext_out; + } else if (cloned != length) { + cloned = ksmbd_vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length); + if (cloned != length) { + if (cloned < 0) + ret = cloned; + else + ret = -EINVAL; + } + } + +dup_ext_out: + ksmbd_fd_put(work, fp_in); + ksmbd_fd_put(work, fp_out); + if (ret < 0) + goto out; + break; + } default: ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", cnt_code); diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index c5c32610aafe..1a8da2122b75 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -851,6 +851,14 @@ struct smb2_write_rsp { #define SMB2_0_IOCTL_IS_FSCTL 0x00000001 +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + struct smb2_ioctl_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 57 */ diff --git a/fs/cifsd/smbfsctl.h b/fs/cifsd/smbfsctl.h index 908c4e68a479..b98418aae20c 100644 --- a/fs/cifsd/smbfsctl.h +++ b/fs/cifsd/smbfsctl.h @@ -64,6 +64,7 @@ #define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ #define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 #define FSCTL_SIS_LINK_FILES 0x0009C104 #define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ #define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 29f31db4e07e..cdbb844fddad 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1789,7 +1789,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, return 0; } -static int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, +int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len) { struct inode *inode_in = file_inode(file_in); diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 0163be4297de..2d19e2bac33a 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -218,7 +218,8 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, struct srv_copychunk *chunks, unsigned int chunk_count, unsigned int *chunk_count_written, unsigned int *chunk_size_written, loff_t *total_size_written); - +int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, size_t len); ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, char **xattr_buf);