Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.15-rc6 331 lines 6.8 kB view raw
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}