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