diff options
Diffstat (limited to 'fs/cifs/cifs_unicode.c')
| -rw-r--r-- | fs/cifs/cifs_unicode.c | 95 |
1 files changed, 89 insertions, 6 deletions
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index fbb9da95184..15e9505aa35 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -203,11 +203,32 @@ cifs_strtoUTF16(__le16 *to, const char *from, int len, int i; wchar_t wchar_to; /* needed to quiet sparse */ + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *) to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { charlen = codepage->char2uni(from, len, &wchar_to); if (charlen < 1) { - cERROR(1, "strtoUTF16: char2uni of 0x%x returned %d", - *from, charlen); + cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n", + *from, charlen); /* A question mark */ wchar_to = 0x003f; charlen = 1; @@ -215,6 +236,7 @@ cifs_strtoUTF16(__le16 *to, const char *from, int len, put_unaligned_le16(wchar_to, &to[i]); } +success: put_unaligned_le16(0, &to[i]); return i; } @@ -268,7 +290,8 @@ int cifsConvertToUTF16(__le16 *target, const char *source, int srclen, const struct nls_table *cp, int mapChars) { - int i, j, charlen; + int i, charlen; + int j = 0; char src_char; __le16 dst_char; wchar_t tmp; @@ -276,12 +299,11 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, if (!mapChars) return cifs_strtoUTF16(target, source, PATH_MAX, cp); - for (i = 0, j = 0; i < srclen; j++) { + for (i = 0; i < srclen; j++) { src_char = source[i]; charlen = 1; switch (src_char) { case 0: - put_unaligned(0, &target[j]); goto ctoUTF16_out; case ':': dst_char = cpu_to_le16(UNI_COLON); @@ -328,6 +350,67 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, } ctoUTF16_out: - return i; + put_unaligned(0, &target[j]); /* Null terminate target unicode string */ + return j; } +#ifdef CONFIG_CIFS_SMB2 +/* + * cifs_local_to_utf16_bytes - how long will a string be after conversion? + * @from - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - source codepage + * + * Walk a string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ + +static int +cifs_local_to_utf16_bytes(const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + /* Failed conversion defaults to a question mark */ + if (charlen < 1) + charlen = 1; + } + return 2 * i; /* UTF16 characters are two bytes */ +} + +/* + * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @utf16_len - the length of the allocated string in bytes (including null) + * @cp - source codepage + * @remap - map special chars + * + * Take a string convert it from the local codepage to UTF16 and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +__le16 * +cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, + const struct nls_table *cp, int remap) +{ + int len; + __le16 *dst; + + len = cifs_local_to_utf16_bytes(src, maxlen, cp); + len += 2; /* NULL */ + dst = kmalloc(len, GFP_KERNEL); + if (!dst) { + *utf16_len = 0; + return NULL; + } + cifsConvertToUTF16(dst, src, strlen(src), cp, remap); + *utf16_len = len; + return dst; +} +#endif /* CONFIG_CIFS_SMB2 */ |
