From ff6945279d45edd8f6b0a5ddb1ef16cecce3ea9c Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 20 Apr 2009 19:45:13 +0000 Subject: [CIFS] Make cifs_unlink consistent in checks for null inode Signed-off-by: Steve French --- fs/cifs/CHANGES | 6 ++++++ fs/cifs/cifsfs.h | 2 +- fs/cifs/inode.c | 21 +++++++++++++++------ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 9d1fb6ec8a5..1bf81813627 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,9 @@ +Version 1.58 +------------ +Guard against buffer overruns in various UCS-2 to UTF-8 string conversions +when the UTF-8 string is composed of unusually long (more than 4 byte) converted +characters. + Version 1.57 ------------ Improve support for multiple security contexts to the same server. We diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 77e190dc288..051b71cfdea 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -100,5 +100,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.57" +#define CIFS_VERSION "1.58" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f36b4e40e44..9c869a6dcba 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -962,13 +962,21 @@ undo_setattr: goto out_close; } + +/* + * If dentry->d_inode is null (usually meaning the cached dentry + * is a negative dentry) then we would attempt a standard SMB delete, but + * if that fails we can not attempt the fall back mechanisms on EACESS + * but will return the EACESS to the caller. Note that the VFS does not call + * unlink on negative dentries currently. + */ int cifs_unlink(struct inode *dir, struct dentry *dentry) { int rc = 0; int xid; char *full_path = NULL; struct inode *inode = dentry->d_inode; - struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifsInodeInfo *cifs_inode; struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *tcon = cifs_sb->tcon; @@ -1012,7 +1020,7 @@ psx_del_no_retry: rc = cifs_rename_pending_delete(full_path, dentry, xid); if (rc == 0) drop_nlink(inode); - } else if (rc == -EACCES && dosattr == 0) { + } else if ((rc == -EACCES) && (dosattr == 0) && inode) { attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); if (attrs == NULL) { rc = -ENOMEM; @@ -1020,7 +1028,8 @@ psx_del_no_retry: } /* try to reset dos attributes */ - origattr = cifsInode->cifsAttrs; + cifs_inode = CIFS_I(inode); + origattr = cifs_inode->cifsAttrs; if (origattr == 0) origattr |= ATTR_NORMAL; dosattr = origattr & ~ATTR_READONLY; @@ -1041,13 +1050,13 @@ psx_del_no_retry: out_reval: if (inode) { - cifsInode = CIFS_I(inode); - cifsInode->time = 0; /* will force revalidate to get info + cifs_inode = CIFS_I(inode); + cifs_inode->time = 0; /* will force revalidate to get info when needed */ inode->i_ctime = current_fs_time(sb); } dir->i_ctime = dir->i_mtime = current_fs_time(sb); - cifsInode = CIFS_I(dir); + cifs_inode = CIFS_I(dir); CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ kfree(full_path); -- cgit v1.2.3-18-g5258 From 968460ebd8006d55661dec0fb86712b40d71c413 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 20 Apr 2009 18:54:21 +0530 Subject: cifs: Rename cifs_strncpy_to_host and fix buffer size There is a possibility for the path_name and node_name buffers to overflow if they contain charcters that are >2 bytes in the local charset. Resize the buffer allocation so to avoid this possibility. Also, as pointed out by Jeff Layton, it would be appropriate to rename the function to cifs_strlcpy_to_host to reflect the fact that the copied string is always NULL terminated. Signed-off-by: Suresh Jayaraman Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a0845dc7b8a..a02c43b3faf 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -88,29 +88,29 @@ static struct { * on failure - errno */ static int -cifs_strncpy_to_host(char **dst, const char *src, const int maxlen, +cifs_strlcpy_to_host(char **dst, const char *src, const int maxlen, const bool is_unicode, const struct nls_table *nls_codepage) { int plen; if (is_unicode) { plen = UniStrnlen((wchar_t *)src, maxlen); - *dst = kmalloc(plen + 2, GFP_KERNEL); + *dst = kmalloc((4 * plen) + 2, GFP_KERNEL); if (!*dst) - goto cifs_strncpy_to_host_ErrExit; + goto cifs_strlcpy_to_host_ErrExit; cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage); + (*dst)[plen] = 0; + (*dst)[plen+1] = 0; /* needed for Unicode */ } else { plen = strnlen(src, maxlen); *dst = kmalloc(plen + 2, GFP_KERNEL); if (!*dst) - goto cifs_strncpy_to_host_ErrExit; - strncpy(*dst, src, plen); + goto cifs_strlcpy_to_host_ErrExit; + strlcpy(*dst, src, plen); } - (*dst)[plen] = 0; - (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */ return 0; -cifs_strncpy_to_host_ErrExit: +cifs_strlcpy_to_host_ErrExit: cERROR(1, ("Failed to allocate buffer for string\n")); return -ENOMEM; } @@ -4029,7 +4029,7 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, /* copy DfsPath */ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; - rc = cifs_strncpy_to_host(&(node->path_name), temp, + rc = cifs_strlcpy_to_host(&(node->path_name), temp, max_len, is_unicode, nls_codepage); if (rc) goto parse_DFS_referrals_exit; @@ -4037,7 +4037,7 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, /* copy link target UNC */ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; - rc = cifs_strncpy_to_host(&(node->node_name), temp, + rc = cifs_strlcpy_to_host(&(node->node_name), temp, max_len, is_unicode, nls_codepage); if (rc) goto parse_DFS_referrals_exit; -- cgit v1.2.3-18-g5258 From 7b0c8fcff47a885743125dd843db64af41af5a61 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 20 Apr 2009 18:54:36 +0530 Subject: cifs: Increase size of tmp_buf in cifs_readdir to avoid potential overflows Increase size of tmp_buf to possible maximum to avoid potential overflows. Pointed-out-by: Jeff Layton Signed-off-by: Suresh Jayaraman Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/readdir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 1a8be622833..ebd0da7ecb3 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -1074,7 +1074,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) with the rare long characters alloc more to account for such multibyte target UTF-8 characters. cifs_unicode.c, which actually does the conversion, has the same limit */ - tmp_buf = kmalloc((2 * NAME_MAX) + 4, GFP_KERNEL); + tmp_buf = kmalloc((4 * NAME_MAX) + 2, GFP_KERNEL); for (i = 0; (i < num_to_fill) && (rc == 0); i++) { if (current_entry == NULL) { /* evaluate whether this case is an error */ -- cgit v1.2.3-18-g5258 From d37dc42ab6f040b8f0f2962ab219c5b2accf748d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 06:45:08 -0400 Subject: nls: add a nls_nullsize inline It's possible for character sets to require a multi-byte null string terminator. Add a helper function that determines the size of the null terminator at runtime. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- include/linux/nls.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/linux/nls.h b/include/linux/nls.h index 6a882208301..52b1a76c1b4 100644 --- a/include/linux/nls.h +++ b/include/linux/nls.h @@ -58,6 +58,25 @@ static inline int nls_strnicmp(struct nls_table *t, const unsigned char *s1, return 0; } +/* + * nls_nullsize - return length of null character for codepage + * @codepage - codepage for which to return length of NULL terminator + * + * Since we can't guarantee that the null terminator will be a particular + * length, we have to check against the codepage. If there's a problem + * determining it, assume a single-byte NULL terminator. + */ +static inline int +nls_nullsize(const struct nls_table *codepage) +{ + int charlen; + char tmp[NLS_MAX_CHARSET_SIZE]; + + charlen = codepage->uni2char(0, tmp, NLS_MAX_CHARSET_SIZE); + + return charlen > 0 ? charlen : 1; +} + #define MODULE_ALIAS_NLS(name) MODULE_ALIAS("nls_" __stringify(name)) #endif /* _LINUX_NLS_H */ -- cgit v1.2.3-18-g5258 From 66345f50f070ae7412a28543ee197cb5eff73598 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 06:45:08 -0400 Subject: cifs: move #defines for mapchars into cifs_unicode.h Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifs_unicode.h | 13 +++++++++++++ fs/cifs/misc.c | 11 ----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 14eb9a2395d..d6fe8ecd1ff 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -37,6 +37,19 @@ #define UNIUPR_NOLOWER /* Example to not expand lower case tables */ +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERIK (__u16) ('*' + 0xF000) +#define UNI_QUESTION (__u16) ('?' + 0xF000) +#define UNI_COLON (__u16) (':' + 0xF000) +#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) +#define UNI_LESSTHAN (__u16) ('<' + 0xF000) +#define UNI_PIPE (__u16) ('|' + 0xF000) +#define UNI_SLASH (__u16) ('\\' + 0xF000) + /* Just define what we want from uniupr.h. We don't want to define the tables * in each source file. */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 4c89c572891..bb81c8af6a9 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -635,17 +635,6 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) return; } -/* Windows maps these to the user defined 16 bit Unicode range since they are - reserved symbols (along with \ and /), otherwise illegal to store - in filenames in NTFS */ -#define UNI_ASTERIK (__u16) ('*' + 0xF000) -#define UNI_QUESTION (__u16) ('?' + 0xF000) -#define UNI_COLON (__u16) (':' + 0xF000) -#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) -#define UNI_LESSTHAN (__u16) ('<' + 0xF000) -#define UNI_PIPE (__u16) ('|' + 0xF000) -#define UNI_SLASH (__u16) ('\\' + 0xF000) - /* Convert 16 bit Unicode pathname from wire format to string in current code page. Conversion may involve remapping up the seven characters that are only legal in POSIX-like OS (if they are present in the string). Path -- cgit v1.2.3-18-g5258 From 7fabf0c9479fef9fdb9528a5fbdb1cb744a744a4 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 06:46:15 -0400 Subject: cifs: add replacement for cifs_strtoUCS_le called cifs_from_ucs2 Add a replacement function for cifs_strtoUCS_le. cifs_from_ucs2 takes args for the source and destination length so that we can ensure that the function is confined within the intended buffers. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifs_unicode.h | 2 + 2 files changed, 126 insertions(+) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 7d75272a6b3..8389f359b03 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -25,6 +25,130 @@ #include "cifsglob.h" #include "cifs_debug.h" +/* + * cifs_mapchar - convert a little-endian char to proper char in codepage + * @target - where converted character should be copied + * @src_char - 2 byte little-endian source character + * @cp - codepage to which character should be converted + * @mapchar - should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + */ +static int +cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (le16_to_cpu(src_char)) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERIK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(le16_to_cpu(src_char), target, + NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + goto out; +} + +/* + * cifs_from_ucs2 - convert utf16le string to local charset + * @to - destination buffer + * @from - source buffer + * @tolen - destination buffer size (in bytes) + * @fromlen - source buffer size (in bytes) + * @codepage - codepage to which characters should be converted + * @mapchar - should characters be remapped according to the mapchars option? + * + * Convert a little-endian ucs2le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UCS-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + */ +int +cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords && from[i]; i++) { + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, from[i], codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], from[i], codepage, mapchar); + outlen += charlen; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + /* * NAME: cifs_strfromUCS() * diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index d6fe8ecd1ff..6aa6533e49f 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -72,6 +72,8 @@ extern struct UniCaseRange UniLowerRange[]; #endif /* UNIUPR_NOLOWER */ #ifdef __KERNEL__ +int cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar); int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *); int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); #endif -- cgit v1.2.3-18-g5258 From 69f801fcaa03be83d58c564f00913b7c172808e4 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 06:46:32 -0400 Subject: cifs: add new function to get unicode string length in bytes Working in units of words means we do a lot of unnecessary conversion back and forth. Standardize on bytes instead since that's more useful for allocating buffers and such. Also, remove hostlen_fromUCS since the new function has a similar purpose. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 31 +++++++++++++++++++++++++++++++ fs/cifs/cifs_unicode.h | 2 ++ fs/cifs/cifssmb.c | 25 ++----------------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 8389f359b03..614512573c6 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -25,6 +25,37 @@ #include "cifsglob.h" #include "cifs_debug.h" +/* + * cifs_ucs2_bytes - how long will a string be after conversion? + * @ucs - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - destination codepage + * + * Walk a ucs2le 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. + */ +int +cifs_ucs2_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + + for (i = 0; from[i] && i < maxwords; i++) { + charlen = codepage->uni2char(le16_to_cpu(from[i]), tmp, + NLS_MAX_CHARSET_SIZE); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; +} + /* * cifs_mapchar - convert a little-endian char to proper char in codepage * @target - where converted character should be copied diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 6aa6533e49f..1857f5ff933 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -74,6 +74,8 @@ extern struct UniCaseRange UniLowerRange[]; #ifdef __KERNEL__ int cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, const struct nls_table *codepage, bool mapchar); +int cifs_ucs2_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage); int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *); int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); #endif diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a02c43b3faf..cadacae46b8 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3928,27 +3928,6 @@ GetInodeNumOut: return rc; } -/* computes length of UCS string converted to host codepage - * @src: UCS string - * @maxlen: length of the input string in UCS characters - * (not in bytes) - * - * return: size of input string in host codepage - */ -static int hostlen_fromUCS(const __le16 *src, const int maxlen, - const struct nls_table *nls_codepage) { - int i; - int hostlen = 0; - char to[4]; - int charlen; - for (i = 0; (i < maxlen) && src[i]; ++i) { - charlen = nls_codepage->uni2char(le16_to_cpu(src[i]), - to, NLS_MAX_CHARSET_SIZE); - hostlen += charlen > 0 ? charlen : 1; - } - return hostlen; -} - /* parses DFS refferal V3 structure * caller is responsible for freeing target_nodes * returns: @@ -4016,8 +3995,8 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, GFP_KERNEL); cifsConvertToUCS((__le16 *) tmp, searchName, PATH_MAX, nls_codepage, remap); - node->path_consumed = hostlen_fromUCS(tmp, - le16_to_cpu(pSMBr->PathConsumed)/2, + node->path_consumed = cifs_ucs2_bytes(tmp, + le16_to_cpu(pSMBr->PathConsumed), nls_codepage); kfree(tmp); } else -- cgit v1.2.3-18-g5258 From 066ce6899484d9026acd6ba3a8dbbedb33d7ae1b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 07:16:14 -0400 Subject: cifs: rename cifs_strlcpy_to_host and make it use new functions Rename cifs_strlcpy_to_host to cifs_strndup since that better describes what this function really does. Then, convert it to use the new string conversion and measurement functions that work in units of bytes rather than wide chars. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 38 ++++++++++++++++++++++++++++++++++ fs/cifs/cifs_unicode.h | 2 ++ fs/cifs/cifssmb.c | 55 +++++++++++--------------------------------------- 3 files changed, 52 insertions(+), 43 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 614512573c6..2a879cff3a4 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -243,3 +243,41 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, return i; } +/* + * cifs_strndup - copy a string from wire format to the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @is_unicode - is this a unicode string? + * @codepage - destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +char * +cifs_strndup(const char *src, const int maxlen, const bool is_unicode, + const struct nls_table *codepage) +{ + int len; + char *dst; + + if (is_unicode) { + len = cifs_ucs2_bytes((__le16 *) src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return NULL; + cifs_from_ucs2(dst, (__le16 *) src, len, maxlen, codepage, + false); + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return NULL; + strlcpy(dst, src, len); + } + + return dst; +} + diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 1857f5ff933..e620f0b4220 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -78,6 +78,8 @@ int cifs_ucs2_bytes(const __le16 *from, int maxbytes, const struct nls_table *codepage); int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *); int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); +char *cifs_strndup(const char *src, const int maxlen, const bool is_unicode, + const struct nls_table *codepage); #endif /* diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index cadacae46b8..f15848374cf 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -81,41 +81,6 @@ static struct { #endif /* CONFIG_CIFS_WEAK_PW_HASH */ #endif /* CIFS_POSIX */ -/* Allocates buffer into dst and copies smb string from src to it. - * caller is responsible for freeing dst if function returned 0. - * returns: - * on success - 0 - * on failure - errno - */ -static int -cifs_strlcpy_to_host(char **dst, const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *nls_codepage) -{ - int plen; - - if (is_unicode) { - plen = UniStrnlen((wchar_t *)src, maxlen); - *dst = kmalloc((4 * plen) + 2, GFP_KERNEL); - if (!*dst) - goto cifs_strlcpy_to_host_ErrExit; - cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage); - (*dst)[plen] = 0; - (*dst)[plen+1] = 0; /* needed for Unicode */ - } else { - plen = strnlen(src, maxlen); - *dst = kmalloc(plen + 2, GFP_KERNEL); - if (!*dst) - goto cifs_strlcpy_to_host_ErrExit; - strlcpy(*dst, src, plen); - } - return 0; - -cifs_strlcpy_to_host_ErrExit: - cERROR(1, ("Failed to allocate buffer for string\n")); - return -ENOMEM; -} - - /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) @@ -4008,20 +3973,24 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, /* copy DfsPath */ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; - rc = cifs_strlcpy_to_host(&(node->path_name), temp, - max_len, is_unicode, nls_codepage); - if (rc) + node->path_name = cifs_strndup(temp, max_len, is_unicode, + nls_codepage); + if (IS_ERR(node->path_name)) { + rc = PTR_ERR(node->path_name); + node->path_name = NULL; goto parse_DFS_referrals_exit; + } /* copy link target UNC */ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; - rc = cifs_strlcpy_to_host(&(node->node_name), temp, - max_len, is_unicode, nls_codepage); - if (rc) + node->node_name = cifs_strndup(temp, max_len, is_unicode, + nls_codepage); + if (IS_ERR(node->node_name)) { + rc = PTR_ERR(node->node_name); + node->node_name = NULL; goto parse_DFS_referrals_exit; - - ref += le16_to_cpu(ref->Size); + } } parse_DFS_referrals_exit: -- cgit v1.2.3-18-g5258 From cc20c031bb067eb3280a1c4b5c42295093e24863 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 07:16:21 -0400 Subject: cifs: convert CIFSTCon to use new unicode helper functions Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/connect.c | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index bacdef1546b..e94d6b29af6 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3638,7 +3638,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, TCONX_RSP *pSMBr; unsigned char *bcc_ptr; int rc = 0; - int length; + int length, bytes_left; __u16 count; if (ses == NULL) @@ -3726,14 +3726,15 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, CIFS_STD_OP); - /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */ /* above now done in SendReceive */ if ((rc == 0) && (tcon != NULL)) { tcon->tidStatus = CifsGood; tcon->need_reconnect = false; tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); - length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); + bytes_left = BCC(smb_buffer_response); + length = strnlen(bcc_ptr, bytes_left - 2); + /* skip service field (NB: this field is always ASCII) */ if (length == 3) { if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && @@ -3748,39 +3749,17 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, } } bcc_ptr += length + 1; + bytes_left -= (length + 1); strncpy(tcon->treeName, tree, MAX_TREE_SIZE); - if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { - length = UniStrnlen((wchar_t *) bcc_ptr, 512); - if ((bcc_ptr + (2 * length)) - - pByteArea(smb_buffer_response) <= - BCC(smb_buffer_response)) { - kfree(tcon->nativeFileSystem); - tcon->nativeFileSystem = - kzalloc((4 * length) + 2, GFP_KERNEL); - if (tcon->nativeFileSystem) { - cifs_strfromUCS_le( - tcon->nativeFileSystem, - (__le16 *) bcc_ptr, - length, nls_codepage); - cFYI(1, ("nativeFileSystem=%s", - tcon->nativeFileSystem)); - } - } - /* else do not bother copying these information fields*/ - } else { - length = strnlen(bcc_ptr, 1024); - if ((bcc_ptr + length) - - pByteArea(smb_buffer_response) <= - BCC(smb_buffer_response)) { - kfree(tcon->nativeFileSystem); - tcon->nativeFileSystem = - kzalloc(length + 1, GFP_KERNEL); - if (tcon->nativeFileSystem) - strncpy(tcon->nativeFileSystem, bcc_ptr, - length); - } - /* else do not bother copying these information fields*/ - } + + /* mostly informational -- no need to fail on error here */ + tcon->nativeFileSystem = cifs_strndup(bcc_ptr, bytes_left, + smb_buffer->Flags2 & + SMBFLG2_UNICODE, + nls_codepage); + + cFYI(1, ("nativeFileSystem=%s", tcon->nativeFileSystem)); + if ((smb_buffer_response->WordCount == 3) || (smb_buffer_response->WordCount == 7)) /* field is in same location */ -- cgit v1.2.3-18-g5258 From 59140797c5817363087b0ffb46e6bb81a11fe0dc Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 07:16:21 -0400 Subject: cifs: fix session setup unicode string saving to use new unicode helpers ...and change decode_unicode_ssetup to be a void function. It never returns an actual error anyway. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/sess.c | 80 ++++++++++++++++------------------------------------------ 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index c652c73760d..93022dc9bab 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -277,12 +277,11 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, *pbcc_area = bcc_ptr; } -static int decode_unicode_ssetup(char **pbcc_area, int bleft, - struct cifsSesInfo *ses, - const struct nls_table *nls_cp) +static void +decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, + const struct nls_table *nls_cp) { - int rc = 0; - int words_left, len; + int len; char *data = *pbcc_area; cFYI(1, ("bleft %d", bleft)); @@ -300,63 +299,29 @@ static int decode_unicode_ssetup(char **pbcc_area, int bleft, ++bleft; } - words_left = bleft / 2; - - /* save off server operating system */ - len = UniStrnlen((wchar_t *) data, words_left); - - if (len >= words_left) - return rc; - kfree(ses->serverOS); - /* UTF-8 string will not grow more than four times as big as UCS-16 */ - ses->serverOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL); - if (ses->serverOS != NULL) { - cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp); - cFYI(1, ("serverOS=%s", ses->serverOS)); - } - data += 2 * (len + 1); - words_left -= len + 1; - - /* save off server network operating system */ - len = UniStrnlen((wchar_t *) data, words_left); - - if (len >= words_left) - return rc; + ses->serverOS = cifs_strndup(data, bleft, true, nls_cp); + cFYI(1, ("serverOS=%s", ses->serverOS)); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; kfree(ses->serverNOS); - ses->serverNOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL); - if (ses->serverNOS != NULL) { - cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len, - nls_cp); - cFYI(1, ("serverNOS=%s", ses->serverNOS)); - if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) { - cFYI(1, ("NT4 server")); - ses->flags |= CIFS_SES_NT4; - } - } - data += 2 * (len + 1); - words_left -= len + 1; - - /* save off server domain */ - len = UniStrnlen((wchar_t *) data, words_left); - - if (len > words_left) - return rc; + ses->serverNOS = cifs_strndup(data, bleft, true, nls_cp); + cFYI(1, ("serverNOS=%s", ses->serverNOS)); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; kfree(ses->serverDomain); - ses->serverDomain = kzalloc((4 * len) + 2, GFP_KERNEL); - if (ses->serverDomain != NULL) { - cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len, - nls_cp); - cFYI(1, ("serverDomain=%s", ses->serverDomain)); - } - data += 2 * (len + 1); - words_left -= len + 1; + ses->serverDomain = cifs_strndup(data, bleft, true, nls_cp); + cFYI(1, ("serverDomain=%s", ses->serverDomain)); - cFYI(1, ("words left: %d", words_left)); - - return rc; + return; } static int decode_ascii_ssetup(char **pbcc_area, int bleft, @@ -709,8 +674,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ++bcc_ptr; --bytes_remaining; } - rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining, - ses, nls_cp); + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); } else { rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); -- cgit v1.2.3-18-g5258 From 460b96960d1946914e50316ffeefe7b41dddce91 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 07:17:56 -0400 Subject: cifs: change CIFSSMBUnixQuerySymLink to use new helpers Change CIFSSMBUnixQuerySymLink to use the new unicode helper functions. Also change the calling conventions so that the allocation of the target name buffer is done in CIFSSMBUnixQuerySymLink rather than by the caller. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 3 +-- fs/cifs/cifssmb.c | 36 ++++++++++++++---------------------- fs/cifs/link.c | 22 +++++----------------- 3 files changed, 20 insertions(+), 41 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4167716d32f..7d54a5a4dd5 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -260,8 +260,7 @@ extern int CIFSUnixCreateSymLink(const int xid, const struct nls_table *nls_codepage); extern int CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - char *syminfo, const int buflen, + const unsigned char *searchName, char **syminfo, const struct nls_table *nls_codepage); extern int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f15848374cf..dfb8e391d53 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2382,8 +2382,7 @@ winCreateHardLinkRetry: int CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - char *symlinkinfo, const int buflen, + const unsigned char *searchName, char **symlinkinfo, const struct nls_table *nls_codepage) { /* SMB_QUERY_FILE_UNIX_LINK */ @@ -2393,6 +2392,7 @@ CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, int bytes_returned; int name_len; __u16 params, byte_count; + char *data_start; cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName)); @@ -2447,30 +2447,22 @@ querySymLinkRetry: /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ - rc = -EIO; /* bad smb */ + if (rc || (pSMBr->ByteCount < 2)) + rc = -EIO; else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + u16 count = le16_to_cpu(pSMBr->t2.DataCount); + + data_start = ((char *) &pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->t2.DataOffset); - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = UniStrnlen((wchar_t *) ((char *) - &pSMBr->hdr.Protocol + data_offset), - min_t(const int, buflen, count) / 2); /* BB FIXME investigate remapping reserved chars here */ - cifs_strfromUCS_le(symlinkinfo, - (__le16 *) ((char *)&pSMBr->hdr.Protocol - + data_offset), - name_len, nls_codepage); - } else { - strncpy(symlinkinfo, - (char *) &pSMBr->hdr.Protocol + - data_offset, - min_t(const int, buflen, count)); - } - symlinkinfo[buflen] = 0; - /* just in case so calling code does not go off the end of buffer */ + *symlinkinfo = cifs_strndup(data_start, count, + pSMBr->hdr.Flags2 & + SMBFLG2_UNICODE, + nls_codepage); + if (!symlinkinfo) + rc = -ENOMEM; } } cifs_buf_release(pSMB); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 63f644000ce..e17a092f43e 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -119,16 +119,11 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) full_path = build_path_from_dentry(direntry); if (!full_path) - goto out_no_free; + goto out; cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; - target_path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!target_path) { - target_path = ERR_PTR(-ENOMEM); - goto out; - } /* We could change this to: if (pTcon->unix_ext) @@ -138,8 +133,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) if (pTcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, - target_path, - PATH_MAX-1, + &target_path, cifs_sb->local_nls); else { /* BB add read reparse point symlink code here */ @@ -148,22 +142,16 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) /* BB Add MAC style xsymlink check here if enabled */ } - if (rc == 0) { - -/* BB Add special case check for Samba DFS symlinks */ - - target_path[PATH_MAX-1] = 0; - } else { + if (rc != 0) { kfree(target_path); target_path = ERR_PTR(rc); } -out: kfree(full_path); -out_no_free: +out: FreeXid(xid); nd_set_link(nd, target_path); - return NULL; /* No cookie */ + return NULL; } int -- cgit v1.2.3-18-g5258 From f58841666bc22e827ca0dcef7b71c7bc2758ce82 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 30 Apr 2009 07:18:00 -0400 Subject: cifs: change cifs_get_name_from_search_buf to use new unicode helper ...and remove cifs_convertUCSpath. There are no more callers. Also add a #define for the buffer used in the readdir path so that we don't have so many magic numbers floating around. Signed-off-by: Jeff Layton Acked-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 -- fs/cifs/misc.c | 60 ----------------------------------------------------- fs/cifs/readdir.c | 26 +++++++++++------------ 3 files changed, 13 insertions(+), 75 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7d54a5a4dd5..fae083930ee 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -306,8 +306,6 @@ extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 *inode_number, const struct nls_table *nls_codepage, int remap_special_chars); -extern int cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, - const struct nls_table *codepage); extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index bb81c8af6a9..e079a9190ec 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -635,66 +635,6 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) return; } -/* Convert 16 bit Unicode pathname from wire format to string in current code - page. Conversion may involve remapping up the seven characters that are - only legal in POSIX-like OS (if they are present in the string). Path - names are little endian 16 bit Unicode on the wire */ -int -cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, - const struct nls_table *cp) -{ - int i, j, len; - __u16 src_char; - - for (i = 0, j = 0; i < maxlen; i++) { - src_char = le16_to_cpu(source[i]); - switch (src_char) { - case 0: - goto cUCS_out; /* BB check this BB */ - case UNI_COLON: - target[j] = ':'; - break; - case UNI_ASTERIK: - target[j] = '*'; - break; - case UNI_QUESTION: - target[j] = '?'; - break; - /* BB We can not handle remapping slash until - all the calls to build_path_from_dentry - are modified, as they use slash as separator BB */ - /* case UNI_SLASH: - target[j] = '\\'; - break;*/ - case UNI_PIPE: - target[j] = '|'; - break; - case UNI_GRTRTHAN: - target[j] = '>'; - break; - case UNI_LESSTHAN: - target[j] = '<'; - break; - default: - len = cp->uni2char(src_char, &target[j], - NLS_MAX_CHARSET_SIZE); - if (len > 0) { - j += len; - continue; - } else { - target[j] = '?'; - } - } - j++; - /* make sure we do not overrun callers allocated temp buffer */ - if (j >= (2 * NAME_MAX)) - break; - } -cUCS_out: - target[j] = 0; - return j; -} - /* Convert 16 bit Unicode pathname to wire format from string in current code page. Conversion may involve remapping up the seven characters that are only legal in POSIX-like OS (if they are present in the string). Path diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index ebd0da7ecb3..e1351fe18a1 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -31,6 +31,13 @@ #include "cifs_fs_sb.h" #include "cifsfs.h" +/* + * To be safe - for UCS to UTF-8 with strings loaded with the rare long + * characters alloc more to account for such multibyte target UTF-8 + * characters. + */ +#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2) + #ifdef CONFIG_CIFS_DEBUG2 static void dump_cifs_file_struct(struct file *file, char *label) { @@ -881,14 +888,11 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst, } if (unicode) { - /* BB fixme - test with long names */ - /* Note converted filename can be longer than in unicode */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - pqst->len = cifs_convertUCSpath((char *)pqst->name, - (__le16 *)filename, len/2, nlt); - else - pqst->len = cifs_strfromUCS_le((char *)pqst->name, - (__le16 *)filename, len/2, nlt); + pqst->len = cifs_from_ucs2((char *) pqst->name, + (__le16 *) filename, + UNICODE_NAME_MAX, max_len, nlt, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); } else { pqst->name = filename; pqst->len = len; @@ -1070,11 +1074,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifsFile->srch_inf.ntwrk_buf_start); end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; - /* To be safe - for UCS to UTF-8 with strings loaded - with the rare long characters alloc more to account for - such multibyte target UTF-8 characters. cifs_unicode.c, - which actually does the conversion, has the same limit */ - tmp_buf = kmalloc((4 * NAME_MAX) + 2, GFP_KERNEL); + tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); for (i = 0; (i < num_to_fill) && (rc == 0); i++) { if (current_entry == NULL) { /* evaluate whether this case is an error */ -- cgit v1.2.3-18-g5258 From 20418acd6874792359b42c12d159f42f17593f34 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 30 Apr 2009 16:13:32 +0000 Subject: [CIFS] Remove older session setup implementation Two years ago, when the session setup code in cifs was rewritten and moved to fs/cifs/sess.c, we were asked to keep the old code for a release or so (which could be reenabled at runtime) since it was such a large change and because the asn (SPNEGO) and NTLMSSP code was not rewritten and needed to be. This was useful to avoid regressions, but is long overdue to be removed. Now that the Kerberos (asn/spnego) code is working in fs/cifs/sess.c, and the NTLMSSP code moved (NTLMSSP blob setup be rewritten with the next patch in this series) quite a bit of dead code from fs/cifs/connect.c now can be removed. This old code should have been removed last year, but the earlier krb5 patches did not move/remove the NTLMSSP code which we had asked to be done first. Since no one else volunteered, I am doing it now. It is extremely important that we continue to examine the documentation for this area, to make sure our code continues to be uptodate with changes since Windows 2003. Signed-off-by: Steve French --- fs/cifs/connect.c | 381 +----------------------------------------------------- 1 file changed, 7 insertions(+), 374 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e94d6b29af6..579a628d1e6 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2592,310 +2592,6 @@ out: return rc; } -static int -CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char session_key[CIFS_SESS_KEY_SIZE], - const struct nls_table *nls_codepage) -{ - struct smb_hdr *smb_buffer; - struct smb_hdr *smb_buffer_response; - SESSION_SETUP_ANDX *pSMB; - SESSION_SETUP_ANDX *pSMBr; - char *bcc_ptr; - char *user; - char *domain; - int rc = 0; - int remaining_words = 0; - int bytes_returned = 0; - int len; - __u32 capabilities; - __u16 count; - - cFYI(1, ("In sesssetup")); - if (ses == NULL) - return -EINVAL; - user = ses->userName; - domain = ses->domainName; - smb_buffer = cifs_buf_get(); - - if (smb_buffer == NULL) - return -ENOMEM; - - smb_buffer_response = smb_buffer; - pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer; - - /* send SMBsessionSetup here */ - header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, - NULL /* no tCon exists yet */ , 13 /* wct */ ); - - smb_buffer->Mid = GetNextMid(ses->server); - pSMB->req_no_secext.AndXCommand = 0xFF; - pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); - pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq); - - if (ses->server->secMode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | - CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - if (ses->capabilities & CAP_UNICODE) { - smb_buffer->Flags2 |= SMBFLG2_UNICODE; - capabilities |= CAP_UNICODE; - } - if (ses->capabilities & CAP_STATUS32) { - smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - capabilities |= CAP_STATUS32; - } - if (ses->capabilities & CAP_DFS) { - smb_buffer->Flags2 |= SMBFLG2_DFS; - capabilities |= CAP_DFS; - } - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - - pSMB->req_no_secext.CaseInsensitivePasswordLength = - cpu_to_le16(CIFS_SESS_KEY_SIZE); - - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_SESS_KEY_SIZE); - bcc_ptr = pByteArea(smb_buffer); - memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); - bcc_ptr += CIFS_SESS_KEY_SIZE; - memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); - bcc_ptr += CIFS_SESS_KEY_SIZE; - - if (ses->capabilities & CAP_UNICODE) { - if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */ - *bcc_ptr = 0; - bcc_ptr++; - } - if (user == NULL) - bytes_returned = 0; /* skip null user */ - else - bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, user, 100, - nls_codepage); - /* convert number of 16 bit words to bytes */ - bcc_ptr += 2 * bytes_returned; - bcc_ptr += 2; /* trailing null */ - if (domain == NULL) - bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, - "CIFS_LINUX_DOM", 32, nls_codepage); - else - bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64, - nls_codepage); - bcc_ptr += 2 * bytes_returned; - bcc_ptr += 2; - bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", - 32, nls_codepage); - bcc_ptr += 2 * bytes_returned; - bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, - 32, nls_codepage); - bcc_ptr += 2 * bytes_returned; - bcc_ptr += 2; - bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 64, nls_codepage); - bcc_ptr += 2 * bytes_returned; - bcc_ptr += 2; - } else { - if (user != NULL) { - strncpy(bcc_ptr, user, 200); - bcc_ptr += strnlen(user, 200); - } - *bcc_ptr = 0; - bcc_ptr++; - if (domain == NULL) { - strcpy(bcc_ptr, "CIFS_LINUX_DOM"); - bcc_ptr += strlen("CIFS_LINUX_DOM") + 1; - } else { - strncpy(bcc_ptr, domain, 64); - bcc_ptr += strnlen(domain, 64); - *bcc_ptr = 0; - bcc_ptr++; - } - strcpy(bcc_ptr, "Linux version "); - bcc_ptr += strlen("Linux version "); - strcpy(bcc_ptr, utsname()->release); - bcc_ptr += strlen(utsname()->release) + 1; - strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); - bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; - } - count = (long) bcc_ptr - (long) pByteArea(smb_buffer); - smb_buffer->smb_buf_length += count; - pSMB->req_no_secext.ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, - &bytes_returned, CIFS_LONG_OP); - if (rc) { -/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */ - } else if ((smb_buffer_response->WordCount == 3) - || (smb_buffer_response->WordCount == 4)) { - __u16 action = le16_to_cpu(pSMBr->resp.Action); - __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); - if (action & GUEST_LOGIN) - cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */ - ses->Suid = smb_buffer_response->Uid; /* UID left in wire format - (little endian) */ - cFYI(1, ("UID = %d ", ses->Suid)); - /* response can have either 3 or 4 word count - Samba sends 3 */ - bcc_ptr = pByteArea(smb_buffer_response); - if ((pSMBr->resp.hdr.WordCount == 3) - || ((pSMBr->resp.hdr.WordCount == 4) - && (blob_len < pSMBr->resp.ByteCount))) { - if (pSMBr->resp.hdr.WordCount == 4) - bcc_ptr += blob_len; - - if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { - if ((long) (bcc_ptr) % 2) { - remaining_words = - (BCC(smb_buffer_response) - 1) / 2; - /* Unicode strings must be word - aligned */ - bcc_ptr++; - } else { - remaining_words = - BCC(smb_buffer_response) / 2; - } - len = - UniStrnlen((wchar_t *) bcc_ptr, - remaining_words - 1); -/* We look for obvious messed up bcc or strings in response so we do not go off - the end since (at least) WIN2K and Windows XP have a major bug in not null - terminating last Unicode string in response */ - kfree(ses->serverOS); - ses->serverOS = kzalloc(2 * (len + 1), - GFP_KERNEL); - if (ses->serverOS == NULL) - goto sesssetup_nomem; - cifs_strfromUCS_le(ses->serverOS, - (__le16 *)bcc_ptr, - len, nls_codepage); - bcc_ptr += 2 * (len + 1); - remaining_words -= len + 1; - ses->serverOS[2 * len] = 0; - ses->serverOS[1 + (2 * len)] = 0; - if (remaining_words > 0) { - len = UniStrnlen((wchar_t *)bcc_ptr, - remaining_words-1); - kfree(ses->serverNOS); - ses->serverNOS = kzalloc(2 * (len + 1), - GFP_KERNEL); - if (ses->serverNOS == NULL) - goto sesssetup_nomem; - cifs_strfromUCS_le(ses->serverNOS, - (__le16 *)bcc_ptr, - len, nls_codepage); - bcc_ptr += 2 * (len + 1); - ses->serverNOS[2 * len] = 0; - ses->serverNOS[1 + (2 * len)] = 0; - if (strncmp(ses->serverNOS, - "NT LAN Manager 4", 16) == 0) { - cFYI(1, ("NT4 server")); - ses->flags |= CIFS_SES_NT4; - } - remaining_words -= len + 1; - if (remaining_words > 0) { - len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); - /* last string is not always null terminated - (for e.g. for Windows XP & 2000) */ - kfree(ses->serverDomain); - ses->serverDomain = - kzalloc(2*(len+1), - GFP_KERNEL); - if (ses->serverDomain == NULL) - goto sesssetup_nomem; - cifs_strfromUCS_le(ses->serverDomain, - (__le16 *)bcc_ptr, - len, nls_codepage); - bcc_ptr += 2 * (len + 1); - ses->serverDomain[2*len] = 0; - ses->serverDomain[1+(2*len)] = 0; - } else { /* else no more room so create - dummy domain string */ - kfree(ses->serverDomain); - ses->serverDomain = - kzalloc(2, GFP_KERNEL); - } - } else { /* no room so create dummy domain - and NOS string */ - - /* if these kcallocs fail not much we - can do, but better to not fail the - sesssetup itself */ - kfree(ses->serverDomain); - ses->serverDomain = - kzalloc(2, GFP_KERNEL); - kfree(ses->serverNOS); - ses->serverNOS = - kzalloc(2, GFP_KERNEL); - } - } else { /* ASCII */ - len = strnlen(bcc_ptr, 1024); - if (((long) bcc_ptr + len) - (long) - pByteArea(smb_buffer_response) - <= BCC(smb_buffer_response)) { - kfree(ses->serverOS); - ses->serverOS = kzalloc(len + 1, - GFP_KERNEL); - if (ses->serverOS == NULL) - goto sesssetup_nomem; - strncpy(ses->serverOS, bcc_ptr, len); - - bcc_ptr += len; - /* null terminate the string */ - bcc_ptr[0] = 0; - bcc_ptr++; - - len = strnlen(bcc_ptr, 1024); - kfree(ses->serverNOS); - ses->serverNOS = kzalloc(len + 1, - GFP_KERNEL); - if (ses->serverNOS == NULL) - goto sesssetup_nomem; - strncpy(ses->serverNOS, bcc_ptr, len); - bcc_ptr += len; - bcc_ptr[0] = 0; - bcc_ptr++; - - len = strnlen(bcc_ptr, 1024); - kfree(ses->serverDomain); - ses->serverDomain = kzalloc(len + 1, - GFP_KERNEL); - if (ses->serverDomain == NULL) - goto sesssetup_nomem; - strncpy(ses->serverDomain, bcc_ptr, - len); - bcc_ptr += len; - bcc_ptr[0] = 0; - bcc_ptr++; - } else - cFYI(1, - ("Variable field of length %d " - "extends beyond end of smb ", - len)); - } - } else { - cERROR(1, ("Security Blob Length extends beyond " - "end of SMB")); - } - } else { - cERROR(1, ("Invalid Word count %d: ", - smb_buffer_response->WordCount)); - rc = -EIO; - } -sesssetup_nomem: /* do not return an error on nomem for the info strings, - since that could make reconnection harder, and - reconnection might be needed to free memory */ - cifs_buf_release(smb_buffer); - - return rc; -} - static int CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, struct cifsSesInfo *ses, bool *pNTLMv2_flag, @@ -3229,6 +2925,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, return rc; } + static int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, char *ntlm_session_key, bool ntlmv2_flag, @@ -3831,83 +3528,19 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, pSesInfo->capabilities = server->capabilities; if (linuxExtEnabled == 0) pSesInfo->capabilities &= (~CAP_UNIX); - /* pSesInfo->sequence_number = 0;*/ + cFYI(1, ("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", server->secMode, server->capabilities, server->timeAdj)); - if (experimEnabled < 2) - rc = CIFS_SessSetup(xid, pSesInfo, first_time, nls_info); - else if (extended_security - && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) - && (server->secType == NTLMSSP)) { - rc = -EOPNOTSUPP; - } else if (extended_security - && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) - && (server->secType == RawNTLMSSP)) { - cFYI(1, ("NTLMSSP sesssetup")); - rc = CIFSNTLMSSPNegotiateSessSetup(xid, pSesInfo, &ntlmv2_flag, - nls_info); - if (!rc) { - if (ntlmv2_flag) { - char *v2_response; - cFYI(1, ("more secure NTLM ver2 hash")); - if (CalcNTLMv2_partial_mac_key(pSesInfo, - nls_info)) { - rc = -ENOMEM; - goto ss_err_exit; - } else - v2_response = kmalloc(16 + 64 /* blob*/, - GFP_KERNEL); - if (v2_response) { - CalcNTLMv2_response(pSesInfo, - v2_response); - /* if (first_time) - cifs_calculate_ntlmv2_mac_key */ - kfree(v2_response); - /* BB Put dummy sig in SessSetup PDU? */ - } else { - rc = -ENOMEM; - goto ss_err_exit; - } - - } else { - SMBNTencrypt(pSesInfo->password, - server->cryptKey, - ntlm_session_key); - - if (first_time) - cifs_calculate_mac_key( - &server->mac_signing_key, - ntlm_session_key, - pSesInfo->password); - } - /* for better security the weaker lanman hash not sent - in AuthSessSetup so we no longer calculate it */ - - rc = CIFSNTLMSSPAuthSessSetup(xid, pSesInfo, - ntlm_session_key, - ntlmv2_flag, - nls_info); - } - } else { /* old style NTLM 0.12 session setup */ - SMBNTencrypt(pSesInfo->password, server->cryptKey, - ntlm_session_key); - - if (first_time) - cifs_calculate_mac_key(&server->mac_signing_key, - ntlm_session_key, - pSesInfo->password); - - rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info); - } + rc = CIFS_SessSetup(xid, pSesInfo, first_time, nls_info); if (rc) { cERROR(1, ("Send error in SessSetup = %d", rc)); } else { cFYI(1, ("CIFS Session Established successfully")); - spin_lock(&GlobalMid_Lock); - pSesInfo->status = CifsGood; - pSesInfo->need_reconnect = false; - spin_unlock(&GlobalMid_Lock); + spin_lock(&GlobalMid_Lock); + pSesInfo->status = CifsGood; + pSesInfo->need_reconnect = false; + spin_unlock(&GlobalMid_Lock); } ss_err_exit: -- cgit v1.2.3-18-g5258 From 1af28ceb923d04357733642a3dbc4497da4db1c2 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 17 Mar 2009 19:00:30 +0300 Subject: Enable dfs submounts to handle remote referrals. Having remote dfs root support in cifs_mount, we can afford to pass into it UNC that is remote. Signed-off-by: Igor Mammedov Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 5fdbf8a1447..896b08fb1ec 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -341,27 +341,23 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) for (i = 0; i < num_referrals; i++) { dump_referral(referrals+i); - /* connect to a storage node */ - if (referrals[i].flags & DFSREF_STORAGE_SERVER) { - int len; - len = strlen(referrals[i].node_name); - if (len < 2) { - cERROR(1, ("%s: Net Address path too short: %s", + /* connect to a node */ + int len; + len = strlen(referrals[i].node_name); + if (len < 2) { + cERROR(1, ("%s: Net Address path too short: %s", __func__, referrals[i].node_name)); - rc = -EINVAL; - goto out_err; - } - mnt = cifs_dfs_do_refmount(nd->path.mnt, - nd->path.dentry, - referrals + i); - cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", - __func__, + rc = -EINVAL; + goto out_err; + } + mnt = cifs_dfs_do_refmount(nd->path.mnt, + nd->path.dentry, referrals + i); + cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, referrals[i].node_name, mnt)); - /* complete mount procedure if we accured submount */ - if (!IS_ERR(mnt)) - break; - } + /* complete mount procedure if we accured submount */ + if (!IS_ERR(mnt)) + break; } /* we need it cause for() above could exit without valid submount */ -- cgit v1.2.3-18-g5258 From 5c2503a8e339fbc82f49d5706c5a4ad650dd9711 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 21 Apr 2009 19:31:05 +0400 Subject: Added loop check when mounting DFS tree. Added loop check when mounting DFS tree. mount will fail with ELOOP if referral walks exceed MAX_NESTED_LINK count. Signed-off-by: Igor Mammedov Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 579a628d1e6..7e5d4fda493 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -2278,6 +2279,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, #ifdef CONFIG_CIFS_DFS_UPCALL struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; + int referral_walks_count = 0; try_mount_again: #endif full_path = NULL; @@ -2525,6 +2527,16 @@ remote_path_check: /* get referral if needed */ if (rc == -EREMOTE) { #ifdef CONFIG_CIFS_DFS_UPCALL + if (referral_walks_count > MAX_NESTED_LINKS) { + /* + * BB: when we implement proper loop detection, + * we will remove this check. But now we need it + * to prevent an indefinite loop if 'DFS tree' is + * misconfigured (i.e. has loops). + */ + rc = -ELOOP; + goto mount_fail_check; + } /* convert forward to back slashes in prepath here if needed */ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) convert_delimiter(cifs_sb->prepath, @@ -2558,6 +2570,7 @@ remote_path_check: cleanup_volume_info(&volume_info); FreeXid(xid); kfree(full_path); + referral_walks_count++; goto try_mount_again; } #else /* No DFS support, return error on mount */ -- cgit v1.2.3-18-g5258 From d185cda7712fd1d9e349174639d76eadc66679be Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 30 Apr 2009 17:45:10 +0000 Subject: [CIFS] rename cifs_strndup to cifs_strndup_from_ucs In most cases, cifs_strndup is converting from Unicode (UCS2 / UTF-32) to the configured local code page for the Linux mount (usually UTF8), so Jeff suggested that to make it more clear that cifs_strndup is doing a conversion not just memory allocation and copy, rename the function to including "from_ucs" (ie Unicode) Signed-off-by: Steve French --- fs/cifs/CHANGES | 6 +++++- fs/cifs/cifs_unicode.c | 6 +++--- fs/cifs/cifs_unicode.h | 7 ++++--- fs/cifs/cifssmb.c | 12 ++++++------ fs/cifs/connect.c | 5 +++-- fs/cifs/sess.c | 8 ++++---- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 1bf81813627..1b0643c2eac 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -2,7 +2,11 @@ Version 1.58 ------------ Guard against buffer overruns in various UCS-2 to UTF-8 string conversions when the UTF-8 string is composed of unusually long (more than 4 byte) converted -characters. +characters. Add support for mounting root of a share which redirects immediately +to DFS target. Convert string conversion functions from Unicode to more +accurately mark string length before allocating memory (which may help the +rare cases where a UTF-8 string is much larger than the UCS2 string that +we converted from). Version 1.57 ------------ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 2a879cff3a4..6382720acf7 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -1,7 +1,7 @@ /* * fs/cifs/cifs_unicode.c * - * Copyright (c) International Business Machines Corp., 2000,2005 + * Copyright (c) International Business Machines Corp., 2000,2009 * Modified by Steve French (sfrench@us.ibm.com) * * This program is free software; you can redistribute it and/or modify @@ -244,7 +244,7 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, } /* - * cifs_strndup - copy a string from wire format to the local codepage + * cifs_strndup_from_ucs - copy a string from wire format to the local codepage * @src - source string * @maxlen - don't walk past this many bytes in the source string * @is_unicode - is this a unicode string? @@ -255,7 +255,7 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, * error. */ char * -cifs_strndup(const char *src, const int maxlen, const bool is_unicode, +cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, const struct nls_table *codepage) { int len; diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index e620f0b4220..1570a701bf3 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -5,7 +5,7 @@ * Convert a unicode character to upper or lower case using * compressed tables. * - * Copyright (c) International Business Machines Corp., 2000,2007 + * Copyright (c) International Business Machines Corp., 2000,2009 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,8 +78,9 @@ int cifs_ucs2_bytes(const __le16 *from, int maxbytes, const struct nls_table *codepage); int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *); int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *