aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;