1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
7 #include "cifs_debug.h"
8 #include "dns_resolve.h"
9 #include "fs_context.h"
13 * dfs_parse_target_referral - set fs context for dfs target referral
15 * @full_path: full path in UNC format.
16 * @ref: dfs referral pointer.
17 * @ctx: smb3 fs context pointer.
19 * Return zero if dfs referral was parsed correctly, otherwise non-zero.
21 int dfs_parse_target_referral(const char *full_path
, const struct dfs_info3_param
*ref
,
22 struct smb3_fs_context
*ctx
)
25 const char *prepath
= NULL
;
28 if (!full_path
|| !*full_path
|| !ref
|| !ctx
)
31 if (WARN_ON_ONCE(!ref
->node_name
|| ref
->path_consumed
< 0))
34 if (strlen(full_path
) - ref
->path_consumed
) {
35 prepath
= full_path
+ ref
->path_consumed
;
36 /* skip initial delimiter */
37 if (*prepath
== '/' || *prepath
== '\\')
41 path
= cifs_build_devname(ref
->node_name
, prepath
);
45 rc
= smb3_parse_devname(path
, ctx
);
49 rc
= dns_resolve_server_name_to_ip(path
, (struct sockaddr
*)&ctx
->dstaddr
, NULL
);
56 static int get_session(struct cifs_mount_ctx
*mnt_ctx
, const char *full_path
)
58 struct smb3_fs_context
*ctx
= mnt_ctx
->fs_ctx
;
61 ctx
->leaf_fullpath
= (char *)full_path
;
62 rc
= cifs_mount_get_session(mnt_ctx
);
63 ctx
->leaf_fullpath
= NULL
;
69 * Get an active reference of @ses so that next call to cifs_put_tcon() won't
70 * release it as any new DFS referrals must go through its IPC tcon.
72 static void set_root_smb_session(struct cifs_mount_ctx
*mnt_ctx
)
74 struct smb3_fs_context
*ctx
= mnt_ctx
->fs_ctx
;
75 struct cifs_ses
*ses
= mnt_ctx
->ses
;
78 spin_lock(&cifs_tcp_ses_lock
);
79 cifs_smb_ses_inc_refcount(ses
);
80 spin_unlock(&cifs_tcp_ses_lock
);
82 ctx
->dfs_root_ses
= ses
;
85 static inline int parse_dfs_target(struct smb3_fs_context
*ctx
,
86 struct dfs_ref_walk
*rw
,
87 struct dfs_info3_param
*tgt
)
90 const char *fpath
= ref_walk_fpath(rw
) + 1;
92 rc
= ref_walk_get_tgt(rw
, tgt
);
94 rc
= dfs_parse_target_referral(fpath
, tgt
, ctx
);
98 static int setup_dfs_ref(struct cifs_mount_ctx
*mnt_ctx
,
99 struct dfs_info3_param
*tgt
,
100 struct dfs_ref_walk
*rw
)
102 struct smb3_fs_context
*ctx
= mnt_ctx
->fs_ctx
;
103 struct cifs_sb_info
*cifs_sb
= mnt_ctx
->cifs_sb
;
104 char *ref_path
, *full_path
;
107 full_path
= smb3_fs_context_fullpath(ctx
, CIFS_DIR_SEP(cifs_sb
));
108 if (IS_ERR(full_path
))
109 return PTR_ERR(full_path
);
111 if (!tgt
|| (tgt
->server_type
== DFS_TYPE_LINK
&&
112 DFS_INTERLINK(tgt
->flags
)))
113 ref_path
= dfs_get_path(cifs_sb
, ctx
->UNC
);
115 ref_path
= dfs_get_path(cifs_sb
, full_path
);
116 if (IS_ERR(ref_path
)) {
117 rc
= PTR_ERR(ref_path
);
121 ref_walk_path(rw
) = ref_path
;
122 ref_walk_fpath(rw
) = full_path
;
123 ref_walk_ses(rw
) = ctx
->dfs_root_ses
;
127 static int __dfs_referral_walk(struct cifs_mount_ctx
*mnt_ctx
,
128 struct dfs_ref_walk
*rw
)
130 struct smb3_fs_context
*ctx
= mnt_ctx
->fs_ctx
;
131 struct dfs_info3_param tgt
= {};
136 ctx
->dfs_root_ses
= ref_walk_ses(rw
);
137 if (ref_walk_empty(rw
)) {
138 rc
= dfs_get_referral(mnt_ctx
, ref_walk_path(rw
) + 1,
139 NULL
, ref_walk_tl(rw
));
141 rc
= cifs_mount_get_tcon(mnt_ctx
);
143 rc
= cifs_is_path_remote(mnt_ctx
);
146 if (!ref_walk_num_tgts(rw
)) {
152 while (ref_walk_next_tgt(rw
)) {
153 rc
= parse_dfs_target(ctx
, rw
, &tgt
);
157 cifs_mount_put_conns(mnt_ctx
);
158 rc
= get_session(mnt_ctx
, ref_walk_path(rw
));
162 ref_walk_set_tgt_hint(rw
);
163 if (tgt
.flags
& DFSREF_STORAGE_SERVER
) {
164 rc
= cifs_mount_get_tcon(mnt_ctx
);
166 rc
= cifs_is_path_remote(mnt_ctx
);
173 set_root_smb_session(mnt_ctx
);
174 rc
= ref_walk_advance(rw
);
176 rc
= setup_dfs_ref(mnt_ctx
, &tgt
, rw
);
185 } while (rc
&& ref_walk_descend(rw
));
188 free_dfs_info_param(&tgt
);
192 static int dfs_referral_walk(struct cifs_mount_ctx
*mnt_ctx
,
193 struct dfs_ref_walk
**rw
)
197 *rw
= ref_walk_alloc();
205 rc
= setup_dfs_ref(mnt_ctx
, NULL
, *rw
);
207 rc
= __dfs_referral_walk(mnt_ctx
, *rw
);
211 static int __dfs_mount_share(struct cifs_mount_ctx
*mnt_ctx
)
213 struct cifs_sb_info
*cifs_sb
= mnt_ctx
->cifs_sb
;
214 struct smb3_fs_context
*ctx
= mnt_ctx
->fs_ctx
;
215 struct dfs_ref_walk
*rw
= NULL
;
216 struct cifs_tcon
*tcon
;
217 char *origin_fullpath
;
220 origin_fullpath
= dfs_get_path(cifs_sb
, ctx
->source
);
221 if (IS_ERR(origin_fullpath
))
222 return PTR_ERR(origin_fullpath
);
224 rc
= dfs_referral_walk(mnt_ctx
, &rw
);
227 * Prevent superblock from being created with any missing
230 if (WARN_ON(!mnt_ctx
->server
))
232 else if (WARN_ON(!mnt_ctx
->ses
))
234 else if (WARN_ON(!mnt_ctx
->tcon
))
240 tcon
= mnt_ctx
->tcon
;
241 spin_lock(&tcon
->tc_lock
);
242 tcon
->origin_fullpath
= origin_fullpath
;
243 origin_fullpath
= NULL
;
244 ref_walk_set_tcon(rw
, tcon
);
245 spin_unlock(&tcon
->tc_lock
);
246 queue_delayed_work(dfscache_wq
, &tcon
->dfs_cache_work
,
247 dfs_cache_get_ttl() * HZ
);
250 kfree(origin_fullpath
);
256 * If @ctx->dfs_automount, then update @ctx->dstaddr earlier with the DFS root
257 * server from where we'll start following any referrals. Otherwise rely on the
258 * value provided by mount(2) as the user might not have dns_resolver key set up
259 * and therefore failing to upcall to resolve UNC hostname under @ctx->source.
261 static int update_fs_context_dstaddr(struct smb3_fs_context
*ctx
)
263 struct sockaddr
*addr
= (struct sockaddr
*)&ctx
->dstaddr
;
266 if (!ctx
->nodfs
&& ctx
->dfs_automount
) {
267 rc
= dns_resolve_server_name_to_ip(ctx
->source
, addr
, NULL
);
269 cifs_set_port(addr
, ctx
->port
);
270 ctx
->dfs_automount
= false;
275 int dfs_mount_share(struct cifs_mount_ctx
*mnt_ctx
)
277 struct smb3_fs_context
*ctx
= mnt_ctx
->fs_ctx
;
278 bool nodfs
= ctx
->nodfs
;
281 rc
= update_fs_context_dstaddr(ctx
);
285 rc
= get_session(mnt_ctx
, NULL
);
290 * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally
291 * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
293 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
294 * to respond with PATH_NOT_COVERED to requests that include the prefix.
297 rc
= dfs_get_referral(mnt_ctx
, ctx
->UNC
+ 1, NULL
, NULL
);
299 cifs_dbg(FYI
, "%s: no dfs referral for %s: %d\n",
300 __func__
, ctx
->UNC
+ 1, rc
);
301 cifs_dbg(FYI
, "%s: assuming non-dfs mount...\n", __func__
);
306 rc
= cifs_mount_get_tcon(mnt_ctx
);
308 rc
= cifs_is_path_remote(mnt_ctx
);
312 if (!ctx
->dfs_conn
) {
313 ctx
->dfs_conn
= true;
314 cifs_mount_put_conns(mnt_ctx
);
315 rc
= get_session(mnt_ctx
, NULL
);
318 set_root_smb_session(mnt_ctx
);
319 rc
= __dfs_mount_share(mnt_ctx
);
324 static int target_share_matches_server(struct TCP_Server_Info
*server
, char *share
,
328 const char *dfs_host
;
331 *target_match
= true;
332 extract_unc_hostname(share
, &dfs_host
, &dfs_host_len
);
334 /* Check if hostnames or addresses match */
335 cifs_server_lock(server
);
336 if (dfs_host_len
!= strlen(server
->hostname
) ||
337 strncasecmp(dfs_host
, server
->hostname
, dfs_host_len
)) {
338 cifs_dbg(FYI
, "%s: %.*s doesn't match %s\n", __func__
,
339 (int)dfs_host_len
, dfs_host
, server
->hostname
);
340 rc
= match_target_ip(server
, dfs_host
, dfs_host_len
, target_match
);
342 cifs_dbg(VFS
, "%s: failed to match target ip: %d\n", __func__
, rc
);
344 cifs_server_unlock(server
);
348 static int tree_connect_dfs_target(const unsigned int xid
,
349 struct cifs_tcon
*tcon
,
350 struct cifs_sb_info
*cifs_sb
,
351 char *tree
, bool islink
,
352 struct dfs_cache_tgt_list
*tl
)
354 const struct smb_version_operations
*ops
= tcon
->ses
->server
->ops
;
355 struct TCP_Server_Info
*server
= tcon
->ses
->server
;
356 struct dfs_cache_tgt_iterator
*tit
;
357 char *share
= NULL
, *prefix
= NULL
;
361 /* Try to tree connect to all dfs targets */
362 for (tit
= dfs_cache_get_tgt_iterator(tl
);
363 tit
; tit
= dfs_cache_get_next_tgt(tl
, tit
)) {
366 share
= prefix
= NULL
;
368 /* Check if share matches with tcp ses */
369 rc
= dfs_cache_get_tgt_share(server
->leaf_fullpath
+ 1, tit
, &share
, &prefix
);
371 cifs_dbg(VFS
, "%s: failed to parse target share: %d\n", __func__
, rc
);
375 rc
= target_share_matches_server(server
, share
, &target_match
);
383 dfs_cache_noreq_update_tgthint(server
->leaf_fullpath
+ 1, tit
);
384 scnprintf(tree
, MAX_TREE_SIZE
, "\\%s", share
);
385 rc
= ops
->tree_connect(xid
, tcon
->ses
, tree
,
386 tcon
, tcon
->ses
->local_nls
);
387 if (islink
&& !rc
&& cifs_sb
)
388 rc
= cifs_update_super_prepath(cifs_sb
, prefix
);
394 dfs_cache_free_tgts(tl
);
398 int cifs_tree_connect(const unsigned int xid
, struct cifs_tcon
*tcon
)
401 struct TCP_Server_Info
*server
= tcon
->ses
->server
;
402 const struct smb_version_operations
*ops
= server
->ops
;
403 DFS_CACHE_TGT_LIST(tl
);
404 struct cifs_sb_info
*cifs_sb
= NULL
;
405 struct super_block
*sb
= NULL
;
406 struct dfs_info3_param ref
= {0};
409 /* only send once per connect */
410 spin_lock(&tcon
->tc_lock
);
412 /* if tcon is marked for needing reconnect, update state */
413 if (tcon
->need_reconnect
)
414 tcon
->status
= TID_NEED_TCON
;
416 if (tcon
->status
== TID_GOOD
) {
417 spin_unlock(&tcon
->tc_lock
);
421 if (tcon
->status
!= TID_NEW
&&
422 tcon
->status
!= TID_NEED_TCON
) {
423 spin_unlock(&tcon
->tc_lock
);
427 tcon
->status
= TID_IN_TCON
;
428 spin_unlock(&tcon
->tc_lock
);
430 tree
= kzalloc(MAX_TREE_SIZE
, GFP_KERNEL
);
437 cifs_server_lock(server
);
438 scnprintf(tree
, MAX_TREE_SIZE
, "\\\\%s\\IPC$", server
->hostname
);
439 cifs_server_unlock(server
);
440 rc
= ops
->tree_connect(xid
, tcon
->ses
, tree
,
441 tcon
, tcon
->ses
->local_nls
);
445 sb
= cifs_get_dfs_tcon_super(tcon
);
447 cifs_sb
= CIFS_SB(sb
);
449 /* Tree connect to last share in @tcon->tree_name if no DFS referral */
450 if (!server
->leaf_fullpath
||
451 dfs_cache_noreq_find(server
->leaf_fullpath
+ 1, &ref
, &tl
)) {
452 rc
= ops
->tree_connect(xid
, tcon
->ses
, tcon
->tree_name
,
453 tcon
, tcon
->ses
->local_nls
);
457 rc
= tree_connect_dfs_target(xid
, tcon
, cifs_sb
, tree
, ref
.server_type
== DFS_TYPE_LINK
,
459 free_dfs_info_param(&ref
);
463 cifs_put_tcp_super(sb
);
466 spin_lock(&tcon
->tc_lock
);
467 if (tcon
->status
== TID_IN_TCON
)
468 tcon
->status
= TID_NEED_TCON
;
469 spin_unlock(&tcon
->tc_lock
);
471 spin_lock(&tcon
->tc_lock
);
472 if (tcon
->status
== TID_IN_TCON
)
473 tcon
->status
= TID_GOOD
;
474 tcon
->need_reconnect
= false;
475 spin_unlock(&tcon
->tc_lock
);