162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/list.h>
762306a36Sopenharmony_ci#include <linux/jhash.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/rwsem.h>
1062306a36Sopenharmony_ci#include <linux/parser.h>
1162306a36Sopenharmony_ci#include <linux/namei.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "share_config.h"
1662306a36Sopenharmony_ci#include "user_config.h"
1762306a36Sopenharmony_ci#include "user_session.h"
1862306a36Sopenharmony_ci#include "../transport_ipc.h"
1962306a36Sopenharmony_ci#include "../misc.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define SHARE_HASH_BITS		3
2262306a36Sopenharmony_cistatic DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
2362306a36Sopenharmony_cistatic DECLARE_RWSEM(shares_table_lock);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct ksmbd_veto_pattern {
2662306a36Sopenharmony_ci	char			*pattern;
2762306a36Sopenharmony_ci	struct list_head	list;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic unsigned int share_name_hash(const char *name)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	return jhash(name, strlen(name), 0);
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void kill_share(struct ksmbd_share_config *share)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	while (!list_empty(&share->veto_list)) {
3862306a36Sopenharmony_ci		struct ksmbd_veto_pattern *p;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		p = list_entry(share->veto_list.next,
4162306a36Sopenharmony_ci			       struct ksmbd_veto_pattern,
4262306a36Sopenharmony_ci			       list);
4362306a36Sopenharmony_ci		list_del(&p->list);
4462306a36Sopenharmony_ci		kfree(p->pattern);
4562306a36Sopenharmony_ci		kfree(p);
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (share->path)
4962306a36Sopenharmony_ci		path_put(&share->vfs_path);
5062306a36Sopenharmony_ci	kfree(share->name);
5162306a36Sopenharmony_ci	kfree(share->path);
5262306a36Sopenharmony_ci	kfree(share);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_civoid ksmbd_share_config_del(struct ksmbd_share_config *share)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	down_write(&shares_table_lock);
5862306a36Sopenharmony_ci	hash_del(&share->hlist);
5962306a36Sopenharmony_ci	up_write(&shares_table_lock);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_civoid __ksmbd_share_config_put(struct ksmbd_share_config *share)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	ksmbd_share_config_del(share);
6562306a36Sopenharmony_ci	kill_share(share);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct ksmbd_share_config *
6962306a36Sopenharmony_ci__get_share_config(struct ksmbd_share_config *share)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	if (!atomic_inc_not_zero(&share->refcount))
7262306a36Sopenharmony_ci		return NULL;
7362306a36Sopenharmony_ci	return share;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct ksmbd_share_config *__share_lookup(const char *name)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct ksmbd_share_config *share;
7962306a36Sopenharmony_ci	unsigned int key = share_name_hash(name);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	hash_for_each_possible(shares_table, share, hlist, key) {
8262306a36Sopenharmony_ci		if (!strcmp(name, share->name))
8362306a36Sopenharmony_ci			return share;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	return NULL;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int parse_veto_list(struct ksmbd_share_config *share,
8962306a36Sopenharmony_ci			   char *veto_list,
9062306a36Sopenharmony_ci			   int veto_list_sz)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int sz = 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (!veto_list_sz)
9562306a36Sopenharmony_ci		return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	while (veto_list_sz > 0) {
9862306a36Sopenharmony_ci		struct ksmbd_veto_pattern *p;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		sz = strlen(veto_list);
10162306a36Sopenharmony_ci		if (!sz)
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
10562306a36Sopenharmony_ci		if (!p)
10662306a36Sopenharmony_ci			return -ENOMEM;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		p->pattern = kstrdup(veto_list, GFP_KERNEL);
10962306a36Sopenharmony_ci		if (!p->pattern) {
11062306a36Sopenharmony_ci			kfree(p);
11162306a36Sopenharmony_ci			return -ENOMEM;
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		list_add(&p->list, &share->veto_list);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		veto_list += sz + 1;
11762306a36Sopenharmony_ci		veto_list_sz -= (sz + 1);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct ksmbd_share_config *share_config_request(struct unicode_map *um,
12462306a36Sopenharmony_ci						       const char *name)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct ksmbd_share_config_response *resp;
12762306a36Sopenharmony_ci	struct ksmbd_share_config *share = NULL;
12862306a36Sopenharmony_ci	struct ksmbd_share_config *lookup;
12962306a36Sopenharmony_ci	int ret;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	resp = ksmbd_ipc_share_config_request(name);
13262306a36Sopenharmony_ci	if (!resp)
13362306a36Sopenharmony_ci		return NULL;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
13662306a36Sopenharmony_ci		goto out;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (*resp->share_name) {
13962306a36Sopenharmony_ci		char *cf_resp_name;
14062306a36Sopenharmony_ci		bool equal;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name);
14362306a36Sopenharmony_ci		if (IS_ERR(cf_resp_name))
14462306a36Sopenharmony_ci			goto out;
14562306a36Sopenharmony_ci		equal = !strcmp(cf_resp_name, name);
14662306a36Sopenharmony_ci		kfree(cf_resp_name);
14762306a36Sopenharmony_ci		if (!equal)
14862306a36Sopenharmony_ci			goto out;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
15262306a36Sopenharmony_ci	if (!share)
15362306a36Sopenharmony_ci		goto out;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	share->flags = resp->flags;
15662306a36Sopenharmony_ci	atomic_set(&share->refcount, 1);
15762306a36Sopenharmony_ci	INIT_LIST_HEAD(&share->veto_list);
15862306a36Sopenharmony_ci	share->name = kstrdup(name, GFP_KERNEL);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
16162306a36Sopenharmony_ci		share->path = kstrdup(ksmbd_share_config_path(resp),
16262306a36Sopenharmony_ci				      GFP_KERNEL);
16362306a36Sopenharmony_ci		if (share->path)
16462306a36Sopenharmony_ci			share->path_sz = strlen(share->path);
16562306a36Sopenharmony_ci		share->create_mask = resp->create_mask;
16662306a36Sopenharmony_ci		share->directory_mask = resp->directory_mask;
16762306a36Sopenharmony_ci		share->force_create_mode = resp->force_create_mode;
16862306a36Sopenharmony_ci		share->force_directory_mode = resp->force_directory_mode;
16962306a36Sopenharmony_ci		share->force_uid = resp->force_uid;
17062306a36Sopenharmony_ci		share->force_gid = resp->force_gid;
17162306a36Sopenharmony_ci		ret = parse_veto_list(share,
17262306a36Sopenharmony_ci				      KSMBD_SHARE_CONFIG_VETO_LIST(resp),
17362306a36Sopenharmony_ci				      resp->veto_list_sz);
17462306a36Sopenharmony_ci		if (!ret && share->path) {
17562306a36Sopenharmony_ci			ret = kern_path(share->path, 0, &share->vfs_path);
17662306a36Sopenharmony_ci			if (ret) {
17762306a36Sopenharmony_ci				ksmbd_debug(SMB, "failed to access '%s'\n",
17862306a36Sopenharmony_ci					    share->path);
17962306a36Sopenharmony_ci				/* Avoid put_path() */
18062306a36Sopenharmony_ci				kfree(share->path);
18162306a36Sopenharmony_ci				share->path = NULL;
18262306a36Sopenharmony_ci			}
18362306a36Sopenharmony_ci		}
18462306a36Sopenharmony_ci		if (ret || !share->name) {
18562306a36Sopenharmony_ci			kill_share(share);
18662306a36Sopenharmony_ci			share = NULL;
18762306a36Sopenharmony_ci			goto out;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	down_write(&shares_table_lock);
19262306a36Sopenharmony_ci	lookup = __share_lookup(name);
19362306a36Sopenharmony_ci	if (lookup)
19462306a36Sopenharmony_ci		lookup = __get_share_config(lookup);
19562306a36Sopenharmony_ci	if (!lookup) {
19662306a36Sopenharmony_ci		hash_add(shares_table, &share->hlist, share_name_hash(name));
19762306a36Sopenharmony_ci	} else {
19862306a36Sopenharmony_ci		kill_share(share);
19962306a36Sopenharmony_ci		share = lookup;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	up_write(&shares_table_lock);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ciout:
20462306a36Sopenharmony_ci	kvfree(resp);
20562306a36Sopenharmony_ci	return share;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistruct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um,
20962306a36Sopenharmony_ci						  const char *name)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct ksmbd_share_config *share;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	down_read(&shares_table_lock);
21462306a36Sopenharmony_ci	share = __share_lookup(name);
21562306a36Sopenharmony_ci	if (share)
21662306a36Sopenharmony_ci		share = __get_share_config(share);
21762306a36Sopenharmony_ci	up_read(&shares_table_lock);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (share)
22062306a36Sopenharmony_ci		return share;
22162306a36Sopenharmony_ci	return share_config_request(um, name);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cibool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
22562306a36Sopenharmony_ci			       const char *filename)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct ksmbd_veto_pattern *p;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	list_for_each_entry(p, &share->veto_list, list) {
23062306a36Sopenharmony_ci		if (match_wildcard(p->pattern, filename))
23162306a36Sopenharmony_ci			return true;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci	return false;
23462306a36Sopenharmony_ci}
235