mirror of
https://github.com/torvalds/linux.git
synced 2024-12-07 03:21:32 +00:00
f5ba1cdaf5
Share config response may contain the share name without casefolding as it is known to the user space daemon. When it is present, casefold and compare it to the share name the share config request was made with. If they differ, we have a share config which is incompatible with the way share config caching is done. This is the case when CONFIG_UNICODE is not set, the share name contains non-ASCII characters, and those non- ASCII characters do not match those in the share name known to user space. In other words, when CONFIG_UNICODE is not set, UTF-8 share names now work but are only case-insensitive in the ASCII range. Signed-off-by: Atte Heikkilä <atteh.mailbox@gmail.com> Acked-by: Tom Talpey <tom@talpey.com> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
235 lines
5.0 KiB
C
235 lines
5.0 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)) {
|
|
share->path = kstrdup(ksmbd_share_config_path(resp),
|
|
GFP_KERNEL);
|
|
if (share->path)
|
|
share->path_sz = strlen(share->path);
|
|
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;
|
|
}
|