diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-19 09:22:44 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:33 -0400 |
commit | b8c32dbb0deb287a5fcb78251e4eae6c7275760d (patch) | |
tree | fa3cddfd4595846921f51a922b7e1722b1e35fa4 /fs/cifs | |
parent | 579f9053236c796d718162c37c72bb3bd32d008c (diff) |
CIFS: Request SMB2.1 leases
if server supports them and we need oplocks.
Signed-off-by: Pavel Shilovsky <piastryyy@gmail.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsfs.c | 28 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 10 | ||||
-rw-r--r-- | fs/cifs/dir.c | 13 | ||||
-rw-r--r-- | fs/cifs/file.c | 21 | ||||
-rw-r--r-- | fs/cifs/smb2file.c | 9 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 21 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 138 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 43 |
8 files changed, 256 insertions, 27 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 4cd68c77ce3..28ac048d54e 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/kthread.h> | 36 | #include <linux/kthread.h> |
37 | #include <linux/freezer.h> | 37 | #include <linux/freezer.h> |
38 | #include <linux/namei.h> | 38 | #include <linux/namei.h> |
39 | #include <linux/random.h> | ||
39 | #include <net/ipv6.h> | 40 | #include <net/ipv6.h> |
40 | #include "cifsfs.h" | 41 | #include "cifsfs.h" |
41 | #include "cifspdu.h" | 42 | #include "cifspdu.h" |
@@ -88,6 +89,10 @@ extern mempool_t *cifs_mid_poolp; | |||
88 | 89 | ||
89 | struct workqueue_struct *cifsiod_wq; | 90 | struct workqueue_struct *cifsiod_wq; |
90 | 91 | ||
92 | #ifdef CONFIG_CIFS_SMB2 | ||
93 | __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE]; | ||
94 | #endif | ||
95 | |||
91 | static int | 96 | static int |
92 | cifs_read_super(struct super_block *sb) | 97 | cifs_read_super(struct super_block *sb) |
93 | { | 98 | { |
@@ -218,9 +223,10 @@ cifs_alloc_inode(struct super_block *sb) | |||
218 | return NULL; | 223 | return NULL; |
219 | cifs_inode->cifsAttrs = 0x20; /* default */ | 224 | cifs_inode->cifsAttrs = 0x20; /* default */ |
220 | cifs_inode->time = 0; | 225 | cifs_inode->time = 0; |
221 | /* Until the file is open and we have gotten oplock | 226 | /* |
222 | info back from the server, can not assume caching of | 227 | * Until the file is open and we have gotten oplock info back from the |
223 | file data or metadata */ | 228 | * server, can not assume caching of file data or metadata. |
229 | */ | ||
224 | cifs_set_oplock_level(cifs_inode, 0); | 230 | cifs_set_oplock_level(cifs_inode, 0); |
225 | cifs_inode->delete_pending = false; | 231 | cifs_inode->delete_pending = false; |
226 | cifs_inode->invalid_mapping = false; | 232 | cifs_inode->invalid_mapping = false; |
@@ -228,10 +234,14 @@ cifs_alloc_inode(struct super_block *sb) | |||
228 | cifs_inode->server_eof = 0; | 234 | cifs_inode->server_eof = 0; |
229 | cifs_inode->uniqueid = 0; | 235 | cifs_inode->uniqueid = 0; |
230 | cifs_inode->createtime = 0; | 236 | cifs_inode->createtime = 0; |
231 | 237 | #ifdef CONFIG_CIFS_SMB2 | |
232 | /* Can not set i_flags here - they get immediately overwritten | 238 | get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); |
233 | to zero by the VFS */ | 239 | #endif |
234 | /* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;*/ | 240 | /* |
241 | * Can not set i_flags here - they get immediately overwritten to zero | ||
242 | * by the VFS. | ||
243 | */ | ||
244 | /* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */ | ||
235 | INIT_LIST_HEAD(&cifs_inode->openFileList); | 245 | INIT_LIST_HEAD(&cifs_inode->openFileList); |
236 | INIT_LIST_HEAD(&cifs_inode->llist); | 246 | INIT_LIST_HEAD(&cifs_inode->llist); |
237 | return &cifs_inode->vfs_inode; | 247 | return &cifs_inode->vfs_inode; |
@@ -1107,6 +1117,10 @@ init_cifs(void) | |||
1107 | spin_lock_init(&cifs_file_list_lock); | 1117 | spin_lock_init(&cifs_file_list_lock); |
1108 | spin_lock_init(&GlobalMid_Lock); | 1118 | spin_lock_init(&GlobalMid_Lock); |
1109 | 1119 | ||
1120 | #ifdef CONFIG_CIFS_SMB2 | ||
1121 | get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE); | ||
1122 | #endif | ||
1123 | |||
1110 | if (cifs_max_pending < 2) { | 1124 | if (cifs_max_pending < 2) { |
1111 | cifs_max_pending = 2; | 1125 | cifs_max_pending = 2; |
1112 | cFYI(1, "cifs_max_pending set to min of 2"); | 1126 | cFYI(1, "cifs_max_pending set to min of 2"); |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e2492e1cdb8..b6ec142028e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -361,6 +361,12 @@ struct smb_version_operations { | |||
361 | const unsigned int); | 361 | const unsigned int); |
362 | /* push brlocks from the cache to the server */ | 362 | /* push brlocks from the cache to the server */ |
363 | int (*push_mand_locks)(struct cifsFileInfo *); | 363 | int (*push_mand_locks)(struct cifsFileInfo *); |
364 | /* get lease key of the inode */ | ||
365 | void (*get_lease_key)(struct inode *, struct cifs_fid *fid); | ||
366 | /* set lease key of the inode */ | ||
367 | void (*set_lease_key)(struct inode *, struct cifs_fid *fid); | ||
368 | /* generate new lease key */ | ||
369 | void (*new_lease_key)(struct cifs_fid *fid); | ||
364 | }; | 370 | }; |
365 | 371 | ||
366 | struct smb_version_values { | 372 | struct smb_version_values { |
@@ -895,6 +901,7 @@ struct cifs_fid { | |||
895 | #ifdef CONFIG_CIFS_SMB2 | 901 | #ifdef CONFIG_CIFS_SMB2 |
896 | __u64 persistent_fid; /* persist file id for smb2 */ | 902 | __u64 persistent_fid; /* persist file id for smb2 */ |
897 | __u64 volatile_fid; /* volatile file id for smb2 */ | 903 | __u64 volatile_fid; /* volatile file id for smb2 */ |
904 | __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ | ||
898 | #endif | 905 | #endif |
899 | }; | 906 | }; |
900 | 907 | ||
@@ -1012,6 +1019,9 @@ struct cifsInodeInfo { | |||
1012 | u64 server_eof; /* current file size on server -- protected by i_lock */ | 1019 | u64 server_eof; /* current file size on server -- protected by i_lock */ |
1013 | u64 uniqueid; /* server inode number */ | 1020 | u64 uniqueid; /* server inode number */ |
1014 | u64 createtime; /* creation time on server */ | 1021 | u64 createtime; /* creation time on server */ |
1022 | #ifdef CONFIG_CIFS_SMB2 | ||
1023 | __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ | ||
1024 | #endif | ||
1015 | #ifdef CONFIG_CIFS_FSCACHE | 1025 | #ifdef CONFIG_CIFS_FSCACHE |
1016 | struct fscache_cookie *fscache; | 1026 | struct fscache_cookie *fscache; |
1017 | #endif | 1027 | #endif |
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index b99a1670dad..4f2147c5adb 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
@@ -340,6 +340,8 @@ cifs_create_get_file_info: | |||
340 | rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, | 340 | rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, |
341 | xid, &fid->netfid); | 341 | xid, &fid->netfid); |
342 | if (newinode) { | 342 | if (newinode) { |
343 | if (server->ops->set_lease_key) | ||
344 | server->ops->set_lease_key(newinode, fid); | ||
343 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) | 345 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) |
344 | newinode->i_mode = mode; | 346 | newinode->i_mode = mode; |
345 | if ((*oplock & CIFS_CREATE_ACTION) && | 347 | if ((*oplock & CIFS_CREATE_ACTION) && |
@@ -418,6 +420,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, | |||
418 | tcon = tlink_tcon(tlink); | 420 | tcon = tlink_tcon(tlink); |
419 | server = tcon->ses->server; | 421 | server = tcon->ses->server; |
420 | 422 | ||
423 | if (server->ops->new_lease_key) | ||
424 | server->ops->new_lease_key(&fid); | ||
425 | |||
421 | rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, | 426 | rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, |
422 | &oplock, &fid, opened); | 427 | &oplock, &fid, opened); |
423 | 428 | ||
@@ -473,10 +478,14 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode, | |||
473 | if (IS_ERR(tlink)) | 478 | if (IS_ERR(tlink)) |
474 | goto out_free_xid; | 479 | goto out_free_xid; |
475 | 480 | ||
476 | rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, | ||
477 | &oplock, &fid, &created); | ||
478 | tcon = tlink_tcon(tlink); | 481 | tcon = tlink_tcon(tlink); |
479 | server = tcon->ses->server; | 482 | server = tcon->ses->server; |
483 | |||
484 | if (server->ops->new_lease_key) | ||
485 | server->ops->new_lease_key(&fid); | ||
486 | |||
487 | rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, | ||
488 | &oplock, &fid, &created); | ||
480 | if (!rc && server->ops->close) | 489 | if (!rc && server->ops->close) |
481 | server->ops->close(xid, tcon, &fid); | 490 | server->ops->close(xid, tcon, &fid); |
482 | 491 | ||
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 2e2e4f9aeb6..ccad858d2d6 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -177,8 +177,9 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, | |||
177 | int disposition; | 177 | int disposition; |
178 | int create_options = CREATE_NOT_DIR; | 178 | int create_options = CREATE_NOT_DIR; |
179 | FILE_ALL_INFO *buf; | 179 | FILE_ALL_INFO *buf; |
180 | struct TCP_Server_Info *server = tcon->ses->server; | ||
180 | 181 | ||
181 | if (!tcon->ses->server->ops->open) | 182 | if (!server->ops->open) |
182 | return -ENOSYS; | 183 | return -ENOSYS; |
183 | 184 | ||
184 | desired_access = cifs_convert_flags(f_flags); | 185 | desired_access = cifs_convert_flags(f_flags); |
@@ -218,9 +219,9 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, | |||
218 | if (backup_cred(cifs_sb)) | 219 | if (backup_cred(cifs_sb)) |
219 | create_options |= CREATE_OPEN_BACKUP_INTENT; | 220 | create_options |= CREATE_OPEN_BACKUP_INTENT; |
220 | 221 | ||
221 | rc = tcon->ses->server->ops->open(xid, tcon, full_path, disposition, | 222 | rc = server->ops->open(xid, tcon, full_path, disposition, |
222 | desired_access, create_options, fid, | 223 | desired_access, create_options, fid, oplock, buf, |
223 | oplock, buf, cifs_sb); | 224 | cifs_sb); |
224 | 225 | ||
225 | if (rc) | 226 | if (rc) |
226 | goto out; | 227 | goto out; |
@@ -372,6 +373,7 @@ int cifs_open(struct inode *inode, struct file *file) | |||
372 | unsigned int xid; | 373 | unsigned int xid; |
373 | __u32 oplock; | 374 | __u32 oplock; |
374 | struct cifs_sb_info *cifs_sb; | 375 | struct cifs_sb_info *cifs_sb; |
376 | struct TCP_Server_Info *server; | ||
375 | struct cifs_tcon *tcon; | 377 | struct cifs_tcon *tcon; |
376 | struct tcon_link *tlink; | 378 | struct tcon_link *tlink; |
377 | struct cifsFileInfo *cfile = NULL; | 379 | struct cifsFileInfo *cfile = NULL; |
@@ -388,6 +390,7 @@ int cifs_open(struct inode *inode, struct file *file) | |||
388 | return PTR_ERR(tlink); | 390 | return PTR_ERR(tlink); |
389 | } | 391 | } |
390 | tcon = tlink_tcon(tlink); | 392 | tcon = tlink_tcon(tlink); |
393 | server = tcon->ses->server; | ||
391 | 394 | ||
392 | full_path = build_path_from_dentry(file->f_path.dentry); | 395 | full_path = build_path_from_dentry(file->f_path.dentry); |
393 | if (full_path == NULL) { | 396 | if (full_path == NULL) { |
@@ -432,6 +435,9 @@ int cifs_open(struct inode *inode, struct file *file) | |||
432 | } | 435 | } |
433 | 436 | ||
434 | if (!posix_open_ok) { | 437 | if (!posix_open_ok) { |
438 | if (server->ops->get_lease_key) | ||
439 | server->ops->get_lease_key(inode, &fid); | ||
440 | |||
435 | rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, | 441 | rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, |
436 | file->f_flags, &oplock, &fid, xid); | 442 | file->f_flags, &oplock, &fid, xid); |
437 | if (rc) | 443 | if (rc) |
@@ -440,8 +446,8 @@ int cifs_open(struct inode *inode, struct file *file) | |||
440 | 446 | ||
441 | cfile = cifs_new_fileinfo(&fid, file, tlink, oplock); | 447 | cfile = cifs_new_fileinfo(&fid, file, tlink, oplock); |
442 | if (cfile == NULL) { | 448 | if (cfile == NULL) { |
443 | if (tcon->ses->server->ops->close) | 449 | if (server->ops->close) |
444 | tcon->ses->server->ops->close(xid, tcon, &fid); | 450 | server->ops->close(xid, tcon, &fid); |
445 | rc = -ENOMEM; | 451 | rc = -ENOMEM; |
446 | goto out; | 452 | goto out; |
447 | } | 453 | } |
@@ -567,6 +573,9 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) | |||
567 | if (backup_cred(cifs_sb)) | 573 | if (backup_cred(cifs_sb)) |
568 | create_options |= CREATE_OPEN_BACKUP_INTENT; | 574 | create_options |= CREATE_OPEN_BACKUP_INTENT; |
569 | 575 | ||
576 | if (server->ops->get_lease_key) | ||
577 | server->ops->get_lease_key(inode, &fid); | ||
578 | |||
570 | /* | 579 | /* |
571 | * Can not refresh inode by passing in file_info buf to be returned by | 580 | * Can not refresh inode by passing in file_info buf to be returned by |
572 | * CIFSSMBOpen and then calling get_inode_info with returned buf since | 581 | * CIFSSMBOpen and then calling get_inode_info with returned buf since |
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 0ddd617ffa1..78fb2050e0d 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c | |||
@@ -63,6 +63,7 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, | |||
63 | int rc; | 63 | int rc; |
64 | __le16 *smb2_path; | 64 | __le16 *smb2_path; |
65 | struct smb2_file_all_info *smb2_data = NULL; | 65 | struct smb2_file_all_info *smb2_data = NULL; |
66 | __u8 smb2_oplock[17]; | ||
66 | 67 | ||
67 | smb2_path = cifs_convert_path_to_utf16(path, cifs_sb); | 68 | smb2_path = cifs_convert_path_to_utf16(path, cifs_sb); |
68 | if (smb2_path == NULL) { | 69 | if (smb2_path == NULL) { |
@@ -78,11 +79,14 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, | |||
78 | } | 79 | } |
79 | 80 | ||
80 | desired_access |= FILE_READ_ATTRIBUTES; | 81 | desired_access |= FILE_READ_ATTRIBUTES; |
81 | *oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; | 82 | *smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; |
83 | |||
84 | if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) | ||
85 | memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); | ||
82 | 86 | ||
83 | rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid, | 87 | rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid, |
84 | &fid->volatile_fid, desired_access, disposition, | 88 | &fid->volatile_fid, desired_access, disposition, |
85 | 0, 0, (__u8 *)oplock, smb2_data); | 89 | 0, 0, smb2_oplock, smb2_data); |
86 | if (rc) | 90 | if (rc) |
87 | goto out; | 91 | goto out; |
88 | 92 | ||
@@ -99,6 +103,7 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, | |||
99 | move_smb2_info_to_cifs(buf, smb2_data); | 103 | move_smb2_info_to_cifs(buf, smb2_data); |
100 | } | 104 | } |
101 | 105 | ||
106 | *oplock = *smb2_oplock; | ||
102 | out: | 107 | out: |
103 | kfree(smb2_data); | 108 | kfree(smb2_data); |
104 | kfree(smb2_path); | 109 | kfree(smb2_path); |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0808b238219..360d9079af4 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -555,6 +555,24 @@ smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, | |||
555 | current->tgid, length, offset, type, wait); | 555 | current->tgid, length, offset, type, wait); |
556 | } | 556 | } |
557 | 557 | ||
558 | static void | ||
559 | smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) | ||
560 | { | ||
561 | memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); | ||
562 | } | ||
563 | |||
564 | static void | ||
565 | smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) | ||
566 | { | ||
567 | memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); | ||
568 | } | ||
569 | |||
570 | static void | ||
571 | smb2_new_lease_key(struct cifs_fid *fid) | ||
572 | { | ||
573 | get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); | ||
574 | } | ||
575 | |||
558 | struct smb_version_operations smb21_operations = { | 576 | struct smb_version_operations smb21_operations = { |
559 | .compare_fids = smb2_compare_fids, | 577 | .compare_fids = smb2_compare_fids, |
560 | .setup_request = smb2_setup_request, | 578 | .setup_request = smb2_setup_request, |
@@ -616,6 +634,9 @@ struct smb_version_operations smb21_operations = { | |||
616 | .mand_lock = smb2_mand_lock, | 634 | .mand_lock = smb2_mand_lock, |
617 | .mand_unlock_range = smb2_unlock_range, | 635 | .mand_unlock_range = smb2_unlock_range, |
618 | .push_mand_locks = smb2_push_mandatory_locks, | 636 | .push_mand_locks = smb2_push_mandatory_locks, |
637 | .get_lease_key = smb2_get_lease_key, | ||
638 | .set_lease_key = smb2_set_lease_key, | ||
639 | .new_lease_key = smb2_new_lease_key, | ||
619 | }; | 640 | }; |
620 | 641 | ||
621 | struct smb_version_values smb21_values = { | 642 | struct smb_version_values smb21_values = { |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d3e1cfca337..89d2824587b 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -304,7 +304,7 @@ free_rsp_buf(int resp_buftype, void *rsp) | |||
304 | cifs_buf_release(rsp); | 304 | cifs_buf_release(rsp); |
305 | } | 305 | } |
306 | 306 | ||
307 | #define SMB2_NUM_PROT 1 | 307 | #define SMB2_NUM_PROT 2 |
308 | 308 | ||
309 | #define SMB2_PROT 0 | 309 | #define SMB2_PROT 0 |
310 | #define SMB21_PROT 1 | 310 | #define SMB21_PROT 1 |
@@ -393,6 +393,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) | |||
393 | 393 | ||
394 | req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); | 394 | req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); |
395 | 395 | ||
396 | memcpy(req->ClientGUID, cifs_client_guid, SMB2_CLIENT_GUID_SIZE); | ||
397 | |||
396 | iov[0].iov_base = (char *)req; | 398 | iov[0].iov_base = (char *)req; |
397 | /* 4 for rfc1002 length field */ | 399 | /* 4 for rfc1002 length field */ |
398 | iov[0].iov_len = get_rfc1002_length(req) + 4; | 400 | iov[0].iov_len = get_rfc1002_length(req) + 4; |
@@ -868,6 +870,83 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) | |||
868 | return rc; | 870 | return rc; |
869 | } | 871 | } |
870 | 872 | ||
873 | static struct create_lease * | ||
874 | create_lease_buf(u8 *lease_key, u8 oplock) | ||
875 | { | ||
876 | struct create_lease *buf; | ||
877 | |||
878 | buf = kmalloc(sizeof(struct create_lease), GFP_KERNEL); | ||
879 | if (!buf) | ||
880 | return NULL; | ||
881 | |||
882 | memset(buf, 0, sizeof(struct create_lease)); | ||
883 | |||
884 | buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key)); | ||
885 | buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8))); | ||
886 | if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) | ||
887 | buf->lcontext.LeaseState = SMB2_LEASE_WRITE_CACHING | | ||
888 | SMB2_LEASE_READ_CACHING; | ||
889 | else if (oplock == SMB2_OPLOCK_LEVEL_II) | ||
890 | buf->lcontext.LeaseState = SMB2_LEASE_READ_CACHING; | ||
891 | else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) | ||
892 | buf->lcontext.LeaseState = SMB2_LEASE_HANDLE_CACHING | | ||
893 | SMB2_LEASE_READ_CACHING | | ||
894 | SMB2_LEASE_WRITE_CACHING; | ||
895 | |||
896 | buf->ccontext.DataOffset = cpu_to_le16(offsetof | ||
897 | (struct create_lease, lcontext)); | ||
898 | buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); | ||
899 | buf->ccontext.NameOffset = cpu_to_le16(offsetof | ||
900 | (struct create_lease, Name)); | ||
901 | buf->ccontext.NameLength = cpu_to_le16(4); | ||
902 | buf->Name[0] = 'R'; | ||
903 | buf->Name[1] = 'q'; | ||
904 | buf->Name[2] = 'L'; | ||
905 | buf->Name[3] = 's'; | ||
906 | return buf; | ||
907 | } | ||
908 | |||
909 | static __u8 | ||
910 | parse_lease_state(struct smb2_create_rsp *rsp) | ||
911 | { | ||
912 | char *data_offset; | ||
913 | struct create_lease *lc; | ||
914 | __u8 oplock = 0; | ||
915 | bool found = false; | ||
916 | |||
917 | data_offset = (char *)rsp; | ||
918 | data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset); | ||
919 | lc = (struct create_lease *)data_offset; | ||
920 | do { | ||
921 | char *name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc; | ||
922 | if (le16_to_cpu(lc->ccontext.NameLength) != 4 || | ||
923 | strncmp(name, "RqLs", 4)) { | ||
924 | lc = (struct create_lease *)((char *)lc | ||
925 | + le32_to_cpu(lc->ccontext.Next)); | ||
926 | continue; | ||
927 | } | ||
928 | if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) | ||
929 | return SMB2_OPLOCK_LEVEL_NOCHANGE; | ||
930 | found = true; | ||
931 | break; | ||
932 | } while (le32_to_cpu(lc->ccontext.Next) != 0); | ||
933 | |||
934 | if (!found) | ||
935 | return oplock; | ||
936 | |||
937 | if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_WRITE_CACHING) { | ||
938 | if (le32_to_cpu(lc->lcontext.LeaseState) & | ||
939 | SMB2_LEASE_HANDLE_CACHING) | ||
940 | oplock = SMB2_OPLOCK_LEVEL_BATCH; | ||
941 | else | ||
942 | oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; | ||
943 | } else if (le32_to_cpu(lc->lcontext.LeaseState) & | ||
944 | SMB2_LEASE_READ_CACHING) | ||
945 | oplock = SMB2_OPLOCK_LEVEL_II; | ||
946 | |||
947 | return oplock; | ||
948 | } | ||
949 | |||
871 | int | 950 | int |
872 | SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | 951 | SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, |
873 | u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, | 952 | u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, |
@@ -878,9 +957,11 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
878 | struct smb2_create_rsp *rsp; | 957 | struct smb2_create_rsp *rsp; |
879 | struct TCP_Server_Info *server; | 958 | struct TCP_Server_Info *server; |
880 | struct cifs_ses *ses = tcon->ses; | 959 | struct cifs_ses *ses = tcon->ses; |
881 | struct kvec iov[2]; | 960 | struct kvec iov[3]; |
882 | int resp_buftype; | 961 | int resp_buftype; |
883 | int uni_path_len; | 962 | int uni_path_len; |
963 | __le16 *copy_path = NULL; | ||
964 | int copy_size; | ||
884 | int rc = 0; | 965 | int rc = 0; |
885 | int num_iovecs = 2; | 966 | int num_iovecs = 2; |
886 | 967 | ||
@@ -895,10 +976,6 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
895 | if (rc) | 976 | if (rc) |
896 | return rc; | 977 | return rc; |
897 | 978 | ||
898 | if (server->oplocks) | ||
899 | req->RequestedOplockLevel = *oplock; | ||
900 | else | ||
901 | req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; | ||
902 | req->ImpersonationLevel = IL_IMPERSONATION; | 979 | req->ImpersonationLevel = IL_IMPERSONATION; |
903 | req->DesiredAccess = cpu_to_le32(desired_access); | 980 | req->DesiredAccess = cpu_to_le32(desired_access); |
904 | /* File attributes ignored on open (used in create though) */ | 981 | /* File attributes ignored on open (used in create though) */ |
@@ -908,7 +985,7 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
908 | req->CreateOptions = cpu_to_le32(create_options); | 985 | req->CreateOptions = cpu_to_le32(create_options); |
909 | uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; | 986 | uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; |
910 | req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) | 987 | req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) |
911 | - 1 /* pad */ - 4 /* do not count rfc1001 len field */); | 988 | - 8 /* pad */ - 4 /* do not count rfc1001 len field */); |
912 | 989 | ||
913 | iov[0].iov_base = (char *)req; | 990 | iov[0].iov_base = (char *)req; |
914 | /* 4 for rfc1002 length field */ | 991 | /* 4 for rfc1002 length field */ |
@@ -919,6 +996,20 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
919 | req->NameLength = cpu_to_le16(uni_path_len - 2); | 996 | req->NameLength = cpu_to_le16(uni_path_len - 2); |
920 | /* -1 since last byte is buf[0] which is sent below (path) */ | 997 | /* -1 since last byte is buf[0] which is sent below (path) */ |
921 | iov[0].iov_len--; | 998 | iov[0].iov_len--; |
999 | if (uni_path_len % 8 != 0) { | ||
1000 | copy_size = uni_path_len / 8 * 8; | ||
1001 | if (copy_size < uni_path_len) | ||
1002 | copy_size += 8; | ||
1003 | |||
1004 | copy_path = kzalloc(copy_size, GFP_KERNEL); | ||
1005 | if (!copy_path) | ||
1006 | return -ENOMEM; | ||
1007 | memcpy((char *)copy_path, (const char *)path, | ||
1008 | uni_path_len); | ||
1009 | uni_path_len = copy_size; | ||
1010 | path = copy_path; | ||
1011 | } | ||
1012 | |||
922 | iov[1].iov_len = uni_path_len; | 1013 | iov[1].iov_len = uni_path_len; |
923 | iov[1].iov_base = path; | 1014 | iov[1].iov_base = path; |
924 | /* | 1015 | /* |
@@ -927,10 +1018,37 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
927 | */ | 1018 | */ |
928 | inc_rfc1001_len(req, uni_path_len - 1); | 1019 | inc_rfc1001_len(req, uni_path_len - 1); |
929 | } else { | 1020 | } else { |
1021 | iov[0].iov_len += 7; | ||
1022 | req->hdr.smb2_buf_length = cpu_to_be32(be32_to_cpu( | ||
1023 | req->hdr.smb2_buf_length) + 8 - 1); | ||
930 | num_iovecs = 1; | 1024 | num_iovecs = 1; |
931 | req->NameLength = 0; | 1025 | req->NameLength = 0; |
932 | } | 1026 | } |
933 | 1027 | ||
1028 | if (!server->oplocks) | ||
1029 | *oplock = SMB2_OPLOCK_LEVEL_NONE; | ||
1030 | |||
1031 | if (!(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) || | ||
1032 | *oplock == SMB2_OPLOCK_LEVEL_NONE) | ||
1033 | req->RequestedOplockLevel = *oplock; | ||
1034 | else { | ||
1035 | iov[num_iovecs].iov_base = create_lease_buf(oplock+1, *oplock); | ||
1036 | if (iov[num_iovecs].iov_base == NULL) { | ||
1037 | cifs_small_buf_release(req); | ||
1038 | kfree(copy_path); | ||
1039 | return -ENOMEM; | ||
1040 | } | ||
1041 | iov[num_iovecs].iov_len = sizeof(struct create_lease); | ||
1042 | req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; | ||
1043 | req->CreateContextsOffset = cpu_to_le32( | ||
1044 | sizeof(struct smb2_create_req) - 4 - 8 + | ||
1045 | iov[num_iovecs-1].iov_len); | ||
1046 | req->CreateContextsLength = cpu_to_le32( | ||
1047 | sizeof(struct create_lease)); | ||
1048 | inc_rfc1001_len(&req->hdr, sizeof(struct create_lease)); | ||
1049 | num_iovecs++; | ||
1050 | } | ||
1051 | |||
934 | rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); | 1052 | rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); |
935 | rsp = (struct smb2_create_rsp *)iov[0].iov_base; | 1053 | rsp = (struct smb2_create_rsp *)iov[0].iov_base; |
936 | 1054 | ||
@@ -955,8 +1073,12 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
955 | buf->DeletePending = 0; | 1073 | buf->DeletePending = 0; |
956 | } | 1074 | } |
957 | 1075 | ||
958 | *oplock = rsp->OplockLevel; | 1076 | if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) |
1077 | *oplock = parse_lease_state(rsp); | ||
1078 | else | ||
1079 | *oplock = rsp->OplockLevel; | ||
959 | creat_exit: | 1080 | creat_exit: |
1081 | kfree(copy_path); | ||
960 | free_rsp_buf(resp_buftype, rsp); | 1082 | free_rsp_buf(resp_buftype, rsp); |
961 | return rc; | 1083 | return rc; |
962 | } | 1084 | } |
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 889ee5e193d..e818a5cc5bd 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -150,6 +150,10 @@ struct smb2_err_rsp { | |||
150 | __u8 ErrorData[1]; /* variable length */ | 150 | __u8 ErrorData[1]; /* variable length */ |
151 | } __packed; | 151 | } __packed; |
152 | 152 | ||
153 | #define SMB2_CLIENT_GUID_SIZE 16 | ||
154 | |||
155 | extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE]; | ||
156 | |||
153 | struct smb2_negotiate_req { | 157 | struct smb2_negotiate_req { |
154 | struct smb2_hdr hdr; | 158 | struct smb2_hdr hdr; |
155 | __le16 StructureSize; /* Must be 36 */ | 159 | __le16 StructureSize; /* Must be 36 */ |
@@ -157,7 +161,7 @@ struct smb2_negotiate_req { | |||
157 | __le16 SecurityMode; | 161 | __le16 SecurityMode; |
158 | __le16 Reserved; /* MBZ */ | 162 | __le16 Reserved; /* MBZ */ |
159 | __le32 Capabilities; | 163 | __le32 Capabilities; |
160 | __u8 ClientGUID[16]; /* MBZ */ | 164 | __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; |
161 | __le64 ClientStartTime; /* MBZ */ | 165 | __le64 ClientStartTime; /* MBZ */ |
162 | __le16 Dialects[2]; /* variable length */ | 166 | __le16 Dialects[2]; /* variable length */ |
163 | } __packed; | 167 | } __packed; |
@@ -307,6 +311,8 @@ struct smb2_tree_disconnect_rsp { | |||
307 | #define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 | 311 | #define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 |
308 | #define SMB2_OPLOCK_LEVEL_BATCH 0x09 | 312 | #define SMB2_OPLOCK_LEVEL_BATCH 0x09 |
309 | #define SMB2_OPLOCK_LEVEL_LEASE 0xFF | 313 | #define SMB2_OPLOCK_LEVEL_LEASE 0xFF |
314 | /* Non-spec internal type */ | ||
315 | #define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 | ||
310 | 316 | ||
311 | /* Desired Access Flags */ | 317 | /* Desired Access Flags */ |
312 | #define FILE_READ_DATA_LE cpu_to_le32(0x00000001) | 318 | #define FILE_READ_DATA_LE cpu_to_le32(0x00000001) |
@@ -404,7 +410,7 @@ struct smb2_create_req { | |||
404 | __le16 NameLength; | 410 | __le16 NameLength; |
405 | __le32 CreateContextsOffset; | 411 | __le32 CreateContextsOffset; |
406 | __le32 CreateContextsLength; | 412 | __le32 CreateContextsLength; |
407 | __u8 Buffer[1]; | 413 | __u8 Buffer[8]; |
408 | } __packed; | 414 | } __packed; |
409 | 415 | ||
410 | struct smb2_create_rsp { | 416 | struct smb2_create_rsp { |
@@ -428,6 +434,39 @@ struct smb2_create_rsp { | |||
428 | __u8 Buffer[1]; | 434 | __u8 Buffer[1]; |
429 | } __packed; | 435 | } __packed; |
430 | 436 | ||
437 | struct create_context { | ||
438 | __le32 Next; | ||
439 | __le16 NameOffset; | ||
440 | __le16 NameLength; | ||
441 | __le16 Reserved; | ||
442 | __le16 DataOffset; | ||
443 | __le32 DataLength; | ||
444 | __u8 Buffer[0]; | ||
445 | } __packed; | ||
446 | |||
447 | #define SMB2_LEASE_NONE __constant_cpu_to_le32(0x00) | ||
448 | #define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01) | ||
449 | #define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02) | ||
450 | #define SMB2_LEASE_WRITE_CACHING __constant_cpu_to_le32(0x04) | ||
451 | |||
452 | #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x02) | ||
453 | |||
454 | #define SMB2_LEASE_KEY_SIZE 16 | ||
455 | |||
456 | struct lease_context { | ||
457 | __le64 LeaseKeyLow; | ||
458 | __le64 LeaseKeyHigh; | ||
459 | __le32 LeaseState; | ||
460 | __le32 LeaseFlags; | ||
461 | __le64 LeaseDuration; | ||
462 | } __packed; | ||
463 | |||
464 | struct create_lease { | ||
465 | struct create_context ccontext; | ||
466 | __u8 Name[8]; | ||
467 | struct lease_context lcontext; | ||
468 | } __packed; | ||
469 | |||
431 | /* Currently defined values for close flags */ | 470 | /* Currently defined values for close flags */ |
432 | #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) | 471 | #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) |
433 | struct smb2_close_req { | 472 | struct smb2_close_req { |