// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018 Samsung Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include "share_config.h" #include "user_config.h" #include "user_session.h" #include "../transport_ipc.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(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_put(struct ksmbd_share_config *share) { down_write(&shares_table_lock); hash_del(&share->hlist); up_write(&shares_table_lock); 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(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(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; 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; } static void strtolower(char *share_name) { while (*share_name) { *share_name = tolower(*share_name); share_name++; } } struct ksmbd_share_config *ksmbd_share_config_get(char *name) { struct ksmbd_share_config *share; strtolower(name); 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(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; } void ksmbd_share_configs_cleanup(void) { struct ksmbd_share_config *share; struct hlist_node *tmp; int i; down_write(&shares_table_lock); hash_for_each_safe(shares_table, i, tmp, share, hlist) { hash_del(&share->hlist); kill_share(share); } up_write(&shares_table_lock); }