Commit | Line | Data |
---|---|---|
abdb1742 PA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de> | |
4 | */ | |
5 | ||
a1c0d005 | 6 | #include <linux/namei.h> |
abdb1742 PA |
7 | #include "cifsproto.h" |
8 | #include "cifs_debug.h" | |
9 | #include "dns_resolve.h" | |
10 | #include "fs_context.h" | |
11 | #include "dfs.h" | |
12 | ||
abdb1742 PA |
13 | /** |
14 | * dfs_parse_target_referral - set fs context for dfs target referral | |
15 | * | |
16 | * @full_path: full path in UNC format. | |
17 | * @ref: dfs referral pointer. | |
18 | * @ctx: smb3 fs context pointer. | |
19 | * | |
20 | * Return zero if dfs referral was parsed correctly, otherwise non-zero. | |
21 | */ | |
22 | int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, | |
23 | struct smb3_fs_context *ctx) | |
24 | { | |
25 | int rc; | |
26 | const char *prepath = NULL; | |
27 | char *path; | |
28 | ||
29 | if (!full_path || !*full_path || !ref || !ctx) | |
30 | return -EINVAL; | |
31 | ||
32 | if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) | |
33 | return -EINVAL; | |
34 | ||
35 | if (strlen(full_path) - ref->path_consumed) { | |
36 | prepath = full_path + ref->path_consumed; | |
37 | /* skip initial delimiter */ | |
38 | if (*prepath == '/' || *prepath == '\\') | |
39 | prepath++; | |
40 | } | |
41 | ||
42 | path = cifs_build_devname(ref->node_name, prepath); | |
43 | if (IS_ERR(path)) | |
44 | return PTR_ERR(path); | |
45 | ||
46 | rc = smb3_parse_devname(path, ctx); | |
47 | if (rc) | |
48 | goto out; | |
49 | ||
6d740164 | 50 | rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL); |
abdb1742 PA |
51 | |
52 | out: | |
53 | kfree(path); | |
54 | return rc; | |
55 | } | |
a1c0d005 PA |
56 | |
57 | /* | |
58 | * cifs_build_path_to_root returns full path to root when we do not have an | |
59 | * existing connection (tcon) | |
60 | */ | |
61 | static char *build_unc_path_to_root(const struct smb3_fs_context *ctx, | |
62 | const struct cifs_sb_info *cifs_sb, bool useppath) | |
63 | { | |
64 | char *full_path, *pos; | |
65 | unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0; | |
66 | unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); | |
67 | ||
68 | if (unc_len > MAX_TREE_SIZE) | |
69 | return ERR_PTR(-EINVAL); | |
70 | ||
71 | full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); | |
72 | if (full_path == NULL) | |
73 | return ERR_PTR(-ENOMEM); | |
74 | ||
75 | memcpy(full_path, ctx->UNC, unc_len); | |
76 | pos = full_path + unc_len; | |
77 | ||
78 | if (pplen) { | |
79 | *pos = CIFS_DIR_SEP(cifs_sb); | |
80 | memcpy(pos + 1, ctx->prepath, pplen); | |
81 | pos += pplen; | |
82 | } | |
83 | ||
84 | *pos = '\0'; /* add trailing null */ | |
85 | convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); | |
86 | cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); | |
87 | return full_path; | |
88 | } | |
89 | ||
90 | static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) | |
91 | { | |
92 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; | |
93 | int rc; | |
94 | ||
95 | ctx->leaf_fullpath = (char *)full_path; | |
96 | rc = cifs_mount_get_session(mnt_ctx); | |
97 | ctx->leaf_fullpath = NULL; | |
98 | ||
99 | return rc; | |
100 | } | |
101 | ||
396935de | 102 | static int get_root_smb_session(struct cifs_mount_ctx *mnt_ctx) |
a1c0d005 | 103 | { |
b56bce50 | 104 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; |
396935de | 105 | struct dfs_root_ses *root_ses; |
b56bce50 PA |
106 | struct cifs_ses *ses = mnt_ctx->ses; |
107 | ||
108 | if (ses) { | |
396935de PA |
109 | root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL); |
110 | if (!root_ses) | |
111 | return -ENOMEM; | |
112 | ||
113 | INIT_LIST_HEAD(&root_ses->list); | |
114 | ||
a1c0d005 | 115 | spin_lock(&cifs_tcp_ses_lock); |
b56bce50 | 116 | ses->ses_count++; |
a1c0d005 | 117 | spin_unlock(&cifs_tcp_ses_lock); |
396935de PA |
118 | root_ses->ses = ses; |
119 | list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list); | |
a1c0d005 | 120 | } |
396935de PA |
121 | ctx->dfs_root_ses = ses; |
122 | return 0; | |
a1c0d005 PA |
123 | } |
124 | ||
125 | static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path, | |
126 | const struct dfs_cache_tgt_iterator *tit) | |
127 | { | |
128 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; | |
129 | struct dfs_info3_param ref = {}; | |
396935de PA |
130 | bool is_refsrv = false; |
131 | int rc, rc2; | |
a1c0d005 PA |
132 | |
133 | rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref); | |
134 | if (rc) | |
135 | return rc; | |
136 | ||
137 | rc = dfs_parse_target_referral(full_path + 1, &ref, ctx); | |
138 | if (rc) | |
139 | goto out; | |
140 | ||
141 | cifs_mount_put_conns(mnt_ctx); | |
142 | rc = get_session(mnt_ctx, ref_path); | |
143 | if (rc) | |
144 | goto out; | |
145 | ||
396935de | 146 | is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER); |
a1c0d005 PA |
147 | |
148 | rc = -EREMOTE; | |
149 | if (ref.flags & DFSREF_STORAGE_SERVER) { | |
150 | rc = cifs_mount_get_tcon(mnt_ctx); | |
151 | if (rc) | |
152 | goto out; | |
153 | ||
154 | /* some servers may not advertise referral capability under ref.flags */ | |
396935de | 155 | is_refsrv |= is_tcon_dfs(mnt_ctx->tcon); |
a1c0d005 PA |
156 | |
157 | rc = cifs_is_path_remote(mnt_ctx); | |
158 | } | |
159 | ||
396935de PA |
160 | if (rc == -EREMOTE && is_refsrv) { |
161 | rc2 = get_root_smb_session(mnt_ctx); | |
162 | if (rc2) | |
163 | rc = rc2; | |
164 | } | |
165 | ||
a1c0d005 PA |
166 | out: |
167 | free_dfs_info_param(&ref); | |
168 | return rc; | |
169 | } | |
170 | ||
171 | static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) | |
172 | { | |
173 | struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; | |
174 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; | |
175 | char *ref_path = NULL, *full_path = NULL; | |
176 | struct dfs_cache_tgt_iterator *tit; | |
177 | struct TCP_Server_Info *server; | |
396935de | 178 | struct cifs_tcon *tcon; |
a1c0d005 PA |
179 | char *origin_fullpath = NULL; |
180 | int num_links = 0; | |
181 | int rc; | |
182 | ||
183 | ref_path = dfs_get_path(cifs_sb, ctx->UNC); | |
184 | if (IS_ERR(ref_path)) | |
185 | return PTR_ERR(ref_path); | |
186 | ||
187 | full_path = build_unc_path_to_root(ctx, cifs_sb, true); | |
188 | if (IS_ERR(full_path)) { | |
189 | rc = PTR_ERR(full_path); | |
190 | full_path = NULL; | |
191 | goto out; | |
192 | } | |
193 | ||
194 | origin_fullpath = kstrdup(full_path, GFP_KERNEL); | |
195 | if (!origin_fullpath) { | |
196 | rc = -ENOMEM; | |
197 | goto out; | |
198 | } | |
199 | ||
200 | do { | |
201 | struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); | |
202 | ||
203 | rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl); | |
204 | if (rc) | |
205 | break; | |
206 | ||
207 | tit = dfs_cache_get_tgt_iterator(&tl); | |
208 | if (!tit) { | |
209 | cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__, | |
210 | ref_path + 1); | |
211 | rc = -ENOENT; | |
212 | dfs_cache_free_tgts(&tl); | |
213 | break; | |
214 | } | |
215 | ||
216 | do { | |
217 | rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit); | |
218 | if (!rc) | |
219 | break; | |
220 | if (rc == -EREMOTE) { | |
221 | if (++num_links > MAX_NESTED_LINKS) { | |
222 | rc = -ELOOP; | |
223 | break; | |
224 | } | |
225 | kfree(ref_path); | |
226 | kfree(full_path); | |
227 | ref_path = full_path = NULL; | |
228 | ||
229 | full_path = build_unc_path_to_root(ctx, cifs_sb, true); | |
230 | if (IS_ERR(full_path)) { | |
231 | rc = PTR_ERR(full_path); | |
232 | full_path = NULL; | |
233 | } else { | |
234 | ref_path = dfs_get_path(cifs_sb, full_path); | |
235 | if (IS_ERR(ref_path)) { | |
236 | rc = PTR_ERR(ref_path); | |
237 | ref_path = NULL; | |
238 | } | |
239 | } | |
240 | break; | |
241 | } | |
242 | } while ((tit = dfs_cache_get_next_tgt(&tl, tit))); | |
243 | dfs_cache_free_tgts(&tl); | |
244 | } while (rc == -EREMOTE); | |
245 | ||
246 | if (!rc) { | |
247 | server = mnt_ctx->server; | |
396935de | 248 | tcon = mnt_ctx->tcon; |
a1c0d005 PA |
249 | |
250 | mutex_lock(&server->refpath_lock); | |
396935de PA |
251 | if (!server->origin_fullpath) { |
252 | server->origin_fullpath = origin_fullpath; | |
253 | server->current_fullpath = server->leaf_fullpath; | |
254 | origin_fullpath = NULL; | |
255 | } | |
a1c0d005 | 256 | mutex_unlock(&server->refpath_lock); |
396935de PA |
257 | |
258 | if (list_empty(&tcon->dfs_ses_list)) { | |
259 | list_replace_init(&mnt_ctx->dfs_ses_list, | |
260 | &tcon->dfs_ses_list); | |
261 | } else { | |
262 | dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list); | |
263 | } | |
a1c0d005 PA |
264 | } |
265 | ||
266 | out: | |
267 | kfree(origin_fullpath); | |
268 | kfree(ref_path); | |
269 | kfree(full_path); | |
270 | return rc; | |
271 | } | |
272 | ||
273 | int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) | |
274 | { | |
275 | struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; | |
276 | struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; | |
277 | int rc; | |
278 | ||
279 | *isdfs = false; | |
280 | ||
281 | rc = get_session(mnt_ctx, NULL); | |
282 | if (rc) | |
283 | return rc; | |
b56bce50 | 284 | ctx->dfs_root_ses = mnt_ctx->ses; |
a1c0d005 PA |
285 | /* |
286 | * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally | |
287 | * try to get an DFS referral (even cached) to determine whether it is an DFS mount. | |
288 | * | |
289 | * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem | |
290 | * to respond with PATH_NOT_COVERED to requests that include the prefix. | |
291 | */ | |
292 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || | |
293 | dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL)) { | |
294 | rc = cifs_mount_get_tcon(mnt_ctx); | |
295 | if (rc) | |
296 | return rc; | |
297 | ||
298 | rc = cifs_is_path_remote(mnt_ctx); | |
299 | if (!rc || rc != -EREMOTE) | |
300 | return rc; | |
301 | } | |
302 | ||
303 | *isdfs = true; | |
396935de PA |
304 | rc = get_root_smb_session(mnt_ctx); |
305 | if (rc) | |
306 | return rc; | |
a1c0d005 PA |
307 | |
308 | return __dfs_mount_share(mnt_ctx); | |
309 | } | |
1d04a6fe PA |
310 | |
311 | /* Update dfs referral path of superblock */ | |
312 | static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, | |
313 | const char *target) | |
314 | { | |
315 | int rc = 0; | |
316 | size_t len = strlen(target); | |
317 | char *refpath, *npath; | |
318 | ||
319 | if (unlikely(len < 2 || *target != '\\')) | |
320 | return -EINVAL; | |
321 | ||
322 | if (target[1] == '\\') { | |
323 | len += 1; | |
324 | refpath = kmalloc(len, GFP_KERNEL); | |
325 | if (!refpath) | |
326 | return -ENOMEM; | |
327 | ||
328 | scnprintf(refpath, len, "%s", target); | |
329 | } else { | |
330 | len += sizeof("\\"); | |
331 | refpath = kmalloc(len, GFP_KERNEL); | |
332 | if (!refpath) | |
333 | return -ENOMEM; | |
334 | ||
335 | scnprintf(refpath, len, "\\%s", target); | |
336 | } | |
337 | ||
338 | npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); | |
339 | kfree(refpath); | |
340 | ||
341 | if (IS_ERR(npath)) { | |
342 | rc = PTR_ERR(npath); | |
343 | } else { | |
344 | mutex_lock(&server->refpath_lock); | |
345 | kfree(server->leaf_fullpath); | |
346 | server->leaf_fullpath = npath; | |
347 | mutex_unlock(&server->refpath_lock); | |
348 | server->current_fullpath = server->leaf_fullpath; | |
349 | } | |
350 | return rc; | |
351 | } | |
352 | ||
39a154fc PA |
353 | static int target_share_matches_server(struct TCP_Server_Info *server, char *share, |
354 | bool *target_match) | |
1d04a6fe PA |
355 | { |
356 | int rc = 0; | |
357 | const char *dfs_host; | |
358 | size_t dfs_host_len; | |
359 | ||
360 | *target_match = true; | |
361 | extract_unc_hostname(share, &dfs_host, &dfs_host_len); | |
362 | ||
363 | /* Check if hostnames or addresses match */ | |
39a154fc PA |
364 | cifs_server_lock(server); |
365 | if (dfs_host_len != strlen(server->hostname) || | |
366 | strncasecmp(dfs_host, server->hostname, dfs_host_len)) { | |
367 | cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__, | |
368 | (int)dfs_host_len, dfs_host, server->hostname); | |
1d04a6fe PA |
369 | rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); |
370 | if (rc) | |
371 | cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); | |
372 | } | |
39a154fc | 373 | cifs_server_unlock(server); |
1d04a6fe PA |
374 | return rc; |
375 | } | |
376 | ||
377 | static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, | |
378 | struct cifs_sb_info *cifs_sb, char *tree, bool islink, | |
379 | struct dfs_cache_tgt_list *tl) | |
380 | { | |
381 | int rc; | |
382 | struct TCP_Server_Info *server = tcon->ses->server; | |
383 | const struct smb_version_operations *ops = server->ops; | |
384 | struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses); | |
385 | struct cifs_tcon *ipc = root_ses->tcon_ipc; | |
386 | char *share = NULL, *prefix = NULL; | |
1d04a6fe PA |
387 | struct dfs_cache_tgt_iterator *tit; |
388 | bool target_match; | |
389 | ||
1d04a6fe PA |
390 | tit = dfs_cache_get_tgt_iterator(tl); |
391 | if (!tit) { | |
392 | rc = -ENOENT; | |
393 | goto out; | |
394 | } | |
395 | ||
396 | /* Try to tree connect to all dfs targets */ | |
397 | for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { | |
398 | const char *target = dfs_cache_get_tgt_name(tit); | |
399 | struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); | |
400 | ||
401 | kfree(share); | |
402 | kfree(prefix); | |
403 | share = prefix = NULL; | |
404 | ||
405 | /* Check if share matches with tcp ses */ | |
406 | rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); | |
407 | if (rc) { | |
408 | cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); | |
409 | break; | |
410 | } | |
411 | ||
39a154fc | 412 | rc = target_share_matches_server(server, share, &target_match); |
1d04a6fe PA |
413 | if (rc) |
414 | break; | |
415 | if (!target_match) { | |
416 | rc = -EHOSTUNREACH; | |
417 | continue; | |
418 | } | |
419 | ||
420 | dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); | |
421 | ||
422 | if (ipc->need_reconnect) { | |
423 | scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); | |
424 | rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); | |
9e6002c8 | 425 | cifs_dbg(FYI, "%s: reconnect ipc: %d\n", __func__, rc); |
1d04a6fe PA |
426 | } |
427 | ||
428 | scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); | |
429 | if (!islink) { | |
430 | rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); | |
431 | break; | |
432 | } | |
433 | /* | |
434 | * If no dfs referrals were returned from link target, then just do a TREE_CONNECT | |
435 | * to it. Otherwise, cache the dfs referral and then mark current tcp ses for | |
436 | * reconnect so either the demultiplex thread or the echo worker will reconnect to | |
437 | * newly resolved target. | |
438 | */ | |
439 | if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, | |
440 | NULL, &ntl)) { | |
441 | rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); | |
442 | if (rc) | |
443 | continue; | |
444 | ||
445 | rc = cifs_update_super_prepath(cifs_sb, prefix); | |
446 | } else { | |
447 | /* Target is another dfs share */ | |
448 | rc = update_server_fullpath(server, cifs_sb, target); | |
449 | dfs_cache_free_tgts(tl); | |
450 | ||
451 | if (!rc) { | |
452 | rc = -EREMOTE; | |
453 | list_replace_init(&ntl.tl_list, &tl->tl_list); | |
454 | } else | |
455 | dfs_cache_free_tgts(&ntl); | |
456 | } | |
457 | break; | |
458 | } | |
459 | ||
460 | out: | |
461 | kfree(share); | |
462 | kfree(prefix); | |
463 | ||
464 | return rc; | |
465 | } | |
466 | ||
467 | static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, | |
468 | struct cifs_sb_info *cifs_sb, char *tree, bool islink, | |
469 | struct dfs_cache_tgt_list *tl) | |
470 | { | |
471 | int rc; | |
472 | int num_links = 0; | |
473 | struct TCP_Server_Info *server = tcon->ses->server; | |
6fbdd5ab | 474 | char *old_fullpath = server->leaf_fullpath; |
1d04a6fe PA |
475 | |
476 | do { | |
477 | rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); | |
478 | if (!rc || rc != -EREMOTE) | |
479 | break; | |
480 | } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); | |
481 | /* | |
6fbdd5ab PA |
482 | * If we couldn't tree connect to any targets from last referral path, then |
483 | * retry it from newly resolved dfs referral. | |
1d04a6fe | 484 | */ |
6fbdd5ab | 485 | if (rc && server->leaf_fullpath != old_fullpath) |
1d04a6fe | 486 | cifs_signal_cifsd_for_reconnect(server, true); |
1d04a6fe PA |
487 | |
488 | dfs_cache_free_tgts(tl); | |
489 | return rc; | |
490 | } | |
491 | ||
492 | int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) | |
493 | { | |
494 | int rc; | |
495 | struct TCP_Server_Info *server = tcon->ses->server; | |
496 | const struct smb_version_operations *ops = server->ops; | |
497 | struct super_block *sb = NULL; | |
498 | struct cifs_sb_info *cifs_sb; | |
499 | struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); | |
500 | char *tree; | |
501 | struct dfs_info3_param ref = {0}; | |
502 | ||
503 | /* only send once per connect */ | |
504 | spin_lock(&tcon->tc_lock); | |
2f0e4f03 SP |
505 | if (tcon->status != TID_NEW && |
506 | tcon->status != TID_NEED_TCON) { | |
507 | spin_unlock(&tcon->tc_lock); | |
508 | return -EHOSTDOWN; | |
509 | } | |
510 | ||
511 | if (tcon->status == TID_GOOD) { | |
1d04a6fe PA |
512 | spin_unlock(&tcon->tc_lock); |
513 | return 0; | |
514 | } | |
515 | tcon->status = TID_IN_TCON; | |
516 | spin_unlock(&tcon->tc_lock); | |
517 | ||
518 | tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); | |
519 | if (!tree) { | |
520 | rc = -ENOMEM; | |
521 | goto out; | |
522 | } | |
523 | ||
524 | if (tcon->ipc) { | |
39a154fc | 525 | cifs_server_lock(server); |
1d04a6fe | 526 | scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); |
39a154fc | 527 | cifs_server_unlock(server); |
1d04a6fe PA |
528 | rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); |
529 | goto out; | |
530 | } | |
531 | ||
532 | sb = cifs_get_tcp_super(server); | |
533 | if (IS_ERR(sb)) { | |
534 | rc = PTR_ERR(sb); | |
535 | cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); | |
536 | goto out; | |
537 | } | |
538 | ||
539 | cifs_sb = CIFS_SB(sb); | |
540 | ||
541 | /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ | |
542 | if (!server->current_fullpath || | |
543 | dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { | |
544 | rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); | |
545 | goto out; | |
546 | } | |
547 | ||
548 | rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, | |
549 | &tl); | |
550 | free_dfs_info_param(&ref); | |
551 | ||
552 | out: | |
553 | kfree(tree); | |
554 | cifs_put_tcp_super(sb); | |
555 | ||
556 | if (rc) { | |
557 | spin_lock(&tcon->tc_lock); | |
558 | if (tcon->status == TID_IN_TCON) | |
559 | tcon->status = TID_NEED_TCON; | |
560 | spin_unlock(&tcon->tc_lock); | |
561 | } else { | |
562 | spin_lock(&tcon->tc_lock); | |
563 | if (tcon->status == TID_IN_TCON) | |
564 | tcon->status = TID_GOOD; | |
565 | spin_unlock(&tcon->tc_lock); | |
566 | tcon->need_reconnect = false; | |
567 | } | |
568 | ||
569 | return rc; | |
570 | } |