162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#ifndef _CIFS_DFS_H
762306a36Sopenharmony_ci#define _CIFS_DFS_H
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "cifsglob.h"
1062306a36Sopenharmony_ci#include "fs_context.h"
1162306a36Sopenharmony_ci#include "cifs_unicode.h"
1262306a36Sopenharmony_ci#include <linux/namei.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define DFS_INTERLINK(v) \
1562306a36Sopenharmony_ci	(((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER))
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct dfs_ref {
1862306a36Sopenharmony_ci	char *path;
1962306a36Sopenharmony_ci	char *full_path;
2062306a36Sopenharmony_ci	struct dfs_cache_tgt_list tl;
2162306a36Sopenharmony_ci	struct dfs_cache_tgt_iterator *tit;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct dfs_ref_walk {
2562306a36Sopenharmony_ci	struct dfs_ref *ref;
2662306a36Sopenharmony_ci	struct dfs_ref refs[MAX_NESTED_LINKS];
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define ref_walk_start(w)	((w)->refs)
3062306a36Sopenharmony_ci#define ref_walk_end(w)	(&(w)->refs[ARRAY_SIZE((w)->refs) - 1])
3162306a36Sopenharmony_ci#define ref_walk_cur(w)	((w)->ref)
3262306a36Sopenharmony_ci#define ref_walk_descend(w)	(--ref_walk_cur(w) >= ref_walk_start(w))
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define ref_walk_tit(w)	(ref_walk_cur(w)->tit)
3562306a36Sopenharmony_ci#define ref_walk_empty(w)	(!ref_walk_tit(w))
3662306a36Sopenharmony_ci#define ref_walk_path(w)	(ref_walk_cur(w)->path)
3762306a36Sopenharmony_ci#define ref_walk_fpath(w)	(ref_walk_cur(w)->full_path)
3862306a36Sopenharmony_ci#define ref_walk_tl(w)		(&ref_walk_cur(w)->tl)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline struct dfs_ref_walk *ref_walk_alloc(void)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct dfs_ref_walk *rw;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	rw = kmalloc(sizeof(*rw), GFP_KERNEL);
4562306a36Sopenharmony_ci	if (!rw)
4662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4762306a36Sopenharmony_ci	return rw;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic inline void ref_walk_init(struct dfs_ref_walk *rw)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	memset(rw, 0, sizeof(*rw));
5362306a36Sopenharmony_ci	ref_walk_cur(rw) = ref_walk_start(rw);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic inline void __ref_walk_free(struct dfs_ref *ref)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	kfree(ref->path);
5962306a36Sopenharmony_ci	kfree(ref->full_path);
6062306a36Sopenharmony_ci	dfs_cache_free_tgts(&ref->tl);
6162306a36Sopenharmony_ci	memset(ref, 0, sizeof(*ref));
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline void ref_walk_free(struct dfs_ref_walk *rw)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct dfs_ref *ref = ref_walk_start(rw);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	for (; ref <= ref_walk_end(rw); ref++)
6962306a36Sopenharmony_ci		__ref_walk_free(ref);
7062306a36Sopenharmony_ci	kfree(rw);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic inline int ref_walk_advance(struct dfs_ref_walk *rw)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct dfs_ref *ref = ref_walk_cur(rw) + 1;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (ref > ref_walk_end(rw))
7862306a36Sopenharmony_ci		return -ELOOP;
7962306a36Sopenharmony_ci	__ref_walk_free(ref);
8062306a36Sopenharmony_ci	ref_walk_cur(rw) = ref;
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic inline struct dfs_cache_tgt_iterator *
8562306a36Sopenharmony_ciref_walk_next_tgt(struct dfs_ref_walk *rw)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct dfs_cache_tgt_iterator *tit;
8862306a36Sopenharmony_ci	struct dfs_ref *ref = ref_walk_cur(rw);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!ref->tit)
9162306a36Sopenharmony_ci		tit = dfs_cache_get_tgt_iterator(&ref->tl);
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		tit = dfs_cache_get_next_tgt(&ref->tl, ref->tit);
9462306a36Sopenharmony_ci	ref->tit = tit;
9562306a36Sopenharmony_ci	return tit;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,
9962306a36Sopenharmony_ci				   struct dfs_info3_param *tgt)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	zfree_dfs_info_param(tgt);
10262306a36Sopenharmony_ci	return dfs_cache_get_tgt_referral(ref_walk_path(rw) + 1,
10362306a36Sopenharmony_ci					  ref_walk_tit(rw), tgt);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline int ref_walk_num_tgts(struct dfs_ref_walk *rw)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	return dfs_cache_get_nr_tgts(ref_walk_tl(rw));
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	dfs_cache_noreq_update_tgthint(ref_walk_path(rw) + 1,
11462306a36Sopenharmony_ci				       ref_walk_tit(rw));
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct dfs_root_ses {
11862306a36Sopenharmony_ci	struct list_head list;
11962306a36Sopenharmony_ci	struct cifs_ses *ses;
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciint dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
12362306a36Sopenharmony_ci			      struct smb3_fs_context *ctx);
12462306a36Sopenharmony_ciint dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *path,
13262306a36Sopenharmony_ci				   struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tl)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
13562306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return dfs_cache_find(mnt_ctx->xid, ctx->dfs_root_ses, cifs_sb->local_nls,
13862306a36Sopenharmony_ci			      cifs_remap(cifs_sb), path, ref, tl);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic inline void dfs_put_root_smb_sessions(struct list_head *head)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct dfs_root_ses *root, *tmp;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	list_for_each_entry_safe(root, tmp, head, list) {
14662306a36Sopenharmony_ci		list_del_init(&root->list);
14762306a36Sopenharmony_ci		cifs_put_smb_ses(root->ses);
14862306a36Sopenharmony_ci		kfree(root);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#endif /* _CIFS_DFS_H */
153