diff options
-rw-r--r-- | fs/cifs/cifsfs.c | 11 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/inode.c | 16 | ||||
-rw-r--r-- | fs/cifs/smb2file.c | 18 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 53 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 46 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 4 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 7 |
8 files changed, 126 insertions, 31 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 270d3c58fb3b..3289b566463f 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -1104,6 +1104,10 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, | |||
1104 | goto out; | 1104 | goto out; |
1105 | } | 1105 | } |
1106 | 1106 | ||
1107 | rc = -EOPNOTSUPP; | ||
1108 | if (!target_tcon->ses->server->ops->copychunk_range) | ||
1109 | goto out; | ||
1110 | |||
1107 | /* | 1111 | /* |
1108 | * Note: cifs case is easier than btrfs since server responsible for | 1112 | * Note: cifs case is easier than btrfs since server responsible for |
1109 | * checks for proper open modes and file type and if it wants | 1113 | * checks for proper open modes and file type and if it wants |
@@ -1115,11 +1119,12 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, | |||
1115 | /* should we flush first and last page first */ | 1119 | /* should we flush first and last page first */ |
1116 | truncate_inode_pages(&target_inode->i_data, 0); | 1120 | truncate_inode_pages(&target_inode->i_data, 0); |
1117 | 1121 | ||
1118 | if (target_tcon->ses->server->ops->copychunk_range) | 1122 | rc = file_modified(dst_file); |
1123 | if (!rc) | ||
1119 | rc = target_tcon->ses->server->ops->copychunk_range(xid, | 1124 | rc = target_tcon->ses->server->ops->copychunk_range(xid, |
1120 | smb_file_src, smb_file_target, off, len, destoff); | 1125 | smb_file_src, smb_file_target, off, len, destoff); |
1121 | else | 1126 | |
1122 | rc = -EOPNOTSUPP; | 1127 | file_accessed(src_file); |
1123 | 1128 | ||
1124 | /* force revalidate of size and timestamps of target file now | 1129 | /* force revalidate of size and timestamps of target file now |
1125 | * that target is updated on the server | 1130 | * that target is updated on the server |
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index aea005703785..4b21a90015a9 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -152,5 +152,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); | |||
152 | extern const struct export_operations cifs_export_ops; | 152 | extern const struct export_operations cifs_export_ops; |
153 | #endif /* CONFIG_CIFS_NFSD_EXPORT */ | 153 | #endif /* CONFIG_CIFS_NFSD_EXPORT */ |
154 | 154 | ||
155 | #define CIFS_VERSION "2.20" | 155 | #define CIFS_VERSION "2.21" |
156 | #endif /* _CIFSFS_H */ | 156 | #endif /* _CIFSFS_H */ |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 1bffe029fb66..56ca4b8ccaba 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -2406,6 +2406,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) | |||
2406 | struct inode *inode = d_inode(direntry); | 2406 | struct inode *inode = d_inode(direntry); |
2407 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | 2407 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
2408 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | 2408 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); |
2409 | struct cifsFileInfo *wfile; | ||
2410 | struct cifs_tcon *tcon; | ||
2409 | char *full_path = NULL; | 2411 | char *full_path = NULL; |
2410 | int rc = -EACCES; | 2412 | int rc = -EACCES; |
2411 | __u32 dosattr = 0; | 2413 | __u32 dosattr = 0; |
@@ -2452,6 +2454,20 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) | |||
2452 | mapping_set_error(inode->i_mapping, rc); | 2454 | mapping_set_error(inode->i_mapping, rc); |
2453 | rc = 0; | 2455 | rc = 0; |
2454 | 2456 | ||
2457 | if (attrs->ia_valid & ATTR_MTIME) { | ||
2458 | rc = cifs_get_writable_file(cifsInode, false, &wfile); | ||
2459 | if (!rc) { | ||
2460 | tcon = tlink_tcon(wfile->tlink); | ||
2461 | rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); | ||
2462 | cifsFileInfo_put(wfile); | ||
2463 | if (rc) | ||
2464 | return rc; | ||
2465 | } else if (rc != -EBADF) | ||
2466 | return rc; | ||
2467 | else | ||
2468 | rc = 0; | ||
2469 | } | ||
2470 | |||
2455 | if (attrs->ia_valid & ATTR_SIZE) { | 2471 | if (attrs->ia_valid & ATTR_SIZE) { |
2456 | rc = cifs_set_file_size(inode, attrs, xid, full_path); | 2472 | rc = cifs_set_file_size(inode, attrs, xid, full_path); |
2457 | if (rc != 0) | 2473 | if (rc != 0) |
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 54bffb2a1786..e6a1fc72018f 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c | |||
@@ -88,14 +88,20 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, | |||
88 | } | 88 | } |
89 | 89 | ||
90 | if (buf) { | 90 | if (buf) { |
91 | /* open response does not have IndexNumber field - get it */ | 91 | /* if open response does not have IndexNumber field - get it */ |
92 | rc = SMB2_get_srv_num(xid, oparms->tcon, fid->persistent_fid, | 92 | if (smb2_data->IndexNumber == 0) { |
93 | rc = SMB2_get_srv_num(xid, oparms->tcon, | ||
94 | fid->persistent_fid, | ||
93 | fid->volatile_fid, | 95 | fid->volatile_fid, |
94 | &smb2_data->IndexNumber); | 96 | &smb2_data->IndexNumber); |
95 | if (rc) { | 97 | if (rc) { |
96 | /* let get_inode_info disable server inode numbers */ | 98 | /* |
97 | smb2_data->IndexNumber = 0; | 99 | * let get_inode_info disable server inode |
98 | rc = 0; | 100 | * numbers |
101 | */ | ||
102 | smb2_data->IndexNumber = 0; | ||
103 | rc = 0; | ||
104 | } | ||
99 | } | 105 | } |
100 | move_smb2_info_to_cifs(buf, smb2_data); | 106 | move_smb2_info_to_cifs(buf, smb2_data); |
101 | } | 107 | } |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0cdc4e47ca87..a5bc1b671c12 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -694,8 +694,51 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) | |||
694 | 694 | ||
695 | smb2_set_related(&rqst[1]); | 695 | smb2_set_related(&rqst[1]); |
696 | 696 | ||
697 | /* | ||
698 | * We do not hold the lock for the open because in case | ||
699 | * SMB2_open needs to reconnect, it will end up calling | ||
700 | * cifs_mark_open_files_invalid() which takes the lock again | ||
701 | * thus causing a deadlock | ||
702 | */ | ||
703 | |||
704 | mutex_unlock(&tcon->crfid.fid_mutex); | ||
697 | rc = compound_send_recv(xid, ses, flags, 2, rqst, | 705 | rc = compound_send_recv(xid, ses, flags, 2, rqst, |
698 | resp_buftype, rsp_iov); | 706 | resp_buftype, rsp_iov); |
707 | mutex_lock(&tcon->crfid.fid_mutex); | ||
708 | |||
709 | /* | ||
710 | * Now we need to check again as the cached root might have | ||
711 | * been successfully re-opened from a concurrent process | ||
712 | */ | ||
713 | |||
714 | if (tcon->crfid.is_valid) { | ||
715 | /* work was already done */ | ||
716 | |||
717 | /* stash fids for close() later */ | ||
718 | struct cifs_fid fid = { | ||
719 | .persistent_fid = pfid->persistent_fid, | ||
720 | .volatile_fid = pfid->volatile_fid, | ||
721 | }; | ||
722 | |||
723 | /* | ||
724 | * caller expects this func to set pfid to a valid | ||
725 | * cached root, so we copy the existing one and get a | ||
726 | * reference. | ||
727 | */ | ||
728 | memcpy(pfid, tcon->crfid.fid, sizeof(*pfid)); | ||
729 | kref_get(&tcon->crfid.refcount); | ||
730 | |||
731 | mutex_unlock(&tcon->crfid.fid_mutex); | ||
732 | |||
733 | if (rc == 0) { | ||
734 | /* close extra handle outside of crit sec */ | ||
735 | SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); | ||
736 | } | ||
737 | goto oshr_free; | ||
738 | } | ||
739 | |||
740 | /* Cached root is still invalid, continue normaly */ | ||
741 | |||
699 | if (rc) | 742 | if (rc) |
700 | goto oshr_exit; | 743 | goto oshr_exit; |
701 | 744 | ||
@@ -711,11 +754,12 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) | |||
711 | tcon->crfid.is_valid = true; | 754 | tcon->crfid.is_valid = true; |
712 | kref_init(&tcon->crfid.refcount); | 755 | kref_init(&tcon->crfid.refcount); |
713 | 756 | ||
757 | /* BB TBD check to see if oplock level check can be removed below */ | ||
714 | if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) { | 758 | if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) { |
715 | kref_get(&tcon->crfid.refcount); | 759 | kref_get(&tcon->crfid.refcount); |
716 | oplock = smb2_parse_lease_state(server, o_rsp, | 760 | smb2_parse_contexts(server, o_rsp, |
717 | &oparms.fid->epoch, | 761 | &oparms.fid->epoch, |
718 | oparms.fid->lease_key); | 762 | oparms.fid->lease_key, &oplock, NULL); |
719 | } else | 763 | } else |
720 | goto oshr_exit; | 764 | goto oshr_exit; |
721 | 765 | ||
@@ -729,8 +773,9 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) | |||
729 | (char *)&tcon->crfid.file_all_info)) | 773 | (char *)&tcon->crfid.file_all_info)) |
730 | tcon->crfid.file_all_info_is_valid = 1; | 774 | tcon->crfid.file_all_info_is_valid = 1; |
731 | 775 | ||
732 | oshr_exit: | 776 | oshr_exit: |
733 | mutex_unlock(&tcon->crfid.fid_mutex); | 777 | mutex_unlock(&tcon->crfid.fid_mutex); |
778 | oshr_free: | ||
734 | SMB2_open_free(&rqst[0]); | 779 | SMB2_open_free(&rqst[0]); |
735 | SMB2_query_info_free(&rqst[1]); | 780 | SMB2_query_info_free(&rqst[1]); |
736 | free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); | 781 | free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index f58e4dc3987b..c8cd7b6cdda2 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -1873,10 +1873,21 @@ create_reconnect_durable_buf(struct cifs_fid *fid) | |||
1873 | return buf; | 1873 | return buf; |
1874 | } | 1874 | } |
1875 | 1875 | ||
1876 | __u8 | 1876 | static void |
1877 | smb2_parse_lease_state(struct TCP_Server_Info *server, | 1877 | parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) |
1878 | { | ||
1879 | struct create_on_disk_id *pdisk_id = (struct create_on_disk_id *)cc; | ||
1880 | |||
1881 | cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", | ||
1882 | pdisk_id->DiskFileId, pdisk_id->VolumeId); | ||
1883 | buf->IndexNumber = pdisk_id->DiskFileId; | ||
1884 | } | ||
1885 | |||
1886 | void | ||
1887 | smb2_parse_contexts(struct TCP_Server_Info *server, | ||
1878 | struct smb2_create_rsp *rsp, | 1888 | struct smb2_create_rsp *rsp, |
1879 | unsigned int *epoch, char *lease_key) | 1889 | unsigned int *epoch, char *lease_key, __u8 *oplock, |
1890 | struct smb2_file_all_info *buf) | ||
1880 | { | 1891 | { |
1881 | char *data_offset; | 1892 | char *data_offset; |
1882 | struct create_context *cc; | 1893 | struct create_context *cc; |
@@ -1884,15 +1895,24 @@ smb2_parse_lease_state(struct TCP_Server_Info *server, | |||
1884 | unsigned int remaining; | 1895 | unsigned int remaining; |
1885 | char *name; | 1896 | char *name; |
1886 | 1897 | ||
1898 | *oplock = 0; | ||
1887 | data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); | 1899 | data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); |
1888 | remaining = le32_to_cpu(rsp->CreateContextsLength); | 1900 | remaining = le32_to_cpu(rsp->CreateContextsLength); |
1889 | cc = (struct create_context *)data_offset; | 1901 | cc = (struct create_context *)data_offset; |
1902 | |||
1903 | /* Initialize inode number to 0 in case no valid data in qfid context */ | ||
1904 | if (buf) | ||
1905 | buf->IndexNumber = 0; | ||
1906 | |||
1890 | while (remaining >= sizeof(struct create_context)) { | 1907 | while (remaining >= sizeof(struct create_context)) { |
1891 | name = le16_to_cpu(cc->NameOffset) + (char *)cc; | 1908 | name = le16_to_cpu(cc->NameOffset) + (char *)cc; |
1892 | if (le16_to_cpu(cc->NameLength) == 4 && | 1909 | if (le16_to_cpu(cc->NameLength) == 4 && |
1893 | strncmp(name, "RqLs", 4) == 0) | 1910 | strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4) == 0) |
1894 | return server->ops->parse_lease_buf(cc, epoch, | 1911 | *oplock = server->ops->parse_lease_buf(cc, epoch, |
1895 | lease_key); | 1912 | lease_key); |
1913 | else if (buf && (le16_to_cpu(cc->NameLength) == 4) && | ||
1914 | strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) | ||
1915 | parse_query_id_ctxt(cc, buf); | ||
1896 | 1916 | ||
1897 | next = le32_to_cpu(cc->Next); | 1917 | next = le32_to_cpu(cc->Next); |
1898 | if (!next) | 1918 | if (!next) |
@@ -1901,7 +1921,10 @@ smb2_parse_lease_state(struct TCP_Server_Info *server, | |||
1901 | cc = (struct create_context *)((char *)cc + next); | 1921 | cc = (struct create_context *)((char *)cc + next); |
1902 | } | 1922 | } |
1903 | 1923 | ||
1904 | return 0; | 1924 | if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) |
1925 | *oplock = rsp->OplockLevel; | ||
1926 | |||
1927 | return; | ||
1905 | } | 1928 | } |
1906 | 1929 | ||
1907 | static int | 1930 | static int |
@@ -2588,12 +2611,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, | |||
2588 | buf->DeletePending = 0; | 2611 | buf->DeletePending = 0; |
2589 | } | 2612 | } |
2590 | 2613 | ||
2591 | if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) | 2614 | |
2592 | *oplock = smb2_parse_lease_state(server, rsp, | 2615 | smb2_parse_contexts(server, rsp, &oparms->fid->epoch, |
2593 | &oparms->fid->epoch, | 2616 | oparms->fid->lease_key, oplock, buf); |
2594 | oparms->fid->lease_key); | ||
2595 | else | ||
2596 | *oplock = rsp->OplockLevel; | ||
2597 | creat_exit: | 2617 | creat_exit: |
2598 | SMB2_open_free(&rqst); | 2618 | SMB2_open_free(&rqst); |
2599 | free_rsp_buf(resp_buftype, rsp); | 2619 | free_rsp_buf(resp_buftype, rsp); |
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 7e2e782f8edd..747de9317659 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -818,7 +818,9 @@ struct durable_reconnect_context_v2 { | |||
818 | } __packed; | 818 | } __packed; |
819 | 819 | ||
820 | /* See MS-SMB2 2.2.14.2.9 */ | 820 | /* See MS-SMB2 2.2.14.2.9 */ |
821 | struct on_disk_id { | 821 | struct create_on_disk_id { |
822 | struct create_context ccontext; | ||
823 | __u8 Name[8]; | ||
822 | __le64 DiskFileId; | 824 | __le64 DiskFileId; |
823 | __le64 VolumeId; | 825 | __le64 VolumeId; |
824 | __u32 Reserved[4]; | 826 | __u32 Reserved[4]; |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 52df125e9189..07ca72486cfa 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -228,9 +228,10 @@ extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); | |||
228 | 228 | ||
229 | extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, | 229 | extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, |
230 | enum securityEnum); | 230 | enum securityEnum); |
231 | extern __u8 smb2_parse_lease_state(struct TCP_Server_Info *server, | 231 | extern void smb2_parse_contexts(struct TCP_Server_Info *server, |
232 | struct smb2_create_rsp *rsp, | 232 | struct smb2_create_rsp *rsp, |
233 | unsigned int *epoch, char *lease_key); | 233 | unsigned int *epoch, char *lease_key, |
234 | __u8 *oplock, struct smb2_file_all_info *buf); | ||
234 | extern int smb3_encryption_required(const struct cifs_tcon *tcon); | 235 | extern int smb3_encryption_required(const struct cifs_tcon *tcon); |
235 | extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, | 236 | extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, |
236 | struct kvec *iov, unsigned int min_buf_size); | 237 | struct kvec *iov, unsigned int min_buf_size); |