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 22 static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); 23 static DECLARE_RWSEM(shares_table_lock); 24 25 struct ksmbd_veto_pattern { 26 char *pattern; 27 struct list_head list; 28 }; 29 30 static unsigned int share_name_hash(const char *name) 31 { 32 return jhash(name, strlen(name), 0); 33 } 34 35 static 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 55 void 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 62 void __ksmbd_share_config_put(struct ksmbd_share_config *share) 63 { 64 ksmbd_share_config_del(share); 65 kill_share(share); 66 } 67 68 static 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 76 static 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 88 static 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 123 static 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 203 out: 204 kvfree(resp); 205 return share; 206 } 207 208 struct 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 224 bool 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 } 235