244 lines
5.3 KiB
C
244 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/jhash.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/parser.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include "share_config.h"
|
|
#include "user_config.h"
|
|
#include "user_session.h"
|
|
#include "../transport_ipc.h"
|
|
#include "../misc.h"
|
|
|
|
#define SHARE_HASH_BITS 3
|
|
static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
|
|
static DECLARE_RWSEM(shares_table_lock);
|
|
|
|
struct ksmbd_veto_pattern {
|
|
char *pattern;
|
|
struct list_head list;
|
|
};
|
|
|
|
static unsigned int share_name_hash(const char *name)
|
|
{
|
|
return jhash(name, strlen(name), 0);
|
|
}
|
|
|
|
static void kill_share(struct ksmbd_share_config *share)
|
|
{
|
|
while (!list_empty(&share->veto_list)) {
|
|
struct ksmbd_veto_pattern *p;
|
|
|
|
p = list_entry(share->veto_list.next,
|
|
struct ksmbd_veto_pattern,
|
|
list);
|
|
list_del(&p->list);
|
|
kfree(p->pattern);
|
|
kfree(p);
|
|
}
|
|
|
|
if (share->path)
|
|
path_put(&share->vfs_path);
|
|
kfree(share->name);
|
|
kfree(share->path);
|
|
kfree(share);
|
|
}
|
|
|
|
void ksmbd_share_config_del(struct ksmbd_share_config *share)
|
|
{
|
|
down_write(&shares_table_lock);
|
|
hash_del(&share->hlist);
|
|
up_write(&shares_table_lock);
|
|
}
|
|
|
|
void __ksmbd_share_config_put(struct ksmbd_share_config *share)
|
|
{
|
|
ksmbd_share_config_del(share);
|
|
kill_share(share);
|
|
}
|
|
|
|
static struct ksmbd_share_config *
|
|
__get_share_config(struct ksmbd_share_config *share)
|
|
{
|
|
if (!atomic_inc_not_zero(&share->refcount))
|
|
return NULL;
|
|
return share;
|
|
}
|
|
|
|
static struct ksmbd_share_config *__share_lookup(const char *name)
|
|
{
|
|
struct ksmbd_share_config *share;
|
|
unsigned int key = share_name_hash(name);
|
|
|
|
hash_for_each_possible(shares_table, share, hlist, key) {
|
|
if (!strcmp(name, share->name))
|
|
return share;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int parse_veto_list(struct ksmbd_share_config *share,
|
|
char *veto_list,
|
|
int veto_list_sz)
|
|
{
|
|
int sz = 0;
|
|
|
|
if (!veto_list_sz)
|
|
return 0;
|
|
|
|
while (veto_list_sz > 0) {
|
|
struct ksmbd_veto_pattern *p;
|
|
|
|
sz = strlen(veto_list);
|
|
if (!sz)
|
|
break;
|
|
|
|
p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
|
|
if (!p)
|
|
return -ENOMEM;
|
|
|
|
p->pattern = kstrdup(veto_list, GFP_KERNEL);
|
|
if (!p->pattern) {
|
|
kfree(p);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
list_add(&p->list, &share->veto_list);
|
|
|
|
veto_list += sz + 1;
|
|
veto_list_sz -= (sz + 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ksmbd_share_config *share_config_request(struct unicode_map *um,
|
|
const char *name)
|
|
{
|
|
struct ksmbd_share_config_response *resp;
|
|
struct ksmbd_share_config *share = NULL;
|
|
struct ksmbd_share_config *lookup;
|
|
int ret;
|
|
|
|
resp = ksmbd_ipc_share_config_request(name);
|
|
if (!resp)
|
|
return NULL;
|
|
|
|
if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
|
|
goto out;
|
|
|
|
if (*resp->share_name) {
|
|
char *cf_resp_name;
|
|
bool equal;
|
|
|
|
cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name);
|
|
if (IS_ERR(cf_resp_name))
|
|
goto out;
|
|
equal = !strcmp(cf_resp_name, name);
|
|
kfree(cf_resp_name);
|
|
if (!equal)
|
|
goto out;
|
|
}
|
|
|
|
share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
|
|
if (!share)
|
|
goto out;
|
|
|
|
share->flags = resp->flags;
|
|
atomic_set(&share->refcount, 1);
|
|
INIT_LIST_HEAD(&share->veto_list);
|
|
share->name = kstrdup(name, GFP_KERNEL);
|
|
|
|
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
|
|
int path_len = PATH_MAX;
|
|
|
|
if (resp->payload_sz)
|
|
path_len = resp->payload_sz - resp->veto_list_sz;
|
|
|
|
share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
|
|
GFP_KERNEL);
|
|
if (share->path) {
|
|
share->path_sz = strlen(share->path);
|
|
while (share->path_sz > 1 &&
|
|
share->path[share->path_sz - 1] == '/')
|
|
share->path[--share->path_sz] = '\0';
|
|
}
|
|
share->create_mask = resp->create_mask;
|
|
share->directory_mask = resp->directory_mask;
|
|
share->force_create_mode = resp->force_create_mode;
|
|
share->force_directory_mode = resp->force_directory_mode;
|
|
share->force_uid = resp->force_uid;
|
|
share->force_gid = resp->force_gid;
|
|
ret = parse_veto_list(share,
|
|
KSMBD_SHARE_CONFIG_VETO_LIST(resp),
|
|
resp->veto_list_sz);
|
|
if (!ret && share->path) {
|
|
ret = kern_path(share->path, 0, &share->vfs_path);
|
|
if (ret) {
|
|
ksmbd_debug(SMB, "failed to access '%s'\n",
|
|
share->path);
|
|
/* Avoid put_path() */
|
|
kfree(share->path);
|
|
share->path = NULL;
|
|
}
|
|
}
|
|
if (ret || !share->name) {
|
|
kill_share(share);
|
|
share = NULL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
down_write(&shares_table_lock);
|
|
lookup = __share_lookup(name);
|
|
if (lookup)
|
|
lookup = __get_share_config(lookup);
|
|
if (!lookup) {
|
|
hash_add(shares_table, &share->hlist, share_name_hash(name));
|
|
} else {
|
|
kill_share(share);
|
|
share = lookup;
|
|
}
|
|
up_write(&shares_table_lock);
|
|
|
|
out:
|
|
kvfree(resp);
|
|
return share;
|
|
}
|
|
|
|
struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um,
|
|
const char *name)
|
|
{
|
|
struct ksmbd_share_config *share;
|
|
|
|
down_read(&shares_table_lock);
|
|
share = __share_lookup(name);
|
|
if (share)
|
|
share = __get_share_config(share);
|
|
up_read(&shares_table_lock);
|
|
|
|
if (share)
|
|
return share;
|
|
return share_config_request(um, name);
|
|
}
|
|
|
|
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
|
|
const char *filename)
|
|
{
|
|
struct ksmbd_veto_pattern *p;
|
|
|
|
list_for_each_entry(p, &share->veto_list, list) {
|
|
if (match_wildcard(p->pattern, filename))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|