diff options
author | Aurelien Aptel <aaptel@suse.com> | 2017-02-13 10:16:49 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2017-03-02 18:05:31 -0500 |
commit | 9d49640a21bffd730a6ebf2a0032e022f7caf84a (patch) | |
tree | b51c69ef2ec32de0b10a53da889bdbb7f9adda9e /fs/cifs | |
parent | f0712928be1a66c99c35f871b4df7fa23ec1574a (diff) |
CIFS: implement get_dfs_refer for SMB2+
in SMB2+ the get_dfs_refer operation uses a FSCTL. The request can be
made on any Tree Connection according to the specs. Since Samba only
accepted it on an IPC connection until recently, try that first.
https://lists.samba.org/archive/samba-technical/2017-February/118859.html
3.2.4.20.3 Application Requests DFS Referral Information:
> The client MUST search for an existing Session and TreeConnect to any
> share on the server identified by ServerName for the user identified by
> UserCredentials. If no Session and TreeConnect are found, the client
> MUST establish a new Session and TreeConnect to IPC$ on the target
> server as described in section 3.2.4.2 using the supplied ServerName and
> UserCredentials.
Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/smb2ops.c | 101 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 8 |
2 files changed, 109 insertions, 0 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index eba00cd3bd16..b360c381b00e 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -1104,6 +1104,103 @@ smb2_new_lease_key(struct cifs_fid *fid) | |||
1104 | generate_random_uuid(fid->lease_key); | 1104 | generate_random_uuid(fid->lease_key); |
1105 | } | 1105 | } |
1106 | 1106 | ||
1107 | static int | ||
1108 | smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, | ||
1109 | const char *search_name, | ||
1110 | struct dfs_info3_param **target_nodes, | ||
1111 | unsigned int *num_of_nodes, | ||
1112 | const struct nls_table *nls_codepage, int remap) | ||
1113 | { | ||
1114 | int rc; | ||
1115 | __le16 *utf16_path = NULL; | ||
1116 | int utf16_path_len = 0; | ||
1117 | struct cifs_tcon *tcon; | ||
1118 | struct fsctl_get_dfs_referral_req *dfs_req = NULL; | ||
1119 | struct get_dfs_referral_rsp *dfs_rsp = NULL; | ||
1120 | u32 dfs_req_size = 0, dfs_rsp_size = 0; | ||
1121 | |||
1122 | cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name); | ||
1123 | |||
1124 | /* | ||
1125 | * Use any tcon from the current session. Here, the first one. | ||
1126 | */ | ||
1127 | spin_lock(&cifs_tcp_ses_lock); | ||
1128 | tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon, | ||
1129 | tcon_list); | ||
1130 | if (tcon) | ||
1131 | tcon->tc_count++; | ||
1132 | spin_unlock(&cifs_tcp_ses_lock); | ||
1133 | |||
1134 | if (!tcon) { | ||
1135 | cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", | ||
1136 | ses); | ||
1137 | rc = -ENOTCONN; | ||
1138 | goto out; | ||
1139 | } | ||
1140 | |||
1141 | utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, | ||
1142 | &utf16_path_len, | ||
1143 | nls_codepage, remap); | ||
1144 | if (!utf16_path) { | ||
1145 | rc = -ENOMEM; | ||
1146 | goto out; | ||
1147 | } | ||
1148 | |||
1149 | dfs_req_size = sizeof(*dfs_req) + utf16_path_len; | ||
1150 | dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); | ||
1151 | if (!dfs_req) { | ||
1152 | rc = -ENOMEM; | ||
1153 | goto out; | ||
1154 | } | ||
1155 | |||
1156 | /* Highest DFS referral version understood */ | ||
1157 | dfs_req->MaxReferralLevel = DFS_VERSION; | ||
1158 | |||
1159 | /* Path to resolve in an UTF-16 null-terminated string */ | ||
1160 | memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); | ||
1161 | |||
1162 | do { | ||
1163 | /* try first with IPC */ | ||
1164 | rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, | ||
1165 | FSCTL_DFS_GET_REFERRALS, | ||
1166 | true /* is_fsctl */, true /* use_ipc */, | ||
1167 | (char *)dfs_req, dfs_req_size, | ||
1168 | (char **)&dfs_rsp, &dfs_rsp_size); | ||
1169 | if (rc == -ENOTCONN) { | ||
1170 | /* try with normal tcon */ | ||
1171 | rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, | ||
1172 | FSCTL_DFS_GET_REFERRALS, | ||
1173 | true /* is_fsctl */, false /*use_ipc*/, | ||
1174 | (char *)dfs_req, dfs_req_size, | ||
1175 | (char **)&dfs_rsp, &dfs_rsp_size); | ||
1176 | } | ||
1177 | } while (rc == -EAGAIN); | ||
1178 | |||
1179 | if (rc) { | ||
1180 | cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc); | ||
1181 | goto out; | ||
1182 | } | ||
1183 | |||
1184 | rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, | ||
1185 | num_of_nodes, target_nodes, | ||
1186 | nls_codepage, remap, search_name, | ||
1187 | true /* is_unicode */); | ||
1188 | if (rc) { | ||
1189 | cifs_dbg(VFS, "parse error in smb2_get_dfs_refer rc=%d\n", rc); | ||
1190 | goto out; | ||
1191 | } | ||
1192 | |||
1193 | out: | ||
1194 | if (tcon) { | ||
1195 | spin_lock(&cifs_tcp_ses_lock); | ||
1196 | tcon->tc_count--; | ||
1197 | spin_unlock(&cifs_tcp_ses_lock); | ||
1198 | } | ||
1199 | kfree(utf16_path); | ||
1200 | kfree(dfs_req); | ||
1201 | kfree(dfs_rsp); | ||
1202 | return rc; | ||
1203 | } | ||
1107 | #define SMB2_SYMLINK_STRUCT_SIZE \ | 1204 | #define SMB2_SYMLINK_STRUCT_SIZE \ |
1108 | (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) | 1205 | (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) |
1109 | 1206 | ||
@@ -2283,6 +2380,7 @@ struct smb_version_operations smb20_operations = { | |||
2283 | .clone_range = smb2_clone_range, | 2380 | .clone_range = smb2_clone_range, |
2284 | .wp_retry_size = smb2_wp_retry_size, | 2381 | .wp_retry_size = smb2_wp_retry_size, |
2285 | .dir_needs_close = smb2_dir_needs_close, | 2382 | .dir_needs_close = smb2_dir_needs_close, |
2383 | .get_dfs_refer = smb2_get_dfs_refer, | ||
2286 | }; | 2384 | }; |
2287 | 2385 | ||
2288 | struct smb_version_operations smb21_operations = { | 2386 | struct smb_version_operations smb21_operations = { |
@@ -2364,6 +2462,7 @@ struct smb_version_operations smb21_operations = { | |||
2364 | .wp_retry_size = smb2_wp_retry_size, | 2462 | .wp_retry_size = smb2_wp_retry_size, |
2365 | .dir_needs_close = smb2_dir_needs_close, | 2463 | .dir_needs_close = smb2_dir_needs_close, |
2366 | .enum_snapshots = smb3_enum_snapshots, | 2464 | .enum_snapshots = smb3_enum_snapshots, |
2465 | .get_dfs_refer = smb2_get_dfs_refer, | ||
2367 | }; | 2466 | }; |
2368 | 2467 | ||
2369 | struct smb_version_operations smb30_operations = { | 2468 | struct smb_version_operations smb30_operations = { |
@@ -2455,6 +2554,7 @@ struct smb_version_operations smb30_operations = { | |||
2455 | .free_transform_rq = smb3_free_transform_rq, | 2554 | .free_transform_rq = smb3_free_transform_rq, |
2456 | .is_transform_hdr = smb3_is_transform_hdr, | 2555 | .is_transform_hdr = smb3_is_transform_hdr, |
2457 | .receive_transform = smb3_receive_transform, | 2556 | .receive_transform = smb3_receive_transform, |
2557 | .get_dfs_refer = smb2_get_dfs_refer, | ||
2458 | }; | 2558 | }; |
2459 | 2559 | ||
2460 | #ifdef CONFIG_CIFS_SMB311 | 2560 | #ifdef CONFIG_CIFS_SMB311 |
@@ -2547,6 +2647,7 @@ struct smb_version_operations smb311_operations = { | |||
2547 | .free_transform_rq = smb3_free_transform_rq, | 2647 | .free_transform_rq = smb3_free_transform_rq, |
2548 | .is_transform_hdr = smb3_is_transform_hdr, | 2648 | .is_transform_hdr = smb3_is_transform_hdr, |
2549 | .receive_transform = smb3_receive_transform, | 2649 | .receive_transform = smb3_receive_transform, |
2650 | .get_dfs_refer = smb2_get_dfs_refer, | ||
2550 | }; | 2651 | }; |
2551 | #endif /* CIFS_SMB311 */ | 2652 | #endif /* CIFS_SMB311 */ |
2552 | 2653 | ||
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index c03b252501a1..18700fd25a0b 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -695,6 +695,14 @@ struct fsctl_get_integrity_information_rsp { | |||
695 | /* Integrity flags for above */ | 695 | /* Integrity flags for above */ |
696 | #define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 | 696 | #define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 |
697 | 697 | ||
698 | /* See MS-DFSC 2.2.2 */ | ||
699 | struct fsctl_get_dfs_referral_req { | ||
700 | __le16 MaxReferralLevel; | ||
701 | __u8 RequestFileName[]; | ||
702 | } __packed; | ||
703 | |||
704 | /* DFS response is struct get_dfs_refer_rsp */ | ||
705 | |||
698 | /* See MS-SMB2 2.2.31.3 */ | 706 | /* See MS-SMB2 2.2.31.3 */ |
699 | struct network_resiliency_req { | 707 | struct network_resiliency_req { |
700 | __le32 Timeout; | 708 | __le32 Timeout; |