share_config.c (5110B)
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 20#define SHARE_HASH_BITS 3 21static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); 22static DECLARE_RWSEM(shares_table_lock); 23 24struct ksmbd_veto_pattern { 25 char *pattern; 26 struct list_head list; 27}; 28 29static unsigned int share_name_hash(char *name) 30{ 31 return jhash(name, strlen(name), 0); 32} 33 34static void kill_share(struct ksmbd_share_config *share) 35{ 36 while (!list_empty(&share->veto_list)) { 37 struct ksmbd_veto_pattern *p; 38 39 p = list_entry(share->veto_list.next, 40 struct ksmbd_veto_pattern, 41 list); 42 list_del(&p->list); 43 kfree(p->pattern); 44 kfree(p); 45 } 46 47 if (share->path) 48 path_put(&share->vfs_path); 49 kfree(share->name); 50 kfree(share->path); 51 kfree(share); 52} 53 54void __ksmbd_share_config_put(struct ksmbd_share_config *share) 55{ 56 down_write(&shares_table_lock); 57 hash_del(&share->hlist); 58 up_write(&shares_table_lock); 59 60 kill_share(share); 61} 62 63static struct ksmbd_share_config * 64__get_share_config(struct ksmbd_share_config *share) 65{ 66 if (!atomic_inc_not_zero(&share->refcount)) 67 return NULL; 68 return share; 69} 70 71static struct ksmbd_share_config *__share_lookup(char *name) 72{ 73 struct ksmbd_share_config *share; 74 unsigned int key = share_name_hash(name); 75 76 hash_for_each_possible(shares_table, share, hlist, key) { 77 if (!strcmp(name, share->name)) 78 return share; 79 } 80 return NULL; 81} 82 83static int parse_veto_list(struct ksmbd_share_config *share, 84 char *veto_list, 85 int veto_list_sz) 86{ 87 int sz = 0; 88 89 if (!veto_list_sz) 90 return 0; 91 92 while (veto_list_sz > 0) { 93 struct ksmbd_veto_pattern *p; 94 95 sz = strlen(veto_list); 96 if (!sz) 97 break; 98 99 p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); 100 if (!p) 101 return -ENOMEM; 102 103 p->pattern = kstrdup(veto_list, GFP_KERNEL); 104 if (!p->pattern) { 105 kfree(p); 106 return -ENOMEM; 107 } 108 109 list_add(&p->list, &share->veto_list); 110 111 veto_list += sz + 1; 112 veto_list_sz -= (sz + 1); 113 } 114 115 return 0; 116} 117 118static struct ksmbd_share_config *share_config_request(char *name) 119{ 120 struct ksmbd_share_config_response *resp; 121 struct ksmbd_share_config *share = NULL; 122 struct ksmbd_share_config *lookup; 123 int ret; 124 125 resp = ksmbd_ipc_share_config_request(name); 126 if (!resp) 127 return NULL; 128 129 if (resp->flags == KSMBD_SHARE_FLAG_INVALID) 130 goto out; 131 132 share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); 133 if (!share) 134 goto out; 135 136 share->flags = resp->flags; 137 atomic_set(&share->refcount, 1); 138 INIT_LIST_HEAD(&share->veto_list); 139 share->name = kstrdup(name, GFP_KERNEL); 140 141 if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { 142 share->path = kstrdup(ksmbd_share_config_path(resp), 143 GFP_KERNEL); 144 if (share->path) 145 share->path_sz = strlen(share->path); 146 share->create_mask = resp->create_mask; 147 share->directory_mask = resp->directory_mask; 148 share->force_create_mode = resp->force_create_mode; 149 share->force_directory_mode = resp->force_directory_mode; 150 share->force_uid = resp->force_uid; 151 share->force_gid = resp->force_gid; 152 ret = parse_veto_list(share, 153 KSMBD_SHARE_CONFIG_VETO_LIST(resp), 154 resp->veto_list_sz); 155 if (!ret && share->path) { 156 ret = kern_path(share->path, 0, &share->vfs_path); 157 if (ret) { 158 ksmbd_debug(SMB, "failed to access '%s'\n", 159 share->path); 160 /* Avoid put_path() */ 161 kfree(share->path); 162 share->path = NULL; 163 } 164 } 165 if (ret || !share->name) { 166 kill_share(share); 167 share = NULL; 168 goto out; 169 } 170 } 171 172 down_write(&shares_table_lock); 173 lookup = __share_lookup(name); 174 if (lookup) 175 lookup = __get_share_config(lookup); 176 if (!lookup) { 177 hash_add(shares_table, &share->hlist, share_name_hash(name)); 178 } else { 179 kill_share(share); 180 share = lookup; 181 } 182 up_write(&shares_table_lock); 183 184out: 185 kvfree(resp); 186 return share; 187} 188 189static void strtolower(char *share_name) 190{ 191 while (*share_name) { 192 *share_name = tolower(*share_name); 193 share_name++; 194 } 195} 196 197struct ksmbd_share_config *ksmbd_share_config_get(char *name) 198{ 199 struct ksmbd_share_config *share; 200 201 strtolower(name); 202 203 down_read(&shares_table_lock); 204 share = __share_lookup(name); 205 if (share) 206 share = __get_share_config(share); 207 up_read(&shares_table_lock); 208 209 if (share) 210 return share; 211 return share_config_request(name); 212} 213 214bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, 215 const char *filename) 216{ 217 struct ksmbd_veto_pattern *p; 218 219 list_for_each_entry(p, &share->veto_list, list) { 220 if (match_wildcard(p->pattern, filename)) 221 return true; 222 } 223 return false; 224} 225 226void ksmbd_share_configs_cleanup(void) 227{ 228 struct ksmbd_share_config *share; 229 struct hlist_node *tmp; 230 int i; 231 232 down_write(&shares_table_lock); 233 hash_for_each_safe(shares_table, i, tmp, share, hlist) { 234 hash_del(&share->hlist); 235 kill_share(share); 236 } 237 up_write(&shares_table_lock); 238}