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 int path_len = PATH_MAX; 162 163 if (resp->payload_sz) 164 path_len = resp->payload_sz - resp->veto_list_sz; 165 166 share->path = kstrndup(ksmbd_share_config_path(resp), path_len, 167 GFP_KERNEL); 168 if (share->path) 169 share->path_sz = strlen(share->path); 170 share->create_mask = resp->create_mask; 171 share->directory_mask = resp->directory_mask; 172 share->force_create_mode = resp->force_create_mode; 173 share->force_directory_mode = resp->force_directory_mode; 174 share->force_uid = resp->force_uid; 175 share->force_gid = resp->force_gid; 176 ret = parse_veto_list(share, 177 KSMBD_SHARE_CONFIG_VETO_LIST(resp), 178 resp->veto_list_sz); 179 if (!ret && share->path) { 180 ret = kern_path(share->path, 0, &share->vfs_path); 181 if (ret) { 182 ksmbd_debug(SMB, "failed to access '%s'\n", 183 share->path); 184 /* Avoid put_path() */ 185 kfree(share->path); 186 share->path = NULL; 187 } 188 } 189 if (ret || !share->name) { 190 kill_share(share); 191 share = NULL; 192 goto out; 193 } 194 } 195 196 down_write(&shares_table_lock); 197 lookup = __share_lookup(name); 198 if (lookup) 199 lookup = __get_share_config(lookup); 200 if (!lookup) { 201 hash_add(shares_table, &share->hlist, share_name_hash(name)); 202 } else { 203 kill_share(share); 204 share = lookup; 205 } 206 up_write(&shares_table_lock); 207 208out: 209 kvfree(resp); 210 return share; 211} 212 213struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, 214 const char *name) 215{ 216 struct ksmbd_share_config *share; 217 218 down_read(&shares_table_lock); 219 share = __share_lookup(name); 220 if (share) 221 share = __get_share_config(share); 222 up_read(&shares_table_lock); 223 224 if (share) 225 return share; 226 return share_config_request(um, name); 227} 228 229bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, 230 const char *filename) 231{ 232 struct ksmbd_veto_pattern *p; 233 234 list_for_each_entry(p, &share->veto_list, list) { 235 if (match_wildcard(p->pattern, filename)) 236 return true; 237 } 238 return false; 239} 240