smb: move client and server files to common directory fs/smb
[sfrench/cifs-2.6.git] / fs / smb / client / dfs.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
4  */
5
6 #include <linux/namei.h>
7 #include "cifsproto.h"
8 #include "cifs_debug.h"
9 #include "dns_resolve.h"
10 #include "fs_context.h"
11 #include "dfs.h"
12
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
50         rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL);
51
52 out:
53         kfree(path);
54         return rc;
55 }
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
102 static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
103 {
104         struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
105         struct dfs_root_ses *root_ses;
106         struct cifs_ses *ses = mnt_ctx->ses;
107
108         if (ses) {
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
115                 spin_lock(&cifs_tcp_ses_lock);
116                 ses->ses_count++;
117                 spin_unlock(&cifs_tcp_ses_lock);
118                 root_ses->ses = ses;
119                 list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
120         }
121         ctx->dfs_root_ses = ses;
122         return 0;
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 = {};
130         bool is_refsrv;
131         int rc, rc2;
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
146         is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER);
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 */
155                 is_refsrv |= is_tcon_dfs(mnt_ctx->tcon);
156
157                 rc = cifs_is_path_remote(mnt_ctx);
158         }
159
160         dfs_cache_noreq_update_tgthint(ref_path + 1, tit);
161
162         if (rc == -EREMOTE && is_refsrv) {
163                 rc2 = add_root_smb_session(mnt_ctx);
164                 if (rc2)
165                         rc = rc2;
166         }
167
168 out:
169         free_dfs_info_param(&ref);
170         return rc;
171 }
172
173 static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
174 {
175         struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
176         struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
177         char *ref_path = NULL, *full_path = NULL;
178         struct dfs_cache_tgt_iterator *tit;
179         struct TCP_Server_Info *server;
180         struct cifs_tcon *tcon;
181         char *origin_fullpath = NULL;
182         int num_links = 0;
183         int rc;
184
185         ref_path = dfs_get_path(cifs_sb, ctx->UNC);
186         if (IS_ERR(ref_path))
187                 return PTR_ERR(ref_path);
188
189         full_path = build_unc_path_to_root(ctx, cifs_sb, true);
190         if (IS_ERR(full_path)) {
191                 rc = PTR_ERR(full_path);
192                 full_path = NULL;
193                 goto out;
194         }
195
196         origin_fullpath = kstrdup(full_path, GFP_KERNEL);
197         if (!origin_fullpath) {
198                 rc = -ENOMEM;
199                 goto out;
200         }
201
202         do {
203                 struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
204
205                 rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl);
206                 if (rc)
207                         break;
208
209                 tit = dfs_cache_get_tgt_iterator(&tl);
210                 if (!tit) {
211                         cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__,
212                                  ref_path + 1);
213                         rc = -ENOENT;
214                         dfs_cache_free_tgts(&tl);
215                         break;
216                 }
217
218                 do {
219                         rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit);
220                         if (!rc)
221                                 break;
222                         if (rc == -EREMOTE) {
223                                 if (++num_links > MAX_NESTED_LINKS) {
224                                         rc = -ELOOP;
225                                         break;
226                                 }
227                                 kfree(ref_path);
228                                 kfree(full_path);
229                                 ref_path = full_path = NULL;
230
231                                 full_path = build_unc_path_to_root(ctx, cifs_sb, true);
232                                 if (IS_ERR(full_path)) {
233                                         rc = PTR_ERR(full_path);
234                                         full_path = NULL;
235                                 } else {
236                                         ref_path = dfs_get_path(cifs_sb, full_path);
237                                         if (IS_ERR(ref_path)) {
238                                                 rc = PTR_ERR(ref_path);
239                                                 ref_path = NULL;
240                                         }
241                                 }
242                                 break;
243                         }
244                 } while ((tit = dfs_cache_get_next_tgt(&tl, tit)));
245                 dfs_cache_free_tgts(&tl);
246         } while (rc == -EREMOTE);
247
248         if (!rc) {
249                 server = mnt_ctx->server;
250                 tcon = mnt_ctx->tcon;
251
252                 mutex_lock(&server->refpath_lock);
253                 spin_lock(&server->srv_lock);
254                 if (!server->origin_fullpath) {
255                         server->origin_fullpath = origin_fullpath;
256                         origin_fullpath = NULL;
257                 }
258                 spin_unlock(&server->srv_lock);
259                 mutex_unlock(&server->refpath_lock);
260
261                 if (list_empty(&tcon->dfs_ses_list)) {
262                         list_replace_init(&mnt_ctx->dfs_ses_list,
263                                           &tcon->dfs_ses_list);
264                         queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
265                                            dfs_cache_get_ttl() * HZ);
266                 } else {
267                         dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
268                 }
269         }
270
271 out:
272         kfree(origin_fullpath);
273         kfree(ref_path);
274         kfree(full_path);
275         return rc;
276 }
277
278 int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
279 {
280         struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
281         struct cifs_ses *ses;
282         char *source = ctx->source;
283         bool nodfs = ctx->nodfs;
284         int rc;
285
286         *isdfs = false;
287         /* Temporarily set @ctx->source to NULL as we're not matching DFS
288          * superblocks yet.  See cifs_match_super() and match_server().
289          */
290         ctx->source = NULL;
291         rc = get_session(mnt_ctx, NULL);
292         if (rc)
293                 goto out;
294
295         ctx->dfs_root_ses = mnt_ctx->ses;
296         /*
297          * If called with 'nodfs' mount option, then skip DFS resolving.  Otherwise unconditionally
298          * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
299          *
300          * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
301          * to respond with PATH_NOT_COVERED to requests that include the prefix.
302          */
303         if (!nodfs) {
304                 rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);
305                 if (rc) {
306                         if (rc != -ENOENT && rc != -EOPNOTSUPP && rc != -EIO)
307                                 goto out;
308                         nodfs = true;
309                 }
310         }
311         if (nodfs) {
312                 rc = cifs_mount_get_tcon(mnt_ctx);
313                 if (!rc)
314                         rc = cifs_is_path_remote(mnt_ctx);
315                 goto out;
316         }
317
318         *isdfs = true;
319         /*
320          * Prevent DFS root session of being put in the first call to
321          * cifs_mount_put_conns().  If another DFS root server was not found
322          * while chasing the referrals (@ctx->dfs_root_ses == @ses), then we
323          * can safely put extra refcount of @ses.
324          */
325         ses = mnt_ctx->ses;
326         mnt_ctx->ses = NULL;
327         mnt_ctx->server = NULL;
328         rc = __dfs_mount_share(mnt_ctx);
329         if (ses == ctx->dfs_root_ses)
330                 cifs_put_smb_ses(ses);
331 out:
332         /*
333          * Restore previous value of @ctx->source so DFS superblock can be
334          * matched in cifs_match_super().
335          */
336         ctx->source = source;
337         return rc;
338 }
339
340 /* Update dfs referral path of superblock */
341 static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
342                                   const char *target)
343 {
344         int rc = 0;
345         size_t len = strlen(target);
346         char *refpath, *npath;
347
348         if (unlikely(len < 2 || *target != '\\'))
349                 return -EINVAL;
350
351         if (target[1] == '\\') {
352                 len += 1;
353                 refpath = kmalloc(len, GFP_KERNEL);
354                 if (!refpath)
355                         return -ENOMEM;
356
357                 scnprintf(refpath, len, "%s", target);
358         } else {
359                 len += sizeof("\\");
360                 refpath = kmalloc(len, GFP_KERNEL);
361                 if (!refpath)
362                         return -ENOMEM;
363
364                 scnprintf(refpath, len, "\\%s", target);
365         }
366
367         npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
368         kfree(refpath);
369
370         if (IS_ERR(npath)) {
371                 rc = PTR_ERR(npath);
372         } else {
373                 mutex_lock(&server->refpath_lock);
374                 spin_lock(&server->srv_lock);
375                 kfree(server->leaf_fullpath);
376                 server->leaf_fullpath = npath;
377                 spin_unlock(&server->srv_lock);
378                 mutex_unlock(&server->refpath_lock);
379         }
380         return rc;
381 }
382
383 static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
384                                        bool *target_match)
385 {
386         int rc = 0;
387         const char *dfs_host;
388         size_t dfs_host_len;
389
390         *target_match = true;
391         extract_unc_hostname(share, &dfs_host, &dfs_host_len);
392
393         /* Check if hostnames or addresses match */
394         cifs_server_lock(server);
395         if (dfs_host_len != strlen(server->hostname) ||
396             strncasecmp(dfs_host, server->hostname, dfs_host_len)) {
397                 cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__,
398                          (int)dfs_host_len, dfs_host, server->hostname);
399                 rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
400                 if (rc)
401                         cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
402         }
403         cifs_server_unlock(server);
404         return rc;
405 }
406
407 static void __tree_connect_ipc(const unsigned int xid, char *tree,
408                                struct cifs_sb_info *cifs_sb,
409                                struct cifs_ses *ses)
410 {
411         struct TCP_Server_Info *server = ses->server;
412         struct cifs_tcon *tcon = ses->tcon_ipc;
413         int rc;
414
415         spin_lock(&ses->ses_lock);
416         spin_lock(&ses->chan_lock);
417         if (cifs_chan_needs_reconnect(ses, server) ||
418             ses->ses_status != SES_GOOD) {
419                 spin_unlock(&ses->chan_lock);
420                 spin_unlock(&ses->ses_lock);
421                 cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n",
422                                 __func__);
423                 return;
424         }
425         spin_unlock(&ses->chan_lock);
426         spin_unlock(&ses->ses_lock);
427
428         cifs_server_lock(server);
429         scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
430         cifs_server_unlock(server);
431
432         rc = server->ops->tree_connect(xid, ses, tree, tcon,
433                                        cifs_sb->local_nls);
434         cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc);
435         spin_lock(&tcon->tc_lock);
436         if (rc) {
437                 tcon->status = TID_NEED_TCON;
438         } else {
439                 tcon->status = TID_GOOD;
440                 tcon->need_reconnect = false;
441         }
442         spin_unlock(&tcon->tc_lock);
443 }
444
445 static void tree_connect_ipc(const unsigned int xid, char *tree,
446                              struct cifs_sb_info *cifs_sb,
447                              struct cifs_tcon *tcon)
448 {
449         struct cifs_ses *ses = tcon->ses;
450
451         __tree_connect_ipc(xid, tree, cifs_sb, ses);
452         __tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses));
453 }
454
455 static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
456                                      struct cifs_sb_info *cifs_sb, char *tree, bool islink,
457                                      struct dfs_cache_tgt_list *tl)
458 {
459         int rc;
460         struct TCP_Server_Info *server = tcon->ses->server;
461         const struct smb_version_operations *ops = server->ops;
462         struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
463         char *share = NULL, *prefix = NULL;
464         struct dfs_cache_tgt_iterator *tit;
465         bool target_match;
466
467         tit = dfs_cache_get_tgt_iterator(tl);
468         if (!tit) {
469                 rc = -ENOENT;
470                 goto out;
471         }
472
473         /* Try to tree connect to all dfs targets */
474         for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
475                 const char *target = dfs_cache_get_tgt_name(tit);
476                 struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl);
477
478                 kfree(share);
479                 kfree(prefix);
480                 share = prefix = NULL;
481
482                 /* Check if share matches with tcp ses */
483                 rc = dfs_cache_get_tgt_share(server->leaf_fullpath + 1, tit, &share, &prefix);
484                 if (rc) {
485                         cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
486                         break;
487                 }
488
489                 rc = target_share_matches_server(server, share, &target_match);
490                 if (rc)
491                         break;
492                 if (!target_match) {
493                         rc = -EHOSTUNREACH;
494                         continue;
495                 }
496
497                 dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
498                 tree_connect_ipc(xid, tree, cifs_sb, tcon);
499
500                 scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
501                 if (!islink) {
502                         rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
503                         break;
504                 }
505
506                 /*
507                  * If no dfs referrals were returned from link target, then just do a TREE_CONNECT
508                  * to it.  Otherwise, cache the dfs referral and then mark current tcp ses for
509                  * reconnect so either the demultiplex thread or the echo worker will reconnect to
510                  * newly resolved target.
511                  */
512                 if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
513                                    NULL, &ntl)) {
514                         rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
515                         if (rc)
516                                 continue;
517
518                         rc = cifs_update_super_prepath(cifs_sb, prefix);
519                 } else {
520                         /* Target is another dfs share */
521                         rc = update_server_fullpath(server, cifs_sb, target);
522                         dfs_cache_free_tgts(tl);
523
524                         if (!rc) {
525                                 rc = -EREMOTE;
526                                 list_replace_init(&ntl.tl_list, &tl->tl_list);
527                         } else
528                                 dfs_cache_free_tgts(&ntl);
529                 }
530                 break;
531         }
532
533 out:
534         kfree(share);
535         kfree(prefix);
536
537         return rc;
538 }
539
540 static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
541                                    struct cifs_sb_info *cifs_sb, char *tree, bool islink,
542                                    struct dfs_cache_tgt_list *tl)
543 {
544         int rc;
545         int num_links = 0;
546         struct TCP_Server_Info *server = tcon->ses->server;
547         char *old_fullpath = server->leaf_fullpath;
548
549         do {
550                 rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
551                 if (!rc || rc != -EREMOTE)
552                         break;
553         } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
554         /*
555          * If we couldn't tree connect to any targets from last referral path, then
556          * retry it from newly resolved dfs referral.
557          */
558         if (rc && server->leaf_fullpath != old_fullpath)
559                 cifs_signal_cifsd_for_reconnect(server, true);
560
561         dfs_cache_free_tgts(tl);
562         return rc;
563 }
564
565 int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
566 {
567         int rc;
568         struct TCP_Server_Info *server = tcon->ses->server;
569         const struct smb_version_operations *ops = server->ops;
570         struct super_block *sb = NULL;
571         struct cifs_sb_info *cifs_sb;
572         struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
573         char *tree;
574         struct dfs_info3_param ref = {0};
575
576         /* only send once per connect */
577         spin_lock(&tcon->tc_lock);
578         if (tcon->status != TID_NEW &&
579             tcon->status != TID_NEED_TCON) {
580                 spin_unlock(&tcon->tc_lock);
581                 return -EHOSTDOWN;
582         }
583
584         if (tcon->status == TID_GOOD) {
585                 spin_unlock(&tcon->tc_lock);
586                 return 0;
587         }
588         tcon->status = TID_IN_TCON;
589         spin_unlock(&tcon->tc_lock);
590
591         tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
592         if (!tree) {
593                 rc = -ENOMEM;
594                 goto out;
595         }
596
597         if (tcon->ipc) {
598                 cifs_server_lock(server);
599                 scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
600                 cifs_server_unlock(server);
601                 rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
602                 goto out;
603         }
604
605         sb = cifs_get_tcp_super(server);
606         if (IS_ERR(sb)) {
607                 rc = PTR_ERR(sb);
608                 cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc);
609                 goto out;
610         }
611
612         cifs_sb = CIFS_SB(sb);
613
614         /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
615         if (!server->leaf_fullpath ||
616             dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
617                 rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls);
618                 goto out;
619         }
620
621         rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK,
622                                      &tl);
623         free_dfs_info_param(&ref);
624
625 out:
626         kfree(tree);
627         cifs_put_tcp_super(sb);
628
629         if (rc) {
630                 spin_lock(&tcon->tc_lock);
631                 if (tcon->status == TID_IN_TCON)
632                         tcon->status = TID_NEED_TCON;
633                 spin_unlock(&tcon->tc_lock);
634         } else {
635                 spin_lock(&tcon->tc_lock);
636                 if (tcon->status == TID_IN_TCON)
637                         tcon->status = TID_GOOD;
638                 spin_unlock(&tcon->tc_lock);
639                 tcon->need_reconnect = false;
640         }
641
642         return rc;
643 }