Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
0004  *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
0005  */
0006 
0007 #include <linux/kernel.h>
0008 #include <linux/xattr.h>
0009 #include <linux/fs.h>
0010 
0011 #include "misc.h"
0012 #include "smb_common.h"
0013 #include "connection.h"
0014 #include "vfs.h"
0015 
0016 #include "mgmt/share_config.h"
0017 
0018 /**
0019  * match_pattern() - compare a string with a pattern which might include
0020  * wildcard '*' and '?'
0021  * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR
0022  *
0023  * @str:    string to compare with a pattern
0024  * @len:    string length
0025  * @pattern:    pattern string which might include wildcard '*' and '?'
0026  *
0027  * Return:  0 if pattern matched with the string, otherwise non zero value
0028  */
0029 int match_pattern(const char *str, size_t len, const char *pattern)
0030 {
0031     const char *s = str;
0032     const char *p = pattern;
0033     bool star = false;
0034 
0035     while (*s && len) {
0036         switch (*p) {
0037         case '?':
0038             s++;
0039             len--;
0040             p++;
0041             break;
0042         case '*':
0043             star = true;
0044             str = s;
0045             if (!*++p)
0046                 return true;
0047             pattern = p;
0048             break;
0049         default:
0050             if (tolower(*s) == tolower(*p)) {
0051                 s++;
0052                 len--;
0053                 p++;
0054             } else {
0055                 if (!star)
0056                     return false;
0057                 str++;
0058                 s = str;
0059                 p = pattern;
0060             }
0061             break;
0062         }
0063     }
0064 
0065     if (*p == '*')
0066         ++p;
0067     return !*p;
0068 }
0069 
0070 /*
0071  * is_char_allowed() - check for valid character
0072  * @ch:     input character to be checked
0073  *
0074  * Return:  1 if char is allowed, otherwise 0
0075  */
0076 static inline int is_char_allowed(char ch)
0077 {
0078     /* check for control chars, wildcards etc. */
0079     if (!(ch & 0x80) &&
0080         (ch <= 0x1f ||
0081          ch == '?' || ch == '"' || ch == '<' ||
0082          ch == '>' || ch == '|' || ch == '*'))
0083         return 0;
0084 
0085     return 1;
0086 }
0087 
0088 int ksmbd_validate_filename(char *filename)
0089 {
0090     while (*filename) {
0091         char c = *filename;
0092 
0093         filename++;
0094         if (!is_char_allowed(c)) {
0095             ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);
0096             return -ENOENT;
0097         }
0098     }
0099 
0100     return 0;
0101 }
0102 
0103 static int ksmbd_validate_stream_name(char *stream_name)
0104 {
0105     while (*stream_name) {
0106         char c = *stream_name;
0107 
0108         stream_name++;
0109         if (c == '/' || c == ':' || c == '\\') {
0110             pr_err("Stream name validation failed: %c\n", c);
0111             return -ENOENT;
0112         }
0113     }
0114 
0115     return 0;
0116 }
0117 
0118 int parse_stream_name(char *filename, char **stream_name, int *s_type)
0119 {
0120     char *stream_type;
0121     char *s_name;
0122     int rc = 0;
0123 
0124     s_name = filename;
0125     filename = strsep(&s_name, ":");
0126     ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);
0127     if (strchr(s_name, ':')) {
0128         stream_type = s_name;
0129         s_name = strsep(&stream_type, ":");
0130 
0131         rc = ksmbd_validate_stream_name(s_name);
0132         if (rc < 0) {
0133             rc = -ENOENT;
0134             goto out;
0135         }
0136 
0137         ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,
0138                 stream_type);
0139         if (!strncasecmp("$data", stream_type, 5))
0140             *s_type = DATA_STREAM;
0141         else if (!strncasecmp("$index_allocation", stream_type, 17))
0142             *s_type = DIR_STREAM;
0143         else
0144             rc = -ENOENT;
0145     }
0146 
0147     *stream_name = s_name;
0148 out:
0149     return rc;
0150 }
0151 
0152 /**
0153  * convert_to_nt_pathname() - extract and return windows path string
0154  *      whose share directory prefix was removed from file path
0155  * @share: ksmbd_share_config pointer
0156  * @path: path to report
0157  *
0158  * Return : windows path string or error
0159  */
0160 
0161 char *convert_to_nt_pathname(struct ksmbd_share_config *share,
0162                  struct path *path)
0163 {
0164     char *pathname, *ab_pathname, *nt_pathname;
0165     int share_path_len = share->path_sz;
0166 
0167     pathname = kmalloc(PATH_MAX, GFP_KERNEL);
0168     if (!pathname)
0169         return ERR_PTR(-EACCES);
0170 
0171     ab_pathname = d_path(path, pathname, PATH_MAX);
0172     if (IS_ERR(ab_pathname)) {
0173         nt_pathname = ERR_PTR(-EACCES);
0174         goto free_pathname;
0175     }
0176 
0177     if (strncmp(ab_pathname, share->path, share_path_len)) {
0178         nt_pathname = ERR_PTR(-EACCES);
0179         goto free_pathname;
0180     }
0181 
0182     nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL);
0183     if (!nt_pathname) {
0184         nt_pathname = ERR_PTR(-ENOMEM);
0185         goto free_pathname;
0186     }
0187     if (ab_pathname[share_path_len] == '\0')
0188         strcpy(nt_pathname, "/");
0189     strcat(nt_pathname, &ab_pathname[share_path_len]);
0190 
0191     ksmbd_conv_path_to_windows(nt_pathname);
0192 
0193 free_pathname:
0194     kfree(pathname);
0195     return nt_pathname;
0196 }
0197 
0198 int get_nlink(struct kstat *st)
0199 {
0200     int nlink;
0201 
0202     nlink = st->nlink;
0203     if (S_ISDIR(st->mode))
0204         nlink--;
0205 
0206     return nlink;
0207 }
0208 
0209 void ksmbd_conv_path_to_unix(char *path)
0210 {
0211     strreplace(path, '\\', '/');
0212 }
0213 
0214 void ksmbd_strip_last_slash(char *path)
0215 {
0216     int len = strlen(path);
0217 
0218     while (len && path[len - 1] == '/') {
0219         path[len - 1] = '\0';
0220         len--;
0221     }
0222 }
0223 
0224 void ksmbd_conv_path_to_windows(char *path)
0225 {
0226     strreplace(path, '/', '\\');
0227 }
0228 
0229 /**
0230  * ksmbd_extract_sharename() - get share name from tree connect request
0231  * @treename:   buffer containing tree name and share name
0232  *
0233  * Return:      share name on success, otherwise error
0234  */
0235 char *ksmbd_extract_sharename(char *treename)
0236 {
0237     char *name = treename;
0238     char *dst;
0239     char *pos = strrchr(name, '\\');
0240 
0241     if (pos)
0242         name = (pos + 1);
0243 
0244     /* caller has to free the memory */
0245     dst = kstrdup(name, GFP_KERNEL);
0246     if (!dst)
0247         return ERR_PTR(-ENOMEM);
0248     return dst;
0249 }
0250 
0251 /**
0252  * convert_to_unix_name() - convert windows name to unix format
0253  * @share:  ksmbd_share_config pointer
0254  * @name:   file name that is relative to share
0255  *
0256  * Return:  converted name on success, otherwise NULL
0257  */
0258 char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
0259 {
0260     int no_slash = 0, name_len, path_len;
0261     char *new_name;
0262 
0263     if (name[0] == '/')
0264         name++;
0265 
0266     path_len = share->path_sz;
0267     name_len = strlen(name);
0268     new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
0269     if (!new_name)
0270         return new_name;
0271 
0272     memcpy(new_name, share->path, path_len);
0273     if (new_name[path_len - 1] != '/') {
0274         new_name[path_len] = '/';
0275         no_slash = 1;
0276     }
0277 
0278     memcpy(new_name + path_len + no_slash, name, name_len);
0279     path_len += name_len + no_slash;
0280     new_name[path_len] = 0x00;
0281     return new_name;
0282 }
0283 
0284 char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
0285                   const struct nls_table *local_nls,
0286                   int *conv_len)
0287 {
0288     char *conv;
0289     int  sz = min(4 * d_info->name_len, PATH_MAX);
0290 
0291     if (!sz)
0292         return NULL;
0293 
0294     conv = kmalloc(sz, GFP_KERNEL);
0295     if (!conv)
0296         return NULL;
0297 
0298     /* XXX */
0299     *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name,
0300                       d_info->name_len, local_nls, 0);
0301     *conv_len *= 2;
0302 
0303     /* We allocate buffer twice bigger than needed. */
0304     conv[*conv_len] = 0x00;
0305     conv[*conv_len + 1] = 0x00;
0306     return conv;
0307 }
0308 
0309 /*
0310  * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
0311  * into Unix UTC (based 1970-01-01, in seconds).
0312  */
0313 struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)
0314 {
0315     struct timespec64 ts;
0316 
0317     /* Subtract the NTFS time offset, then convert to 1s intervals. */
0318     s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
0319     u64 abs_t;
0320 
0321     /*
0322      * Unfortunately can not use normal 64 bit division on 32 bit arch, but
0323      * the alternative, do_div, does not work with negative numbers so have
0324      * to special case them
0325      */
0326     if (t < 0) {
0327         abs_t = -t;
0328         ts.tv_nsec = do_div(abs_t, 10000000) * 100;
0329         ts.tv_nsec = -ts.tv_nsec;
0330         ts.tv_sec = -abs_t;
0331     } else {
0332         abs_t = t;
0333         ts.tv_nsec = do_div(abs_t, 10000000) * 100;
0334         ts.tv_sec = abs_t;
0335     }
0336 
0337     return ts;
0338 }
0339 
0340 /* Convert the Unix UTC into NT UTC. */
0341 inline u64 ksmbd_UnixTimeToNT(struct timespec64 t)
0342 {
0343     /* Convert to 100ns intervals and then add the NTFS time offset. */
0344     return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET;
0345 }
0346 
0347 inline long long ksmbd_systime(void)
0348 {
0349     struct timespec64   ts;
0350 
0351     ktime_get_real_ts64(&ts);
0352     return ksmbd_UnixTimeToNT(ts);
0353 }