diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-16 19:19:31 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-16 19:19:31 -0500 |
commit | 1213959d4ad2f523290d0d7c94f712edef63852c (patch) | |
tree | 530a5f546b52a5b6e459b8f42d823a3570eb390f /fs/cifs | |
parent | 673fdfe3f0630b03f3854d0361b1232f2e5ef7fb (diff) | |
parent | 0cbaa53cdd33080c1e2d67ad9295b83c7954f2b3 (diff) |
Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS fixes from Steve French:
"A set of cifs fixes most important of which is Pavel's fix for some
problems with handling Windows reparse points and also the security
fix for setfacl over a cifs mount to Samba removing part of the ACL.
Both of these fixes are for stable as well.
Also added most of copychunk (copy offload) support to cifs although I
expect a final patch in that series (to fix handling of larger files)
in a few days (had to hold off on that in order to incorporate some
additional code review feedback).
Also added support for O_DIRECT on forcedirectio mounts (needed in
order to run some of the server benchmarks over cifs and smb2/smb3
mounts)"
* 'for-linus' of git://git.samba.org/sfrench/cifs-2.6:
[CIFS] Warn if SMB3 encryption required by server
setfacl removes part of ACL when setting POSIX ACLs to Samba
[CIFS] Set copychunk defaults
CIFS: SMB2/SMB3 Copy offload support (refcopy) phase 1
cifs: Use data structures to compute NTLMv2 response offsets
[CIFS] O_DIRECT opens should work on directio mounts
cifs: don't spam the logs on unexpected lookup errors
cifs: change ERRnomem error mapping from ENOMEM to EREMOTEIO
CIFS: Fix symbolic links usage
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsencrypt.c | 40 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 8 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 8 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 8 | ||||
-rw-r--r-- | fs/cifs/dir.c | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 22 | ||||
-rw-r--r-- | fs/cifs/inode.c | 23 | ||||
-rw-r--r-- | fs/cifs/ioctl.c | 111 | ||||
-rw-r--r-- | fs/cifs/netmisc.c | 2 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 40 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 21 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 16 | ||||
-rw-r--r-- | fs/cifs/smb2maperror.c | 2 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 82 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 12 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 33 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 2 |
17 files changed, 358 insertions, 74 deletions
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index fc6f4f3a1a9d..4934347321d3 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
@@ -548,7 +548,13 @@ static int | |||
548 | CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) | 548 | CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) |
549 | { | 549 | { |
550 | int rc; | 550 | int rc; |
551 | unsigned int offset = CIFS_SESS_KEY_SIZE + 8; | 551 | struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) |
552 | (ses->auth_key.response + CIFS_SESS_KEY_SIZE); | ||
553 | unsigned int hash_len; | ||
554 | |||
555 | /* The MD5 hash starts at challenge_key.key */ | ||
556 | hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + | ||
557 | offsetof(struct ntlmv2_resp, challenge.key[0])); | ||
552 | 558 | ||
553 | if (!ses->server->secmech.sdeschmacmd5) { | 559 | if (!ses->server->secmech.sdeschmacmd5) { |
554 | cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); | 560 | cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); |
@@ -556,7 +562,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) | |||
556 | } | 562 | } |
557 | 563 | ||
558 | rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, | 564 | rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, |
559 | ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); | 565 | ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); |
560 | if (rc) { | 566 | if (rc) { |
561 | cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", | 567 | cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", |
562 | __func__); | 568 | __func__); |
@@ -570,20 +576,21 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) | |||
570 | } | 576 | } |
571 | 577 | ||
572 | if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) | 578 | if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) |
573 | memcpy(ses->auth_key.response + offset, | 579 | memcpy(ntlmv2->challenge.key, |
574 | ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); | 580 | ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); |
575 | else | 581 | else |
576 | memcpy(ses->auth_key.response + offset, | 582 | memcpy(ntlmv2->challenge.key, |
577 | ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); | 583 | ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); |
578 | rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | 584 | rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, |
579 | ses->auth_key.response + offset, ses->auth_key.len - offset); | 585 | ntlmv2->challenge.key, hash_len); |
580 | if (rc) { | 586 | if (rc) { |
581 | cifs_dbg(VFS, "%s: Could not update with response\n", __func__); | 587 | cifs_dbg(VFS, "%s: Could not update with response\n", __func__); |
582 | return rc; | 588 | return rc; |
583 | } | 589 | } |
584 | 590 | ||
591 | /* Note that the MD5 digest over writes anon.challenge_key.key */ | ||
585 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | 592 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, |
586 | ses->auth_key.response + CIFS_SESS_KEY_SIZE); | 593 | ntlmv2->ntlmv2_hash); |
587 | if (rc) | 594 | if (rc) |
588 | cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); | 595 | cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); |
589 | 596 | ||
@@ -627,7 +634,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) | |||
627 | int rc; | 634 | int rc; |
628 | int baselen; | 635 | int baselen; |
629 | unsigned int tilen; | 636 | unsigned int tilen; |
630 | struct ntlmv2_resp *buf; | 637 | struct ntlmv2_resp *ntlmv2; |
631 | char ntlmv2_hash[16]; | 638 | char ntlmv2_hash[16]; |
632 | unsigned char *tiblob = NULL; /* target info blob */ | 639 | unsigned char *tiblob = NULL; /* target info blob */ |
633 | 640 | ||
@@ -660,13 +667,14 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) | |||
660 | } | 667 | } |
661 | ses->auth_key.len += baselen; | 668 | ses->auth_key.len += baselen; |
662 | 669 | ||
663 | buf = (struct ntlmv2_resp *) | 670 | ntlmv2 = (struct ntlmv2_resp *) |
664 | (ses->auth_key.response + CIFS_SESS_KEY_SIZE); | 671 | (ses->auth_key.response + CIFS_SESS_KEY_SIZE); |
665 | buf->blob_signature = cpu_to_le32(0x00000101); | 672 | ntlmv2->blob_signature = cpu_to_le32(0x00000101); |
666 | buf->reserved = 0; | 673 | ntlmv2->reserved = 0; |
667 | buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | 674 | /* Must be within 5 minutes of the server */ |
668 | get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); | 675 | ntlmv2->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); |
669 | buf->reserved2 = 0; | 676 | get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); |
677 | ntlmv2->reserved2 = 0; | ||
670 | 678 | ||
671 | memcpy(ses->auth_key.response + baselen, tiblob, tilen); | 679 | memcpy(ses->auth_key.response + baselen, tiblob, tilen); |
672 | 680 | ||
@@ -706,7 +714,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) | |||
706 | } | 714 | } |
707 | 715 | ||
708 | rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | 716 | rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, |
709 | ses->auth_key.response + CIFS_SESS_KEY_SIZE, | 717 | ntlmv2->ntlmv2_hash, |
710 | CIFS_HMAC_MD5_HASH_SIZE); | 718 | CIFS_HMAC_MD5_HASH_SIZE); |
711 | if (rc) { | 719 | if (rc) { |
712 | cifs_dbg(VFS, "%s: Could not update with response\n", __func__); | 720 | cifs_dbg(VFS, "%s: Could not update with response\n", __func__); |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 26b1c1dc93f6..d9ea7ada1378 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -261,7 +261,7 @@ struct smb_version_operations { | |||
261 | /* query path data from the server */ | 261 | /* query path data from the server */ |
262 | int (*query_path_info)(const unsigned int, struct cifs_tcon *, | 262 | int (*query_path_info)(const unsigned int, struct cifs_tcon *, |
263 | struct cifs_sb_info *, const char *, | 263 | struct cifs_sb_info *, const char *, |
264 | FILE_ALL_INFO *, bool *); | 264 | FILE_ALL_INFO *, bool *, bool *); |
265 | /* query file data from the server */ | 265 | /* query file data from the server */ |
266 | int (*query_file_info)(const unsigned int, struct cifs_tcon *, | 266 | int (*query_file_info)(const unsigned int, struct cifs_tcon *, |
267 | struct cifs_fid *, FILE_ALL_INFO *); | 267 | struct cifs_fid *, FILE_ALL_INFO *); |
@@ -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 | ||
386 | struct smb_version_values { | 389 | struct smb_version_values { |
@@ -855,6 +858,9 @@ struct cifs_tcon { | |||
855 | __le64 vol_create_time; | 858 | __le64 vol_create_time; |
856 | __u32 ss_flags; /* sector size flags */ | 859 | __u32 ss_flags; /* sector size flags */ |
857 | __u32 perf_sector_size; /* best sector size for perf */ | 860 | __u32 perf_sector_size; /* best sector size for perf */ |
861 | __u32 max_chunks; | ||
862 | __u32 max_bytes_chunk; | ||
863 | __u32 max_bytes_copy; | ||
858 | #endif /* CONFIG_CIFS_SMB2 */ | 864 | #endif /* CONFIG_CIFS_SMB2 */ |
859 | #ifdef CONFIG_CIFS_FSCACHE | 865 | #ifdef CONFIG_CIFS_FSCACHE |
860 | u64 resource_id; /* server resource id */ | 866 | u64 resource_id; /* server resource id */ |
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 9e5ee34de986..33df36ef9d52 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h | |||
@@ -697,7 +697,13 @@ struct ntlmssp2_name { | |||
697 | } __attribute__((packed)); | 697 | } __attribute__((packed)); |
698 | 698 | ||
699 | struct ntlmv2_resp { | 699 | struct ntlmv2_resp { |
700 | char ntlmv2_hash[CIFS_ENCPWD_SIZE]; | 700 | union { |
701 | char ntlmv2_hash[CIFS_ENCPWD_SIZE]; | ||
702 | struct { | ||
703 | __u8 reserved[8]; | ||
704 | __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; | ||
705 | } __attribute__((packed)) challenge; | ||
706 | } __attribute__((packed)); | ||
701 | __le32 blob_signature; | 707 | __le32 blob_signature; |
702 | __u32 reserved; | 708 | __u32 reserved; |
703 | __le64 time; | 709 | __le64 time; |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 93b29474714a..124aa0230c1b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -3369,11 +3369,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, | |||
3369 | return 0; | 3369 | return 0; |
3370 | } | 3370 | } |
3371 | cifs_acl->version = cpu_to_le16(1); | 3371 | cifs_acl->version = cpu_to_le16(1); |
3372 | if (acl_type == ACL_TYPE_ACCESS) | 3372 | if (acl_type == ACL_TYPE_ACCESS) { |
3373 | cifs_acl->access_entry_count = cpu_to_le16(count); | 3373 | cifs_acl->access_entry_count = cpu_to_le16(count); |
3374 | else if (acl_type == ACL_TYPE_DEFAULT) | 3374 | cifs_acl->default_entry_count = __constant_cpu_to_le16(0xFFFF); |
3375 | } else if (acl_type == ACL_TYPE_DEFAULT) { | ||
3375 | cifs_acl->default_entry_count = cpu_to_le16(count); | 3376 | cifs_acl->default_entry_count = cpu_to_le16(count); |
3376 | else { | 3377 | cifs_acl->access_entry_count = __constant_cpu_to_le16(0xFFFF); |
3378 | } else { | ||
3377 | cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); | 3379 | cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); |
3378 | return 0; | 3380 | return 0; |
3379 | } | 3381 | } |
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 5384c2a640ca..11ff5f116b20 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
@@ -756,7 +756,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, | |||
756 | /* if it was once a directory (but how can we tell?) we could do | 756 | /* if it was once a directory (but how can we tell?) we could do |
757 | shrink_dcache_parent(direntry); */ | 757 | shrink_dcache_parent(direntry); */ |
758 | } else if (rc != -EACCES) { | 758 | } else if (rc != -EACCES) { |
759 | cifs_dbg(VFS, "Unexpected lookup error %d\n", rc); | 759 | cifs_dbg(FYI, "Unexpected lookup error %d\n", rc); |
760 | /* We special case check for Access Denied - since that | 760 | /* We special case check for Access Denied - since that |
761 | is a common return code */ | 761 | is a common return code */ |
762 | } | 762 | } |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 7ddddf2e2504..5a5a87240fe2 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -3663,6 +3663,27 @@ void cifs_oplock_break(struct work_struct *work) | |||
3663 | } | 3663 | } |
3664 | } | 3664 | } |
3665 | 3665 | ||
3666 | /* | ||
3667 | * The presence of cifs_direct_io() in the address space ops vector | ||
3668 | * allowes open() O_DIRECT flags which would have failed otherwise. | ||
3669 | * | ||
3670 | * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests | ||
3671 | * so this method should never be called. | ||
3672 | * | ||
3673 | * Direct IO is not yet supported in the cached mode. | ||
3674 | */ | ||
3675 | static ssize_t | ||
3676 | cifs_direct_io(int rw, struct kiocb *iocb, const struct iovec *iov, | ||
3677 | loff_t pos, unsigned long nr_segs) | ||
3678 | { | ||
3679 | /* | ||
3680 | * FIXME | ||
3681 | * Eventually need to support direct IO for non forcedirectio mounts | ||
3682 | */ | ||
3683 | return -EINVAL; | ||
3684 | } | ||
3685 | |||
3686 | |||
3666 | const struct address_space_operations cifs_addr_ops = { | 3687 | const struct address_space_operations cifs_addr_ops = { |
3667 | .readpage = cifs_readpage, | 3688 | .readpage = cifs_readpage, |
3668 | .readpages = cifs_readpages, | 3689 | .readpages = cifs_readpages, |
@@ -3672,6 +3693,7 @@ const struct address_space_operations cifs_addr_ops = { | |||
3672 | .write_end = cifs_write_end, | 3693 | .write_end = cifs_write_end, |
3673 | .set_page_dirty = __set_page_dirty_nobuffers, | 3694 | .set_page_dirty = __set_page_dirty_nobuffers, |
3674 | .releasepage = cifs_release_page, | 3695 | .releasepage = cifs_release_page, |
3696 | .direct_IO = cifs_direct_io, | ||
3675 | .invalidatepage = cifs_invalidate_page, | 3697 | .invalidatepage = cifs_invalidate_page, |
3676 | .launder_page = cifs_launder_page, | 3698 | .launder_page = cifs_launder_page, |
3677 | }; | 3699 | }; |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 867b7cdc794a..36f9ebb93ceb 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -542,7 +542,8 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, | |||
542 | /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */ | 542 | /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */ |
543 | static void | 543 | static void |
544 | cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | 544 | cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, |
545 | struct cifs_sb_info *cifs_sb, bool adjust_tz) | 545 | struct cifs_sb_info *cifs_sb, bool adjust_tz, |
546 | bool symlink) | ||
546 | { | 547 | { |
547 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | 548 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); |
548 | 549 | ||
@@ -569,7 +570,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
569 | fattr->cf_createtime = le64_to_cpu(info->CreationTime); | 570 | fattr->cf_createtime = le64_to_cpu(info->CreationTime); |
570 | 571 | ||
571 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); | 572 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); |
572 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | 573 | |
574 | if (symlink) { | ||
575 | fattr->cf_mode = S_IFLNK; | ||
576 | fattr->cf_dtype = DT_LNK; | ||
577 | } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | ||
573 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; | 578 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
574 | fattr->cf_dtype = DT_DIR; | 579 | fattr->cf_dtype = DT_DIR; |
575 | /* | 580 | /* |
@@ -578,10 +583,6 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
578 | */ | 583 | */ |
579 | if (!tcon->unix_ext) | 584 | if (!tcon->unix_ext) |
580 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; | 585 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; |
581 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE) { | ||
582 | fattr->cf_mode = S_IFLNK; | ||
583 | fattr->cf_dtype = DT_LNK; | ||
584 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); | ||
585 | } else { | 586 | } else { |
586 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; | 587 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; |
587 | fattr->cf_dtype = DT_REG; | 588 | fattr->cf_dtype = DT_REG; |
@@ -626,7 +627,8 @@ cifs_get_file_info(struct file *filp) | |||
626 | rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); | 627 | rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); |
627 | switch (rc) { | 628 | switch (rc) { |
628 | case 0: | 629 | case 0: |
629 | cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false); | 630 | cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false, |
631 | false); | ||
630 | break; | 632 | break; |
631 | case -EREMOTE: | 633 | case -EREMOTE: |
632 | cifs_create_dfs_fattr(&fattr, inode->i_sb); | 634 | cifs_create_dfs_fattr(&fattr, inode->i_sb); |
@@ -673,6 +675,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
673 | bool adjust_tz = false; | 675 | bool adjust_tz = false; |
674 | struct cifs_fattr fattr; | 676 | struct cifs_fattr fattr; |
675 | struct cifs_search_info *srchinf = NULL; | 677 | struct cifs_search_info *srchinf = NULL; |
678 | bool symlink = false; | ||
676 | 679 | ||
677 | tlink = cifs_sb_tlink(cifs_sb); | 680 | tlink = cifs_sb_tlink(cifs_sb); |
678 | if (IS_ERR(tlink)) | 681 | if (IS_ERR(tlink)) |
@@ -702,12 +705,12 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
702 | } | 705 | } |
703 | data = (FILE_ALL_INFO *)buf; | 706 | data = (FILE_ALL_INFO *)buf; |
704 | rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, | 707 | rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, |
705 | data, &adjust_tz); | 708 | data, &adjust_tz, &symlink); |
706 | } | 709 | } |
707 | 710 | ||
708 | if (!rc) { | 711 | if (!rc) { |
709 | cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *)data, cifs_sb, | 712 | cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz, |
710 | adjust_tz); | 713 | symlink); |
711 | } else if (rc == -EREMOTE) { | 714 | } else if (rc == -EREMOTE) { |
712 | cifs_create_dfs_fattr(&fattr, sb); | 715 | cifs_create_dfs_fattr(&fattr, sb); |
713 | rc = 0; | 716 | rc = 0; |
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 | ||
36 | static 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; | ||
122 | out_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 | } | ||
132 | out_fput: | ||
133 | fdput(src_file); | ||
134 | out_drop_write: | ||
135 | mnt_drop_write_file(dst_file); | ||
136 | return rc; | ||
137 | } | ||
138 | |||
31 | long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) | 139 | long 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/netmisc.c b/fs/cifs/netmisc.c index 651a5279607b..049884552e76 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c | |||
@@ -51,7 +51,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { | |||
51 | {ERRnoaccess, -EACCES}, | 51 | {ERRnoaccess, -EACCES}, |
52 | {ERRbadfid, -EBADF}, | 52 | {ERRbadfid, -EBADF}, |
53 | {ERRbadmcb, -EIO}, | 53 | {ERRbadmcb, -EIO}, |
54 | {ERRnomem, -ENOMEM}, | 54 | {ERRnomem, -EREMOTEIO}, |
55 | {ERRbadmem, -EFAULT}, | 55 | {ERRbadmem, -EFAULT}, |
56 | {ERRbadenv, -EFAULT}, | 56 | {ERRbadenv, -EFAULT}, |
57 | {ERRbadformat, -EINVAL}, | 57 | {ERRbadformat, -EINVAL}, |
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 53a75f3d0179..5940ecabbe6a 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
@@ -134,22 +134,6 @@ out: | |||
134 | dput(dentry); | 134 | dput(dentry); |
135 | } | 135 | } |
136 | 136 | ||
137 | /* | ||
138 | * Is it possible that this directory might turn out to be a DFS referral | ||
139 | * once we go to try and use it? | ||
140 | */ | ||
141 | static bool | ||
142 | cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb) | ||
143 | { | ||
144 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
145 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | ||
146 | |||
147 | if (tcon->Flags & SMB_SHARE_IS_IN_DFS) | ||
148 | return true; | ||
149 | #endif | ||
150 | return false; | ||
151 | } | ||
152 | |||
153 | static void | 137 | static void |
154 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | 138 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) |
155 | { | 139 | { |
@@ -159,27 +143,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | |||
159 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | 143 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { |
160 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; | 144 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
161 | fattr->cf_dtype = DT_DIR; | 145 | fattr->cf_dtype = DT_DIR; |
162 | /* | ||
163 | * Windows CIFS servers generally make DFS referrals look | ||
164 | * like directories in FIND_* responses with the reparse | ||
165 | * attribute flag also set (since DFS junctions are | ||
166 | * reparse points). We must revalidate at least these | ||
167 | * directory inodes before trying to use them (if | ||
168 | * they are DFS we will get PATH_NOT_COVERED back | ||
169 | * when queried directly and can then try to connect | ||
170 | * to the DFS target) | ||
171 | */ | ||
172 | if (cifs_dfs_is_possible(cifs_sb) && | ||
173 | (fattr->cf_cifsattrs & ATTR_REPARSE)) | ||
174 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; | ||
175 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE) { | ||
176 | fattr->cf_mode = S_IFLNK; | ||
177 | fattr->cf_dtype = DT_LNK; | ||
178 | } else { | 146 | } else { |
179 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; | 147 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; |
180 | fattr->cf_dtype = DT_REG; | 148 | fattr->cf_dtype = DT_REG; |
181 | } | 149 | } |
182 | 150 | ||
151 | /* | ||
152 | * We need to revalidate it further to make a decision about whether it | ||
153 | * is a symbolic link, DFS referral or a reparse point with a direct | ||
154 | * access like junctions, deduplicated files, NFS symlinks. | ||
155 | */ | ||
156 | if (fattr->cf_cifsattrs & ATTR_REPARSE) | ||
157 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; | ||
158 | |||
183 | /* non-unix readdir doesn't provide nlink */ | 159 | /* non-unix readdir doesn't provide nlink */ |
184 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; | 160 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; |
185 | 161 | ||
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 384cffe42850..5f5ba0dc2ee1 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c | |||
@@ -534,10 +534,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, | |||
534 | static int | 534 | static int |
535 | cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | 535 | cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, |
536 | struct cifs_sb_info *cifs_sb, const char *full_path, | 536 | struct cifs_sb_info *cifs_sb, const char *full_path, |
537 | FILE_ALL_INFO *data, bool *adjustTZ) | 537 | FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink) |
538 | { | 538 | { |
539 | int rc; | 539 | int rc; |
540 | 540 | ||
541 | *symlink = false; | ||
542 | |||
541 | /* could do find first instead but this returns more info */ | 543 | /* could do find first instead but this returns more info */ |
542 | rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, | 544 | rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, |
543 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 545 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & |
@@ -554,6 +556,23 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | |||
554 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 556 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
555 | *adjustTZ = true; | 557 | *adjustTZ = true; |
556 | } | 558 | } |
559 | |||
560 | if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) { | ||
561 | int tmprc; | ||
562 | int oplock = 0; | ||
563 | __u16 netfid; | ||
564 | |||
565 | /* Need to check if this is a symbolic link or not */ | ||
566 | tmprc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, | ||
567 | FILE_READ_ATTRIBUTES, 0, &netfid, &oplock, | ||
568 | NULL, cifs_sb->local_nls, | ||
569 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
570 | if (tmprc == -EOPNOTSUPP) | ||
571 | *symlink = true; | ||
572 | else | ||
573 | CIFSSMBClose(xid, tcon, netfid); | ||
574 | } | ||
575 | |||
557 | return rc; | 576 | return rc; |
558 | } | 577 | } |
559 | 578 | ||
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 78ff88c467b9..84c012a6aba0 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c | |||
@@ -123,12 +123,13 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) | |||
123 | int | 123 | int |
124 | smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | 124 | smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, |
125 | struct cifs_sb_info *cifs_sb, const char *full_path, | 125 | struct cifs_sb_info *cifs_sb, const char *full_path, |
126 | FILE_ALL_INFO *data, bool *adjust_tz) | 126 | FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink) |
127 | { | 127 | { |
128 | int rc; | 128 | int rc; |
129 | struct smb2_file_all_info *smb2_data; | 129 | struct smb2_file_all_info *smb2_data; |
130 | 130 | ||
131 | *adjust_tz = false; | 131 | *adjust_tz = false; |
132 | *symlink = false; | ||
132 | 133 | ||
133 | smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, | 134 | smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, |
134 | GFP_KERNEL); | 135 | GFP_KERNEL); |
@@ -136,9 +137,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | |||
136 | return -ENOMEM; | 137 | return -ENOMEM; |
137 | 138 | ||
138 | rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, | 139 | rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, |
139 | FILE_READ_ATTRIBUTES, FILE_OPEN, | 140 | FILE_READ_ATTRIBUTES, FILE_OPEN, 0, |
140 | OPEN_REPARSE_POINT, smb2_data, | 141 | smb2_data, SMB2_OP_QUERY_INFO); |
141 | SMB2_OP_QUERY_INFO); | 142 | if (rc == -EOPNOTSUPP) { |
143 | *symlink = true; | ||
144 | /* Failed on a symbolic link - query a reparse point info */ | ||
145 | rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, | ||
146 | FILE_READ_ATTRIBUTES, FILE_OPEN, | ||
147 | OPEN_REPARSE_POINT, smb2_data, | ||
148 | SMB2_OP_QUERY_INFO); | ||
149 | } | ||
142 | if (rc) | 150 | if (rc) |
143 | goto out; | 151 | goto out; |
144 | 152 | ||
diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index 7c2f45c06fc2..94bd4fbb13d3 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c | |||
@@ -306,7 +306,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { | |||
306 | {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, | 306 | {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, |
307 | {STATUS_MORE_PROCESSING_REQUIRED, -EIO, | 307 | {STATUS_MORE_PROCESSING_REQUIRED, -EIO, |
308 | "STATUS_MORE_PROCESSING_REQUIRED"}, | 308 | "STATUS_MORE_PROCESSING_REQUIRED"}, |
309 | {STATUS_NO_MEMORY, -ENOMEM, "STATUS_NO_MEMORY"}, | 309 | {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, |
310 | {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, | 310 | {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, |
311 | "STATUS_CONFLICTING_ADDRESSES"}, | 311 | "STATUS_CONFLICTING_ADDRESSES"}, |
312 | {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, | 312 | {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, |
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 | ||
496 | static int | 496 | static int |
497 | SMB2_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 | |||
521 | req_res_key_exit: | ||
522 | kfree(res_key); | ||
523 | return rc; | ||
524 | } | ||
525 | |||
526 | static int | ||
527 | smb2_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 | |||
575 | static int | ||
497 | smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, | 576 | smb2_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 | ||
1022 | struct smb_version_operations smb21_operations = { | 1102 | struct 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 | ||
1095 | struct smb_version_operations smb30_operations = { | 1176 | struct 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 | ||
1170 | struct smb_version_values smb20_values = { | 1252 | struct smb_version_values smb20_values = { |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 8ab05b0d6778..d65270c290a1 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -630,6 +630,8 @@ ssetup_ntlmssp_authenticate: | |||
630 | goto ssetup_exit; | 630 | goto ssetup_exit; |
631 | 631 | ||
632 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); | 632 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); |
633 | if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) | ||
634 | cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); | ||
633 | ssetup_exit: | 635 | ssetup_exit: |
634 | free_rsp_buf(resp_buftype, rsp); | 636 | free_rsp_buf(resp_buftype, rsp); |
635 | 637 | ||
@@ -717,6 +719,14 @@ static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) | |||
717 | 719 | ||
718 | #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) | 720 | #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) |
719 | 721 | ||
722 | /* These are similar values to what Windows uses */ | ||
723 | static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) | ||
724 | { | ||
725 | tcon->max_chunks = 256; | ||
726 | tcon->max_bytes_chunk = 1048576; | ||
727 | tcon->max_bytes_copy = 16777216; | ||
728 | } | ||
729 | |||
720 | int | 730 | int |
721 | SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, | 731 | SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, |
722 | struct cifs_tcon *tcon, const struct nls_table *cp) | 732 | struct cifs_tcon *tcon, const struct nls_table *cp) |
@@ -818,7 +828,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, | |||
818 | if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && | 828 | if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && |
819 | ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) | 829 | ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) |
820 | cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); | 830 | cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); |
821 | 831 | init_copy_chunk_defaults(tcon); | |
822 | tcon_exit: | 832 | tcon_exit: |
823 | free_rsp_buf(resp_buftype, rsp); | 833 | free_rsp_buf(resp_buftype, rsp); |
824 | kfree(unc_path); | 834 | kfree(unc_path); |
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 6183b1b7550f..f88320bbb477 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -122,6 +122,23 @@ struct smb2_pdu { | |||
122 | __le16 StructureSize2; /* size of wct area (varies, request specific) */ | 122 | __le16 StructureSize2; /* size of wct area (varies, request specific) */ |
123 | } __packed; | 123 | } __packed; |
124 | 124 | ||
125 | struct smb2_transform_hdr { | ||
126 | __be32 smb2_buf_length; /* big endian on wire */ | ||
127 | /* length is only two or three bytes - with | ||
128 | one or two byte type preceding it that MBZ */ | ||
129 | __u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */ | ||
130 | __u8 Signature[16]; | ||
131 | __u8 Nonce[11]; | ||
132 | __u8 Reserved[5]; | ||
133 | __le32 OriginalMessageSize; | ||
134 | __u16 Reserved1; | ||
135 | __le16 EncryptionAlgorithm; | ||
136 | __u64 SessionId; | ||
137 | } __packed; | ||
138 | |||
139 | /* Encryption Algorithms */ | ||
140 | #define SMB2_ENCRYPTION_AES128_CCM __constant_cpu_to_le16(0x0001) | ||
141 | |||
125 | /* | 142 | /* |
126 | * SMB2 flag definitions | 143 | * SMB2 flag definitions |
127 | */ | 144 | */ |
@@ -237,6 +254,7 @@ struct smb2_sess_setup_req { | |||
237 | /* Currently defined SessionFlags */ | 254 | /* Currently defined SessionFlags */ |
238 | #define SMB2_SESSION_FLAG_IS_GUEST 0x0001 | 255 | #define SMB2_SESSION_FLAG_IS_GUEST 0x0001 |
239 | #define SMB2_SESSION_FLAG_IS_NULL 0x0002 | 256 | #define SMB2_SESSION_FLAG_IS_NULL 0x0002 |
257 | #define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 | ||
240 | struct smb2_sess_setup_rsp { | 258 | struct smb2_sess_setup_rsp { |
241 | struct smb2_hdr hdr; | 259 | struct smb2_hdr hdr; |
242 | __le16 StructureSize; /* Must be 9 */ | 260 | __le16 StructureSize; /* Must be 9 */ |
@@ -534,9 +552,16 @@ struct create_durable { | |||
534 | } Data; | 552 | } Data; |
535 | } __packed; | 553 | } __packed; |
536 | 554 | ||
555 | #define COPY_CHUNK_RES_KEY_SIZE 24 | ||
556 | struct resume_key_req { | ||
557 | char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; | ||
558 | __le32 ContextLength; /* MBZ */ | ||
559 | char Context[0]; /* ignored, Windows sets to 4 bytes of zero */ | ||
560 | } __packed; | ||
561 | |||
537 | /* this goes in the ioctl buffer when doing a copychunk request */ | 562 | /* this goes in the ioctl buffer when doing a copychunk request */ |
538 | struct copychunk_ioctl { | 563 | struct copychunk_ioctl { |
539 | char SourceKey[24]; | 564 | char SourceKey[COPY_CHUNK_RES_KEY_SIZE]; |
540 | __le32 ChunkCount; /* we are only sending 1 */ | 565 | __le32 ChunkCount; /* we are only sending 1 */ |
541 | __le32 Reserved; | 566 | __le32 Reserved; |
542 | /* array will only be one chunk long for us */ | 567 | /* array will only be one chunk long for us */ |
@@ -546,6 +571,12 @@ struct copychunk_ioctl { | |||
546 | __u32 Reserved2; | 571 | __u32 Reserved2; |
547 | } __packed; | 572 | } __packed; |
548 | 573 | ||
574 | struct copychunk_ioctl_rsp { | ||
575 | __le32 ChunksWritten; | ||
576 | __le32 ChunkBytesWritten; | ||
577 | __le32 TotalBytesWritten; | ||
578 | } __packed; | ||
579 | |||
549 | /* Response and Request are the same format */ | 580 | /* Response and Request are the same format */ |
550 | struct validate_negotiate_info { | 581 | struct validate_negotiate_info { |
551 | __le32 Capabilities; | 582 | __le32 Capabilities; |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 313813e4c19b..b4eea105b08c 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -61,7 +61,7 @@ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, | |||
61 | extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | 61 | extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, |
62 | struct cifs_sb_info *cifs_sb, | 62 | struct cifs_sb_info *cifs_sb, |
63 | const char *full_path, FILE_ALL_INFO *data, | 63 | const char *full_path, FILE_ALL_INFO *data, |
64 | bool *adjust_tz); | 64 | bool *adjust_tz, bool *symlink); |
65 | extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, | 65 | extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, |
66 | const char *full_path, __u64 size, | 66 | const char *full_path, __u64 size, |
67 | struct cifs_sb_info *cifs_sb, bool set_alloc); | 67 | struct cifs_sb_info *cifs_sb, bool set_alloc); |