brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · 4cc6e08 Raw
643 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *4 *   Copyright (c) International Business Machines  Corp., 2000,20095 *   Modified by Steve French (sfrench@us.ibm.com)6 */7#include <linux/fs.h>8#include <linux/slab.h>9#include "cifs_fs_sb.h"10#include "cifs_unicode.h"11#include "cifspdu.h"12#include "cifsglob.h"13#include "cifs_debug.h"14 15int cifs_remap(struct cifs_sb_info *cifs_sb)16{17	int map_type;18 19	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)20		map_type = SFM_MAP_UNI_RSVD;21	else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)22		map_type = SFU_MAP_UNI_RSVD;23	else24		map_type = NO_MAP_UNI_RSVD;25 26	return map_type;27}28 29/* Convert character using the SFU - "Services for Unix" remapping range */30static bool31convert_sfu_char(const __u16 src_char, char *target)32{33	/*34	 * BB: Cannot handle remapping UNI_SLASH until all the calls to35	 *     build_path_from_dentry are modified, as they use slash as36	 *     separator.37	 */38	switch (src_char) {39	case UNI_COLON:40		*target = ':';41		break;42	case UNI_ASTERISK:43		*target = '*';44		break;45	case UNI_QUESTION:46		*target = '?';47		break;48	case UNI_PIPE:49		*target = '|';50		break;51	case UNI_GRTRTHAN:52		*target = '>';53		break;54	case UNI_LESSTHAN:55		*target = '<';56		break;57	default:58		return false;59	}60	return true;61}62 63/* Convert character using the SFM - "Services for Mac" remapping range */64static bool65convert_sfm_char(const __u16 src_char, char *target)66{67	if (src_char >= 0xF001 && src_char <= 0xF01F) {68		*target = src_char - 0xF000;69		return true;70	}71	switch (src_char) {72	case SFM_COLON:73		*target = ':';74		break;75	case SFM_DOUBLEQUOTE:76		*target = '"';77		break;78	case SFM_ASTERISK:79		*target = '*';80		break;81	case SFM_QUESTION:82		*target = '?';83		break;84	case SFM_PIPE:85		*target = '|';86		break;87	case SFM_GRTRTHAN:88		*target = '>';89		break;90	case SFM_LESSTHAN:91		*target = '<';92		break;93	case SFM_SPACE:94		*target = ' ';95		break;96	case SFM_PERIOD:97		*target = '.';98		break;99	default:100		return false;101	}102	return true;103}104 105 106/*107 * cifs_mapchar - convert a host-endian char to proper char in codepage108 * @target - where converted character should be copied109 * @src_char - 2 byte host-endian source character110 * @cp - codepage to which character should be converted111 * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2?112 *113 * This function handles the conversion of a single character. It is the114 * responsibility of the caller to ensure that the target buffer is large115 * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).116 */117static int118cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp,119	     int maptype)120{121	int len = 1;122	__u16 src_char;123 124	src_char = *from;125 126	if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target))127		return len;128	else if ((maptype == SFU_MAP_UNI_RSVD) &&129		  convert_sfu_char(src_char, target))130		return len;131 132	/* if character not one of seven in special remap set */133	len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);134	if (len <= 0)135		goto surrogate_pair;136 137	return len;138 139surrogate_pair:140	/* convert SURROGATE_PAIR and IVS */141	if (strcmp(cp->charset, "utf8"))142		goto unknown;143	len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6);144	if (len <= 0)145		goto unknown;146	return len;147 148unknown:149	*target = '?';150	len = 1;151	return len;152}153 154/*155 * cifs_from_utf16 - convert utf16le string to local charset156 * @to - destination buffer157 * @from - source buffer158 * @tolen - destination buffer size (in bytes)159 * @fromlen - source buffer size (in bytes)160 * @codepage - codepage to which characters should be converted161 * @mapchar - should characters be remapped according to the mapchars option?162 *163 * Convert a little-endian utf16le string (as sent by the server) to a string164 * in the provided codepage. The tolen and fromlen parameters are to ensure165 * that the code doesn't walk off of the end of the buffer (which is always166 * a danger if the alignment of the source buffer is off). The destination167 * string is always properly null terminated and fits in the destination168 * buffer. Returns the length of the destination string in bytes (including169 * null terminator).170 *171 * Note that some windows versions actually send multiword UTF-16 characters172 * instead of straight UTF16-2. The linux nls routines however aren't able to173 * deal with those characters properly. In the event that we get some of174 * those characters, they won't be translated properly.175 */176int177cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,178		const struct nls_table *codepage, int map_type)179{180	int i, charlen, safelen;181	int outlen = 0;182	int nullsize = nls_nullsize(codepage);183	int fromwords = fromlen / 2;184	char tmp[NLS_MAX_CHARSET_SIZE];185	__u16 ftmp[3];		/* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */186 187	/*188	 * because the chars can be of varying widths, we need to take care189	 * not to overflow the destination buffer when we get close to the190	 * end of it. Until we get to this offset, we don't need to check191	 * for overflow however.192	 */193	safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);194 195	for (i = 0; i < fromwords; i++) {196		ftmp[0] = get_unaligned_le16(&from[i]);197		if (ftmp[0] == 0)198			break;199		if (i + 1 < fromwords)200			ftmp[1] = get_unaligned_le16(&from[i + 1]);201		else202			ftmp[1] = 0;203		if (i + 2 < fromwords)204			ftmp[2] = get_unaligned_le16(&from[i + 2]);205		else206			ftmp[2] = 0;207 208		/*209		 * check to see if converting this character might make the210		 * conversion bleed into the null terminator211		 */212		if (outlen >= safelen) {213			charlen = cifs_mapchar(tmp, ftmp, codepage, map_type);214			if ((outlen + charlen) > (tolen - nullsize))215				break;216		}217 218		/* put converted char into 'to' buffer */219		charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type);220		outlen += charlen;221 222		/* charlen (=bytes of UTF-8 for 1 character)223		 * 4bytes UTF-8(surrogate pair) is charlen=4224		 *   (4bytes UTF-16 code)225		 * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4226		 *   (2 UTF-8 pairs divided to 2 UTF-16 pairs) */227		if (charlen == 4)228			i++;229		else if (charlen >= 5)230			/* 5-6bytes UTF-8 */231			i += 2;232	}233 234	/* properly null-terminate string */235	for (i = 0; i < nullsize; i++)236		to[outlen++] = 0;237 238	return outlen;239}240 241/*242 * NAME:	cifs_strtoUTF16()243 *244 * FUNCTION:	Convert character string to unicode string245 *246 */247int248cifs_strtoUTF16(__le16 *to, const char *from, int len,249	      const struct nls_table *codepage)250{251	int charlen;252	int i;253	wchar_t wchar_to; /* needed to quiet sparse */254 255	/* special case for utf8 to handle no plane0 chars */256	if (!strcmp(codepage->charset, "utf8")) {257		/*258		 * convert utf8 -> utf16, we assume we have enough space259		 * as caller should have assumed conversion does not overflow260		 * in destination len is length in wchar_t units (16bits)261		 */262		i  = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN,263				       (wchar_t *) to, len);264 265		/* if success terminate and exit */266		if (i >= 0)267			goto success;268		/*269		 * if fails fall back to UCS encoding as this270		 * function should not return negative values271		 * currently can fail only if source contains272		 * invalid encoded characters273		 */274	}275 276	for (i = 0; len && *from; i++, from += charlen, len -= charlen) {277		charlen = codepage->char2uni(from, len, &wchar_to);278		if (charlen < 1) {279			cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n",280				 *from, charlen);281			/* A question mark */282			wchar_to = 0x003f;283			charlen = 1;284		}285		put_unaligned_le16(wchar_to, &to[i]);286	}287 288success:289	put_unaligned_le16(0, &to[i]);290	return i;291}292 293/*294 * cifs_utf16_bytes - how long will a string be after conversion?295 * @utf16 - pointer to input string296 * @maxbytes - don't go past this many bytes of input string297 * @codepage - destination codepage298 *299 * Walk a utf16le string and return the number of bytes that the string will300 * be after being converted to the given charset, not including any null301 * termination required. Don't walk past maxbytes in the source buffer.302 */303int304cifs_utf16_bytes(const __le16 *from, int maxbytes,305		const struct nls_table *codepage)306{307	int i;308	int charlen, outlen = 0;309	int maxwords = maxbytes / 2;310	char tmp[NLS_MAX_CHARSET_SIZE];311	__u16 ftmp[3];312 313	for (i = 0; i < maxwords; i++) {314		ftmp[0] = get_unaligned_le16(&from[i]);315		if (ftmp[0] == 0)316			break;317		if (i + 1 < maxwords)318			ftmp[1] = get_unaligned_le16(&from[i + 1]);319		else320			ftmp[1] = 0;321		if (i + 2 < maxwords)322			ftmp[2] = get_unaligned_le16(&from[i + 2]);323		else324			ftmp[2] = 0;325 326		charlen = cifs_mapchar(tmp, ftmp, codepage, NO_MAP_UNI_RSVD);327		outlen += charlen;328	}329 330	return outlen;331}332 333/*334 * cifs_strndup_from_utf16 - copy a string from wire format to the local335 * codepage336 * @src - source string337 * @maxlen - don't walk past this many bytes in the source string338 * @is_unicode - is this a unicode string?339 * @codepage - destination codepage340 *341 * Take a string given by the server, convert it to the local codepage and342 * put it in a new buffer. Returns a pointer to the new string or NULL on343 * error.344 */345char *346cifs_strndup_from_utf16(const char *src, const int maxlen,347			const bool is_unicode, const struct nls_table *codepage)348{349	int len;350	char *dst;351 352	if (is_unicode) {353		len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage);354		len += nls_nullsize(codepage);355		dst = kmalloc(len, GFP_KERNEL);356		if (!dst)357			return NULL;358		cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage,359				NO_MAP_UNI_RSVD);360	} else {361		dst = kstrndup(src, maxlen, GFP_KERNEL);362	}363 364	return dst;365}366 367static __le16 convert_to_sfu_char(char src_char)368{369	__le16 dest_char;370 371	switch (src_char) {372	case ':':373		dest_char = cpu_to_le16(UNI_COLON);374		break;375	case '*':376		dest_char = cpu_to_le16(UNI_ASTERISK);377		break;378	case '?':379		dest_char = cpu_to_le16(UNI_QUESTION);380		break;381	case '<':382		dest_char = cpu_to_le16(UNI_LESSTHAN);383		break;384	case '>':385		dest_char = cpu_to_le16(UNI_GRTRTHAN);386		break;387	case '|':388		dest_char = cpu_to_le16(UNI_PIPE);389		break;390	default:391		dest_char = 0;392	}393 394	return dest_char;395}396 397static __le16 convert_to_sfm_char(char src_char, bool end_of_string)398{399	__le16 dest_char;400 401	if (src_char >= 0x01 && src_char <= 0x1F) {402		dest_char = cpu_to_le16(src_char + 0xF000);403		return dest_char;404	}405	switch (src_char) {406	case ':':407		dest_char = cpu_to_le16(SFM_COLON);408		break;409	case '"':410		dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);411		break;412	case '*':413		dest_char = cpu_to_le16(SFM_ASTERISK);414		break;415	case '?':416		dest_char = cpu_to_le16(SFM_QUESTION);417		break;418	case '<':419		dest_char = cpu_to_le16(SFM_LESSTHAN);420		break;421	case '>':422		dest_char = cpu_to_le16(SFM_GRTRTHAN);423		break;424	case '|':425		dest_char = cpu_to_le16(SFM_PIPE);426		break;427	case '.':428		if (end_of_string)429			dest_char = cpu_to_le16(SFM_PERIOD);430		else431			dest_char = 0;432		break;433	case ' ':434		if (end_of_string)435			dest_char = cpu_to_le16(SFM_SPACE);436		else437			dest_char = 0;438		break;439	default:440		dest_char = 0;441	}442 443	return dest_char;444}445 446/*447 * Convert 16 bit Unicode pathname to wire format from string in current code448 * page. Conversion may involve remapping up the six characters that are449 * only legal in POSIX-like OS (if they are present in the string). Path450 * names are little endian 16 bit Unicode on the wire451 */452int453cifsConvertToUTF16(__le16 *target, const char *source, int srclen,454		 const struct nls_table *cp, int map_chars)455{456	int i, charlen;457	int j = 0;458	char src_char;459	__le16 dst_char;460	wchar_t tmp;461	wchar_t *wchar_to;	/* UTF-16 */462	int ret;463	unicode_t u;464 465	if (map_chars == NO_MAP_UNI_RSVD)466		return cifs_strtoUTF16(target, source, PATH_MAX, cp);467 468	wchar_to = kzalloc(6, GFP_KERNEL);469 470	for (i = 0; i < srclen; j++) {471		src_char = source[i];472		charlen = 1;473 474		/* check if end of string */475		if (src_char == 0)476			goto ctoUTF16_out;477 478		/* see if we must remap this char */479		if (map_chars == SFU_MAP_UNI_RSVD)480			dst_char = convert_to_sfu_char(src_char);481		else if (map_chars == SFM_MAP_UNI_RSVD) {482			bool end_of_string;483 484			/**485			 * Remap spaces and periods found at the end of every486			 * component of the path. The special cases of '.' and487			 * '..' are need to be handled because of symlinks.488			 * They are treated as non-end-of-string to avoid489			 * remapping and breaking symlinks pointing to . or ..490			 **/491			if ((i == 0 || source[i-1] == '\\') &&492			    source[i] == '.' &&493			    (i == srclen-1 || source[i+1] == '\\'))494				end_of_string = false; /* "." case */495			else if (i >= 1 &&496				 (i == 1 || source[i-2] == '\\') &&497				 source[i-1] == '.' &&498				 source[i] == '.' &&499				 (i == srclen-1 || source[i+1] == '\\'))500				end_of_string = false; /* ".." case */501			else if ((i == srclen - 1) || (source[i+1] == '\\'))502				end_of_string = true;503			else504				end_of_string = false;505 506			dst_char = convert_to_sfm_char(src_char, end_of_string);507		} else508			dst_char = 0;509		/*510		 * FIXME: We can not handle remapping backslash (UNI_SLASH)511		 * until all the calls to build_path_from_dentry are modified,512		 * as they use backslash as separator.513		 */514		if (dst_char == 0) {515			charlen = cp->char2uni(source + i, srclen - i, &tmp);516			dst_char = cpu_to_le16(tmp);517 518			/*519			 * if no match, use question mark, which at least in520			 * some cases serves as wild card521			 */522			if (charlen > 0)523				goto ctoUTF16;524 525			/* convert SURROGATE_PAIR */526			if (strcmp(cp->charset, "utf8") || !wchar_to)527				goto unknown;528			if (*(source + i) & 0x80) {529				charlen = utf8_to_utf32(source + i, 6, &u);530				if (charlen < 0)531					goto unknown;532			} else533				goto unknown;534			ret  = utf8s_to_utf16s(source + i, charlen,535					       UTF16_LITTLE_ENDIAN,536					       wchar_to, 6);537			if (ret < 0)538				goto unknown;539 540			i += charlen;541			dst_char = cpu_to_le16(*wchar_to);542			if (charlen <= 3)543				/* 1-3bytes UTF-8 to 2bytes UTF-16 */544				put_unaligned(dst_char, &target[j]);545			else if (charlen == 4) {546				/* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16547				 * 7-8bytes UTF-8(IVS) divided to 2 UTF-16548				 *   (charlen=3+4 or 4+4) */549				put_unaligned(dst_char, &target[j]);550				dst_char = cpu_to_le16(*(wchar_to + 1));551				j++;552				put_unaligned(dst_char, &target[j]);553			} else if (charlen >= 5) {554				/* 5-6bytes UTF-8 to 6bytes UTF-16 */555				put_unaligned(dst_char, &target[j]);556				dst_char = cpu_to_le16(*(wchar_to + 1));557				j++;558				put_unaligned(dst_char, &target[j]);559				dst_char = cpu_to_le16(*(wchar_to + 2));560				j++;561				put_unaligned(dst_char, &target[j]);562			}563			continue;564 565unknown:566			dst_char = cpu_to_le16(0x003f);567			charlen = 1;568		}569 570ctoUTF16:571		/*572		 * character may take more than one byte in the source string,573		 * but will take exactly two bytes in the target string574		 */575		i += charlen;576		put_unaligned(dst_char, &target[j]);577	}578 579ctoUTF16_out:580	put_unaligned(0, &target[j]); /* Null terminate target unicode string */581	kfree(wchar_to);582	return j;583}584 585/*586 * cifs_local_to_utf16_bytes - how long will a string be after conversion?587 * @from - pointer to input string588 * @maxbytes - don't go past this many bytes of input string589 * @codepage - source codepage590 *591 * Walk a string and return the number of bytes that the string will592 * be after being converted to the given charset, not including any null593 * termination required. Don't walk past maxbytes in the source buffer.594 */595 596static int597cifs_local_to_utf16_bytes(const char *from, int len,598			  const struct nls_table *codepage)599{600	int charlen;601	int i;602	wchar_t wchar_to;603 604	for (i = 0; len && *from; i++, from += charlen, len -= charlen) {605		charlen = codepage->char2uni(from, len, &wchar_to);606		/* Failed conversion defaults to a question mark */607		if (charlen < 1)608			charlen = 1;609	}610	return 2 * i; /* UTF16 characters are two bytes */611}612 613/*614 * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage615 * @src - source string616 * @maxlen - don't walk past this many bytes in the source string617 * @utf16_len - the length of the allocated string in bytes (including null)618 * @cp - source codepage619 * @remap - map special chars620 *621 * Take a string convert it from the local codepage to UTF16 and622 * put it in a new buffer. Returns a pointer to the new string or NULL on623 * error.624 */625__le16 *626cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len,627		      const struct nls_table *cp, int remap)628{629	int len;630	__le16 *dst;631 632	len = cifs_local_to_utf16_bytes(src, maxlen, cp);633	len += 2; /* NULL */634	dst = kmalloc(len, GFP_KERNEL);635	if (!dst) {636		*utf16_len = 0;637		return NULL;638	}639	cifsConvertToUTF16(dst, src, strlen(src), cp, remap);640	*utf16_len = len;641	return dst;642}643