brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · 1a5faa6 Raw
383 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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#include <linux/unicode.h>11 12#include "misc.h"13#include "smb_common.h"14#include "connection.h"15#include "vfs.h"16 17#include "mgmt/share_config.h"18 19/**20 * match_pattern() - compare a string with a pattern which might include21 * wildcard '*' and '?'22 * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR23 *24 * @str:	string to compare with a pattern25 * @len:	string length26 * @pattern:	pattern string which might include wildcard '*' and '?'27 *28 * Return:	0 if pattern matched with the string, otherwise non zero value29 */30int match_pattern(const char *str, size_t len, const char *pattern)31{32	const char *s = str;33	const char *p = pattern;34	bool star = false;35 36	while (*s && len) {37		switch (*p) {38		case '?':39			s++;40			len--;41			p++;42			break;43		case '*':44			star = true;45			str = s;46			if (!*++p)47				return true;48			pattern = p;49			break;50		default:51			if (tolower(*s) == tolower(*p)) {52				s++;53				len--;54				p++;55			} else {56				if (!star)57					return false;58				str++;59				s = str;60				p = pattern;61			}62			break;63		}64	}65 66	if (*p == '*')67		++p;68	return !*p;69}70 71/*72 * is_char_allowed() - check for valid character73 * @ch:		input character to be checked74 *75 * Return:	1 if char is allowed, otherwise 076 */77static inline int is_char_allowed(char ch)78{79	/* check for control chars, wildcards etc. */80	if (!(ch & 0x80) &&81	    (ch <= 0x1f ||82	     ch == '?' || ch == '"' || ch == '<' ||83	     ch == '>' || ch == '|' || ch == '*'))84		return 0;85 86	return 1;87}88 89int ksmbd_validate_filename(char *filename)90{91	while (*filename) {92		char c = *filename;93 94		filename++;95		if (!is_char_allowed(c)) {96			ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);97			return -ENOENT;98		}99	}100 101	return 0;102}103 104static int ksmbd_validate_stream_name(char *stream_name)105{106	while (*stream_name) {107		char c = *stream_name;108 109		stream_name++;110		if (c == '/' || c == ':' || c == '\\') {111			pr_err("Stream name validation failed: %c\n", c);112			return -ENOENT;113		}114	}115 116	return 0;117}118 119int parse_stream_name(char *filename, char **stream_name, int *s_type)120{121	char *stream_type;122	char *s_name;123	int rc = 0;124 125	s_name = filename;126	filename = strsep(&s_name, ":");127	ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);128	if (strchr(s_name, ':')) {129		stream_type = s_name;130		s_name = strsep(&stream_type, ":");131 132		rc = ksmbd_validate_stream_name(s_name);133		if (rc < 0) {134			rc = -ENOENT;135			goto out;136		}137 138		ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,139			    stream_type);140		if (!strncasecmp("$data", stream_type, 5))141			*s_type = DATA_STREAM;142		else if (!strncasecmp("$index_allocation", stream_type, 17))143			*s_type = DIR_STREAM;144		else145			rc = -ENOENT;146	}147 148	*stream_name = s_name;149out:150	return rc;151}152 153/**154 * convert_to_nt_pathname() - extract and return windows path string155 *      whose share directory prefix was removed from file path156 * @share: ksmbd_share_config pointer157 * @path: path to report158 *159 * Return : windows path string or error160 */161 162char *convert_to_nt_pathname(struct ksmbd_share_config *share,163			     const struct path *path)164{165	char *pathname, *ab_pathname, *nt_pathname;166	int share_path_len = share->path_sz;167 168	pathname = kmalloc(PATH_MAX, GFP_KERNEL);169	if (!pathname)170		return ERR_PTR(-EACCES);171 172	ab_pathname = d_path(path, pathname, PATH_MAX);173	if (IS_ERR(ab_pathname)) {174		nt_pathname = ERR_PTR(-EACCES);175		goto free_pathname;176	}177 178	if (strncmp(ab_pathname, share->path, share_path_len)) {179		nt_pathname = ERR_PTR(-EACCES);180		goto free_pathname;181	}182 183	nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL);184	if (!nt_pathname) {185		nt_pathname = ERR_PTR(-ENOMEM);186		goto free_pathname;187	}188	if (ab_pathname[share_path_len] == '\0')189		strcpy(nt_pathname, "/");190	strcat(nt_pathname, &ab_pathname[share_path_len]);191 192	ksmbd_conv_path_to_windows(nt_pathname);193 194free_pathname:195	kfree(pathname);196	return nt_pathname;197}198 199int get_nlink(struct kstat *st)200{201	int nlink;202 203	nlink = st->nlink;204	if (S_ISDIR(st->mode))205		nlink--;206 207	return nlink;208}209 210void ksmbd_conv_path_to_unix(char *path)211{212	strreplace(path, '\\', '/');213}214 215void ksmbd_strip_last_slash(char *path)216{217	int len = strlen(path);218 219	while (len && path[len - 1] == '/') {220		path[len - 1] = '\0';221		len--;222	}223}224 225void ksmbd_conv_path_to_windows(char *path)226{227	strreplace(path, '/', '\\');228}229 230char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name)231{232	char *cf_name;233	int cf_len;234 235	cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL);236	if (!cf_name)237		return ERR_PTR(-ENOMEM);238 239	if (IS_ENABLED(CONFIG_UNICODE) && um) {240		const struct qstr q_name = {.name = name, .len = strlen(name)};241 242		cf_len = utf8_casefold(um, &q_name, cf_name,243				       KSMBD_REQ_MAX_SHARE_NAME);244		if (cf_len < 0)245			goto out_ascii;246 247		return cf_name;248	}249 250out_ascii:251	cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME);252	if (cf_len < 0) {253		kfree(cf_name);254		return ERR_PTR(-E2BIG);255	}256 257	for (; *cf_name; ++cf_name)258		*cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name;259	return cf_name - cf_len;260}261 262/**263 * ksmbd_extract_sharename() - get share name from tree connect request264 * @um: pointer to a unicode_map structure for character encoding handling265 * @treename:	buffer containing tree name and share name266 *267 * Return:      share name on success, otherwise error268 */269char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename)270{271	const char *name = treename, *pos = strrchr(name, '\\');272 273	if (pos)274		name = (pos + 1);275 276	/* caller has to free the memory */277	return ksmbd_casefold_sharename(um, name);278}279 280/**281 * convert_to_unix_name() - convert windows name to unix format282 * @share:	ksmbd_share_config pointer283 * @name:	file name that is relative to share284 *285 * Return:	converted name on success, otherwise NULL286 */287char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)288{289	int no_slash = 0, name_len, path_len;290	char *new_name;291 292	if (name[0] == '/')293		name++;294 295	path_len = share->path_sz;296	name_len = strlen(name);297	new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);298	if (!new_name)299		return new_name;300 301	memcpy(new_name, share->path, path_len);302	if (new_name[path_len - 1] != '/') {303		new_name[path_len] = '/';304		no_slash = 1;305	}306 307	memcpy(new_name + path_len + no_slash, name, name_len);308	path_len += name_len + no_slash;309	new_name[path_len] = 0x00;310	return new_name;311}312 313char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,314				  const struct nls_table *local_nls,315				  int *conv_len)316{317	char *conv;318	int  sz = min(4 * d_info->name_len, PATH_MAX);319 320	if (!sz)321		return NULL;322 323	conv = kmalloc(sz, GFP_KERNEL);324	if (!conv)325		return NULL;326 327	/* XXX */328	*conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name,329				      d_info->name_len, local_nls, 0);330	*conv_len *= 2;331 332	/* We allocate buffer twice bigger than needed. */333	conv[*conv_len] = 0x00;334	conv[*conv_len + 1] = 0x00;335	return conv;336}337 338/*339 * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)340 * into Unix UTC (based 1970-01-01, in seconds).341 */342struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)343{344	struct timespec64 ts;345 346	/* Subtract the NTFS time offset, then convert to 1s intervals. */347	s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;348	u64 abs_t;349 350	/*351	 * Unfortunately can not use normal 64 bit division on 32 bit arch, but352	 * the alternative, do_div, does not work with negative numbers so have353	 * to special case them354	 */355	if (t < 0) {356		abs_t = -t;357		ts.tv_nsec = do_div(abs_t, 10000000) * 100;358		ts.tv_nsec = -ts.tv_nsec;359		ts.tv_sec = -abs_t;360	} else {361		abs_t = t;362		ts.tv_nsec = do_div(abs_t, 10000000) * 100;363		ts.tv_sec = abs_t;364	}365 366	return ts;367}368 369/* Convert the Unix UTC into NT UTC. */370inline u64 ksmbd_UnixTimeToNT(struct timespec64 t)371{372	/* Convert to 100ns intervals and then add the NTFS time offset. */373	return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET;374}375 376inline long long ksmbd_systime(void)377{378	struct timespec64	ts;379 380	ktime_get_real_ts64(&ts);381	return ksmbd_UnixTimeToNT(ts);382}383