aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorSteve French <smfrench@gmail.com>2013-11-14 01:05:36 -0500
committerSteve French <smfrench@gmail.com>2013-11-14 01:05:36 -0500
commit41c1358e9181ab1ebd773905b3fa8039b61aa0e9 (patch)
tree144d08d6758ac33212c200421a973120910c6fc8 /fs/cifs
parent2c957ddf30897787e39462ac56cdc4bf21eb0465 (diff)
CIFS: SMB2/SMB3 Copy offload support (refcopy) phase 1
This first patch adds the ability for us to do a server side copy (ie fast copy offloaded to the server to perform, aka refcopy) "cp --reflink" of one file to another located on the same server. This is much faster than traditional copy (which requires reading and writing over the network and extra memcpys). This first version is not going to be copy files larger than about 1MB (to Samba) until I add support for multiple chunks and for autoconfiguring the chunksize. It includes: 1) processing of the ioctl 2) marshalling and sending the SMB2/SMB3 fsctl over the network 3) simple parsing of the response It does not include yet (these will be in followon patches to come soon): 1) support for multiple chunks 2) support for autoconfiguring and remembering the chunksize 3) Support for the older style copychunk which Samba 4.1 server supports (because this requires write permission on the target file, which cp does not give you, apparently per-posix). This may require a distinct tool (other than cp) and other ioctl to implement. Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsglob.h3
-rw-r--r--fs/cifs/ioctl.c111
-rw-r--r--fs/cifs/smb2ops.c82
-rw-r--r--fs/cifs/smb2pdu.h15
4 files changed, 210 insertions, 1 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index cddb807addde..50a6ca1bb521 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -381,6 +381,9 @@ struct smb_version_operations {
381 char * (*create_lease_buf)(u8 *, u8); 381 char * (*create_lease_buf)(u8 *, u8);
382 /* parse lease context buffer and return oplock/epoch info */ 382 /* parse lease context buffer and return oplock/epoch info */
383 __u8 (*parse_lease_buf)(void *, unsigned int *); 383 __u8 (*parse_lease_buf)(void *, unsigned int *);
384 int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
385 struct cifsFileInfo *target_file, u64 src_off, u64 len,
386 u64 dest_off);
384}; 387};
385 388
386struct smb_version_values { 389struct smb_version_values {
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index ba54bf6ab116..409b45eefe70 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -22,12 +22,120 @@
22 */ 22 */
23 23
24#include <linux/fs.h> 24#include <linux/fs.h>
25#include <linux/file.h>
26#include <linux/mount.h>
27#include <linux/mm.h>
28#include <linux/pagemap.h>
29#include <linux/btrfs.h>
25#include "cifspdu.h" 30#include "cifspdu.h"
26#include "cifsglob.h" 31#include "cifsglob.h"
27#include "cifsproto.h" 32#include "cifsproto.h"
28#include "cifs_debug.h" 33#include "cifs_debug.h"
29#include "cifsfs.h" 34#include "cifsfs.h"
30 35
36static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
37 unsigned long srcfd, u64 off, u64 len, u64 destoff)
38{
39 int rc;
40 struct cifsFileInfo *smb_file_target = dst_file->private_data;
41 struct inode *target_inode = file_inode(dst_file);
42 struct cifs_tcon *target_tcon;
43 struct fd src_file;
44 struct cifsFileInfo *smb_file_src;
45 struct inode *src_inode;
46 struct cifs_tcon *src_tcon;
47
48 cifs_dbg(FYI, "ioctl clone range\n");
49 /* the destination must be opened for writing */
50 if (!(dst_file->f_mode & FMODE_WRITE)) {
51 cifs_dbg(FYI, "file target not open for write\n");
52 return -EINVAL;
53 }
54
55 /* check if target volume is readonly and take reference */
56 rc = mnt_want_write_file(dst_file);
57 if (rc) {
58 cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
59 return rc;
60 }
61
62 src_file = fdget(srcfd);
63 if (!src_file.file) {
64 rc = -EBADF;
65 goto out_drop_write;
66 }
67
68 if ((!src_file.file->private_data) || (!dst_file->private_data)) {
69 rc = -EBADF;
70 cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
71 goto out_fput;
72 }
73
74 rc = -EXDEV;
75 smb_file_target = dst_file->private_data;
76 smb_file_src = src_file.file->private_data;
77 src_tcon = tlink_tcon(smb_file_src->tlink);
78 target_tcon = tlink_tcon(smb_file_target->tlink);
79
80 /* check if source and target are on same tree connection */
81 if (src_tcon != target_tcon) {
82 cifs_dbg(VFS, "file copy src and target on different volume\n");
83 goto out_fput;
84 }
85
86 src_inode = src_file.file->f_dentry->d_inode;
87
88 /*
89 * Note: cifs case is easier than btrfs since server responsible for
90 * checks for proper open modes and file type and if it wants
91 * server could even support copy of range where source = target
92 */
93
94 /* so we do not deadlock racing two ioctls on same files */
95 if (target_inode < src_inode) {
96 mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT);
97 mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD);
98 } else {
99 mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT);
100 mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD);
101 }
102
103 /* determine range to clone */
104 rc = -EINVAL;
105 if (off + len > src_inode->i_size || off + len < off)
106 goto out_unlock;
107 if (len == 0)
108 len = src_inode->i_size - off;
109
110 cifs_dbg(FYI, "about to flush pages\n");
111 /* should we flush first and last page first */
112 truncate_inode_pages_range(&target_inode->i_data, destoff,
113 PAGE_CACHE_ALIGN(destoff + len)-1);
114
115 if (target_tcon->ses->server->ops->clone_range)
116 rc = target_tcon->ses->server->ops->clone_range(xid,
117 smb_file_src, smb_file_target, off, len, destoff);
118
119 /* force revalidate of size and timestamps of target file now
120 that target is updated on the server */
121 CIFS_I(target_inode)->time = 0;
122out_unlock:
123 /* although unlocking in the reverse order from locking is not
124 strictly necessary here it is a little cleaner to be consistent */
125 if (target_inode < src_inode) {
126 mutex_unlock(&src_inode->i_mutex);
127 mutex_unlock(&target_inode->i_mutex);
128 } else {
129 mutex_unlock(&target_inode->i_mutex);
130 mutex_unlock(&src_inode->i_mutex);
131 }
132out_fput:
133 fdput(src_file);
134out_drop_write:
135 mnt_drop_write_file(dst_file);
136 return rc;
137}
138
31long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) 139long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
32{ 140{
33 struct inode *inode = file_inode(filep); 141 struct inode *inode = file_inode(filep);
@@ -105,6 +213,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
105 cifs_dbg(FYI, "set compress flag rc %d\n", rc); 213 cifs_dbg(FYI, "set compress flag rc %d\n", rc);
106 } 214 }
107 break; 215 break;
216 case BTRFS_IOC_CLONE:
217 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
218 break;
108 default: 219 default:
109 cifs_dbg(FYI, "unsupported ioctl\n"); 220 cifs_dbg(FYI, "unsupported ioctl\n");
110 break; 221 break;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index c571be8cb76e..11dde4b24f8a 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -494,6 +494,85 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
494} 494}
495 495
496static int 496static int
497SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
498 u64 persistent_fid, u64 volatile_fid,
499 struct copychunk_ioctl *pcchunk)
500{
501 int rc;
502 unsigned int ret_data_len;
503 struct resume_key_req *res_key;
504
505 rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
506 FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
507 NULL, 0 /* no input */,
508 (char **)&res_key, &ret_data_len);
509
510 if (rc) {
511 cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
512 goto req_res_key_exit;
513 }
514 if (ret_data_len < sizeof(struct resume_key_req)) {
515 cifs_dbg(VFS, "Invalid refcopy resume key length\n");
516 rc = -EINVAL;
517 goto req_res_key_exit;
518 }
519 memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE);
520
521req_res_key_exit:
522 kfree(res_key);
523 return rc;
524}
525
526static int
527smb2_clone_range(const unsigned int xid,
528 struct cifsFileInfo *srcfile,
529 struct cifsFileInfo *trgtfile, u64 src_off,
530 u64 len, u64 dest_off)
531{
532 int rc;
533 unsigned int ret_data_len;
534 struct copychunk_ioctl *pcchunk;
535 char *retbuf = NULL;
536
537 pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
538
539 if (pcchunk == NULL)
540 return -ENOMEM;
541
542 cifs_dbg(FYI, "in smb2_clone_range - about to call request res key\n");
543 /* Request a key from the server to identify the source of the copy */
544 rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink),
545 srcfile->fid.persistent_fid,
546 srcfile->fid.volatile_fid, pcchunk);
547
548 /* Note: request_res_key sets res_key null only if rc !=0 */
549 if (rc)
550 return rc;
551
552 /* For now array only one chunk long, will make more flexible later */
553 pcchunk->ChunkCount = __constant_cpu_to_le32(1);
554 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;
559
560 /* Request that server copy to target from src file identified by key */
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
567 /* BB need to special case rc = EINVAL to alter chunk size */
568
569 cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
570
571 kfree(pcchunk);
572 return rc;
573}
574
575static int
497smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, 576smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
498 struct cifs_fid *fid) 577 struct cifs_fid *fid)
499{ 578{
@@ -1017,6 +1096,7 @@ struct smb_version_operations smb20_operations = {
1017 .set_oplock_level = smb2_set_oplock_level, 1096 .set_oplock_level = smb2_set_oplock_level,
1018 .create_lease_buf = smb2_create_lease_buf, 1097 .create_lease_buf = smb2_create_lease_buf,
1019 .parse_lease_buf = smb2_parse_lease_buf, 1098 .parse_lease_buf = smb2_parse_lease_buf,
1099 .clone_range = smb2_clone_range,
1020}; 1100};
1021 1101
1022struct smb_version_operations smb21_operations = { 1102struct smb_version_operations smb21_operations = {
@@ -1090,6 +1170,7 @@ struct smb_version_operations smb21_operations = {
1090 .set_oplock_level = smb21_set_oplock_level, 1170 .set_oplock_level = smb21_set_oplock_level,
1091 .create_lease_buf = smb2_create_lease_buf, 1171 .create_lease_buf = smb2_create_lease_buf,
1092 .parse_lease_buf = smb2_parse_lease_buf, 1172 .parse_lease_buf = smb2_parse_lease_buf,
1173 .clone_range = smb2_clone_range,
1093}; 1174};
1094 1175
1095struct smb_version_operations smb30_operations = { 1176struct smb_version_operations smb30_operations = {
@@ -1165,6 +1246,7 @@ struct smb_version_operations smb30_operations = {
1165 .set_oplock_level = smb3_set_oplock_level, 1246 .set_oplock_level = smb3_set_oplock_level,
1166 .create_lease_buf = smb3_create_lease_buf, 1247 .create_lease_buf = smb3_create_lease_buf,
1167 .parse_lease_buf = smb3_parse_lease_buf, 1248 .parse_lease_buf = smb3_parse_lease_buf,
1249 .clone_range = smb2_clone_range,
1168}; 1250};
1169 1251
1170struct smb_version_values smb20_values = { 1252struct smb_version_values smb20_values = {
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 6183b1b7550f..b50a129572cd 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -534,9 +534,16 @@ struct create_durable {
534 } Data; 534 } Data;
535} __packed; 535} __packed;
536 536
537#define COPY_CHUNK_RES_KEY_SIZE 24
538struct resume_key_req {
539 char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
540 __le32 ContextLength; /* MBZ */
541 char Context[0]; /* ignored, Windows sets to 4 bytes of zero */
542} __packed;
543
537/* this goes in the ioctl buffer when doing a copychunk request */ 544/* this goes in the ioctl buffer when doing a copychunk request */
538struct copychunk_ioctl { 545struct copychunk_ioctl {
539 char SourceKey[24]; 546 char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
540 __le32 ChunkCount; /* we are only sending 1 */ 547 __le32 ChunkCount; /* we are only sending 1 */
541 __le32 Reserved; 548 __le32 Reserved;
542 /* array will only be one chunk long for us */ 549 /* array will only be one chunk long for us */
@@ -546,6 +553,12 @@ struct copychunk_ioctl {
546 __u32 Reserved2; 553 __u32 Reserved2;
547} __packed; 554} __packed;
548 555
556struct copychunk_ioctl_rsp {
557 __le32 ChunksWritten;
558 __le32 ChunkBytesWritten;
559 __le32 TotalBytesWritten;
560} __packed;
561
549/* Response and Request are the same format */ 562/* Response and Request are the same format */
550struct validate_negotiate_info { 563struct validate_negotiate_info {
551 __le32 Capabilities; 564 __le32 Capabilities;