linux/fs/ksmbd/mgmt/share_config.c
<<
>>
Prefs
   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}
 239