linux/fs/ksmbd/misc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
   4 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/xattr.h>
   9#include <linux/fs.h>
  10
  11#include "misc.h"
  12#include "smb_common.h"
  13#include "connection.h"
  14#include "vfs.h"
  15
  16#include "mgmt/share_config.h"
  17
  18/**
  19 * match_pattern() - compare a string with a pattern which might include
  20 * wildcard '*' and '?'
  21 * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR
  22 *
  23 * @string:     string to compare with a pattern
  24 * @len:        string length
  25 * @pattern:    pattern string which might include wildcard '*' and '?'
  26 *
  27 * Return:      0 if pattern matched with the string, otherwise non zero value
  28 */
  29int match_pattern(const char *str, size_t len, const char *pattern)
  30{
  31        const char *s = str;
  32        const char *p = pattern;
  33        bool star = false;
  34
  35        while (*s && len) {
  36                switch (*p) {
  37                case '?':
  38                        s++;
  39                        len--;
  40                        p++;
  41                        break;
  42                case '*':
  43                        star = true;
  44                        str = s;
  45                        if (!*++p)
  46                                return true;
  47                        pattern = p;
  48                        break;
  49                default:
  50                        if (tolower(*s) == tolower(*p)) {
  51                                s++;
  52                                len--;
  53                                p++;
  54                        } else {
  55                                if (!star)
  56                                        return false;
  57                                str++;
  58                                s = str;
  59                                p = pattern;
  60                        }
  61                        break;
  62                }
  63        }
  64
  65        if (*p == '*')
  66                ++p;
  67        return !*p;
  68}
  69
  70/*
  71 * is_char_allowed() - check for valid character
  72 * @ch:         input character to be checked
  73 *
  74 * Return:      1 if char is allowed, otherwise 0
  75 */
  76static inline int is_char_allowed(char ch)
  77{
  78        /* check for control chars, wildcards etc. */
  79        if (!(ch & 0x80) &&
  80            (ch <= 0x1f ||
  81             ch == '?' || ch == '"' || ch == '<' ||
  82             ch == '>' || ch == '|' || ch == '*'))
  83                return 0;
  84
  85        return 1;
  86}
  87
  88int ksmbd_validate_filename(char *filename)
  89{
  90        while (*filename) {
  91                char c = *filename;
  92
  93                filename++;
  94                if (!is_char_allowed(c)) {
  95                        ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);
  96                        return -ENOENT;
  97                }
  98        }
  99
 100        return 0;
 101}
 102
 103static int ksmbd_validate_stream_name(char *stream_name)
 104{
 105        while (*stream_name) {
 106                char c = *stream_name;
 107
 108                stream_name++;
 109                if (c == '/' || c == ':' || c == '\\') {
 110                        pr_err("Stream name validation failed: %c\n", c);
 111                        return -ENOENT;
 112                }
 113        }
 114
 115        return 0;
 116}
 117
 118int parse_stream_name(char *filename, char **stream_name, int *s_type)
 119{
 120        char *stream_type;
 121        char *s_name;
 122        int rc = 0;
 123
 124        s_name = filename;
 125        filename = strsep(&s_name, ":");
 126        ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);
 127        if (strchr(s_name, ':')) {
 128                stream_type = s_name;
 129                s_name = strsep(&stream_type, ":");
 130
 131                rc = ksmbd_validate_stream_name(s_name);
 132                if (rc < 0) {
 133                        rc = -ENOENT;
 134                        goto out;
 135                }
 136
 137                ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,
 138                            stream_type);
 139                if (!strncasecmp("$data", stream_type, 5))
 140                        *s_type = DATA_STREAM;
 141                else if (!strncasecmp("$index_allocation", stream_type, 17))
 142                        *s_type = DIR_STREAM;
 143                else
 144                        rc = -ENOENT;
 145        }
 146
 147        *stream_name = s_name;
 148out:
 149        return rc;
 150}
 151
 152/**
 153 * convert_to_nt_pathname() - extract and return windows path string
 154 *      whose share directory prefix was removed from file path
 155 * @filename : unix filename
 156 * @sharepath: share path string
 157 *
 158 * Return : windows path string or error
 159 */
 160
 161char *convert_to_nt_pathname(char *filename)
 162{
 163        char *ab_pathname;
 164
 165        if (strlen(filename) == 0)
 166                filename = "\\";
 167
 168        ab_pathname = kstrdup(filename, GFP_KERNEL);
 169        if (!ab_pathname)
 170                return NULL;
 171
 172        ksmbd_conv_path_to_windows(ab_pathname);
 173        return ab_pathname;
 174}
 175
 176int get_nlink(struct kstat *st)
 177{
 178        int nlink;
 179
 180        nlink = st->nlink;
 181        if (S_ISDIR(st->mode))
 182                nlink--;
 183
 184        return nlink;
 185}
 186
 187void ksmbd_conv_path_to_unix(char *path)
 188{
 189        strreplace(path, '\\', '/');
 190}
 191
 192void ksmbd_strip_last_slash(char *path)
 193{
 194        int len = strlen(path);
 195
 196        while (len && path[len - 1] == '/') {
 197                path[len - 1] = '\0';
 198                len--;
 199        }
 200}
 201
 202void ksmbd_conv_path_to_windows(char *path)
 203{
 204        strreplace(path, '/', '\\');
 205}
 206
 207/**
 208 * ksmbd_extract_sharename() - get share name from tree connect request
 209 * @treename:   buffer containing tree name and share name
 210 *
 211 * Return:      share name on success, otherwise error
 212 */
 213char *ksmbd_extract_sharename(char *treename)
 214{
 215        char *name = treename;
 216        char *dst;
 217        char *pos = strrchr(name, '\\');
 218
 219        if (pos)
 220                name = (pos + 1);
 221
 222        /* caller has to free the memory */
 223        dst = kstrdup(name, GFP_KERNEL);
 224        if (!dst)
 225                return ERR_PTR(-ENOMEM);
 226        return dst;
 227}
 228
 229/**
 230 * convert_to_unix_name() - convert windows name to unix format
 231 * @path:       name to be converted
 232 * @tid:        tree id of mathing share
 233 *
 234 * Return:      converted name on success, otherwise NULL
 235 */
 236char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
 237{
 238        int no_slash = 0, name_len, path_len;
 239        char *new_name;
 240
 241        if (name[0] == '/')
 242                name++;
 243
 244        path_len = share->path_sz;
 245        name_len = strlen(name);
 246        new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
 247        if (!new_name)
 248                return new_name;
 249
 250        memcpy(new_name, share->path, path_len);
 251        if (new_name[path_len - 1] != '/') {
 252                new_name[path_len] = '/';
 253                no_slash = 1;
 254        }
 255
 256        memcpy(new_name + path_len + no_slash, name, name_len);
 257        path_len += name_len + no_slash;
 258        new_name[path_len] = 0x00;
 259        return new_name;
 260}
 261
 262char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
 263                                  const struct nls_table *local_nls,
 264                                  int *conv_len)
 265{
 266        char *conv;
 267        int  sz = min(4 * d_info->name_len, PATH_MAX);
 268
 269        if (!sz)
 270                return NULL;
 271
 272        conv = kmalloc(sz, GFP_KERNEL);
 273        if (!conv)
 274                return NULL;
 275
 276        /* XXX */
 277        *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name,
 278                                      d_info->name_len, local_nls, 0);
 279        *conv_len *= 2;
 280
 281        /* We allocate buffer twice bigger than needed. */
 282        conv[*conv_len] = 0x00;
 283        conv[*conv_len + 1] = 0x00;
 284        return conv;
 285}
 286
 287/*
 288 * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
 289 * into Unix UTC (based 1970-01-01, in seconds).
 290 */
 291struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)
 292{
 293        struct timespec64 ts;
 294
 295        /* Subtract the NTFS time offset, then convert to 1s intervals. */
 296        s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
 297        u64 abs_t;
 298
 299        /*
 300         * Unfortunately can not use normal 64 bit division on 32 bit arch, but
 301         * the alternative, do_div, does not work with negative numbers so have
 302         * to special case them
 303         */
 304        if (t < 0) {
 305                abs_t = -t;
 306                ts.tv_nsec = do_div(abs_t, 10000000) * 100;
 307                ts.tv_nsec = -ts.tv_nsec;
 308                ts.tv_sec = -abs_t;
 309        } else {
 310                abs_t = t;
 311                ts.tv_nsec = do_div(abs_t, 10000000) * 100;
 312                ts.tv_sec = abs_t;
 313        }
 314
 315        return ts;
 316}
 317
 318/* Convert the Unix UTC into NT UTC. */
 319inline u64 ksmbd_UnixTimeToNT(struct timespec64 t)
 320{
 321        /* Convert to 100ns intervals and then add the NTFS time offset. */
 322        return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET;
 323}
 324
 325inline long long ksmbd_systime(void)
 326{
 327        struct timespec64       ts;
 328
 329        ktime_get_real_ts64(&ts);
 330        return ksmbd_UnixTimeToNT(ts);
 331}
 332