Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
  4 */
  5
  6#include <linux/list.h>
  7#include <linux/jhash.h>
  8#include <linux/slab.h>
  9#include <linux/rwsem.h>
 10#include <linux/parser.h>
 11#include <linux/namei.h>
 12#include <linux/sched.h>
 13#include <linux/mm.h>
 14
 15#include "share_config.h"
 16#include "user_config.h"
 17#include "user_session.h"
 18#include "../transport_ipc.h"
 19#include "../misc.h"
 20
 21#define SHARE_HASH_BITS		3
 22static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
 23static DECLARE_RWSEM(shares_table_lock);
 24
 25struct ksmbd_veto_pattern {
 26	char			*pattern;
 27	struct list_head	list;
 28};
 29
 30static unsigned int share_name_hash(const char *name)
 31{
 32	return jhash(name, strlen(name), 0);
 33}
 34
 35static void kill_share(struct ksmbd_share_config *share)
 36{
 37	while (!list_empty(&share->veto_list)) {
 38		struct ksmbd_veto_pattern *p;
 39
 40		p = list_entry(share->veto_list.next,
 41			       struct ksmbd_veto_pattern,
 42			       list);
 43		list_del(&p->list);
 44		kfree(p->pattern);
 45		kfree(p);
 46	}
 47
 48	if (share->path)
 49		path_put(&share->vfs_path);
 50	kfree(share->name);
 51	kfree(share->path);
 52	kfree(share);
 53}
 54
 55void ksmbd_share_config_del(struct ksmbd_share_config *share)
 56{
 57	down_write(&shares_table_lock);
 58	hash_del(&share->hlist);
 59	up_write(&shares_table_lock);
 60}
 61
 62void __ksmbd_share_config_put(struct ksmbd_share_config *share)
 63{
 64	ksmbd_share_config_del(share);
 65	kill_share(share);
 66}
 67
 68static struct ksmbd_share_config *
 69__get_share_config(struct ksmbd_share_config *share)
 70{
 71	if (!atomic_inc_not_zero(&share->refcount))
 72		return NULL;
 73	return share;
 74}
 75
 76static struct ksmbd_share_config *__share_lookup(const char *name)
 77{
 78	struct ksmbd_share_config *share;
 79	unsigned int key = share_name_hash(name);
 80
 81	hash_for_each_possible(shares_table, share, hlist, key) {
 82		if (!strcmp(name, share->name))
 83			return share;
 84	}
 85	return NULL;
 86}
 87
 88static int parse_veto_list(struct ksmbd_share_config *share,
 89			   char *veto_list,
 90			   int veto_list_sz)
 91{
 92	int sz = 0;
 93
 94	if (!veto_list_sz)
 95		return 0;
 96
 97	while (veto_list_sz > 0) {
 98		struct ksmbd_veto_pattern *p;
 99
100		sz = strlen(veto_list);
101		if (!sz)
102			break;
103
104		p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
105		if (!p)
106			return -ENOMEM;
107
108		p->pattern = kstrdup(veto_list, GFP_KERNEL);
109		if (!p->pattern) {
110			kfree(p);
111			return -ENOMEM;
112		}
113
114		list_add(&p->list, &share->veto_list);
115
116		veto_list += sz + 1;
117		veto_list_sz -= (sz + 1);
118	}
119
120	return 0;
121}
122
123static struct ksmbd_share_config *share_config_request(struct unicode_map *um,
124						       const char *name)
125{
126	struct ksmbd_share_config_response *resp;
127	struct ksmbd_share_config *share = NULL;
128	struct ksmbd_share_config *lookup;
129	int ret;
130
131	resp = ksmbd_ipc_share_config_request(name);
132	if (!resp)
133		return NULL;
134
135	if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
136		goto out;
137
138	if (*resp->share_name) {
139		char *cf_resp_name;
140		bool equal;
141
142		cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name);
143		if (IS_ERR(cf_resp_name))
144			goto out;
145		equal = !strcmp(cf_resp_name, name);
146		kfree(cf_resp_name);
147		if (!equal)
148			goto out;
149	}
150
151	share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
152	if (!share)
153		goto out;
154
155	share->flags = resp->flags;
156	atomic_set(&share->refcount, 1);
157	INIT_LIST_HEAD(&share->veto_list);
158	share->name = kstrdup(name, GFP_KERNEL);
159
160	if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
161		share->path = kstrdup(ksmbd_share_config_path(resp),
162				      GFP_KERNEL);
163		if (share->path)
164			share->path_sz = strlen(share->path);
165		share->create_mask = resp->create_mask;
166		share->directory_mask = resp->directory_mask;
167		share->force_create_mode = resp->force_create_mode;
168		share->force_directory_mode = resp->force_directory_mode;
169		share->force_uid = resp->force_uid;
170		share->force_gid = resp->force_gid;
171		ret = parse_veto_list(share,
172				      KSMBD_SHARE_CONFIG_VETO_LIST(resp),
173				      resp->veto_list_sz);
174		if (!ret && share->path) {
175			ret = kern_path(share->path, 0, &share->vfs_path);
176			if (ret) {
177				ksmbd_debug(SMB, "failed to access '%s'\n",
178					    share->path);
179				/* Avoid put_path() */
180				kfree(share->path);
181				share->path = NULL;
182			}
183		}
184		if (ret || !share->name) {
185			kill_share(share);
186			share = NULL;
187			goto out;
188		}
189	}
190
191	down_write(&shares_table_lock);
192	lookup = __share_lookup(name);
193	if (lookup)
194		lookup = __get_share_config(lookup);
195	if (!lookup) {
196		hash_add(shares_table, &share->hlist, share_name_hash(name));
197	} else {
198		kill_share(share);
199		share = lookup;
200	}
201	up_write(&shares_table_lock);
202
203out:
204	kvfree(resp);
205	return share;
206}
207
208struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um,
209						  const char *name)
210{
211	struct ksmbd_share_config *share;
212
213	down_read(&shares_table_lock);
214	share = __share_lookup(name);
215	if (share)
216		share = __get_share_config(share);
217	up_read(&shares_table_lock);
218
219	if (share)
220		return share;
221	return share_config_request(um, name);
222}
223
224bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
225			       const char *filename)
226{
227	struct ksmbd_veto_pattern *p;
228
229	list_for_each_entry(p, &share->veto_list, list) {
230		if (match_wildcard(p->pattern, filename))
231			return true;
232	}
233	return false;
234}